【重學Vue】資料響應原理真的是雙向繫結嗎?
最近 Ant Design Vue 作者 - 唐金州,在某平臺開課了,在整個課程中系統的講述了Vue的開發實戰。在第八講中介紹了Vue雙向繫結的問題,這裡我整理一些資料客觀的分析一下 Vue資料響應原理到底是不是雙向繫結 。
很多同學在理解 Vue 的時候都把 Vue 的資料響應原理理解為雙向繫結,但實際上這是不準確的,我們之前提到的資料響應,都是通過資料的改變去驅動 DOM 檢視的變化,而雙向繫結除了資料驅動 DOM 外, DOM 的變化反過來影響資料,是一個雙向關係,在 Vue 中,我們可以通過 v-model
來實現雙向繫結。
在Vue中體現出雙向繫結作用的方式有兩種,在分析之前我們先介紹這兩種使用方式有什麼區別。
1)v-model 屬性
2).sync 修飾符
v-model
2.2.0+ 新增
一個元件上的 v-model
預設會利用名為 value
的 prop 和名為 input
的事件,但是像單選框、複選框等型別的輸入控制元件可能會將 value
特性用於不同的目的。 model
選項可以用來避免這樣的衝突:
ChildBox.vue
<template> <input type="checkbox" :checked="checked" @change="$emit('change', $event.target.checked)"/> </template> <script> export default { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean } } </script> 複製程式碼
Index.vue
<template> <div> <ChildBox v-model="val"/> <!-- 解析後 --> <ChildBox :value="val" @change="data => (val = data)"/> </div> </template> 複製程式碼
分析一下上面的程式碼。ChildBox.vue檔案對checkbox做了簡單的封裝,提供了checked引數。Index.vue檔案為父元件,引用了ChildBox作為自己的子元件,這裡需要注意一下。val值的繫結使用的v-model而並不是v-bind:checkbox。一開始我們有說到雙向繫結方式有兩種一種是v-model,另一種是.sync(這個後面講)。如果使用v-model,子元件的props應該設定value值,而向上傳遞應該為$emit('input')才對。所以這裡還有一個重點, model
的作用。
model
2.2.0 新增
允許一個自定義元件在使用 v-model
時定製 prop 和 event。預設情況下,一個元件上的 v-model
會把 value
用作 prop 且把 input
用作 event,但是一些輸入型別比如單選框和複選框按鈕可能想使用 value
prop 來達到不同的目的。使用 model
選項可以迴避這些情況產生的衝突。
.sync 修飾符
2.3.0+ 新增
在有些情況下,我們可能需要對一個 prop 進行“雙向繫結”。不幸的是,真正的雙向繫結會帶來維護上的問題,因為子元件可以修改父元件,且在父元件和子元件都沒有明顯的改動來源。
這也是為什麼我們推薦以 update:myPropName
的模式觸發事件取而代之。舉個例子,在一個包含 title
prop 的假設的元件中,我們可以用以下方法表達對其賦新值的意圖:
ChildBox.vue
<template> <input type="checkbox" :checked="checked" @change="$emit('update:checked', $event.target.checked)"/> </template> <script> export default { props: { checked: Boolean } } </script> 複製程式碼
Index.vue
<template> <div> <ChildBox :checked.sync="val"/> <!-- 解析後 --> <ChildBox :checked="val" @update:checked="data => (val = data)"/> </div> </template> 複製程式碼
分析一下上面的程式碼有什麼變化,父元件 v-model
被 :checked.sync
替換掉。子元件因不適用 v-model
,所以不需要model配置。change函式改為event.target.checked)。
v-model原始碼分析
藉助 ustbhuangyi Vue.js技術揭祕 。這裡只做總結比對,詳細分析過程可檢視連結。
以下面程式碼為例:
let vm = new Vue({ el: '#app', template: '<div>' + '<input v-model="message" placeholder="edit me">' + '<p>Message is: {{ message }}</p>' + '</div>', data() { return { message: '' } } }) 複製程式碼
在 input 元素上設定了 v-model
屬性,綁定了 message
,當我們在 input 上輸入內容時, message
也會同時發生變化。
原始碼generate函式:
function generate ( ast, options ) { var state = new CodegenState(options); var code = ast ? genElement(ast, state) : '_c("div")'; return { render: ("with(this){return " + code + "}"), staticRenderFns: state.staticRenderFns } } 複製程式碼
對我們的例子而言,最終生成的 render
程式碼如下:
with(this) { return _c('div',[_c('input',{ directives:[{ name:"model", rawName:"v-model", value:(message), expression:"message" }], attrs:{"placeholder":"edit me"}, domProps:{"value":(message)}, on:{"input":function($event){ if($event.target.composing) return; message=$event.target.value }}}),_c('p',[_v("Message is: "+_s(message))]) ]) } 複製程式碼
最終轉化為:
<input v-bind:value="message" v-on:input="message=$event.target.value"> 複製程式碼
動態綁定了 input
的 value
指向了 messgae
變數,並且在觸發 input
事件的時候去動態把 message
設定為目標值,這樣實際上就完成了資料雙向綁定了,所以說 v-model
實際上就是語法糖。
ustbhuangyi
在Vue.js 技術揭祕中也詳細的介紹v-model在元件中的實現原理,這裡就不過多的陳述了。
總結
我們瞭解到 v-model
是 Vue 雙向繫結的真正實現,但本質上就是一種語法糖,它即可以支援原生表單元素,也可以支援自定義元件。在元件的實現中,我們是可以配置子元件接收的 prop
名稱,以及派發的事件名稱。
最後有一個問題 v-model
和 .sync
都可以實現資料雙向繫結的效果,那到底用哪種更合理呢?歡迎回復闡述你的觀點。
祝學習進步
鄧文斌
2019年3月21日