Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

39. Vue 实战:vue-property-decorator #40

Open
hello-chinese opened this issue Sep 27, 2021 · 0 comments
Open

39. Vue 实战:vue-property-decorator #40

hello-chinese opened this issue Sep 27, 2021 · 0 comments
Labels
typescript学习 Improvements or additions to documentation

Comments

@hello-chinese
Copy link
Owner

Vue 实战:vue-property-decorator

我们打开项目,发现最大的不同是额外引入了一个库 vue-property-decorator

由于它的存在,我们可以使用基于类的注解装饰器进行开发,这个语法就有点像 Angular 了。

你可能听说过另外一个库 -- vue-class-component,它是 Vue 官方推出的一个支持使用 class 方式来开发 vue 单文件组件的库。

vue-property-decorator 正是基于此而来,它在此基础上增加了装饰器相关的功能,因此它也同时拥有 vue-class-component 的功能。

主要功能

vue-class-component 功能如下:

  • methods 可以直接声明为类的成员方法
  • 计算属性可以被声明为类的属性访问器
  • 初始化的 data 可以被声明为类属性
  • data、render 以及所有的 Vue 生命周期钩子可以直接作为类的成员方法
  • 所有其他属性,需要放在装饰器中

vue-property-decorator 主要提供了多个装饰器和一个函数:

vue-class-component 主要功能

@component

Component装饰器它注明了此类为一个Vue组件,因此即使没有设置选项也不能省略。如果需要定义比如 name、components、filters、directives以及自定义属性,就可以在Component装饰器中定义。

JavaScript 中的代码如下:

import {componentA,componentB} from '@/components';

export default{
components:{
componentA,
componentB,
},
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
}

在 TypeScript中如下:

import {Component,Vue} from 'vue-property-decorator';
import {componentA,componentB} from '@/components';

 @Component({
    components:{
        componentA,
        componentB,
    },
    directives: {
        focus: {
            // 指令的定义
            inserted: function (el) {
                el.focus()
            }
        }
    }
})
export default class YourCompoent extends Vue{
   
}

Computed、Data、Methods语法

这里取消了组件的data和methods属性,以往data返回对象中的属性、methods中的方法需要直接定义在Class中,当做类的属性和方法。

@Component
export default class HelloDecorator extends Vue {
    count: number = 123 // 类属性相当于以前的 data
add(): <span class="hljs-built_in">number</span> { <span class="hljs-comment">// 类方法就是以前的方法</span>
    <span class="hljs-keyword">this</span>.count + <span class="hljs-number">1</span>
}

<span class="hljs-comment">// 获取计算属性</span>
<span class="hljs-keyword">get</span> total(): <span class="hljs-built_in">number</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.count + <span class="hljs-number">1</span>
}

<span class="hljs-comment">// 设置计算属性</span>
<span class="hljs-keyword">set</span> total(param:<span class="hljs-built_in">number</span>): <span class="hljs-built_in">void</span> {
  <span class="hljs-keyword">this</span>.count = param
}

}

vue-property-decorator 主要 API 解读

@prop

这个装饰器是属性相关的装饰器, @Prop(options: (PropOptions | Constructor[] | Constructor) = {})

在使用Prop装饰器定义属性时,如果我们打开了 tsconfig.js 配置文件中的 strictpropertyinitialize 选项,我们就需要通过附加一个 ! 给定义的属性,还记得这个语法吗?显式复制断言。

我们看一下 JavaScript 版的一个 Vue 代码片段:

export default{
    props:{
        propA:String,
        propB:[String,Number],
        propC:{
            type:Array,
            default:()=>{
                return ['a','b']
            },
            required: true,
            validator:(value) => {
                return [
                    'a',
                    'b'
                 ].indexOf(value) !== -1
        }
    }
}
}

如果我们使用的是 TypeScript:


import {Component,Vue,Prop} from vue-property-decorator;

@Component
export default class YourComponent extends Vue {
@Prop(String)
propA:string;

<span class="hljs-meta">@Prop</span>([<span class="hljs-built_in">String</span>,<span class="hljs-built_in">Number</span>])
propB:<span class="hljs-built_in">string</span>|<span class="hljs-built_in">number</span>;

<span class="hljs-meta">@Prop</span>({
 <span class="hljs-keyword">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-comment">// type: [String , Number]</span>
 <span class="hljs-keyword">default</span>: <span class="hljs-string">'default value'</span>, <span class="hljs-comment">// 一般为String或Number</span>
  <span class="hljs-comment">//如果是对象或数组的话。默认值从一个工厂函数中返回</span>
  <span class="hljs-comment">// defatult: () =&gt; {</span>
  <span class="hljs-comment">//     return ['a','b']</span>
  <span class="hljs-comment">// }</span>
 required: <span class="hljs-literal">true</span>,
 validator: <span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> [
      <span class="hljs-string">'InProcess'</span>,
      <span class="hljs-string">'Settled'</span>
    ].indexOf(value) !== <span class="hljs-number">-1</span>
 }
})
propC:<span class="hljs-built_in">string</span>;

}

@watch

这个装饰器其实就是 Vue 中的侦听器。

@Watch(path: string, options: WatchOptions = {})

JavaScript 版 vue 中写法:

export default {
  watch: {
    child: [
      {
        handler: 'onChildChanged',
        immediate: false,
        deep: false
      }
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true,
        deep: true
      },
      {
        handler: 'onPersonChanged2',
        immediate: false,
        deep: false
      }
    ]
  },
  methods: {
    onChildChanged(val, oldVal) {},
    onPersonChanged1(val, oldVal) {},
    onPersonChanged2(val, oldVal) {}
  }
}

在 TypeScript 中:

import { Vue, Component, Watch } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
@Watch('child')
onChildChanged(val: string, oldVal: string) {}

@Watch('person', { immediate: true, deep: true })
onPersonChanged1(val: Person, oldVal: Person) {}

@Watch('person')
onPersonChanged2(val: Person, oldVal: Person) {}
}

使用了装饰器后明显代码简洁了不少。

@emit

关于 Vue 中的事件的监听与触发,Vue 提供了两个函数 $emit$on。那么在 vue-property-decorator 中如何使用呢?

这就需要用到 vue-property-decorator 提供的 @emit 装饰器。

在 JavaScript 中如下:

    import Vue from 'vue';
    export default {
        mounted(){
            this.$on('emit-todo', function(n) {
                console.log(n)
            })
            this.emitTodo('world');
        },
        methods: {
            emitTodo(n){
                console.log('hello');
                this.$emit('emit-todo', n);
            }
        }
    }

在 TypeScript 中如下:

   import {Vue, Component, Emit} from 'vue-property-decorator';
    @Component({})
    export default class Some extends Vue{
        mounted(){
            this.$on('emit-todo', function(n) {
                console.log(n)
            })
            this.emitTodo('world');
        }
            @Emit()
        emitTodo(n: string){
            console.log('hello');
        }
    }

@Emit 装饰器的函数会在运行之后触发等同于其函数名(驼峰式会转为横杠式写法)的事件, 并将其函数传递给 $emit. 如果我们想触发特定的事件呢,比如在 emitTodo 下触发 reset事件:

 import {Vue, Component, Emit} from 'vue-property-decorator';
    @Component({})
    export default class "组件名" extends Vue{
        @Emit('reset')
        emitTodo(n: string){
        }
    }

我们只需要给装饰器 @emit 传递一个事件名参数 reset,这样函数 emitTodo 运行之后就会触发 reset 事件.

在 Vue 中我们是使用 $emit 触发事件,使用 vue-property-decorator 时,可以借助 @emit 装饰器来实现。

@emit 修饰的函数所接受的参数会在运行之后触发事件的时候传递过去。

@emit触发事件有两种写法:

  • @Emit() 不传参数,那么它触发的事件名就是它所修饰的函数名
  • @Emit(name: string) 里面传递一个字符串,该字符串为要触发的事件名

@model

Vue 组件提供 model: {prop?: string, event?: string} 让我们可以定制 prop 和 event。

默认情况下,一个组件上的 v-model 会把 value 用作 prop且把 input用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop来达到不同的目的,使用model选项可以回避这些情况产生的冲突。

比如:

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    // this allows using the `value` prop for a different purpose
    value: String,
    // use `checked` as the prop which take the place of `value`
    checked: {
      type: Number,
      default: 0
    }
  },
  // ...
})

在模板中使用:

<my-checkbox v-model="foo" value="some value"></my-checkbox>

此模板相当于:

<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

使用 vue-property-decorator 提供的 @model 改造上面的例子:

import { Vue, Component, Model} from 'vue-property-decorator';
@Component
export class myCheck extends Vue{
   @Model ('change', {type: Boolean})  checked!: boolean;
}

@Model() 接收两个参数, 第一个是 event 值, 第二个是prop 的类型说明。

小结

我们通过本节学习了 TypeScript 版的 Vue Class 化的语法,要多处用到装饰器和 Class,这跟 Vue 原本的语法想去很远,但是其本质是一样的,我们可以进入实战来快速熟悉这些语法。

@hello-chinese hello-chinese added the typescript学习 Improvements or additions to documentation label Sep 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
typescript学习 Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

1 participant