TypeScript

vue-property-decorator 使用

2020-03-28  本文已影响0人  修朋飞

@Component(options)

这一个是与另一个 vue 的库 vue-class-component 一样的用法. 这个装饰器库源自 class 库, 只是再封装了一层, 使代码更为简洁明了. options 里面需要配置 decorator 库不支持的属性, 哪些是不支持的呢?
那就请看完全文, 凡是没写的都是不支持的. 比如components, filters, directives等

表示该组件引入了哪些子组件

<template>
  <div id="app">
    <HelloWorld />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({
  components: {
    HelloWorld, // 声明子组件的引用
  }
})
export default class App extends Vue {}
</script>

filter 表示对数据的筛选, 跟 linux 中的管道符十分相似, 数据通过 filter 进行处理变成新的数据.注意, 在这里配置时一定要使用 filters, 不要忘了 s, 否则不会报错但是也没作用。

<template>
  <div>{{msg | filterA}}</div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({
  filters: {
    addWorld: (value: string) => `${value} world`,
  },
})
export default class App extends Vue {
  private msg = 'Hello' // filter 之后显示 hello world
}
</script>

具体的介绍可以看 Vue 的官方介绍 简单来说就是 DOM 节点在一些特定钩子触发时添加一些额外的功能。

钩子函数有:

如果bind 和 update 时调用一样可以进行简写, 不用指定某个钩子

钩子函数参数:

  1. el 对应绑定的 DOM

  2. binding

  1. vnode vue 的虚拟节点, 可参考源码查看可用属性

  2. oldVnode 上一个虚拟节点(update 和 componentUpdated 可用)

看个简单的实例:

<template>
  <span v-demo:foo.a="1+1">test</span>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({
  directives: {
    demo: {
      bind(el, binding, vnode) {
        console.log(`bindingName: ${binding.name}, value: ${binding.value}, args: ${binding.arg}, expression: ${binding.expression}`); // bindingName: demo, value: 2, args: foo, expression: 1+1
        console.log('modifier:', binding.modifiers); // {a:true}, 无法转为 primitive, 所以单独打印
      },
    },
    demoSimplify(el, binding, vnode) {
      // do stuff
    },
  },
})
export default class App extends Vue {}
</script>

@Prop()

父子组件传递数据 props的修饰符

参数可以传

父组件:

<template>
  <div id="app">
    <PropComponent :count="count" />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component
class Parent extends Vue {
  private count = 101;
}
</script>

子组件:

<template>
  <div>{{ count }}</div>
</template>

<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"

@Component
export default class PropsComponent extends Vue {
  // !表示有值, 否则 ts 会告警未初始化 }
  @Prop({
    type: Number,
    validator: value => {
      return value > 100
    },
    required: true
  })
  private count!: string
}
</script>

@PropSync() 与 Prop 的区别是子组件可以对 props 进行更改, 并同步给父组件,

子组件:

<template>
  <div>
    <p>{{count}}</p>
    <button @click="innerCount += 1">increment</button>
  </div>
</template>
<script lang="ts">
    import { Component, Vue, PropSync } from "vue-property-decorator"

    @Component
    export default class PropSyncComponent extends Vue {
      // 注意@PropSync 里的参数不能与定义的实例属性同名, 因为还是那个原理, props 是只读的
      @PropSync('count') private innerCount!: number
    }
</script>

父组件: 注意父组件里绑定 props 时需要加修饰符 .sync

<template>
  <PropSyncComponent :count.sync="count" />
</template> 

<script lang="ts">
    import { Component, Vue, PropSync } from "vue-property-decorator"

    @Component({ components: PropSyncComponent })

    export default class PropSyncComponent extends Vue {
      // 注意@PropSync 里的参数不能与定义的实例属性同名, 因为还是那个原理, props 是只读的.
      @PropSync('count') private innerCount!: number
    }
</script>

也可结合 input 元素的 v-model 绑定数据, 实时更新. 由读者自行实现.

@Watch监听属性发生更改时被触发.

可接受配置参数 options

<template>
  <div>
    <button @click="person.name.firstName = '修'">change deeper</button>
    <button @click="person.name = '修'">change deep</button>
  </div>
</template>
<script lang="ts">
    import { Component, Vue, Watch } from 'vue-property-decorator'

    @Component
    export default class PropSyncComponent extends Vue {  
      private person = { name: { firstName: '修' } }  
      @Watch('person', { deep: true })
        private firstNameChange(person: object, oldPerson: object) {   
        console.log(person, oldPerson)
      }
    } 
 </script>

@Emit

子组件:

<template>
  <div>
    <button @click="emitChange">Emit!!</button>
  </div>
</template>

<script lang="ts">
    import { Component, Vue, Emit } from "vue-property-decorator";

    @Component
    export default class EmitComponent extends Vue {
      private count = 0
      @Emit("button-click") private emitChange() {
        this.count += 1
        return this.count
      }
    }
</script>

父组件, 父组件的对应元素上绑定事件即可:

<template>
  <EmitComponent v-on:button-click="listenChange" />
</template>

<script lang="ts">
    import { Component, Vue } from "vue-property-decorator"

    @Component({ components: { EmitComponent } })
    export default class App extends Vue {
      private listenChange(value: number, event: any) {
        console.log(value, e)
      }
    }
</script>

@Ref 跟 react 中的一样, ref 是用于引用实际的 DOM 元素或者子组件.应尽可能避免直接使用, 但如果不得不用 ref 比 document 拿要方便很多, 参数传一个字符串refKey?:string, 注意这里如果省略传输参数, 那么会自动将属性名作为参数, 注意与@Emit的区别, @Emit在不传参数的情况下会转为 dash-case, 而 @Ref不会转, 为原属性名

<template>
  <div>
    <span>Name:</span>
    <input type="text" v-model="value" ref="name" />
  </div>
</template>
<script lang="ts"> 
    import { Component, Vue, Ref } from 'vue-property-decorator'

    @Component 
    export default class RefComponent extends Vue { 
      @Ref('name') readonly name!: string
      private value = '修'  
      private mounted() {   
        console.log(this.name)
      }
    }
</script>

@Provide/@inject && @ProvideReactive/@InjectReactive

其本质是转换为 inject 和 provide, 这是 vue 中元素向更深层的子组件传递数据的方式.两者需要一起使用.与 react 的 context 十分的像

任意代的子组件:

<template>
  <span>Inject deeper: {{ bar }}</span>
</template> 
<script lang="ts">
    import { Component, Vue, Inject } from "vue-property-decorator";

    @Component
    export default class InjectComponent extends Vue {
      @Inject()
      private bar!: string
      private mounted() {
        console.log(this.bar)
      }
    }
</script>

任意祖先元素:

<script lang="ts">
    import { Component, Vue, Provide } from "vue-property-decorator"
    export default class App extends Vue {
      @Provide()
      private bar = "deeper 修"
    }
</script>

方便很多, 如果为了避免命名冲突, 可以使用 ES6 的 Symbol 特性作为 key, 以祖先元素举例:

需要注意的是避免相互引用的问题, symbol 的引用最好放到组件外单独有个文件存起来.

export const s = Symbol()

父组件:

<script lang="ts">
    import { Component, Vue, Provide } from "vue-property-decorator"

    export const s = Symbol()

    @Component
    export default class App extends Vue {
      @Provide(s)
      private bar = "deeper 修"
    }
</script>

子组件:

<script lang="ts">
    import { Component, Vue, Inject } from "vue-property-decorator";

    import { s } from 'xxx'

    @Component
    export default class App extends Vue {
      @Inject(s) private baz = "deeper 修"
    }
</script>

@ProvideReactive/@InjectReactive 顾名思义就是响应式的注入, 会同步更新到子组件中.比如下例可以实现在 input 中的输入实时注入到子组件中 父组件

<template>
  <div id="app">
    <input type="text" v-model="bar" />
    <InjectComponent />
  </div>
</template> 
<script lang="ts">
    import { Component, Vue, ProvideReactive } from "vue-property-decorator"

    export const s = Symbol();

    @Component({ InjectComponent })
    export default class App extends Vue {
      @ProvideReactive(s) private bar = "deeper 修"
    }
</script>

子组件:

<script lang="ts">
   import { Component, Vue, InjectReactive } from "vue-property-decorator"

   @Component
   export default class InjectComponent extends Vue {
     @InjectReactive(s) private baz!: string
   }
</script>

@Model

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

默认情况下,一个组件上的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,
        checked: { // use `checked` as the prop which take the place of `value`
            type: Number, default: 0
        }
    },  // ... 
})
<template>
  <my-checkbox v-model="foo" value="some value"></my-checkbox>
</template>

上述代码相当于:

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

即foo双向绑定的是组件的checke, 触发双向绑定数值的事件是change

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

<script lang="ts">
    import { Vue, Component, Model } from "vue-property-decorator"
    @Component
    export class myCheck extends Vue {
      @Model("change", { type: Boolean }) checked!: boolean
    }
</script>
上一篇下一篇

猜你喜欢

热点阅读