vuejs 語法
簡介
採用簡潔的模板語法來宣告式地將資料渲染進 DOM 的系統
1 | <!-- html part --> |
1 | //js part |
在 html tag 中 加入v-
開頭的attributes以實作邏輯
元件化應用構建
v-bind
: todo-item 元件接受一個"prop",類似於一個自定義 attribute。這個 prop 名為 todo。
1 | <div id="app"> |
Instance
用 Vue 函式建立一個新的 Vue 例項
當一個 Vue 例項被建立時,它將 data 物件中的所有的 property 加入到 Vue 的響應式系統中
只有當例項被建立時就已經存在於 data 中的 property 才是響應式的
唯一的例外是使用Object.freeze()
,這會阻止修改現有的 property,也意味著響應系統無法再追蹤變化。
1 | // 我們的資料物件 |
生命週期鉤子
created
、mounted
、updated
和 destroyed
不要在選項 property 或 callback 使用箭頭函式。因為箭頭函式並沒有 this,this 會作為變數一直向上級詞法作用域查詢,直至找到為止,經常導致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之類的錯誤。
資料繫結
資料繫結最常見的形式就是使用“Mustache”語法 (雙大括號) 的文字插值:
雙大括號會將資料解釋為普通文字,而非 HTML 程式碼
<span>Message: {{ msg }}</span>
可使用表示式
{{ ok ? 'YES' : 'NO' }}
透過使用 v-once
指令,你也能執行一次性地插值,當資料改變時,插值處的內容不會更新。但請留心這會影響到該節點上的其它資料繫結:
<span v-once>這個將不會改變: {{ msg }}</span>
為了輸出真正的 HTML,你需要使用 v-html
指令
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
動態渲染 HTML 可能會非常危險,因為它很容易導致 XSS 攻擊。請只對可信內容使用 HTML 插值,絕不要對使用者提供的內容使用插值。
Mustache 語法不能作用在 HTML attribute 上,應該使用 v-bind
指令:
<div v-bind:id="dynamicId"></div>
<div v-bind:id="'list-' + id"></div>
指令
指令 (Directives) 是帶有 v-
字首的特殊 attribute。
指令 attribute 的值預期是單個 JavaScript 表示式 (v-for
是例外)。
指令的職責是,當表示式的值改變時,將其產生的連帶影響,響應式地作用於 DOM。
一些指令能夠接收一個“引數”,在指令名稱之後以冒號表示。例如,v-bind
指令可以用於響應式地更新 HTML attribute
<a v-bind:href>
從 2.6.0 開始,可以用方括號括起來的 JavaScript 表示式作為一個指令的引數:
<a v-bind:[attributeName]="url"> ... </a>
動態引數預期會求出一個字串,異常情況下值為 null。這個特殊的 null 值可以用於移除繫結。任何其它非字串型別的值都將會觸發一個警告。
修飾符
修飾符 (modifier) 是以 .
指明的特殊字尾,用於指出一個指令應該以特殊方式繫結。例如.prevent
修飾符告訴 v-on
指令對於觸發的事件呼叫 event.preventDefault()
<form v-on:submit.prevent="onSubmit">...</form>
Vue 為 v-bind
和 v-on
這兩個最常用的指令,提供了特定簡寫:
1 | <!-- 完整語法 --> |
計算屬性(computed)
對於任何複雜邏輯,你都應當使用計算屬性
計算屬性只在相關響應式依賴發生改變時它們才會重新求值,這是比使用function好的地方
1 | var vm = new Vue({ |
下面的計算屬性將不再更新,因為 Date.now() 不是響應式依賴:
1 | computed: { |
計算屬性預設只有 getter,不過在需要時你也可以提供一個 setter:
1 | computed: { |
watch
監視某個資料,當其發生改變,這個函式就會執行
1 | <div id="watch-example"> |
Class 與 Style 繫結
<div v-bind:class="{ active: isActive }"></div>
上面的語法表示 active 這個 class 存在與否將取決於資料 property isActive 的 truthiness。
我們也可以在這裡繫結一個返回物件的計算屬性。這是一個常用且強大的模式:
1 | <div v-bind:class="classObject"></div> |
陣列語法中也可以使用物件語法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
v-if
1 | <div v-if="type === 'A'"> |
在html用template
作為隱形元素,不渲染
1 | <template v-if="ok"> |
Vue 會儘可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染。這麼做除了使 Vue 變得非常快之外,還有其它一些好處。例如,如果你允許使用者在不同的登入方式之間切換:
1 | <template v-if="loginType === 'username'"> |
那麼在上面的程式碼中切換 loginType 將不會清除使用者已經輸入的內容。因為兩個模板使用了相同的元素,<input>
不會被替換掉——僅僅是替換了它的 placeholder。
帶有 v-show
的元素始終會被渲染並保留在 DOM 中。v-show
只是簡單地切換元素的 CSS property display。
注意,v-show
不支援 <template>
元素,也不支援 v-else
。
v-if
是“真正”的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子元件適當地被銷毀和重建。
v-if
也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。
相比之下,v-show
就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS 進行切換。
一般來說,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show 較好;如果在執行時條件很少改變,則使用 v-if 較好。
不推薦同時使用 v-if 和 v-for。請查閱風格指南以獲取更多資訊。
v-for
v-for
還支援一個可選的第二個引數,即當前項的索引。
1 | <ul id="example-2"> |
你也可以用 of
替代 in
作為分隔符,因為它更接近 JavaScript 迭代器的語法:
<div v-for="item of items"></div>
可以用 v-for
來遍歷一個物件的 property。
也可以提供第二個的引數為 property 名稱 (也就是key)
1 | <ul id="v-for-object" class="demo"> |
v-for
渲染的元素列表時,它預設使用“就地更新”的策略。如果資料項的順序被改變,Vue 將不會移動 DOM 元素來匹配資料項的順序,而是就地更新每個元素,並且確保它們在每個索引位置正確渲染
這個預設的模式是高效的,但是隻適用於不依賴子元件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一的 key attribute
自動檢測變化的限制
由於 JavaScript 的限制,Vue 不能檢測陣列(array)和物件(object)的變化
Vue 將被偵聽的陣列的變更方法進行了包裹,所以它們也將會觸發檢視更新。這些被包裹過的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
相比之下,也有非變更方法,例如 filter()
、concat()
和 slice()
。它們不會變更原始陣列,而總是返回一個新陣列
is
注意這裡的 is="todo-item"
attribute。這種做法在使用 DOM 模板時是十分必要的,因為在 <ul>
元素內只有 <li>
元素會被看作有效內容。這樣做實現的效果與 <todo-item>
相同,但是可以避開一些潛在的瀏覽器解析錯誤。
1 | <div id="todo-list-example"> |
v-on: dom event
用特殊變數 $event
訪問原始的 DOM 事件
1 | <button v-on:click="warn('Form cannot be submitted yet.', $event)"> |
Vue.js 為 v-on
提供了事件修飾符。之前提過,修飾符是由點開頭的指令字尾來表示的。
.stop
.prevent
.capture
.self
.once
.passive
1 | <!-- 阻止單擊事件繼續傳播 --> |
v-on: input
Vue 允許為 v-on
在監聽鍵盤事件時新增按鍵修飾符:
你可以直接將 KeyboardEvent.key 暴露的任意有效按鍵名轉換為 kebab-case 來作為修飾符。
1 | <!-- 只有在 `key` 是 `Enter` 時呼叫 `vm.submit()` --> |
滑鼠按鈕修飾符
.left
.right
.middle
v-model: Input
這是 Vue 完全贏過 React 的部分
你可以用 v-model
指令在表單 <input>
、<textarea>
及 <select>
元素上建立雙向資料繫結。它會根據控制元件型別自動選取正確的方法來更新元素。儘管有些神奇,但 v-model
本質上不過是語法糖。它負責監聽使用者的輸入事件以更新資料,並對一些極端場景進行一些特殊處理。
v-model
只看binding data的值,會忽略所有表單元素的 value、checked、selected attribute 的初始值
v-model
在內部為不同的輸入元素使用不同的 property 並丟擲不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 欄位將 value 作為 prop 並將 change 作為事件。
1 | <input v-model="searchText"> |
1 | <!-- checkbox example --> |
1 | <!-- select example --> |
如果 v-model
表示式的初始值未能匹配任何選項,<select>
元素將被渲染為“未選中”狀態。在 iOS 中,這會使使用者無法選擇第一個選項。因為這樣的情況下,iOS 不會觸發 change
事件。因此,更推薦像上面這樣提供一個值為空的禁用選項。
input 繫結 variable
1 | <input |
1 | <!-- v-model as object --> |
v-model: 修飾符
.lazy
在預設情況下,v-model 在每次 input 事件觸發後將輸入框的值與資料進行同步 (除了上述輸入法組合文字時)。
新增 lazy 修飾符,等到滑鼠移到輸入框外,觸發 change 事件才更新。
.number
自動將使用者的輸入值轉為數值型別
<input v-model.number="age" type="number">
這通常很有用,因為即使在 type="number" 時,HTML 輸入元素的值也總會返回字串。如果這個值無法被 parseFloat() 解析,則會返回原始的值。
.trim
如果要自動過濾使用者輸入的首尾空白字元,可以給 v-model 新增 trim 修飾符:
元件 Components
1 | // 定義一個名為 button-counter 的新元件 |
元件是可復用的 Vue 例項,且帶有一個名字:在這個例子中是 <button-counter>
。我們可以在一個透過 new Vue 建立的 Vue 根例項中,把這個元件作為自定義元素來使用:
1 | <div id="components-demo"> |
因為元件是可復用的 Vue 例項,所以它們與 new Vue 接收相同的選項,例如 data、computed、watch、methods 以及生命週期鉤子等。僅有的例外是像 el 這樣根例項特有的選項。
為了能在模板中使用,這些元件必須先註冊以便 Vue 能夠識別。這裡有兩種元件的註冊型別:全域性註冊和區域性註冊。至此,我們的元件都只是透過 Vue.component 全域性註冊的
1 | Vue.component('my-component-name', { |
全域性註冊的元件可以用在其被註冊之後的任何 (透過 new Vue) 新建立的 Vue 根例項,也包括其元件樹中的所有子元件的模板中。
而區域性註冊的好處是當你使用的是webpack這樣的構建系統時,如果是用全域性註冊的這種方法註冊的元件,那麼當你不使用某一個元件的時候,它仍然會存在最終的構建結果之中,這就增加的無謂的js下載。
data 是一個函式
data 是一個函式,因此每個例項可以維護一份被返回物件的獨立的複製:
1 | data: function () { |
範例
每個元件必須只有一個根元素。你可以將模板的內容包裹在一個父元素內,來修復這個問題,例如:
1 | <div class="blog-post"> |
當元件變得越來越複雜的時候,為每個相關的資訊定義一個 prop 會變得很麻煩:
所以是時候重構一下這個 <blog-post>
元件了,讓它變成接受一個單獨的 post prop:
1 | <blog-post |
父級元件可以像處理 native DOM 事件一樣透過 v-on
監聽子元件例項的任意事件
1 | <blog-post |
使用事件傳遞一個值
1 | <button v-on:click="$emit('enlarge-text', 0.1)"> |
emit input的值使 custom-input 可以更新v-model
1 | <custom-input v-model="searchText"></custom-input> |
slot
1 | <div id="app"> |
動態切換component
有的時候,在不同元件之間進行動態切換是非常有用的,比如在一個多標籤的介面裡:
上述內容可以透過 Vue 的 <component>
元素加一個特殊的 is attribute 來實現:
1 | <!-- 元件會在 `currentTabComponent` 改變時改變 --> |
在上述示例中,currentTabComponent 可以包括已註冊元件的名字,或一個元件的選項物件
解析 DOM 模板時的注意事項
有些 HTML 元素,諸如 <ul>
、<ol>
、<table>
和 <select>
,對於哪些元素可以出現在其內部是有嚴格限制的。而有些元素,諸如 <li>
、<tr>
和 <option>
,只能出現在其它某些特定的元素內部。
1 | <!-- wrong --> |
需要注意的是如果我們從以下來源使用模板的話,這條限制是不存在的:
- 字串 (例如:template: '...')
- 單檔案元件 (.vue)
<script type="text/x-template">
實作
使用vue cli
說回App.vue這個檔案,這是一個檢視(或者說元件和頁面),想像一下我們的index.html中什麼也沒有,只有一個檢視,這個檢視相當於一個容器,然後我們往這個容器中放各種各樣的積木(其他元件或者其他頁面)
將App.vue
放到#app
中,然後以<App/>
來指代我們的#app
。
1 | import Vue from 'vue' |
參考資料
- https://cn.vuejs.org/v2/guide/
- https://peterhpchen.github.io/
- https://cythilya.github.io/2017/10/11/vue-component-slot/