• home > webfront > ECMAS > typescript >

    vue2.x+vuex项目Typescript改造:vue + typescript学习笔记

    Author:[email protected] Date:

    vue+typescript,怎么看怎么怪异,感觉就是一个活脱脱的怪胎。vue相比react对typescript支持,很鸡肋。但是阻挡不了国人对vue的热爱。携vue以令我不得不审视vue+typescript

    Typescript+JSX语法改造项目

    本人最推荐的写法,就是直接React 化,typescript,

    import Vue, { VueConstructor, VNode } from 'vue';
    export default (Vue as VueConstructor<RadioInstance>).extend({
      name,
      inheritAttrs: false,
      model: {
        prop: 'checked',
        event: 'change',
      },
      inject: {
        radioGroup: { default: undefined },
      },
      props: {
        className: { type: String },
        checked: { type: Boolean, default: undefined },
        defaultChecked: { type: Boolean, default: undefined },
        disabled: { type: Boolean, default: undefined },
        value: { default: undefined },
        name: String,
      },
      render(): VNode {
        const { $attrs, $listeners, $scopedSlots, radioGroup } = this;
        return (
          <label>
            <input
              type="radio"
              onChange={this.handleChange}
            />
            <span >
              {name || null}
            </span>
          </label>
        ) as VNode;
      },
      methods: {
        handleChange(e: Event) {
          const target: HTMLInputElement = e.target as HTMLInputElement;
          this.$emit('change', target.checked);
          this.$emit('input', e);
        },
      },
    });

    这个,其实没有啥好说的,改编其实不是很大。


    vue-class-component 对 Vue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化:

    首先,使用单文件组件有以下要点:

    • 需要在 index.ts 中引入 .vue 单文件组件。

    • 使用 <script lang="ts"> 标签从而使用 TypeScript 编写脚本

    • 可以在 style 标签中编写相关的 CSS 样式,而在 .ts 组件中并不能编写样式。

    • 默认输出一个 Vue.extend 方法调用(而不是使用自动生成的)。

    在此使用了继承 Vue 得类和使用 @Component 装饰器来定义组件而不是使用 Vue.extend。 @Component 包含在 vue-class-component 包里面但是被 vue-property-decorator 重新输出。

    还有可以在属性前面添加 @Prop() 来定义一个 props 属性。 @Prop() 同样来自于 vue-property-deorator 包。

    所以项目需要vue-class-component vue-property-decorator加持才行

    • vue-class-component:强化 Vue 组件,使用 TypeScript/装饰器 增强 Vue 组件

    • vue-property-decorator:在 vue-class-component 上增强更多的结合 Vue 特性的装饰器


    import Vue from 'vue'
    import Component from 'vue-class-component'
    import { Getter, Action, Mutation }  from 'vuex-class';
    import { mapState } from 'vuex';
    import NavBar from '@/components/NavBar/index.vue';
    
    @Component({
      components: {
        NavBar,
      },
      computed: {
        ...mapState('user', ['userInfo', 'theme']),
        // ...mapGetters('user', ['determineLogin']),
      },
    })
    export default class App extends Vue {
    
      @Action('user/getUserInfo')
      getUserInfo
    
      @Mutation('user/setUserInfo')
      setUserInfo
      
      @Watch('theme', { immediate: true })
      handler(newValue: string) {
       this.$nextTick(() => {
        const root = document.documentElement || document.body;
        root.classList.add(`${newValue}-mode-box`);
       });
      }
      
      msg = 123  // 初始化数据
      mounted() {} // 声明周期钩子
      get computedMsg() {} // 计算属性
      greet() {} // 方法
    }

    vue-property-decorator 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器

    • @Prop

    • @PropSync

    • @Provide

    • @Model

    • @Watch

    • @Inject

    • @Provide

    • @Emit

    • @Component (provided by vue-class-component)

    • Mixins (the helper function named mixins provided by vue-class-component)

    一定要使用 @compoenet 去修饰这个组件,否则其它的装饰器无法正常使用。@component({option}) 中接收的参数 option 就是传统的配置,mixin 和子组件的注册都要在这里声明。

    在这里列举几个常用的@Prop/@Watch/@Component

    import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'
    
    @Component
    export class MyComponent extends Vue {
      @Watch('child')
      onChildChanged(val: string, oldVal: string) { }
      
      @Prop()
      propA: number = 1
    
      @Prop({ default: 'default value' })
      propB: string
    
      @Prop([String, Boolean])
      propC: string | boolean
    
      @Prop({ type: null })
      propD: any
      
      @Prop(Number) readonly propA: number | undefined
      @Prop({ default: 'default value' }) readonly propB!: string
      @Prop([String, Boolean]) readonly propC: string | boolean | undefined
    }

    typescript VS JavaScript

    了解了上面的基础知识,现在我将同一段代码分别使用js 和 ts来书写,现在我们来对比他们之间的差别。

    1. Props (Properties)

      使用js,我们有很多中方式来定义组件的 Props,但是大多都掺杂了 Vue 的私有特征,与 ES 格格不入,例如左边的代码,明明我们是把这个对象的 prop 属性定义成为了一个包含两个 string 元素的对象,但是我们却可以直接通过这个对象来访问 "name" 字段,这很明显是不符合 ES 语义的。

      再来看看右边的 TS 选手,通过 Prop 装饰器把指定的字段标记为了 Prop,既保留了 ES 语法的语义,而且还能与 Vue 完美的配合,更棒的是,我们可以在编码的过程中享受 TS 对 Prop 字段的静态类型检查。

    2. Method 和 data

      再来看看 Method,JS 中定义 method 还是有我们上面提到的那个不符合 ES 语义的毛病。而在 TS 中,method 不需要额外的装饰器——实例方法就会自动成为 Vue 组件的 method。类似的还有 data ,使用 TS 的语法,实例字段即可自动成为 Vue 组件的 data。

    3. Computed

      在传统的使用 JS 编写的 Vue 代码中,如果要定义计算属性,我们需要在 computed 属性中定义相应的函数。而这在 ES 中其实早就已经有了对应语义的语法——getter,所以在使用了 vue-class-component 的 vue 组件中,我们可以直接使用 getter 来定义计算属性,不管是在语法上还是在语义上,相比普通的 JS 都略胜一筹

    在Vue项目中定义data和props

    1. 为什么使用的写法 @Prop(Number!:) propA!: number,而不是@Prop(Number) propA: number?

      因为我们定一个Phone这个类,没有对phone、condition这些字段通过constructor进行初始化,所以需要在属性上使用 显式赋值断言来帮助类型系统识别类型,这样能够让属性会被间接地初始化。

    2. 为什么Prop需要前后写两次类型?

      前面括号里面的类型标注是Vue提供的类型检查。冒号后面的是为TS提供的类型标注。


    参考文章:

    vue + typescript 项目起手式 https://segmentfault.com/a/1190000011744210

    在Vue 中使用Typescript https://www.cnblogs.com/stone-lyl/p/9606917.html

    Vue.js 破浪——使用 Typescript 编写简单的单页面组件 ://blog.csdn.net/qq_17004327/article/details/78913830


    转载本站文章《vue2.x+vuex项目Typescript改造:vue + typescript学习笔记》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/typescript/2015_0513_5573.html