• vue-property-decorator知识梳理


    仓库地址:

    /* npm 仓库地址 */
    // https://www.npmjs.com/package/vue-property-decorator
    
    /* github地址 */
    // https://github.com/kaorun343/vue-property-decorator

    @Component(options)

    这一个是与另一个 vue 的库 vue-class-component一样的用法. 这个装饰器库源自 class 库, 只是再封装了一层, 使代码更为简洁明了. options 里面需要配置 decorator 库不支持的属性, 哪些是不支持的呢?

    那就请看完全文, 凡是没写的都是不支持的. 比如components, filters, directives

    components

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

    <template>
      <div id="app">
        <HelloWorld />
      </div>
    </template>
    
    <script lang="ts">
    @Component({
      components: {
        HelloWorld, // 声明子组件的引用
      }
    })
    export default class App extends Vue {}
    </script>
     

    filters

    filter 表示对数据的筛选, 跟 linux 中的管道符十分相似, 数据通过 filter 进行处理变成新的数据.

    注意, 在这里配置时一定要使用 filters, 不要忘了 s, 否则不会报错但是也没作用.

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

    directives

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

    钩子函数有:

    • bind 指令绑定到元素时调用, 可以进行一次性设置
    • inserted 绑定元素被插入到父节点时调用
    • update VNode 更新时调用
    • componentUpdated VNode 及子 VNode 全部更新后调用
    • unbind 元素解绑时调用

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

    钩子函数参数:

    • el 对应绑定的 DOM
    • binding
      • name 指令名 v-demo="1+1" 为 "demo"
      • value 绑定的值 v-demo="1+1" 为 2
      • oldValue 在 update componentUpdated 可用
      • expression 字符串形式的表达式 v-demo="1+1" 为 "1+1"
      • arg 指令的参数 v-demo:foo 为 'foo', 注意要在 modifier 前使用 arg, 不然会将 arg 作为 modifier 的一部分, 如v-demo.a:foo arg 为 undefined, modifier 为{'a:foo': true}
      • modifiers 包含修饰符的对象 比如 v-demo.a 这个值为 {a:true}
    • vnode vue 的虚拟节点, 可参考源码查看可用属性
    • oldVnode 上一个虚拟节点(update 和 componentUpdated 可用)

    看个简单的实例:

    <template>
      <span v-demo:foo.a="1+1">test</span>
    </template>
    
    <script lang="ts">
    @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的修饰符, 参数可以传

    • Constructor 例如String, Number, Boolean
    • Constructor[], 构造函数的队列, 类型在这队列中即可
    • PropOptions
      • type 类型不对会报错 Invalid prop: type check failed for prop "xxx". Expected Function, got String with value "xxx".
      • default 如果父组件没有传的话为该值, 注意只能用这一种形式来表示默认值, 不能@Prop() name = 1来表示默认值 1, 虽然看起来一样, 但是会在 console 里报错, 不允许修改 props 中的值
      • required 没有会报错 [Vue warn]: Missing required prop: "xxx"
      • validator 为一个函数, 参数为传入的值, 比如(value) => value > 100

    父组件:

    <template>
      <div id="app">
        <PropComponent :count='count' />
      </div>
    </template>
    <script lang="ts">
    @Component
    class Parent extends Vue {
      private count = 101
    }
    </script>
     

    子组件:

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

    @PropSync()

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

    子组件:

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

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

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

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

    @Watch

    监听属性发生更改时被触发. 可接受配置参数 options

    • immediate?: boolean 是否在侦听开始之后立即调用该函数
    • deep?: boolean 是否深度监听.
    <template>
      <div>
        <button @click="innerName.name.firstName = 'lorry'">change deeper</button>
        <button @click="innerName.name = 'lorry'">change deep</button>
      </div>
    </template>
    <script lang="ts">
    @Component
    export default class PropSyncComponent extends Vue {
      private person = { name: { firstName: 'jiang' } }
    
      @Watch('person', {
        deep: true,
      })
      private firstNameChange(person: number, oldPerson:number) {
        console.log(`count change from${oldName.name.first}to: ${oldName.name.}`);
      }
    }
    </script>
     

    @Emit

    • 接受一个参数 event?: string, 如果没有的话会自动将 camelCase 转为 dash-case 作为事件名.
    • 会将函数的返回值作为回调函数的第二个参数, 如果是 Promise 对象,则回调函数会等 Promise resolve 掉之后触发.
    • 如果$emit 还有别的参数, 比如点击事件的 event , 会在返回值之后, 也就是第三个参数.

    子组件:

    <template>
      <div>
        <button @click="emitChange">Emit!!</button>
      </div>
    </template>
    
    <script lang="ts">
    @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">
    @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">
    @Component
    export default class RefComponent extends Vue {
      @Ref('name') readonly name!: string;
      private value = 'lorry'
      private mounted() {
        console.log(this.inputName); // <input type="text">
        // do stuff to ref
      }
    }
    </script>
     

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

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

    任意代的子组件:

    <template>
      <span>Inject deeper: {{bar}}</span>
    </template>
    
    <script lang="ts">
    @Component
    export default class InjectComponent extends Vue {
      @Inject() private bar!: string
    
      private mounted() {
        console.log(this.bar);
      }
    }
    </script>
     

    任意祖先元素:

    <script>
    export default class App extends Vue {
      @Provide() private bar = 'deeper lorry'
    }
    </script>
     

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

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

    export const s = Symbol()

    父组件:

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

    子组件:

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

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

    <template>
      <div id="app">
        <input type="text" v-model="bar">
        <InjectComponent />
      </div>
    </template>
    <script>
    @Component({
      InjectComponent
    })
    export default class App extends Vue {
      @ProvideReactive(s) private bar = 'deeper lorry'
    }
    </script>
     

    子组件:

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

    欢迎关注公众号,进一步技术交流:

  • 相关阅读:
    再也不用为word 中表达式的上标和下标发愁了
    创建链接
    ps钢笔工具隐藏的知识。
    学Ps个人遇到的小细节
    新手琢磨ps,学问深着呢。。
    数据库2012终于知道数据库攻击注入参数
    想脱离鼠标,不想要鼠标就只想用键盘完成所有编程,你说可能吗?
    vs2013中的快捷键
    如何在C/C++中动态分配二维数组【转载】
    转载:C++的那些事:表达式与语句
  • 原文地址:https://www.cnblogs.com/cczlovexw/p/11444984.html
Copyright © 2020-2023  润新知