VUE深入淺出(學習過程)
- 2020 年 3 月 3 日
- 筆記
VUE
2020年02月26日06:27:10 複習過Java8新特性之後開始學習VUE。
了解node了之後,來了解一下VUE。針對於學習VUE用什麼開發工具這個問題上,我這裡有vsCode和webstrom。工具只是工具,大娃用的VScode。
起步
開始一門新技術,如何開始呢?https://cn.vuejs.org/
我的方法是,打開官網。跟著裡邊的學。
介紹
Vue (讀音 /vjuː/,類似於 view) 是一套用於構建用戶介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注視圖層,不僅易於上手,還便於與第三方庫或既有項目整合。另一方面,當與現代化的工具鏈以及各種支援類庫結合使用時,Vue 也完全能夠為複雜的單頁應用提供驅動。
聲明式渲染
<div id="app"> {{ message }} </div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
安裝與部署
兼容性
Vue 不支援 IE8 及以下版本,因為 Vue 使用了 IE8 無法模擬的 ECMAScript 5 特性。但它支援所有兼容 ECMAScript 5 的瀏覽器。
引用Vue的方式有多種:
- 直接用Script引用
- CDN
- NPM
- 命令行工具
組件化應用構建
組件系統是 Vue 的另一個重要概念,因為它是一種抽象,允許我們使用小型、獨立和通常可復用的組件構建大型應用。仔細想想,幾乎任意類型的應用介面都可以抽象為一個組件樹.
與自定義元素的關係
你可能已經注意到 Vue 組件非常類似於自定義元素——它是 Web 組件規範的一部分,這是因為 Vue 的組件語法部分參考了該規範。例如 Vue 組件實現了 Slot API 與 is
attribute。但是,還是有幾個關鍵差別:
- Web Components 規範已經完成並通過,但未被所有瀏覽器原生實現。目前 Safari 10.1+、Chrome 54+ 和 Firefox 63+ 原生支援 Web Components。相比之下,Vue 組件不需要任何 polyfill,並且在所有支援的瀏覽器 (IE9 及更高版本) 之下表現一致。必要時,Vue 組件也可以包裝於原生自定義元素之內。
- Vue 組件提供了純自定義元素所不具備的一些重要功能,最突出的是跨組件數據流、自定義事件通訊以及構建工具集成。
雖然 Vue 內部沒有使用自定義元素,不過在應用使用自定義元素、或以自定義元素形式發布時,依然有很好的互操作性。Vue CLI 也支援將 Vue 組件構建成為原生的自定義元素。
深入理解VUE基礎
Vue實例
創建一個Vue實例
每一個Vue應用都是通過用Vue函數創建一個新的Vue實例開始的:
var vm = new Vue(){ //選項 }
雖然沒有完全遵循 MVVM 模型,但是 Vue 的設計也受到了它的啟發。因此在文檔中經常會使用
vm
(ViewModel 的縮寫) 這個變數名表示 Vue 實例。當創建一個 Vue 實例時,你可以傳入一個選項對象。這篇教程主要描述的就是如何使用這些選項來創建你想要的行為。作為參考,你也可以在 API 文檔 中瀏覽完整的選項列表。
一個 Vue 應用由一個通過
new Vue
創建的根 Vue 實例,以及可選的嵌套的、可復用的組件樹組成。舉個例子,一個 todo 應用的組件樹可以是這樣的:根實例 └─ TodoList ├─ TodoItem │ ├─ DeleteTodoButton │ └─ EditTodoButton └─ TodoListFooter ├─ ClearTodosButton └─ TodoListStatistics
所有的 Vue 組件都是 Vue 實例,並且接受相同的選項對象 (一些根實例特有的選項除外)。
數據與方法
當一個 Vue 實例被創建時,它將 data
對象中的所有的屬性加入到 Vue 的響應式系統中。當這些屬性的值發生改變時,視圖將會產生「響應」,即匹配更新為新的值。
// 我們的數據對象 var data = { a: 1 } // 該對象被加入到一個 Vue 實例中 var vm = new Vue({ data: data }) // 獲得這個實例上的屬性 // 返回源數據中對應的欄位 vm.a == data.a // => true // 設置屬性也會影響到原始數據 vm.a = 2 data.a // => 2 // ……反之亦然 data.a = 3 vm.a // => 3
當這些數據改變時,視圖會進行重渲染。值得注意的是只有當實例被創建時就已經存在於 data
中的屬性才是響應式的。也就是說如果你添加一個新的屬性,比如:
vm.b = 'hi'
那麼對 b
的改動將不會觸發任何視圖的更新。如果你知道你會在晚些時候需要一個屬性,但是一開始它為空或不存在,那麼你僅需要設置一些初始值。比如:
data: { newTodoText: '', visitCount: 0, hideCompletedTodos: false, todos: [], error: null }
這裡唯一的例外是使用 Object.freeze()
,這會阻止修改現有的屬性,也意味著響應系統無法再追蹤變化。
var obj = { foo: 'bar' } Object.freeze(obj) new Vue({ el: '#app', data: obj }) <div id="app"> <p>{{ foo }}</p> <!-- 這裡的 `foo` 不會更新! --> <button v-on:click="foo = 'baz'">Change it</button> </div>
除了數據屬性,Vue 實例還暴露了一些有用的實例屬性與方法。它們都有前綴 $
,以便與用戶定義的屬性區分開來。例如:
var data = { a: 1 } var vm = new Vue({ el: '#example', data: data }) vm.$data === data // => true vm.$el === document.getElementById('example') // => true // $watch 是一個實例方法 vm.$watch('a', function (newValue, oldValue) { // 這個回調將在 `vm.a` 改變後調用 })
實例聲明周期鉤子
每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做生命周期鉤子的函數,這給了用戶在不同階段添加自己的程式碼的機會。
比如 created
鉤子可以用來在一個實例被創建之後執行程式碼:
new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 實例 console.log('a is: ' + this.a) } }) // => "a is: 1"
也有一些其它的鉤子,在實例生命周期的不同階段被調用,如 mounted
、updated
和 destroyed
。生命周期鉤子的 this
上下文指向調用它的 Vue 實例。
不要在選項屬性或回調上使用箭頭函數,比如
created: () => console.log(this.a)
或vm.$watch('a', newValue => this.myMethod())
。因為箭頭函數並沒有this
,this
會作為變數一直向上級詞法作用域查找,直至找到為止,經常導致Uncaught TypeError: Cannot read property of undefined
或Uncaught TypeError: this.myMethod is not a function
之類的錯誤。
生命周期圖示
下圖展示了實例的生命周期。你不需要立馬弄明白所有的東西,不過隨著你的不斷學習和使用,它的參考價值會越來越高。
看到這裡的時候和大娃進行了溝通。一門技術,你學會怎麼用就行,具體用到了某個特定的功能的時候查文檔。那些經常用的東西,自然而然的就會記住了。
模板語法
插值
文本
數據綁定最常見的形式就是使用「Mustache」語法 (雙大括弧) 的文本插值:
<span>Message: {{ msg }}</span>
Mustache 標籤將會被替代為對應數據對象上 msg
屬性的值。無論何時,綁定的數據對象上 msg
屬性發生了改變,插值處的內容都會更新。
通過使用 v-once 指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。但請留心這會影響到該節點上的其它數據綁定:
<span v-once>這個將不會改變: {{ msg }}</span>
原始 HTML
雙大括弧會將數據解釋為普通文本,而非 HTML 程式碼。為了輸出真正的 HTML,你需要使用 v-html
指令:
<p>Using mustaches: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p>
Using mustaches: This should be red.
Using v-html directive: This should be red.
這個 span
的內容將會被替換成為屬性值 rawHtml
,直接作為 HTML——會忽略解析屬性值中的數據綁定。注意,你不能使用 v-html
來複合局部模板,因為 Vue 不是基於字元串的模板引擎。反之,對於用戶介面 (UI),組件更適合作為可重用和可組合的基本單位。
你的站點上動態渲染的任意 HTML 可能會非常危險,因為它很容易導致 XSS 攻擊。請只對可信內容使用 HTML 插值,絕不要對用戶提供的內容使用插值。
Attribute
Mustache 語法不能作用在 HTML attribute 上,遇到這種情況應該使用 v-bind
指令:
<div v-bind:id="dynamicId"></div>
對於布爾 attribute (它們只要存在就意味著值為 true
),v-bind
工作起來略有不同,在這個例子中:
<button v-bind:disabled="isButtonDisabled">Button</button>
如果 isButtonDisabled
的值是 null
、undefined
或 false
,則 disabled
attribute 甚至不會被包含在渲染出來的 “ 元素中。
使用 JavaScript 表達式
迄今為止,在我們的模板中,我們一直都只綁定簡單的屬性鍵值。但實際上,對於所有的數據綁定,Vue.js 都提供了完全的 JavaScript 表達式支援。
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} <div v-bind:id="'list-' + id"></div>
這些表達式會在所屬 Vue 實例的數據作用域下作為 JavaScript 被解析。有個限制就是,每個綁定都只能包含單個表達式,所以下面的例子都不會生效。
<!-- 這是語句,不是表達式 --> {{ var a = 1 }} <!-- 流控制也不會生效,請使用三元表達式 --> {{ if (ok) { return message } }}
模板表達式都被放在沙盒中,只能訪問全局變數的一個白名單,如 Math
和 Date
。你不應該在模板表達式中試圖訪問用戶定義的全局變數。
指令
指令 (Directives) 是帶有 v-
前綴的特殊 attribute。指令 attribute 的值預期是單個 JavaScript 表達式 (v-for
是例外情況,稍後我們再討論)。指令的職責是,當表達式的值改變時,將其產生的連帶影響,響應式地作用於 DOM。回顧我們在介紹中看到的例子:
<p v-if="seen">現在你看到我了</p>
這裡,v-if
指令將根據表達式 seen
的值的真假來插入/移除 “ 元素。
參數
一些指令能夠接收一個「參數」,在指令名稱之後以冒號表示。例如,v-bind
指令可以用於響應式地更新 HTML attribute:
<a v-bind:href="url">...</a>
在這裡 href
是參數,告知 v-bind
指令將該元素的 href
attribute 與表達式 url
的值綁定。
另一個例子是 v-on
指令,它用於監聽 DOM 事件:
<a v-on:click="doSomething">...</a>
在這裡參數是監聽的事件名。我們也會更詳細地討論事件處理。
動態參數
2.6.0 新增
從 2.6.0 開始,可以用方括弧括起來的 JavaScript 表達式作為一個指令的參數:
<!-- 注意,參數表達式的寫法存在一些約束,如之後的「對動態參數表達式的約束」章節所述。 --> <a v-bind:[attributeName]="url"> ... </a>
這裡的 attributeName
會被作為一個 JavaScript 表達式進行動態求值,求得的值將會作為最終的參數來使用。例如,如果你的 Vue 實例有一個 data
屬性 attributeName
,其值為 "href"
,那麼這個綁定將等價於 v-bind:href
。
同樣地,你可以使用動態參數為一個動態的事件名綁定處理函數:
<a v-on:[eventName]="doSomething"> ... </a>
在這個示例中,當 eventName
的值為 "focus"
時,v-on:[eventName]
將等價於 v-on:focus
。
對動態參數的值的約束
動態參數預期會求出一個字元串,異常情況下值為 null
。這個特殊的 null
值可以被顯性地用於移除綁定。任何其它非字元串類型的值都將會觸發一個警告。
對動態參數表達式的約束
動態參數表達式有一些語法約束,因為某些字元,如空格和引號,放在 HTML attribute 名里是無效的。例如:
<!-- 這會觸發一個編譯警告 --> <a v-bind:['foo' + bar]="value"> ... </a>
變通的辦法是使用沒有空格或引號的表達式,或用計算屬性替代這種複雜表達式。
在 DOM 中使用模板時 (直接在一個 HTML 文件里撰寫模板),還需要避免使用大寫字元來命名鍵名,因為瀏覽器會把 attribute 名全部強制轉為小寫:
<!-- 在 DOM 中使用模板時這段程式碼會被轉換為 `v-bind:[someattr]`。 除非在實例中有一個名為「someattr」的 property,否則程式碼不會工作。 --> <a v-bind:[someAttr]="value"> ... </a>
修飾符
修飾符 (modifier) 是以半形句號 .
指明的特殊後綴,用於指出一個指令應該以特殊方式綁定。例如,.prevent
修飾符告訴 v-on
指令對於觸發的事件調用 event.preventDefault()
:
<form v-on:submit.prevent="onSubmit">...</form>
在接下來對 v-on
和 v-for
等功能的探索中,你會看到修飾符的其它例子。
縮寫
v-
前綴作為一種視覺提示,用來識別模板中 Vue 特定的 attribute。當你在使用 Vue.js 為現有標籤添加動態行為 (dynamic behavior) 時,v-
前綴很有幫助,然而,對於一些頻繁用到的指令來說,就會感到使用繁瑣。同時,在構建由 Vue 管理所有模板的單頁面應用程式 (SPA – single page application) 時,v-
前綴也變得沒那麼重要了。因此,Vue 為 v-bind
和 v-on
這兩個最常用的指令,提供了特定簡寫:
v-bind
縮寫
<!-- 完整語法 --> <a v-bind:href="url">...</a> <!-- 縮寫 --> <a :href="url">...</a> <!-- 動態參數的縮寫 (2.6.0+) --> <a :[key]="url"> ... </a>
v-on
縮寫
<!-- 完整語法 --> <a v-on:click="doSomething">...</a> <!-- 縮寫 --> <a @click="doSomething">...</a> <!-- 動態參數的縮寫 (2.6.0+) --> <a @[event]="doSomething"> ... </a>
它們看起來可能與普通的 HTML 略有不同,但 :
與 @
對於 attribute 名來說都是合法字元,在所有支援 Vue 的瀏覽器都能被正確地解析。而且,它們不會出現在最終渲染的標記中。縮寫語法是完全可選的,但隨著你更深入地了解它們的作用,你會慶幸擁有它們。
計算屬性和監聽器
計算屬性
模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。例如:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
在這個地方,模板不再是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裡是想要顯示變數 message
的翻轉字元串。當你想要在模板中多次引用此處的翻轉字元串時,就會更加難以處理。
所以,對於任何複雜邏輯,你都應當使用計算屬性。
基礎例子
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 計算屬性的 getter reversedMessage: function () { // `this` 指向 vm 實例 return this.message.split('').reverse().join('') } } })
結果:
Original message: "Hello"
Computed reversed message: "olleH"
這裡我們聲明了一個計算屬性 reversedMessage
。我們提供的函數將用作屬性 vm.reversedMessage
的 getter 函數:
console.log(vm.reversedMessage) // => 'olleH' vm.message = 'Goodbye' console.log(vm.reversedMessage) // => 'eybdooG'
計算屬性快取 VS 方法
你可能已經注意到我們可以通過在表達式中調用方法來達到同樣的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p> // 在組件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } }
我們可以將同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的響應式依賴進行快取的。只在相關響應式依賴發生改變時它們才會重新求值。這就意味著只要 message
還沒有發生改變,多次訪問 reversedMessage
計算屬性會立即返回之前的計算結果,而不必再次執行函數。
這也同樣意味著下面的計算屬性將不再更新,因為 Date.now()
不是響應式依賴:
computed: { now: function () { return Date.now() } }
相比之下,每當觸發重新渲染時,調用方法將總會再次執行函數。
我們為什麼需要快取?假設我們有一個性能開銷比較大的計算屬性 A,它需要遍歷一個巨大的數組並做大量的計算。然後我們可能有其他的計算屬性依賴於 A 。如果沒有快取,我們將不可避免的多次執行 A 的 getter!如果你不希望有快取,請用方法來替代。
計算屬性 VS 監聽屬性
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 } } })
好得多了,不是嗎?
計算屬性的setter
計算屬性默認只有 getter ,不過在需要時你也可以提供一個 setter :
// ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ...
現在再運行 vm.fullName = 'John Doe'
時,setter 會被調用,vm.firstName
和 vm.lastName
也會相應地被更新。
監聽器
雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。這就是為什麼 Vue 通過 watch
選項提供了一個更通用的方法,來響應數據的變化。當需要在數據變化時執行非同步或開銷較大的操作時,這個方式是最有用的。
例如:
<div id="watch-example"> <p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div> <!-- 因為 AJAX 庫和通用工具的生態已經相當豐富,Vue 核心程式碼沒有重複 --> <!-- 提供這些功能以保持精簡。這也可以讓你自由選擇自己更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: 'I cannot give you an answer until you ask a question!' }, watch: { // 如果 `question` 發生改變,這個函數就會運行 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, created: function () { // `_.debounce` 是一個通過 Lodash 限制操作頻率的函數。 // 在這個例子中,我們希望限制訪問 yesno.wtf/api 的頻率 // AJAX 請求直到用戶輸入完畢才會發出。想要了解更多關於 // `_.debounce` 函數 (及其近親 `_.throttle`) 的知識, // 請參考:https://lodash.com/docs#debounce this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' var vm = this axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = 'Error! Could not reach the API. ' + error }) } } }) </script>
結果:
Ask a yes/no question:
No
在這個示例中,使用 watch
選項允許我們執行非同步操作 (訪問一個 API),限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
除了 watch
選項之外,您還可以使用命令式的 vm.$watch API。
CSS與Style綁定
操作元素的 class 列表和內聯樣式是數據綁定的一個常見需求。因為它們都是屬性,所以我們可以用
v-bind
處理它們:只需要通過表達式計算出字元串結果即可。不過,字元串拼接麻煩且易錯。因此,在將v-bind
用於class
和style
時,Vue.js 做了專門的增強。表達式結果的類型除了字元串之外,還可以是對象或數組。
綁定HTML CLass
對象語法
對象語法 1. 我們可以傳給 `v-bind:class` 一個對象,以動態地切換 class: <div v-bind:class="{ active: isActive }"></div> 2. 在對象中傳入更多屬性來動態切換多個 class <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div> data: { isActive: true, hasError: false } 3. 綁定的數據對象不必內聯定義在模板里: <div v-bind:class="classObject"></div> data: { classObject: { active: true, 'text-danger': false } } 4. 綁定一個返回對象的計算屬性,這是一個常用且強大的模式: <div v-bind:class="classObject"></div> data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } }
數組語法
有三種形式: 1. 我們可以把一個數組傳給 `v-bind:class`,以應用一個 class 列表: <div v-bind:class="[activeClass, errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' } 2. 也可以使用三元表達式: <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> 3. 也可以使用對象語法 <div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在組件上
等學習完組件之後再了解這個是啥。
綁定內聯樣式
對象語法
1. v-bind:style 的對象語法十分直觀——看著非常像 CSS,但其實是一個 JavaScript 對象。CSS 屬性名可以用駝峰式 (camelCase) 或短橫線分隔 (kebab-case,記得用引號括起來) 來命名: <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 } 2. 直接綁定到一個樣式對象通常更好,這會讓模板更清晰: <div v-bind:style="styleObject"></div> data: { styleObject: { color: 'red', fontSize: '13px' } }
數組語法
v-bind:style
的數組語法可以將多個樣式對象應用到同一個元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
自動添加前綴
當 v-bind:style
使用需要添加瀏覽器引擎前綴的 CSS 屬性時,如 transform
,Vue.js 會自動偵測並添加相應的前綴。
JavaScript中的真值
在 JavaScript 中,truthy(真值)指的是在布爾值上下文中,轉換後的值為真的值。所有值都是真值,除非它們被定義為 假值(即除
false
、0
、""
、null
、undefined
和NaN
以外皆為真值)。
條件渲染
v-if
1. <template> 元素上使用 v-if 條件渲染分組: 因為 v-if 是一個指令,所以必須將它添加到一個元素上。但是如果想切換多個元素呢?此時可以把一個 <template> 元素當做不可見的包裹元素,並在上面使用 v-if。最終的渲染結果將不包含 <template> 元素。 <template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> 2. v-if 指令用於條件性地渲染一塊內容。這塊內容只會在指令的表達式返回 truthy 值的時候被渲染。 3. 也可以用 v-else 添加一個「else 塊」: <h1 v-if="awesome">Vue is awesome!</h1> <h1 v-else>Oh no ?</h1> v-else 元素必須緊跟在帶 v-if 或者 v-else-if 的元素的後面,否則它將不會被識別。 4. v-else-if,顧名思義,充當 v-if 的「else-if 塊」,可以連續使用: <div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else> Not A/B/C </div>
用 key 管理可復用的元素
Vue 會儘可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染。這麼做除了使 Vue 變得非常快之外,還有其它一些好處。例如,如果你允許用戶在不同的登錄方式之間切換: <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template> 那麼在上面的程式碼中切換 loginType 將不會清除用戶已經輸入的內容。因為兩個模板使用了相同的元素,<input> 不會被替換掉——僅僅是替換了它的 placeholder。 自己動手試一試,在輸入框中輸入一些文本,然後按下切換按鈕: 這樣也不總是符合實際需求,所以 Vue 為你提供了一種方式來表達「這兩個元素是完全獨立的,不要復用它們」。只需添加一個具有唯一值的 key 屬性即可: <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address" key="email-input"> </template> 現在,每次切換時,輸入框都將被重新渲染。請看: 注意,<label> 元素仍然會被高效地復用,因為它們沒有添加 key 屬性。
v-show
另一個用於根據條件展示元素的選項是 v-show
指令。用法大致一樣:
<h1 v-show="ok">Hello!</h1>
不同的是帶有 v-show
的元素始終會被渲染並保留在 DOM 中。v-show
只是簡單地切換元素的 CSS 屬性 display
。
注意,v-show
不支援 template 元素,也不支援 v-else
。
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-if
與v-for
一起使用時,v-for
具有比v-if
更高的優先順序。請查閱列表渲染指南 以獲取詳細資訊。
列表渲染
用v-for 把一個數組對應為一組元素
我們可以用 v-for
指令基於一個數組來渲染一個列表。v-for
指令需要使用 item in items
形式的特殊語法,其中 items
是源數據數組,而 item
則是被迭代的數組元素的別名。
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul> var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
在 v-for
塊中,我們可以訪問所有父作用域的屬性。v-for
還支援一個可選的第二個參數,即當前項的索引。
<ul id="example-2"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul> var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
你也可以用 of
替代 in
作為分隔符,因為它更接近 JavaScript 迭代器的語法:
<div v-for="item of items"></div>
在 v-for 中使用對象
- 你也可以用
v-for
來遍歷一個對象的屬性。
<ul id="v-for-object" class="demo"> <li v-for="value in object"> {{ value }} </li> </ul> new Vue({ el: '#v-for-object', data: { object: { title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' } } })
- 你也可以提供第二個的參數為 property 名稱 (也就是鍵名):
<div v-for="(value, name) in object"> {{ name }}: {{ value }} </div>
- 還可以用第三個參數作為索引:
<div v-for="(value, name, index) in object"> {{ index }}. {{ name }}: {{ value }} </div>
維護狀態
當 Vue 正在更新使用 v-for
渲染的元素列表時,它默認使用「就地更新」的策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序,而是就地更新每個元素,並且確保它們在每個索引位置正確渲染。這個類似 Vue 1.x 的 track-by="$index"
。
這個默認的模式是高效的,但是只適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key
屬性:
<div v-for="item in items" v-bind:key="item.id"> <!-- 內容 --> </div>
建議儘可能在使用 v-for
時提供 key
attribute,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升。
因為它是 Vue 識別節點的一個通用機制,key
並不僅與 v-for
特別關聯。後面我們將在指南中看到,它還具有其它用途。
不要使用對象或數組之類的非基本類型值作為 v-for
的 key
。請用字元串或數值類型的值。
數據更新檢測
1. 變異方法 (mutation method) Vue 將被偵聽的數組的變異方法進行了包裹,所以它們也將會觸發視圖更新。這些被包裹過的方法包括: push() pop() shift() unshift() splice() sort() reverse() 你可以打開控制台,然後對前面例子的 items 數組嘗試調用變異方法。 比如 example1.items.push({ message: 'Baz' })。 2. 替換數組 變異方法,顧名思義,會改變調用了這些方法的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如 filter()、concat() 和 slice() 。它們不會改變原始數組,而總是返回一個新數組。當使用非變異方法時,可以用新數組替換舊數組: example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) }) 你可能認為這將導致 Vue 丟棄現有 DOM 並重新渲染整個列表。幸運的是,事實並非如此。Vue 為了使得 DOM 元素得到最大範圍的重用而實現了一些智慧的啟發式方法,所以用一個含有相同元素的數組去替換原來的數組是非常高效的操作。 3. 注意事項 由於 JavaScript 的限制,Vue 不能檢測以下數組的變動: 當你利用索引直接設置一個數組項時,例如:vm.items[indexOfItem] = newValue 當你修改數組的長度時,例如:vm.items.length = 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.set(object, propertyName, 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 循環中) 你可以使用一個方法: <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 中使用值範圍
v-for
也可以接受整數。在這種情況下,它會把模板重複對應次數。
<div> <span v-for="n in 10">{{ n }} </span> </div> 結果: 1 2 3 4 5 6 7 8 9 10
在 template 上使用v-for
類似於 v-if
,你也可以利用帶有 v-for
的 ` 來循環渲染一段包含多個元素的內容。比如:
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </template> </ul>
v-for與 v-if一同使用
注意我們不推薦在同一元素上使用 v-if
和 v-for
當它們處於同一節點,v-for
的優先順序比 v-if
更高,這意味著 v-if
將分別重複運行於每個 v-for
循環中。當你只想為部分項渲染節點時,這種優先順序的機制會十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
上面的程式碼將只渲染未完成的 todo。
而如果你的目的是有條件地跳過循環的執行,那麼可以將 v-if
置於外層元素 (或 `)上。如:
<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
在組件上使用v-for
這部分內容假定你已經了解組件相關知識。你也完全可以先跳過它,以後再回來查看。
以後學習完組件回來再看。
事件處理
監聽事件
可以用 v-on 指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 程式碼。 <div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
事件處理方法
然而許多事件處理邏輯會更為複雜,所以直接把 JavaScript 程式碼寫在 v-on
指令中是不可行的。因此 v-on
還可以接收一個需要調用的方法名稱。
<div id="example-2"> <!-- `greet` 是在下面定義的方法名 --> <button v-on:click="greet">Greet</button> </div> var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 對象中定義方法 methods: { greet: function (event) { // `this` 在方法里指向當前 Vue 實例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } }) // 也可以用 JavaScript 直接調用方法 example2.greet() // => 'Hello Vue.js!'
內聯處理器中的方法
除了直接綁定到一個方法,也可以在內聯 JavaScript 語句中調用方法:
<div id="example-3"> <button v-on:click="say('hi')">Say hi</button> <button v-on:click="say('what')">Say what</button> </div> new Vue({ el: '#example-3', methods: { say: function (message) { alert(message) } } })
有時也需要在內聯語句處理器中訪問原始的 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) } }
事件修飾符
在事件處理程式中調用 event.preventDefault()
或 event.stopPropagation()
是非常常見的需求。儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。
為了解決這個問題,Vue.js 為 v-on
提供了事件修飾符。之前提過,修飾符是由點開頭的指令後綴來表示的。
.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止單擊事件繼續傳播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重載頁面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修飾符可以串聯 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修飾符 --> <form v-on:submit.prevent></form> <!-- 添加事件監聽器時使用事件捕獲模式 --> <!-- 即內部元素觸發的事件先在此處理,然後才交由內部元素進行處理 --> <div v-on:click.capture="doThis">...</div> <!-- 只當在 event.target 是當前元素自身時觸發處理函數 --> <!-- 即事件不是從內部元素觸發的 --> <div v-on:click.self="doThat">...</div> 2.1.4 新增 <!-- 點擊事件將只會觸發一次 --> <a v-on:click.once="doThis"></a> 不像其它只能對原生的 DOM 事件起作用的修飾符,.once 修飾符還能被用到自定義的組件事件上 2.3.0 新增 Vue 還對應 addEventListener 中的 passive 選項提供了 .passive 修飾符 <!-- 滾動事件的默認行為 (即滾動行為) 將會立即觸發 --> <!-- 而不會等待 `onScroll` 完成 --> <!-- 這其中包含 `event.preventDefault()` 的情況 --> <div v-on:scroll.passive="onScroll">...</div> 這個 .passive 修飾符尤其能夠提升移動端的性能。
注意事項:
使用修飾符時,順序很重要;相應的程式碼會以同樣的順序產生。因此,用
v-on:click.prevent.self
會阻止所有的點擊,而v-on:click.self.prevent
只會阻止對元素自身的點擊。不要把
.passive
和.prevent
一起使用,因為.prevent
將會被忽略,同時瀏覽器可能會向你展示一個警告。請記住,.passive
會告訴瀏覽器你不想阻止事件的默認行為。
案件修飾符
在監聽鍵盤事件時,我們經常需要檢查詳細的按鍵。Vue 允許為 `v-on` 在監聽鍵盤事件時添加按鍵修飾符: <!-- 只有在 `key` 是 `Enter` 時調用 `vm.submit()` --> <input v-on:keyup.enter="submit"> 你可以直接將 KeyboardEvent.key 暴露的任意有效按鍵名轉換為 kebab-case 來作為修飾符。 <input v-on:keyup.page-down="onPageDown"> 在上述示例中,處理函數只會在 $event.key 等於 PageDown 時被調用。
## 按鍵碼 keyCode 的事件用法已經被廢棄了並可能不會被最新的瀏覽器支援。 使用 keyCode attribute 也是允許的: <input v-on:keyup.13="submit"> 為了在必要的情況下支援舊瀏覽器,Vue 提供了絕大多數常用的按鍵碼的別名: .enter .tab .delete (捕獲「刪除」和「退格」鍵) .esc .space .up .down .left .right 有一些按鍵 (.esc 以及所有的方向鍵) 在 IE9 中有不同的 key 值, 如果你想支援 IE9,這些內置的別名應該是首選。 你還可以通過全局 config.keyCodes 對象自定義按鍵修飾符別名: // 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
系統修飾鍵
可以用如下修飾符來實現僅在按下相應按鍵時才觸發滑鼠或鍵盤事件的監聽器。 .ctrl .alt .shift .meta 注意:在 Mac 系統鍵盤上,meta 對應 command 鍵 (⌘)。在 Windows 系統鍵盤 meta 對應 Windows 徽標鍵 (⊞)。 例如: <!-- Alt + C --> <input @keyup.alt.67="clear"> <!-- Ctrl + Click --> <div @click.ctrl="doSomething">Do something</div> # .exact 修飾符 .exact 修飾符允許你控制由精確的系統修飾符組合觸發的事件。 <!-- 即使 Alt 或 Shift 被一同按下時也會觸發 --> <button @click.ctrl="onClick">A</button> <!-- 有且只有 Ctrl 被按下的時候才觸發 --> <button @click.ctrl.exact="onCtrlClick">A</button> <!-- 沒有任何系統修飾符被按下的時候才觸發 --> <button @click.exact="onClick">A</button> # 滑鼠按鈕修飾符 .left .right .middle 這些修飾符會限制處理函數僅響應特定的滑鼠按鈕。
為什麼在HTML中監聽事件
你可能注意到這種事件監聽的方式違背了關注點分離 (separation of concern) 這個長期以來的優良傳統。但不必擔心,因為所有的 Vue.js 事件處理方法和表達式都嚴格綁定在當前視圖的 ViewModel 上,它不會導致任何維護上的困難。實際上,使用
v-on
有幾個好處:
- 掃一眼 HTML 模板便能輕鬆定位在 JavaScript 程式碼里對應的方法。
- 因為你無須在 JavaScript 裏手動綁定事件,你的 ViewModel 程式碼可以是非常純粹的邏輯,和 DOM 完全解耦,更易於測試。
- 當一個 ViewModel 被銷毀時,所有的事件處理器都會自動被刪除。你無須擔心如何清理它們
表單輸入綁定
基礎用法
你可以用
v-model
指令在表單 input、textarea 及 select 元素上創建雙向數據綁定。它會根據控制項類型自動選取正確的方法來更新元素。儘管有些神奇,但v-model
本質上不過是語法糖。它負責監聽用戶的輸入事件以更新數據,並對一些極端場景進行一些特殊處理。
v-model
會忽略所有表單元素的value
、checked
、selected
attribute 的初始值而總是將 Vue 實例的數據作為數據來源。你應該通過 JavaScript 在組件的data
選項中聲明初始值。
v-model
在內部為不同的輸入元素使用不同的屬性並拋出不同的事件:
- text 和 textarea 元素使用
value
屬性和input
事件;- checkbox 和 radio 使用
checked
屬性和change
事件;- select 欄位將
value
作為 prop 並將change
作為事件。對於需要使用輸入法 (如中文、日文、韓文等) 的語言,你會發現
v-model
不會在輸入法組合文字過程中得到更新。如果你也想處理這個過程,請使用input
事件。
1. 文本 <input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p> 2. 多行文本 <span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <br> <textarea v-model="message" placeholder="add multiple lines"></textarea> 在文本區域插值 (<textarea>{{text}}</textarea>) 並不會生效,應用 v-model 來代替 3. 複選框 單個複選框,綁定到布爾值: <input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label> 多個複選框,綁定到同一個數組: <div id='example-3'> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <br> <span>Checked names: {{ checkedNames }}</span> </div> new Vue({ el: '#example-3', data: { checkedNames: [] } }) 4. 單選按鈕 <div id="example-4"> <input type="radio" id="one" value="One" v-model="picked"> <label for="one">One</label> <br> <input type="radio" id="two" value="Two" v-model="picked"> <label for="two">Two</label> <br> <span>Picked: {{ picked }}</span> </div> new Vue({ el: '#example-4', data: { picked: '' } }) 5. 選擇框 單選時: <div id="example-5"> <select v-model="selected"> <option disabled value="">請選擇</option> <option>A</option> <option>B</option> <option>C</option> </select> <span>Selected: {{ selected }}</span> </div> new Vue({ el: 'example-5', data: { selected: '' } }) 如果 v-model 表達式的初始值未能匹配任何選項,<select> 元素將被渲染為「未選中」狀態。在 iOS 中,這會使用戶無法選擇第一個選項。因為這樣的情況下,iOS 不會觸發 change 事件。因此,更推薦像上面這樣提供一個值為空的禁用選項。 多選時 (綁定到一個數組): <div id="example-6"> <select v-model="selected" multiple style="width: 50px;"> <option>A</option> <option>B</option> <option>C</option> </select> <br> <span>Selected: {{ selected }}</span> </div> new Vue({ el: '#example-6', data: { selected: [] } }) 用 v-for 渲染的動態選項: <select v-model="selected"> <option v-for="option in options" v-bind:value="option.value"> {{ option.text }} </option> </select> <span>Selected: {{ selected }}</span> new Vue({ el: '...', data: { selected: 'A', options: [ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ] } })
值綁定
1. 對於單選按鈕,複選框及選擇框的選項,`v-model` 綁定的值通常是靜態字元串 (對於複選框也可以是布爾值): <!-- 當選中時,`picked` 為字元串 "a" --> <input type="radio" v-model="picked" value="a"> <!-- `toggle` 為 true 或 false --> <input type="checkbox" v-model="toggle"> <!-- 當選中第一個選項時,`selected` 為字元串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select> 但是有時我們可能想把值綁定到 Vue 實例的一個動態屬性上,這時可以用 `v-bind` 實現,並且這個屬性的值可以不是字元串。 2. 複選框 <input type="checkbox" v-model="toggle" true-value="yes" false-value="no" > // 當選中時 vm.toggle === 'yes' // 當沒有選中時 vm.toggle === 'no' 這裡的 true-value 和 false-value attribute 並不會影響輸入控制項的 value attribute,因為瀏覽器在提交表單時並不會包含未被選中的複選框。如果要確保表單中這兩個值中的一個能夠被提交,(比如「yes」或「no」),請換用單選按鈕。 3. 單選按鈕 <input type="radio" v-model="pick" v-bind:value="a"> // 當選中時 vm.pick === vm.a 4. 選中框的選項 <select v-model="selected"> <!-- 內聯對象字面量 --> <option v-bind:value="{ number: 123 }">123</option> </select> // 當選中時 typeof vm.selected // => 'object' vm.selected.number // => 123
修飾符
1. .lazy 在默認情況下,v-model 在每次 input 事件觸發後將輸入框的值與數據進行同步 (除了上述輸入法組合文字時)。你可以添加 lazy 修飾符,從而轉變為使用 change 事件進行同步: <!-- 在「change」時而非「input」時更新 --> <input v-model.lazy="msg" > 2. .number 如果想自動將用戶的輸入值轉為數值類型,可以給 v-model 添加 number 修飾符: <input v-model.number="age" type="number"> 這通常很有用,因為即使在 type="number" 時,HTML 輸入元素的值也總會返回字元串。如果這個值無法被 parseFloat() 解析,則會返回原始的值。 3. .trim 如果要自動過濾用戶輸入的首尾空白字元,可以給 v-model 添加 trim 修飾符: <input v-model.trim="msg">
在組件上使用v-model
如果你還不熟悉 Vue 的組件,可以暫且跳過這裡。
在深入了解組件章節會涉及到此內容。
組件基礎
基本實例
這裡有一個 Vue 組件的示例: // 定義一個名為 button-counter 的新組件 Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) 組件是可復用的 Vue 實例,且帶有一個名字:在這個例子中是 <button-counter>。我們可以在一個通過 new Vue 創建的 Vue 根實例中,把這個組件作為自定義元素來使用: <div id="components-demo"> <button-counter></button-counter> </div> new Vue({ el: '#components-demo' }) 因為組件是可復用的 Vue 實例,所以它們與 new Vue 接收相同的選項,例如 data、computed、watch、methods 以及生命周期鉤子等。僅有的例外是像 el 這樣根實例特有的選項。
組件的復用
1. 你可以將組件進行任意次數的復用: <div id="components-demo"> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> </div> 注意當點擊按鈕時,每個組件都會各自獨立維護它的 count。因為你每用一次組件,就會有一個它的新實例被創建。 2. data 必須是一個函數 當我們定義這個 <button-counter> 組件時,你可能會發現它的 data 並不是像這樣直接提供一個對象: data: { count: 0 } 取而代之的是,一個組件的 data 選項必須是一個函數,因此每個實例可以維護一份被返回對象的獨立的拷貝: data: function () { return { count: 0 } } 如果 Vue 沒有這條規則,點擊一個按鈕就可能影響到其它所有實例.
組件的組織
通常一個應用會以一棵嵌套的組件樹的形式來組織:
例如,你可能會有頁頭、側邊欄、內容區等組件,每個組件又包含了其它的像導航鏈接、博文之類的組件。
為了能在模板中使用,這些組件必須先註冊以便 Vue 能夠識別。這裡有兩種組件的註冊類型:全局註冊和局部註冊。至此,我們的組件都只是通過 Vue.component
全局註冊的:
Vue.component('my-component-name', { // ... options ... })
全局註冊的組件可以用在其被註冊之後的任何 (通過 new Vue
) 新創建的 Vue 根實例,也包括其組件樹中的所有子組件的模板中。
到目前為止,關於組件註冊你需要了解的就這些了,如果你閱讀完本頁內容並掌握了它的內容,我們會推薦你再回來把組件註冊讀完。
通過Prop向子組件傳遞數據
早些時候,我們提到了創建一個博文組件的事情。問題是如果你不能向這個組件傳遞某一篇博文的標題或內容之類的我們想展示的數據的話,它是沒有辦法使用的。這也正是 prop 的由來。
Prop 是你可以在組件上註冊的一些自定義 attribute。當一個值傳遞給一個 prop attribute 的時候,它就變成了那個組件實例的一個屬性。為了給博文組件傳遞一個標題,我們可以用一個 props
選項將其包含在該組件可接受的 prop 列表中:
Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
一個組件默認可以擁有任意數量的 prop,任何值都可以傳遞給任何 prop。在上述模板中,你會發現我們能夠在組件實例中訪問這個值,就像訪問 data
中的值一樣。
一個 prop 被註冊之後,你就可以像這樣把數據作為一個自定義 attribute 傳遞進來:
<blog-post title="My journey with Vue"></blog-post> <blog-post title="Blogging with Vue"></blog-post> <blog-post title="Why Vue is so fun"></blog-post>
然而在一個典型的應用中,你可能在 data
里有一個博文的數組:
new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
並想要為每篇博文渲染一個組件:
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
如上所示,你會發現我們可以使用 v-bind
來動態傳遞 prop。這在你一開始不清楚要渲染的具體內容,比如從一個 API 獲取博文列表的時候,是非常有用的。
單個根元素
當構建一個 “ 組件時,你的模板最終會包含的東西遠不止一個標題:
<h3>{{ title }}</h3>
最最起碼,你會包含這篇博文的正文:
<h3>{{ title }}</h3> <div v-html="content"></div>
然而如果你在模板中嘗試這樣寫,Vue 會顯示一個錯誤,並解釋道 every component must have a single root element (每個組件必須只有一個根元素)。你可以將模板的內容包裹在一個父元素內,來修復這個問題,例如:
<div class="blog-post"> <h3>{{ title }}</h3> <div v-html="content"></div> </div>
監聽子組件事件
同時子組件可以通過調用內建的 $emit 方法 並傳入事件名稱來觸發一個事件: <button v-on:click="$emit('enlarge-text')"> Enlarge text </button> 有了這個 v-on:enlarge-text="postFontSize += 0.1" 監聽器,父級組件就會接收該事件並更新 postFontSize 的值。
使用事件拋出一個值
有的時候用一個事件來拋出一個特定的值是非常有用的。例如我們可能想讓 <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 attribute 綁定到一個名叫 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>
通過插槽分發內容
和 HTML 元素一樣,我們經常需要向一個組件傳遞內容,像這樣: <alert-box> Something bad happened. </alert-box> 可能會渲染出這樣的東西: 幸好,Vue 自定義的 <slot> 元素讓這變得非常簡單: Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` }) 如你所見,我們只要在需要的地方加入插槽就行了——就這麼簡單!
動態組件
有的時候,在不同組件之間進行動態切換是非常有用的,比如在一個多標籤的介面里:
上述內容可以通過 Vue 的 `元素加一個特殊的
is` attribute 來實現:
<!-- 組件會在 `currentTabComponent` 改變時改變 --> <component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent
可以包括
- 已註冊組件的名字,或
- 一個組件的選項對象
你可以在這裡查閱並體驗完整的程式碼,或在這個版本了解綁定組件選項對象,而不是已註冊組件名的示例。
請留意,這個 attribute 可以用於常規 HTML 元素,但這些元素將被視為組件,這意味著所有的 attribute 都會作為 DOM attribute 被綁定。對於像 value
這樣的 property,若想讓其如預期般工作,你需要使用 .prop
修飾器。
到目前為止,關於動態組件你需要了解的大概就這些了,如果你閱讀完本頁內容並掌握了它的內容,我們會推薦你再回來把動態和非同步組件讀完。
解析DOM模板時的注意事項
有些 HTML 元素,諸如 <ul>、<ol>、<table> 和 <select>,對於哪些元素可以出現在其內部是有嚴格限制的。而有些元素,諸如 <li>、<tr> 和 <option>,只能出現在其它某些特定的元素內部。 這會導致我們使用這些有約束條件的元素時遇到一些問題。例如: <table> <blog-post-row></blog-post-row> </table> 這個自定義組件 <blog-post-row> 會被作為無效的內容提升到外部,並導致最終渲染結果出錯。幸好這個特殊的 is attribute 給了我們一個變通的辦法: <table> <tr is="blog-post-row"></tr> </table> 需要注意的是如果我們從以下來源使用模板的話,這條限制是不存在的: 字元串 (例如:template: '...') 單文件組件 (.vue) <script type="text/x-template"> 到這裡,你需要了解的解析 DOM 模板時的注意事項——實際上也是 Vue 的全部必要內容,大概就是這些了。恭喜你!接下來還有很多東西要去學習,不過首先,我們推薦你先休息一下,試用一下 Vue,自己隨意做些好玩的東西。
VUE基礎學習小結
作為一個後端開發人員,學習VUE,了解VUE,到現在為止,已經把VUE的基礎重要部分了解完畢了。終點還是要放在後端的基礎及技術方面。VUE學習到此結束。
最後來個個人小感悟:我們的目標是什麼?
我們的目標是什麼?幾點起來都可以?如果你給自己定的目標就是看書和寫讀書筆記,或者把讀書筆記寫成思維導圖,結果可能收效甚微。你讀100本書和讀10本書,只是數字的差別而已。 但是,目標明確與不明確,對於現在的我們,真的重要嗎?真的需要刻意的去定一下現在的目標是什麼?背幾個單詞,學幾分鐘技術,讀幾頁書嗎?如果把目標定在了固定的數字,當我們達到這個數字的時候,是不是內心就會放鬆,就會感覺到滿足,而不會繼續的背下去,學下去,讀下去了。是的, 這樣的結果同樣很重要。就算是目標不明確,對於不同的人有不同的看法,對於同一個人在不同的時間下也有不同的看法。任何事情沒有好壞,就看自己的思維現在是什麼想的。再比如說,現在是早上5點,別人都說早上背單詞好,而我卻在讀書寫字,這樣好嗎?誰能說好,誰能說不好。有一個大致的方向,往前走就對了。不然計劃,目標,終將只是成為條條框框,限制了你一個又一個的人生。
嘿嘿。2020年03月03日06:28:57。over。