亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術文章
文章詳情頁

Vue 的雙向綁定原理與用法揭秘

瀏覽:3日期:2023-01-23 08:46:20

本文實例講述了Vue 的雙向綁定原理與用法。分享給大家供大家參考,具體如下:

Vue 中需要輸入什么內容的時候,自然會想到使用 <input v-model='xxx' /> 的方式來實現雙向綁定。下面是一個最簡單的示例

<div id='app'> <h2>What’s your name:</h2> <input v-model='name' /> <div>Hello {{ name }}</div></div>

new Vue({ el: '#app', data: { name: '' }});

在這個示例的輸入框中輸入的內容,會隨后呈現出來。這是 Vue 原生對 <input> 的良好支持,也是一個父組件和子組件之間進行雙向數據傳遞的典型示例。不過 v-model 是 Vue 2.2.0 才加入的一個新功能,在此之前,Vue 只支持單向數據流。

Vue 的單向數據流

Vue 的單向數據流和 React 相似,父組件可以通過設置子組件的屬性(Props)來向子組件傳遞數據,而父組件想獲得子組件的數據,得向子組件注冊事件,在子組件高興的時候觸發這個事件把數據傳遞出來。一句話總結起來就是,Props 向下傳遞數據,事件向上傳遞數據。

上面那個例子,如果不使用 v-model,它應該是這樣的

<input :value='name' @input='name = $event.target.value' />

由于事件處理寫成了內聯模式,所以腳本部分不需要修改。但是多數情況下,事件一般都會定義成一個方法,代碼就會復雜得多

<input :value='name' @input='updateName' />

new Vue({ // .... methods: { updateName(e) { this.name = e.target.value; } }})

從上面的示例來看 v-model 節約了不少代碼,最重要的是可以少定義一個事件處理函數。所以 v-model 實際干的事件包括

使用 v-bind(即 :)單向綁定一個屬性(示例::value='name') 綁定 input 事件(即 @input)到一個默認實現的事件處理函數(示例:@input=updateName 這個默認的事件處理函數會根據事件對象帶入的值來修改被綁定的數據(示例:this.name = e.target.value)自定義組件的 v-model

Vue 對原生組件進行了封裝,所以 <input> 在輸入的時候會觸發 input 事件。但是自定義組件應該怎么呢?這里不妨借助 JsFiddle Vue 樣板的 Todo List 示例。

JsFiddle 的 Vue 樣板點擊 JsFilddle 的 Logo,在上面彈出面板中選擇 Vue 樣板即可

樣板代碼包含 HTML 和 Vue(js) 兩個部分,代碼如下:

<div id='app'> <h2>Todos:</h2> <ol> <li v-for='todo in todos'> <label> <input type='checkbox' v-on:change='toggle(todo)' v-bind:checked='todo.done'> <del v-if='todo.done'> {{ todo.text }} </del> <span v-else> {{ todo.text }} </span> </label> </li> </ol></div>

new Vue({ el: '#app', data: { todos: [ { text: 'Learn JavaScript', done: false }, { text: 'Learn Vue', done: false }, { text: 'Play around in JSFiddle', done: true }, { text: 'Build something awesome', done: true } ] }, methods: { toggle: function(todo){ todo.done = !todo.done } }})定義 Todo 組件

JsFiddle 的 Vue 模板默認實現一個 Todo 列表的展示,數據是固定的,所有內容在一個模板中完成。我們首先要做事情是把單個 Todo 改成一個子組件。因為在 JsFiddle 中不能寫成多文件的形式,所以組件使用 Vue.component() 在腳本中定義,主要是把 <li> 內容中的那部分拎出來:

Vue.component('todo', { template: `<label> <input type='checkbox' @change='toggle' :checked='isDone'> <del v-if='isDone'> {{ text }} </del> <span v-else> {{ text }} </span></label>`, props: ['text', 'done'], data() { return { isDone: this.done }; }, methods: { toggle() { this.isDone = !this.isDone; } }});

原來定義在 App 中的 toggle() 方法也稍作改動,定義在組件內了。toggle() 調用的時候會修改表示是否完成的 done 的值。但由于 done 是定義在 props 中的屬性,不能直接賦值,所以采用了官方推薦的第一種方法,定義一個數據 isDone,初始化為 this.done,并在組件內使用 isDone 來控制是否完成這一狀態。

相應的 App 部分的模板和代碼精減了不少:

<div id='app'> <h2>Todos:</h2> <ol> <li v-for='todo in todos'> <todo :text='todo.text' :done='todo.done'></todo> </li> </ol></div>

new Vue({ el: '#app', data: { todos: [ { text: 'Learn JavaScript', done: false }, { text: 'Learn Vue', done: false }, { text: 'Play around in JSFiddle', done: true }, { text: 'Build something awesome', done: true } ] }});

不過到此為止,數據仍然是單向的。從效果上來看,點擊復選框可以反饋出刪除線線效果,但這些動態變化都是在 todo 組件內部完成的,不存在數據綁定的問題。

為 Todo List 添加計數

為了讓 todo 組件內部的狀態變化能在 Todo List 中呈現出來,我們在 Todo List 中添加計數,展示已經完成的 Todo 數量。因為這個數量受 todo 組件內部狀態(數據)的影響,這就需要將 todo 內部數據變化反應到其父組件中,這才有 v-model 的用武之地。

這個數量我們在標題中以 n/m 的形式呈現,比如 2/4 表示一共 4 條 Todo,已經完成 2 條。這需要對 Todo List 的模板和代碼部分進行修改,添加 countDone 和 count 兩個計算屬性:

<div id='app'> <h2>Todos ({{ countDone }}/{{ count }}):</h2> <!-- ... --></div>

new Vue({ // ... computed: { count() { return this.todos.length; }, countDone() { return this.todos.filter(todo => todo.done).length; } }});

現在計數呈現出來了,但是現在改變任務狀態并不會對這個計數產生影響。我們要讓子組件的變動對父組件的數據產生影響。v-model 待會兒再說,先用最常見的方法,事件:

子組件 todo 在 toggle() 中觸發 toggle 事件并將 isDone 作為事件參數 父組件為子組件的 toggle 事件定義事件處理函數

Vue.component('todo', { //... methods: { toggle(e) { this.isDone = !this.isDone; this.$emit('toggle', this.isDone); } }});

<!-- #app 中其它代碼略 --><todo :text='todo.text' :done='todo.done' @toggle='todo.done = $event'></todo>

這里為 @toggle 綁定的是一個表達式。因為這里的 todo 是一個臨時變量,如果在 methods 中定義專門的事件處理函數很難將這個臨時變量綁定過去(當然定義普通方法通過調用的形式是可以實現的)。

事件處理函數,一般直接對應于要處理的事情,比如定義 onToggle(e),綁定為 @toggle='onToggle'。這種情況下不能傳入 todo 作為參數。

普通方法,可以定義成 toggle(todo, e),在事件定義中以函數調用表達式的形式調用:@toggle='toggle(todo, $event)'。它和 todo.done = $event` 同屬表達式。

注意二者的區別,前者是綁定的處理函數(引用),后者是綁定的表達式(調用)

現在通過事件方式已經達到了預期效果

改造成 v-model

之前我們說了要用 v-model 實現的,現在來改造一下。注意實現 v-model 的幾個要素

子組件通過 value 屬性(Prop)接受輸入 子組件通過觸發 input 事件輸出,帶數組參數 父組件中用 v-model 綁定

Vue.component('todo', { // ... props: ['text', 'value'], // <-- 注意 done 改成了 value data() { return { isDone: this.value // <-- 注意 this.done 改成了 this.value }; }, methods: { toggle(e) { this.isDone = !this.isDone; this.$emit('input', this.isDone); // <-- 注意事件名稱變了 } }});

<!-- #app 中其它代碼略 --><todo :text='todo.text' v-model='todo.done'></todo>.sync 實現其它數據綁定

前面講到了 Vue 2.2.0 引入 v-model 特性。由于某些原因,它的輸入屬性是 value,但輸出事件叫 input。v-model、value、input 這三個名稱從字面上看不到半點關系。雖然這看起來有點奇葩,但這不是重點,重點是一個控件只能雙向綁定一個屬性嗎?

Vue 2.3.0 引入了 .sync 修飾語用于修飾 v-bind(即 :),使之成為雙向綁定。這同樣是語法糖,添加了 .sync 修飾的數據綁定會像 v-model 一樣自動注冊事件處理函數來對被綁定的數據進行賦值。這種方式同樣要求子組件觸發特定的事件。不過這個事件的名稱好歹和綁定屬性名有點關系,是在綁定屬性名前添加 update: 前綴。

比如 <sub :some.sync='any' /> 將子組件的 some 屬性與父組件的 any 數據綁定起來,子組件中需要通過 $emit('update:some', value) 來觸發變更。

上面的示例中,使用 v-model 綁定始終感覺有點別扭,因為 v-model 的字面意義是雙向綁定一個數值,而表示是否未完成的 done 其實是一個狀態,而不是一個數值。所以我們再次對其進行修改,仍然使用 done 這個屬性名稱(而不是 value),通過 .sync 來實現雙向綁定。

Vue.component('todo', { // ... props: ['text', 'done'], // <-- 恢復成 done data() { return { isDone: this.done // <-- 恢復成 done }; }, methods: { toggle(e) { this.isDone = !this.isDone; this.$emit('update:done', this.isDone); // <-- 事件名稱:update:done } }});

<!-- #app 中其它代碼略 --><!-- 注意 v-model 變成了 :done.sync,別忘了冒號喲 --><todo :text='todo.text' :done.sync='todo.done'></todo>揭密 Vue 雙向綁定

通過上面的講述,我想大家應該已經明白了 Vue 的雙向綁定其實就是普通單向綁定和事件組合來完成的,只不過通過 v-model 和 .sync 注冊了默認的處理函數來更新數據。Vue 源碼中有這么一段

// @file: src/compiler/parser/index.jsif (modifiers.sync) { addHandler( el, `update:${camelize(name)}`, genAssignmentCode(value, `$event`) )}

從這段代碼可以看出來,.sync 雙向綁定的時候,編譯器會添加一個 update:${camelize(name)} 的事件處理函數來對數據進行賦值(genAssignmentCode 的字面意思是生成賦值的代碼)。

展望

目前 Vue 的雙向綁定還需要通過觸發事件來實現數據回傳。這和很多所的期望的賦值回傳還是有一定的差距。造成這一差距的主要原因有兩個

需要通過事件回傳數據 屬性(prop)不可賦值

在現在的 Vue 版本中,可以通過定義計算屬性來實現簡化,比如

computed: { isDone: { get() { return this.done; }, set(value) { this.$emit('update:done', value); } }}

說實在的,要多定義一個意義相同名稱不同的變量名也是挺費腦筋的。希望 Vue 在將來的版本中可以通過一定的技術手段減化這一過程,比如為屬性(Prop)聲明添加 sync 選項,只要聲明 sync: true 的都可以直接賦值并自動觸發 update:xxx 事件。

當然作為一個框架,在解決一個問題的時候,還要考慮對其它特性的影響,以及框架的擴展性等問題,所以最終雙向綁定會演進成什么樣子,我們對 Vue 3.0 拭目以待。

感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運行效果。

希望本文所述對大家vue.js程序設計有所幫助。

標簽: Vue
相關文章:
主站蜘蛛池模板: 一区二区免费视频观看 | 免费高清资源黄网站在线观看 | 一区二区三区四区视频在线观看 | 欧美成人观看视频在线 | 国产欧美日韩不卡一区二区三区 | 一本久道热中字伊人 | 999热这里只有精品 999热精品这里在线观看 | 黄网站免费在线观看 | 大陆一级毛片免费高清 | 大陆一级毛片免费视频观看i | 毛片爽爽爽免费看 | 日本美女黄视频 | 二区国产| 一级特级欧美午夜片免费观看 | 色婷婷色综合激情国产日韩 | 亚洲人成在线观看一区二区 | 亚洲国产婷婷综合在线精品 | 欧美日韩一区二区综合 | 全国男人的天堂网站 | 亚洲精品一区二区三区婷婷 | 老司机精品视频线观看 | 国产午夜精品福利视频 | 伊人久久视频 | 亚洲图色视频 | 国产成人一区在线播放 | 国产在线麻豆精品 | 91福利社在线观看 | 午夜一级毛片不卡 | 国产在线自在拍91精品黑人 | 国产剧情福利 | 999久爱视频在线观看 | 国产a高清 | 一级看片免费视频 | 在线观看国产小屁孩cao大人 | 黄色三级毛片 | 色综合色狠狠天天久久婷婷基地 | 国产乱人伦精品一区二区 | 久草视频福利在线 | 亚洲福利秒拍一区二区 | 免费看片免费播放 | 日本在线一级 |