以下內容是我在學習和研究Vue時,對Vue的特性、重點和注意事項的提取、精練和總結,可以做為Vue特性的字典;
1. 性能優化:
相對于計算屬性computed、方法methods、觀察watch,優先考慮使用計算屬性實現,因為計算屬性會緩存它的值,只有當計算屬性的相關依賴發生改變時才會重新求值;
盡可能最大化地讓Vue復用可用的元素,即,在用key管理可利用元素的地方看是否有其它更快速的替代方案;例如:對于僅展示數據不一樣的列表項,盡量設置相同的 key ,以便其重用;
適當地選擇v-if和v-show,當元素被渲染后,需要經常被切換時,使用v-show;否則,使用v-if;
對于不會變化的組件或元素,盡量使用v-once指令以使其路過重新渲染;
對于僅使用JavaScript鉤子進行過渡和動畫的元素,盡量添加
v-bind:css="false"
,這樣Vue就會跳過css的檢測;從而提高性能;對于界面布局相同的路由組件,盡可能用同一個路由組件通過路由參數來實現與渲染多個路由組件的效果,因為這不需要銷毀再創建路由組件;
-
如果一個組件不需要生命周期鉤子函數,則優先使用 函數式組件 ;比如下列使用場景:
- 程序化地在多個組件中選擇一個;
- 在將 children, props, data 傳遞給子組件之前操作它們;
-
在使用 DOM 內模板或 JavaScript 內的字符串模板時,模板會在運行時被編譯為渲染函數;為了減少運行時開銷,可以通過打包工具
webpack
直接把模板編譯為渲染函數;
注意:- 當使用單文件組件時,腳手架的相關的構建設置會自動把預編譯處理好,所以構建好的代碼已經包含了編譯出來的渲染函數而不是原始的模板字符串;
- 當分離 JavaScript 和模板文件時,可以為 webpack 設置
vue-template-loader
,它也可以在構建過程中把模板文件轉換成為 JavaScript 渲染函數;
當使用單文件組件時,組件內的 CSS 會以
<style>
標簽的方式通過 JavaScript 動態注入,這有一些小小的運行時開銷,如果你使用服務端渲染,這也會導致一段“無樣式內容閃爍 (fouc)”。將所有組件的 CSS 提取到同一個文件可以避免這個問題,也會讓 CSS 更好地進行壓縮和緩存;在使用單文件組件時,考慮到瀏覽器渲染各種 CSS 選擇器的方式,當 p { color: red } 設置了作用域時 (即與特性選擇器組合使用時) 會慢很多倍。如果你使用 class 或者 id 取而代之,比如 .example { color: red },性能影響就會消除;
盡量給不含轉換、編譯的節點加上
v-pre
指令,以告訴編譯器不需要編譯相應節點及其子節點,從而提交編譯速度;對于頻繁創建和銷毀的組件嘗試用
<keep-alive>
組件包裹;
2. v-if和v-show
v-if支持v-else和v-else-if語法,也支持< template>語法;v-show不支持這些;
v-show是通過簡單地切換元素的CSS屬性display屬性來實現顯示隱藏效果;
v-if 是“真正的”條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建;
v-if 也是惰性的:如果在初始渲染時條件為假,則什么也不做——直到條件第一次變為真時,才會開始渲染條件塊;
相比之下, v-show 就簡單得多——不管初始條件是什么,元素總是會被渲染,并且只是簡單地基于 CSS 進行切換;
也可以使用key標簽屬性來實現元素的切換顯示;
3. 支持< template>的指令
v-if、v-else、v-else-if、v-for,等結構性指令;
4. 模板語法
- 雙大括號
{{表達式}}
語法會將表達式的值作為純文本,而非 HTML;若要輸出真正的 HTML ,需要使用 v-html 指令; - 雙大括號
{{表達式}}
語法不能作用在 標簽屬性上;若要作用在標簽屬性上,應該使用 v-bind 指令; - 插值模板表達式只能是單個表達式;
- 插值表達式是被放在沙盒中執行的,只能訪問全局變量的一個白名單,如 Math 和 Date 。不應該在模板表達式中試圖訪問用戶定義的全局變量;
5. 組件
- Vue.component的主要功能是注冊組件,不是創建組件;
- 所有的 Vue.js 組件其實都是被擴展的 Vue 實例;
- 使用Vue.component注冊組件時,創建Vue實例的代碼必須在注冊組件的代碼的后面,否則注冊的組件不會被顯示;
- Vue.component(id,[definition])的參數id是字符串類型,它用于定義組件的標簽名,所以不能通過傳入id選擇器、類選擇器、屬性選擇器等等來定義組件;
6. Vue特性
當 v-if 與 v-for 一起使用時,v-for 具有比 v-if 更高的優先級。
事件可以以鏈式的方式添加多個事件修飾符,修飾符會按照修飾的順序起作用;
在vue中,雙大括號{{}}語法不能用在標簽屬性中;
在textarea標簽中使用雙大括號
{{}}
語法并不會生效,應用v-model來實事插值;在Vue中,對于所有的數據綁定,都支持JavaScripot表達式,這些表達式會在所屬 Vue 實例的數據作用域下作為 JavaScript 被解析。有個限制就是,每個綁定都只能包含單個表達式;
當使用DOM 作為模版時 (例如,將 el 選項掛載到一個已存在的元素上), 會受到 HTML 的一些限制,因為 Vue 只有在瀏覽器解析和標準化 HTML 后才能獲取模版內容,所以,如果DOM模版中包含了一些非法的標簽,則將會被瀏覽器移除掉;
如果需要在某個組件的根元素上監聽一個原生事件,可以使用 .native 修飾 v-on綁定的事件;
只能在字符串模板中使用自動閉合的組件名標簽,因為對瀏覽器來說,自動閉合的自定義元素是無效的HTML標簽;
Vue 不允許在已經創建的實例上動態添加新的根級響應式屬性(root-level reactive property);
當你設置 vm.someData = 'new value' ,該組件不會立即重新渲染。當刷新隊列時,組件會在事件循環隊列清空時的下一個“tick”更新;
如果同時設置了transitionend 和animationend ,則需要使用transition標簽的 type 標簽屬性并設置 animation 或 transition 來明確聲明你需要 Vue 監聽的類型;
使用 FLIP 過渡的元素不能設置為 display: inline 。作為替代方案,可以設置為 display: inline-block 或者放置于 flex 中;
當 v-bind:style 使用需要特定前綴的 CSS 屬性時,如 transform,Vue.js 會自動偵測并添加相應的前綴;
7. 當創建組件時,data選項應是是函數類型的原因
選項data可以是Object類型,也可以是Function類型,當data是Function類型時,data函數必須返回一個Object實例作為真正的data選項的值;
但在定義組件時,選項data最好應該是函數類型,因為,如果data是對象類型,則當此組件需要被用來創建多個實例時,則所有的實例將都引用同一個data實例;然而,如果將data定義為函數類型,并在函數里面返回新創建的對象,則每當此組件創建實例時,就會引用 data函數返回的全新的對象,從而不會使此組件的實例共享同一個data對象;
8. 指令
指令(Directives)是帶有 v- 前綴的特殊標簽屬性。指令屬性的值預期是單一 JavaScript 表達式(除了 v-for)。指令的職責就是當其表達式的值改變時相應地將某些行為應用到 DOM 上。
- 參數:一些指令能接受一個“參數”,在指令后以冒號指明;
- 修飾符:修飾符(Modifiers)是以半角句號
.
指明的特殊后綴,用于指出一個指令應該以特殊方式綁定; -
v-bind:
可以簡寫為:
-
v-on:
可以簡寫為@
-
:
與@
對于標簽的特性名來說都是合法字符,在所有支持 Vue.js 的瀏覽器都能被正確地解析。而且,它們不會出現在最終渲染的標記中。縮寫語法是完全可選的;
9. 過濾器
Vue允許你自定義過濾器,可被用作一些常見的文本格式化。過濾器可以用在兩個地方:{{}}
插值和 v-bind 表達式。過濾器應該被添加在 JavaScript 表達式的尾部,由管道符|
指示開始位置;
10. 選項:template
- 類型:string
- 說明:
一個字符串模板作為 Vue 實例的標識使用。模板將會 替換 掛載的元素。掛載元素的內容都將被忽略,除非模板的內容有分發 slot。
如果值以 # 開始,則它會被當作ID選擇器,將使用匹配元素的 innerHTML 作為模板。
如果選項中不包含template,則Vue實例將使用被掛載的元素的outerHTML作為template;
如果選項中包含 render 函數,template 選項將被忽略。
11. 選項:data
- 類型:Object | Function
- 限制:組件的定義只接受 function。
- 說明:
Vue 實例的數據對象。Vue 將會遞歸將 data 的屬性轉換為 getter/setter,從而讓 data 的屬性能夠響應數據變化。對象必須是純粹的對象 (含有零個或多個的 key/value 對):瀏覽器 API 創建的原生對象,原型上的屬性會被忽略。大概來說,data 應該只能是數據 - 不推薦觀察擁有狀態行為的對象。
一旦觀察過,不需要再次在數據對象上添加響應式屬性。因此推薦在創建實例之前,就聲明所有的根級響應式屬性。
實例創建之后,可以通過 vm.$data 訪問原始數據對象。Vue 實例也代理了 data 對象上所有的屬性,因此訪問 vm.a 等價于訪問 vm.$data.a。
以 _ 或 $ 開頭的屬性 不會 被 Vue 實例代理,因為它們可能和 Vue 內置的屬性、API 方法沖突。你可以使用例如 vm.$data._property 的方式訪問這些屬性。
12. 選項:computed
- 類型:{ [key: string]: Function | { get: Function, set: Function } }
- 說明:
計算屬性將被混入到 Vue 實例中。所有 getter 和 setter 的 this 上下文自動地綁定為 Vue 實例。
計算屬性的結果會被緩存,除非依賴的響應式屬性變化才會重新計算。注意,如果實例范疇之外的依賴 (比如非響應式的 not reactive) 是不會觸發計算屬性更新的。
注意: 不應該使用箭頭函數來定義計算屬性函數 (例如 aDouble: () => this.a * 2)。理由是箭頭函數綁定了父級作用域的上下文,所以 this 將不會按照期望指向 Vue 實例,this.a 將是 undefined。
13. 選項:watch
- 類型: { [key: string]: string | Function | Object }
- 說明:
一個對象,鍵是需要觀察的表達式,值是對應回調函數。值也可以是方法名,或者包含選項的對象。Vue 實例將會在實例化時調用 $watch(),遍歷 watch 對象的每一個屬性。
14. 標簽屬性key的理解
Vue 為了盡可能高效地渲染元素,通常會復用已有元素而不是重新創建元素;為了不讓vue復用已有的元素,可以給元素加個標簽屬性key
,并給該標簽屬性賦一個字符串類型的值;我認為這個標簽屬性key
可以理解為IOS中的重用標識符;
15. 數組
由于 JavaScript 的限制, Vue 不能檢測以下變動的數組:
- 當你利用索引直接設置一個項時,例如:
vm.items[indexOfItem] = newValue
- 當你修改數組的長度時,例如:
vm.items.length = newLength
為了解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue
相同的效果, 同時也將觸發狀態更新:
方法1:
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
方法2:
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
為了解決第二類問題,可以使用splice
:
example1.items.splice(newLength)
16. 對象更改檢測注意事項
- 由于 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除;
- 對于已經創建的實例,Vue 不能動態添加根級別的響應式屬性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套對象添加響應式屬性;
17. 事件
所有的 Vue.js 事件處理方法和表達式都嚴格綁定在當前視圖的ViewModel上;當一個 ViewModel 被銷毀時,所有的事件處理器都會自動被刪除;
可以通過Vue.config.keyCodes
對象自定義鍵值修飾符別名,格式如下:
// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112
或者:
Vue.config.keyCodes = {
v: 86,
f1: 112,
// camelCase 不可用
mediaPlayPause: 179,
// 取而代之的是 kebab-case 且用雙引號括起來
"media-play-pause": 179,
up: [38, 87]
}
18. 雙向綁定v-model指令
- v-model 會忽略所有表單元素的 value、checked、selected 特性的初始值。因為它會選擇 Vue 實例數據來作為具體的值。所以應該通過 JavaScript 在組件的 data 選項中聲明初始值。
- 當在select元素中使用v-model時,如果 v-model 表達初始的值不匹配任何的選項,select 元素就會以”未選中”的狀態渲染。
19. props
- 在使用組件時,若需要通過標簽屬性給子組件傳數據,則需要先顯式地在 props 選項聲明;
- prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,但是不會反過來。
- 在JavaScript 中對象是引用類型,如果 prop 是一個對象,在子組件內部改變它的屬性值會影響父組件的狀態;
- props 會在組件實例創建之前進行校驗,所以在 default 或 validator 函數里,諸如 data、computed 或 methods 等實例屬性還無法使用。
20. Vue中實現雙向綁定的指令
Vue中實現雙向綁定的所有方式都是一個語法糖,本質都是通過v-bind指令 和 v-on指令實現的;
它們的具體實現方式如下:
-
修飾符
.sync
示例:< comp v-bind:foo.sync="bar"></comp>
等效語法:
< comp :foo="bar" @update:foo="val => bar = val"></comp>
當子組件需要更新 foo 的值時,它需要顯式地觸發一個更新事件:
this.$emit('update:foo', newValue)
-
指令
v-model
- 當
v-model
用于原生input元素時,有如下等效:
示例:
< input v-model="something">
等效語法:
< input v-bind:value="something" v-on:input="something = $event.target.value">
- 當
v-model
用于自定義組件時,有如下等效:
示例:
< my-component v-model="something">
等效語法:
< my-component v-bind:value="something" v-on:input="something = arguments[0]">< /my-component>
所以,如果要在組件上使用v-model,則組件應該具備以下條件:
- 有一個名字是value的prop;
- 當需要向父組件更新新值時,需要通過觸發名字為input的事件傳值;
- 更改組件中
v-model
默認綁定的prop和事件
默認情況下,一個組件的 v-model 會使用 value 屬性和 input 事件,不過,可以通過選項model
更改v-model默認綁定的prop和事件;
示例如下:
Vue.my-component('my-component', { props: { checked: Boolean }, model: { prop: 'checked', event: 'change' } })
然后,可以如下使用v-model:
< my-component v-model="foo" value="some value">< /my-component>
等效語法:
< my-component :checked="foo" @change="val => { foo = val }">< /my-component>
- 當
21. 內容分發
- 如果組件模板中沒有包含slot插口,則在使用組件時,組件標簽內的內容將被丟棄;
- 如果組件模板只有一個沒有屬性的slot,則在使用組件時,組件標簽內的內容都將插入slot所在的DOM位置,并替換掉slot標簽本身;
- 組件模版中slot標簽中的任何內容都被視為備用內容;備用內容在子組件的作用域內編譯;并有只有沒有要插入的內容時,才會顯示備用內容;
- 可以通過配置slot的name標簽屬性來分發內容;仍然可以有一個匿名 slot,它是默認 slot,作為找不到匹配的內容片段的備用插槽。如果沒有默認的 slot,這些找不到匹配的內容片段將被拋棄;
22. 作用域插槽
作用域插槽是一種特殊類型的插槽,用作使用一個 (能夠傳遞數據到) 可重用模板替換已渲染元素;
使用方式如下:
在組件模板中給slot標簽設置的標簽屬性,可以在使用組件時,通過該組件標簽內部的template元素的scope標簽屬性訪問;
具體示例如下:
子組件的模板:
< div class="child">
< slot text="hello from child">< /slot>
< /div>
使用子組件:
< div class="parent">
< child>
< template scope="props">
< span>hello from parent< /span>
< span>{{ props.text }}< /span>
< /template>
< /child>
< /div>
標簽屬性scope是被用來定義臨時變量的,被定義的臨時變量就是scope的值,也就是說:此例中的props是被定義臨時變量;這個臨時變量保存的是從子組件中傳遞出來的props對象;
如果我們渲染以上結果,得到的輸出會是:
< div class="parent">
< div class="child">
< span>hello from parent< /span>
< span>hello from child< /span>
< /div>
< /div>
注意:
- slot-scope 的值實際上是一個可以出現在函數簽名參數位置的合法的 JavaScript 表達式;也就是說,slot-scope 的值在解析后會成為函數的參數,所以,一切合法的 函數參數 表達式 都可作為 slot-scope 的值。這也意味著在受支持的環境 (單文件組件或現代瀏覽器) 中,您還可以在表達式中使用 ES2015 解構:
slot-scope="{ text }"
; - 在 版本2.5.0+的Vue中,slot-scope 能被用在任意元素或組件中而不再局限于
<template>
;
23. 動態定義組件的模板的方式
- 內容分發:用slot標簽;
- 作用域插槽:用template和slot標簽;
- 動態組件:用預定義的component標簽;
- 內聯模版:用標簽屬性inline-template;
- 路由;
24. 路由
- 同一個路徑可以匹配多個路由,此時,匹配的優先級就按照路由的定義順序:誰先定義的,誰的優先級就最高;
25. 子組件實例的引用
通過如下方式,可以在JavaScript中獲取子組件實例的引用:
-
在子組件上設置 ref 標簽屬性;
<child-component ref="引用名字" >
-
通過當前組件實例的 $refs 屬性
當前組件實例.$refs.引用名字
獲取子組件實例的引用,如:this.$refs.引用名字
注意:
- 當 ref 和 v-for 一起使用時,獲取到的引用會是一個數組,包含和循環數據源對應的子組件;
- $refs 只在組件渲染完成后才填充,并且它是非響應式的。它僅僅是一個直接操作子組件的應急方案——應當避免在模板或計算屬性中使用 $refs ;
26. 內聯模板
如果子組件有 inline-template 特性,組件將把它的內容當作它的模板,而不是把它當作分發內容。這讓模板編寫起來更靈活。
<my-component inline-template>
<div>
<p>這些將作為組件自身的模板。</p>
<p>而非父組件透傳進來的內容。</p>
</div>
</my-component>
但是 inline-template 讓模板的作用域難以理解。使用 template 選項在組件內定義模板或者在 .vue 文件中使用 template 元素才是最佳實踐。
27. X-Template
另一種定義模板的方式是在 JavaScript 標簽里使用 text/x-template 類型,并且指定一個 id。例如:
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
template: '#hello-world-template'
})
這在有很多大模板的演示應用或者特別小的應用中可能有用,其它場合應該避免使用,因為這將模板和組件的其它定義分離了。