Vue.js: data、v-model 與雙向綁定
14 Apr 2017如何利用 data 與 v-model 實現雙向綁定?原理、語法與表單元件範例。
data
vue instance 可傳入選項物件,其中可設定 data(資料)。
data 是用來
- 儲存元件內部狀態或資料
- 和 v-model 合作實現雙向綁定
資料型別
data 可以是 object 或 function,但元件(component)的 data 只能是 function,這是因為元件內各自擁有自己的 data,而非共用的關係。
原理
在 observer 中,data 透過Object.defineProperty()
為元件內屬性重新定義getter
和setter
method。當 data 被修改時,會透過setter
通知變化,觸發 watcher 重新計算、更新與渲染 DOM element。
語法
設定 data,如下所示。
<div id="app">
${ message }
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
message: 'Hello World!',
},
});
// 由於部落格會把使用雙花括號的內容吃掉,所以設定 delimiters 以顯示完整程式碼。
元件的 data 只能是 function。
var Component = Vue.extend({
data: function() {
return {
msg: 'Hello World!',
};
},
});
打開瀏覽器會看到
由於 vue instance 重新設定了getter
和setter
方法,因此 vm.$data.message
完全等於 vm.message
。
vm.$data.message === vm.message; // true
注意
- 自訂屬性名稱不要使用
$
或_
開頭,以免和 vue 所定義的屬性或 API 衝突。因此,對於使用$
或_
開頭命名的屬性,vue 都不會處理。 - 動態加入的屬性無法擁有 reactivity 的特性(例如:雙向綁定等),因此在建立實體前要先宣告所有會用到的屬性。
- 如果要對 data 做深拷貝(deep copy),可將
vm.$data
傳入JSON.parse(JSON.stringify())
。
v-model
v-model 是綁定在表單元件或自訂元件上,為實現雙向綁定用的。表單元件像是<input>
、<select>
和<textarea>
,分別說明如下。
Input Text 單行文字
<div id="app">
<input type="text" v-model="message" />
<div>${ message }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
message: 'Hello World!',
},
});
Textarea 多行文字
<div id="app">
<textarea v-model="message"/></textarea>
<div>${ message }</div>
</div>
js 程式碼同上-Input Text 單行文字。
Checkbox 複選框
使用v-model
綁定 toggle 的值:當 toggle 為 true,勾選此項目;當 toggle 為 false,不勾選此項目。
一開始先設定 toggle 為 false,不勾選此項目。
<div id="app">
<input type="checkbox" v-model="toggle" /><label>我是複選框</label>
toggle = ${ toggle }
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
toggle: false,
},
});
再來,可在開發者工具的 console tab 上操作 toggle 為 true,勾選此項目。
toggle = true;
或依據給訂的值決定是否勾選,若 toggle 為 1 則勾選,若 toggle 為 2 則不勾選。
<div id="app">
<input type="checkbox" v-model="toggle" :true-value="1" :false-value="2" />
<label>我是複選框</label>
toggle = ${ toggle }
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
toggle: 0,
},
});
設定 toggle 為 1,勾選此項目。
vm.toggle = 1;
設定 toggle 為 2,不勾選此項目。
vm.toggle = 2;
將多個 checkbox 綁定到同一個群組。在這裡設定 group 為一個陣列,裡面存放勾選選項的 value 字串。例如,若 group 為 ["1", "2"]
,則勾選第一個和第二個複選框。
<div id="app">
<input type="checkbox" v-model="group" value="1" /><label>我是複選框 1</label>
<input type="checkbox" v-model="group" value="2" /><label>我是複選框 2</label>
<input type="checkbox" v-model="group" value="3" /><label>我是複選框 3</label>
<div>group = ${ group }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
group: [],
},
});
Radio Button 單選按鈕
<div id="app">
<input type="radio" v-model="selected" value="我是單選按鈕 1" /><label>我是單選按鈕 1</label>
<input type="radio" v-model="selected" value="我是單選按鈕 2" /><label>我是單選按鈕 2</label>
<input type="radio" v-model="selected" value="我是單選按鈕 3" /><label>我是單選按鈕 3</label>
<div>selected = ${ selected }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
selected: '尚未選擇任何一項',
},
});
Select 下拉選單
單選。
<div id="app">
<select v-model="selected">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<div>selected = ${ selected }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
selected: null,
},
});
多選,在<select>
加上 multiple 屬性。
<div id="app">
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<div>selected = ${ selected }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
selected: [],
},
});
選定選項 B。
vm.selected = ['B'];
Modifiers 修飾符
.lazy
原本雙向綁定的更新方式是以 input 事件監聽,亦即資料變動即更新,但使用.lazy
會改用 change 事件監聽,event trigger 才更新。
如下所示,更改 input 內的值並不會馬上變更 model 的資料,而是等到滑鼠移到輸入框外,觸發 change 事件才更新。
<div id="app">
<input v-model.lazy="message" type="text" />
<div>${ message }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
message: 'Hello World!',
},
});
對照一下前面的例子,若無.lazy
,是以 input 事件監聽,資料改變就會更新。
.number
將字串轉為數字。
如果沒有強制轉換,我們在v-model
所得到的值的資料型態是 string。
如下所示,在輸入框輸入數字,然後印出這個數字的資料型別。
<div id="app">
<input v-model="count" />
<div>${ count }</div>
<div>${ type }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
count: 0,
type: '',
},
watch: {
count: function(val) {
this.type = typeof val;
},
},
});
使用.number
強制轉為數字。
如下所示,輸入 1 後,得到的資料型別是 number,而非 string。
<div id="app">
<input v-model.number="count" />
<div>${ count }</div>
<div>${ type }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
count: 0,
type: '',
},
watch: {
count: function(val) {
this.type = typeof val;
},
},
});
.trim
去除首尾空白。
如下所示,輸入前面有空白的字串,滑鼠移開後不會去除空白。
<div id="app">
<input v-model="message" />
<div>${ message }</div>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
message: 'Hello World!',
type: '',
},
});
v-model 使用.trim
去除空白。
如下所示,輸入前面有空白的字串,滑鼠移開後會去除空白。
<div id="app">
<input v-model.trim="message" />
<div>${ message }</div>
</div>
關於自定義元件與v-model
的狀況可參考這篇-使用自定義事件的表單輸入元件。