vue-property-decorator知識梳理

倉庫地址:javascript

/* 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 庫不支持的屬性, 哪些是不支持的呢?html

那就請看徹底文, 凡是沒寫的都是不支持的. 好比components, filters, directivesvue

components

表示該組件引入了哪些子組件java

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

<script lang="ts">
@Component({
  components: {
    HelloWorld, // 聲明子組件的引用
  }
})
export default class App extends Vue {}
</script>
 

filters

filter 表示對數據的篩選, 跟 linux 中的管道符十分類似, 數據經過 filter 進行處理變成新的數據.node

注意, 在這裏配置時必定要使用 filters, 不要忘了 s, 不然不會報錯可是也沒做用.react

<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 節點在一些特定鉤子觸發時添加一些額外的功能linux

鉤子函數有:git

  • bind 指令綁定到元素時調用, 能夠進行一次性設置
  • inserted 綁定元素被插入到父節點時調用
  • update VNode 更新時調用
  • componentUpdated VNode 及子 VNode 所有更新後調用
  • unbind 元素解綁時調用

若是bind 和 update 時調用同樣能夠進行簡寫, 不用指定某個鉤子github

鉤子函數參數:express

  • 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>

 

 

歡迎關注公衆號,進一步技術交流:

相關文章
相關標籤/搜索