VUE 是前端三劍客之一, 以前一直處在寫過 demo 的地步, 沒有很深刻的體會, 此次公司由於招不到 react 的人, 因此但願嘗試將技術棧換爲 vue, 以便更方便招人.javascript
因而就有了 VUE 的踏坑之旅, 並且一開始就是配合 ts 來食用, 由於在 react 中 ts 的配合至關的好, jsx 裏的代碼類型提示應有盡有, 能夠很大的提高開發效率以及減小 bug. 覺得在 VUE 也能獲得這樣好的體驗.html
可是....前端
VUE 雖然也能夠支持 JSX, 官方仍是推薦使用模板渲染, 而模板裏壓根沒有代碼提示, 因此 ts 的功力就廢了一半(剩下的一半還在 script 裏), 具體的討論請參考vue
前戲差很少了, 開始步入正題.java
安裝 vue cli 3.x
這個腳手架能夠快速啓動項目 yarn global add @vue/cli
推薦全局安裝.node
在任意文件夾下 vue create .
. 會在當前文件夾中注入初始項目腳手架, 包含 webpack, babel, ts, lint, jest, jsdoc 等一整套模板react
使用 ts 會默認使用 vue-property-decorator
這個庫 github, 這個庫用裝飾器的寫法, 將不少 js 中繁雜的配置抽象出來, 一開始不習慣. 習慣了能夠大大提升效率的.linux
vue-property-decorator
的食用方法這一個是與另外一個 vue 的庫 vue-class-component同樣的用法. 這個裝飾器庫源自 class 庫, 只是再封裝了一層, 使代碼更爲簡潔明瞭. options 裏面須要配置 decorator 庫不支持的屬性, 哪些是不支持的呢? 那就請看徹底文, 凡是沒寫的都是不支持的. 好比components
, filters
, directives
等webpack
表示該組件引入了哪些子組件git
<template>
<div id="app">
<HelloWorld />
</div>
</template>
<script lang="ts"> @Component({ components: { HelloWorld, // 聲明子組件的引用 } }) export default class App extends Vue {} </script>
複製代碼
filter 表示對數據的篩選, 跟 linux 中的管道符十分類似, 數據經過 filter 進行處理變成新的數據.
注意, 在這裏配置時必定要使用 filters, 不要忘了 s, 不然不會報錯可是也沒做用.
<template>
<div>{{msg | addWorld}}</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>
複製代碼
具體的介紹能夠看 Vue 的官方介紹. 簡單來講就是 DOM 節點在一些特定鉤子觸發時添加一些額外的功能
鉤子函數有:
若是bind 和 update 時調用同樣能夠進行簡寫, 不用指定某個鉤子
鉤子函數參數:
v-demo="1+1"
爲 "demo"v-demo="1+1"
爲 2v-demo="1+1"
爲 "1+1"v-demo:foo
爲 'foo', 注意要在 modifier 前使用 arg, 否則會將 arg 做爲 modifier 的一部分, 如v-demo.a:foo
arg 爲 undefined, modifier 爲{'a:foo': true}
v-demo.a
這個值爲 {a:true}
看個簡單的實例:
<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>
複製代碼
父子組件傳遞數據 props的修飾符, 參數能夠傳
String, Number, Boolean
Invalid prop: type check failed for prop "xxx". Expected Function, got String with value "xxx".
@Prop() name = 1
來表示默認值 1, 雖然看起來同樣, 可是會在 console 裏報錯, 不容許修改 props 中的值[Vue warn]: Missing required prop: "xxx"
(value) => value > 100
父組件:
<template>
<div id="app">
<PropComponent :count='count' />
</div>
</template>
<script lang="ts"> @Component({ components: {PropComponent} }) 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>
複製代碼
與 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
綁定數據, 實時更新. 由讀者自行實現.
監聽屬性發生更改時被觸發. 可接受配置參數 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>
複製代碼
event?: string
, 若是沒有的話會自動將 camelCase 轉爲 dash-case 做爲事件名.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>
複製代碼
跟 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>
複製代碼
其本質是轉換爲 inject
和 provide
, 這是 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>
複製代碼
以上爲文檔中羅列的用法,之後項目過程當中遇到了別的會回來更新.
敬請指正.