Vue.js 的核心是一個允許採用簡潔的模板語法來聲明式地將數據渲染進 DOM 的系統。這裏我推薦一個鏈接,裏面簡單解釋了vue的語法和部分命令,官網上太過複雜和冗餘。
https://www.cnblogs.com/yueyue-love/p/6215711.html
vue的開發是在html頁面中引入vue.js,我是通過npm的腳手架開發的,安裝鏈接:https://www.cnblogs.com/goldlong/p/8027997.html
菜鳥教程:http://www.runoob.com/vue2/vue-tutorial.html
裏面的知識點清晰明確,適用性強。
下面我只挑重點去書寫:
當一個 Vue 實例被創建時,它向 Vue 的響應式系統中加入了其 data
對象中能找到的所有的屬性。當這些屬性的值發生改變時,視圖將會產生“響應”,即匹配更新爲新的值。
注意:Object.freeze()
,這會阻止修改現有的屬性,也意味着響應系統無法再追蹤變化。
實例生命週期鉤子
每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做生命週期鉤子的函數,這給了用戶在不同階段添加自己的代碼的機會。生命週期圖示如下:
數據綁定最常見的形式就是使用“Mustache”語法 (雙大括號) 的文本插值:
<span>Message: {{ msg }}</span> |
Mustache 標籤將會被替代爲對應數據對象上 msg
屬性的值。無論何時,綁定的數據對象上 msg
屬性發生了改變,插值處的內容都會更新。
通過使用 v-once 指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。但請留心這會影響到該節點上的其它數據綁定:
<span v-once>這個將不會改變: {{ msg }}</span> |
methods和computed和watch:
兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的依賴進行緩存的。只在相關依賴發生改變時它們纔會重新求值。這就意味着只要 message
還沒有發生改變,多次訪問 reversedMessage
計算屬性會立即返回之前的計算結果,而不必再次執行函數。
Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變動:偵聽屬性。當你有一些數據需要隨着其它數據變動而變動時,你很容易濫用 watch
——特別是如果你之前使用過 AngularJS。然而,通常更好的做法是使用計算屬性而不是命令式的 watch
回調。
<div id="demo">{{ fullName }}</div> |
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } }) |
上面代碼是命令式且重複的。將它與計算屬性的版本進行比較,計算屬性更爲簡潔:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } }) |
偵聽器
雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。這就是爲什麼 Vue 通過 watch
選項提供了一個更通用的方法,來響應數據的變化。當需要在數據變化時執行異步或開銷較大的操作時,這個方式是最有用的。
v-bind:style
的對象語法十分直觀——看着非常像 CSS,但其實是一個 JavaScript 對象。CSS 屬性名可以用駝峯式 (camelCase) 或短橫線分隔 (kebab-case,記得用單引號括起來) 來命名:
從 2.3.0 起你可以爲 style
綁定中的屬性提供一個包含多個值的數組,常用於提供多個帶前綴的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div> |
這樣寫只會渲染數組中最後一個被瀏覽器支持的值。在本例中,如果瀏覽器支持不帶瀏覽器前綴的 flexbox,那麼就只會渲染 display: flex
。
v-if
vs v-show
v-if
是“真正”的條件渲染,因爲它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
v-if
也是惰性的:如果在初始渲染時條件爲假,則什麼也不做——直到條件第一次變爲真時,纔會開始渲染條件塊。
相比之下,v-show
就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS 進行切換。
一般來說,v-if
有更高的切換開銷,而 v-show
有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show
較好;如果在運行時條件很少改變,則使用 v-if
較好。
不推薦同時使用 v-if
和 v-for
當 v-if
與 v-for
一起使用時,v-for
具有比 v-if
更高的優先級。
數組更新檢測
變異方法
Vue 包含一組觀察數組的變異方法,所以它們也將會觸發視圖更新。這些方法如下:
方法 (mutation method),顧名思義,會改變被這些方法調用的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如:filter()
, concat()
和 slice()
。這些不會改變原始數組,但總是返回一個新數組。當使用非變異方法時,可以用新數組替換舊數組:
example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) }) |
你可能認爲這將導致 Vue 丟棄現有 DOM 並重新渲染整個列表。幸運的是,事實並非如此。Vue 爲了使得 DOM 元素得到最大範圍的重用而實現了一些智能的、啓發式的方法,所以用一個含有相同元素的數組去替換原來的數組是非常高效的操作。
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
由於 JavaScript 的限制,Vue 不能檢測以下變動的數組:
當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue
當你修改數組的長度時,例如:vm.items.length = newLength
vm.items.splice(newLength) |
舉個例子:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是響應性的 vm.items.length = 2 // 不是響應性的 |
爲了解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue
相同的效果,同時也將觸發狀態更新:
// Vue.set Vue.set(vm.items, indexOfItem, newValue) |
// Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) |
你也可以使用 vm.$set
實例方法,該方法是全局方法 Vue.set
的一個別名:
vm.$set(vm.items, indexOfItem, newValue) |
爲了解決第二類問題,你可以使用 splice
:
vm.items.splice(newLength) |
還是由於 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除:
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 現在是響應式的 vm.b = 2 // `vm.b` 不是響應式的 |
對於已經創建的實例,Vue 不能動態添加根級別的響應式屬性。但是,可以使用 Vue.set(object, key, value)
方法向嵌套對象添加響應式屬性。例如,對於:
var vm = new Vue({ data: { userProfile: { name: 'Anika' } } }) |
你可以添加一個新的 age
屬性到嵌套的 userProfile
對象:
Vue.set(vm.userProfile, 'age', 27) |
你還可以使用 vm.$set
實例方法,它只是全局 Vue.set
的別名:
vm.$set(vm.userProfile, 'age', 27) |
有時你可能需要爲已有對象賦予多個新屬性,比如使用 Object.assign()
或 _.extend()
。在這種情況下,你應該用兩個對象的屬性創建一個新的對象。所以,如果你想添加新的響應式屬性,不要像這樣:
Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) |
你應該這樣做:
vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) |
有時,我們想要顯示一個數組的過濾或排序副本,而不實際改變或重置原始數據。在這種情況下,可以創建返回過濾或排序數組的計算屬性。
例如:
<li v-for="n in evenNumbers">{{ n }}</li> |
data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } } |
在計算屬性不適用的情況下 (例如,在嵌套 v-for
循環中) 你可以使用一個 method 方法:
<li v-for="n in even(numbers)">{{ n }}</li> |
data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } } |
v-for
2.2.0+ 的版本里,當在組件中使用 v-for
時,key
現在是必須的。
內聯處理器中的方法
v-on:有時也需要在內聯語句處理器中訪問原始的 DOM 事件。可以用特殊變量 $event
把它傳入方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> |
// ... methods: { warn: function (message, event) { // 現在我們可以訪問原生事件對象 if (event) event.preventDefault() alert(message) } } |
事件修飾符
不要把 .passive
和 .prevent
一起使用,因爲 .prevent
將會被忽略,同時瀏覽器可能會向你展示一個警告。請記住,.passive
會告訴瀏覽器你不想阻止事件的默認行爲。
父子組件傳值:
父組件向子組件傳遞使用props,父組件監聽子組件:
級組件可以像處理 native DOM 事件一樣通過 v-on
監聽子組件實例的任意事件:
<blog-post ... v-on:enlarge-text="postFontSize += 0.1" ></blog-post> |
同時子組件可以通過調用內建的 $emit
方法 並傳入事件名稱來觸發一個事件:
<button v-on:click="$emit('enlarge-text')"> Enlarge text </button> |
有的時候用一個事件來拋出一個特定的值是非常有用的。例如我們可能想讓 <blog-post>
組件決定它的文本要放大多少。這時可以使用 $emit
的第二個參數來提供這個值:
<button v-on:click="$emit('enlarge-text', 0.1)"> Enlarge text </button> |
然後當在父級組件監聽這個事件的時候,我們可以通過 $event
訪問到被拋出的這個值:
<blog-post ... v-on:enlarge-text="postFontSize += $event" ></blog-post> |
或者,如果這個事件處理函數是一個方法:
<blog-post ... v-on:enlarge-text="onEnlargeText" ></blog-post> |
那麼這個值將會作爲第一個參數傳入這個方法:
methods: { onEnlargeText: function (enlargeAmount) { this.postFontSize += enlargeAmount } } |
在組件上使用 v-model
自定義事件也可以用於創建支持 v-model
的自定義輸入組件。記住:
<input v-model="searchText"> |
等價於:
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value" > |
當用在組件上時,v-model
則會這樣:
<custom-input v-bind:value="searchText" v-on:input="searchText = $event" ></custom-input> |
爲了讓它正常工作,這個組件內的 <input>
必須:
- 將其
value
特性綁定到一個名叫value
的 prop 上 - 在其
input
事件被觸發時,將新的值通過自定義的input
事件拋出
寫成代碼之後是這樣的:
Vue.component('custom-input', { props: ['value'], template: ` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > ` }) |
現在 v-model
就應該可以在這個組件上完美地工作起來了:
<custom-input v-model="searchText"></custom-input> |
動態組件
有的時候,在不同組件之間進行動態切換是非常有用的,比如在一個多標籤的界面裏:
HomePostsArchive
Home component
上述內容可以通過 Vue 的 <component>
元素加一個特殊的 is
特性來實現:
<!-- 組件會在 `currentTabComponent` 改變時改變 --> <component v-bind:is="currentTabComponent"></component> |
基礎組件,它們會在各個組件中被頻繁的用到。所以會導致很多組件裏都會有一個包含基礎組件的長列表,但是我們只需要其中的一部分。如果你使用了 webpack (或在內部使用了 webpack 的 Vue CLI 3+),那麼就可以使用 require.context
只全局註冊這些非常通用的基礎組件。
import Vue from 'vue' import upperFirst from 'lodash/upperFirst' import camelCase from 'lodash/camelCase' const requireComponent = require.context( // 其組件目錄的相對路徑 './components', // 是否查詢其子目錄 false, // 匹配基礎組件文件名的正則表達式 /Base[A-Z]\w+\.(vue|js)$/ )
Prop 的大小寫 (camelCase vs kebab-case)
camelCase (駝峯命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名:重申一次,如果你使用字符串模板,那麼這個限制就不存在了。
Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' }) |
<!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post> |
非 Prop 的特性
一個非 prop 特性是指傳向一個組件,但是該組件並沒有相應 prop 定義的特性。
因爲顯式定義的 prop 適用於向一個子組件傳入信息,然而組件庫的作者並不總能預見組件會被用於怎樣的場景。這也是爲什麼組件可以接受任意的特性,而這些特性會被添加到這個組件的根元素上。
例如,想象一下你通過一個 Bootstrap 插件使用了一個第三方的 <bootstrap-date-input>
組件,這個插件需要在其 <input>
上用到一個 data-date-picker
特性。我們可以將這個特性添加到你的組件實例上:
<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input> |
然後這個 data-date-picker="activated"
特性就會自動添加到 <bootstrap-date-input>
的根元素上。
替換/合併已有的特性
想象一下 <bootstrap-date-input>
的模板是這樣的:
<input type="date" class="form-control"> |
爲了給我們的日期選擇器插件定製一個主題,我們可能需要像這樣添加一個特別的類名:
<bootstrap-date-input data-date-picker="activated" class="date-picker-theme-dark" ></bootstrap-date-input> |
在這種情況下,我們定義了兩個不同的 class
的值:
form-control
,這是在組件的模板內設置好的date-picker-theme-dark
,這是從組件的父級傳入的
對於絕大多數特性來說,從外部提供給組件的值會替換掉組件內部設置好的值。所以如果傳入 type="text"
就會替換掉 type="date"
並把它破壞!慶幸的是,class
和 style
特性會稍微智能一些,即兩邊的值會被合併起來,從而得到最終的值:form-control date-picker-theme-dark
。
禁用特性繼承
如果你不希望組件的根元素繼承特性,你可以在組件的選項中設置 inheritAttrs: false
。例如:
Vue.component('my-component', { inheritAttrs: false, // ... }) |
這尤其適合配合實例的 $attrs
屬性使用,該屬性包含了傳遞給一個組件的特性名和特性值,例如:
{ class: 'username-input', placeholder: 'Enter your username' } |
有了 inheritAttrs: false
和 $attrs
,你就可以手動決定這些特性會被賦予哪個元素。在撰寫基礎組件的時候是常會用到的:
Vue.component('base-input', { inheritAttrs: false, props: ['label', 'value'], template: ` <label> {{ label }} <input v-bind="$attrs" v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > </label> ` }) |
這個模式允許你在使用基礎組件的時候更像是使用原始的 HTML 元素,而不會擔心哪個元素是真正的根元素:
<base-input v-model="username" class="username-input" placeholder="Enter your username" ></base-input> |
自定義事件
事件名
不同於組件和 prop,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名需要完全匹配監聽這個事件所用的名稱。舉個例子,如果觸發一個 camelCase 名字的事件:
this.$emit('myEvent') |
則監聽這個名字的 kebab-case 版本是不會有任何效果的:
<my-component v-on:my-event="doSomething"></my-component> |
不同於組件和 prop,事件名不會被用作一個 JavaScript 變量名或屬性名,所以就沒有理由使用 camelCase 或 PascalCase 了。並且 v-on
事件監聽器在 DOM 模板中會被自動轉換爲全小寫 (因爲 HTML 是大小寫不敏感的),所以 v-on:myEvent
將會變成 v-on:myevent
——導致 myEvent
不可能被監聽到。
因此,我們推薦你始終使用 kebab-case 的事件名。
在動態組件上使用 keep-alive
我們之前曾經在一個多標籤的界面中使用 is
特性來切換不同的組件:
<component v-bind:is="currentTabComponent"></component> |
當在這些組件之間切換的時候,你有時會想保持這些組件的狀態,以避免反覆重渲染導致的性能問題。例如我們來展開說一說這個多標籤界面:
PostsArchive
- Cat Ipsum
- Hipster Ipsum
- Cupcake Ipsum
Click on a blog title to the left to view it.
你會注意到,如果你選擇了一篇文章,切換到 Archive 標籤,然後再切換回 Posts,是不會繼續展示你之前選擇的文章的。這是因爲你每次切換新標籤的時候,Vue 都創建了一個新的 currentTabComponent
實例。
重新創建動態組件的行爲通常是非常有用的,但是在這個案例中,我們更希望那些標籤的組件實例能夠被在它們第一次被創建的時候緩存下來。爲了解決這個問題,我們可以用一個 <keep-alive>
元素將其動態組件包裹起來。
<!-- 失活的組件將會被緩存!--> <keep-alive> <component v-bind:is="currentTabComponent"></component> </keep-alive> |
異步組件
在大型應用中,我們可能需要將應用分割成小一些的代碼塊,並且只在需要的時候才從服務器加載一個模塊。爲了簡化,Vue 允許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件需要被渲染的時候纔會觸發該工廠函數,且會把結果緩存起來供未來重渲染。例如:
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // 向 `resolve` 回調傳遞組件定義 resolve({ template: '<div>I am async!</div>' }) }, 1000) }) |
如你所見,這個工廠函數會收到一個 resolve
回調,這個回調函數會在你從服務器得到組件定義的時候被調用。你也可以調用 reject(reason)
來表示加載失敗。這裏的 setTimeout
是爲了演示用的,如何獲取組件取決於你自己。一個推薦的做法是將異步組件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) { // 這個特殊的 `require` 語法將會告訴 webpack // 自動將你的構建代碼切割成多個包,這些包 // 會通過 Ajax 請求加載 require(['./my-async-component'], resolve) }) |
你也可以在工廠函數中返回一個 Promise
,所以把 webpack 2 和 ES2015 語法加在一起,我們可以寫成這樣:
Vue.component( 'async-webpack-example', // 這個 `import` 函數會返回一個 `Promise` 對象。 () => import('./my-async-component') ) |
當使用局部註冊的時候,你也可以直接提供一個返回 Promise
的函數:
new Vue({ // ... components: { 'my-component': () => import('./my-async-component') } }) |
處理加載狀態
2.3.0+ 新增
這裏的異步組件工廠函數也可以返回一個如下格式的對象:
const AsyncComponent = () => ({ // 需要加載的組件 (應該是一個 `Promise` 對象) component: import('./MyComponent.vue'), // 異步組件加載時使用的組件 loading: LoadingComponent, // 加載失敗時使用的組件 error: ErrorComponent, // 展示加載時組件的延時時間。默認值是 200 (毫秒) delay: 200, // 如果提供了超時時間且組件加載也超時了, // 則使用加載失敗時使用的組件。默認值是:`Infinity` timeout: 3000 }) |
訪問元素 & 組件
在絕大多數情況下,我們最好不要觸達另一個組件實例內部或手動操作 DOM 元素。不過也確實在一些情況下做這些事情是合適的。
訪問根實例
在每個 new Vue
實例的子組件中,其根實例可以通過 $root
屬性進行訪問。例如,在這個根實例中:
// Vue 根實例 new Vue({ data: { foo: 1 }, computed: { bar: function () { /* ... */ } }, methods: { baz: function () { /* ... */ } } }) |
所有的子組件都可以將這個實例作爲一個全局 store 來訪問或使用。
// 獲取根組件的數據 this.$root.foo // 寫入根組件的數據 this.$root.foo = 2 // 訪問根組件的計算屬性 this.$root.bar // 調用根組件的方法 this.$root.baz() |
訪問父級組件實例
和 $root
類似,$parent
屬性可以用來從一個子組件訪問父組件的實例。它提供了一種機會,可以在後期隨時觸達父級組件,以替代將數據以 prop 的方式傳入子組件的方式。
在絕大多數情況下,觸達父級組件會使得你的應用更難調試和理解,尤其是當你變更了父級組件的數據的時候。當我們稍後回看那個組件的時候,很難找出那個變更是從哪裏發起的。
訪問子組件實例或子元素
儘管存在 prop 和事件,有的時候你仍可能需要在 JavaScript 裏直接訪問一個子組件。爲了達到這個目的,你可以通過 ref
特性爲這個子組件賦予一個 ID 引用。例如:
<base-input ref="usernameInput"></base-input> |
現在在你已經定義了這個 ref
的組件裏,你可以使用:
this.$refs.usernameInput |
來訪問這個 <base-input>
實例,以便不時之需。比如程序化地從一個父級組件聚焦這個輸入框。在剛纔那個例子中,該 <base-input>
組件也可以使用一個類似的 ref
提供對內部這個指定元素的訪問,例如:
<input ref="input"> |
甚至可以通過其父級組件定義方法:
methods: { // 用來從父級組件聚焦輸入框 focus: function () { this.$refs.input.focus() } } |
這樣就允許父級組件通過下面的代碼聚焦 <base-input>
裏的輸入框:
this.$refs.usernameInput.focus() |
當 ref
和 v-for
一起使用的時候,你得到的引用將會是一個包含了對應數據源的這些子組件的數組。
依賴注入
在此之前,在我們描述訪問父級組件實例的時候,展示過一個類似這樣的例子:
<google-map> <google-map-region v-bind:shape="cityBoundaries"> <google-map-markers v-bind:places="iceCreamShops"></google-map-markers> </google-map-region> </google-map> |
在這個組件裏,所有 <google-map>
的後代都需要訪問一個 getMap
方法,以便知道要跟哪個地圖進行交互。不幸的是,使用 $parent
屬性無法很好的擴展到更深層級的嵌套組件上。這也是依賴注入的用武之地,它用到了兩個新的實例選項:provide
和 inject
。
provide
選項允許我們指定我們想要提供給後代組件的數據/方法。在這個例子中,就是 <google-map>
內部的 getMap
方法:
provide: function () { return { getMap: this.getMap } } |
然後在任何後代組件裏,我們都可以使用 inject
選項來接收指定的我們想要添加在這個實例上的屬性:
inject: ['getMap'] |
你可以在這裏看到完整的示例。相比 $parent
來說,這個用法可以讓我們在任意後代組件中訪問 getMap
,而不需要暴露整個 <google-map>
實例。這允許我們更好的持續研發該組件,而不需要擔心我們可能會改變/移除一些子組件依賴的東西。同時這些組件之間的接口是始終明確定義的,就和 props
一樣。
實際上,你可以把依賴注入看作一部分“大範圍有效的 prop”,除了:
- 祖先組件不需要知道哪些後代組件使用它提供的屬性
- 後代組件不需要知道被注入的屬性來自哪裏
然而,依賴注入還是有負面影響的。它將你的應用以目前的組件組織方式耦合了起來,使重構變得更加困難。同時所提供的屬性是非響應式的。這是出於設計的考慮,因爲使用它們來創建一箇中心化規模化的數據跟使用 $root
做這件事都是不夠好的。如果你想要共享的這個屬性是你的應用特有的,而不是通用化的,或者如果你想在祖先組件中更新所提供的數據,那麼這意味着你可能需要換用一個像 Vuex 這樣真正的狀態管理方案了。
$emit
的用法,它可以被 v-on
偵聽,但是 Vue 實例同時在其事件接口中提供了其它的方法。我們可以:
渲染函數 & JSX
render()函數:
- 通過
$on(eventName, eventHandler)
偵聽一個事件 - 通過
$once(eventName, eventHandler)
一次性偵聽一個事件 - 通過
$off(eventName, eventHandler)
停止偵聽一個事件
<h1>{{ blogTitle }}</h1> |
或者一個渲染函數裏:
render: function (createElement) { return createElement('h1', this.blogTitle) } |
約束
VNodes 必須唯一
組件樹中的所有 VNodes 必須是唯一的。這意味着,下面的 render function 是無效的:
render: function (createElement) { var myParagraphVNode = createElement('p', 'hi') return createElement('div', [ // 錯誤-重複的 VNodes myParagraphVNode, myParagraphVNode ]) } |
如果你真的需要重複很多次的元素/組件,你可以使用工廠函數來實現。例如,下面這個例子 render 函數完美有效地渲染了 20 個相同的段落:
render: function (createElement) { return createElement('div', Array.apply(null, { length: 20 }).map(function () { return createElement('p', 'hi') }) ) }
|
Vue.js 允許你自定義過濾器,可被用於一些常見的文本格式化。過濾器可以用在兩個地方:雙花括號插值和 v-bind
表達式 (後者從 2.1.0+ 開始支持)。過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符號指示:
<!-- 在雙花括號中 --> {{ message | capitalize }} <!-- 在 `v-bind` 中 --> <div v-bind:id="rawId | formatId"></div> |
你可以在一個組件的選項中定義本地的過濾器:
filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } |
或者在創建 Vue 實例之前全局定義過濾器:
Vue.filter('capitalize', function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) }) new Vue({ // ... }) |
過濾器函數總接收表達式的值 (之前的操作鏈的結果) 作爲第一個參數。在上述例子中,capitalize
過濾器函數將會收到 message
的值作爲第一個參數。
過濾器可以串聯:
{{ message | filterA | filterB }} |
在這個例子中,filterA
被定義爲接收單個參數的過濾器函數,表達式 message
的值將作爲參數傳入到函數中。然後繼續調用同樣被定義爲接收單個參數的過濾器函數 filterB
,將 filterA
的結果傳遞到 filterB
中。
過濾器是 JavaScript 函數,因此可以接收參數:
{{ message | filterA('arg1', arg2) }} |
這裏,filterA
被定義爲接收三個參數的過濾器函數。其中 message
的值作爲第一個參數,普通字符串 'arg1'
作爲第二個參數,表達式 arg2
的值作爲第三個參數。