【重學Vue】數據響應原理真的是雙向綁定嗎?

最近 Ant Design Vue 做者 - 唐金州,在某平臺開課了,在整個課程中系統的講述了Vue的開發實戰。在第八講中介紹了Vue雙向綁定的問題,這裏我整理一些資料客觀的分析一下 Vue數據響應原理究竟是不是雙向綁定javascript


不少同窗在理解 Vue 的時候都把 Vue 的數據響應原理理解爲雙向綁定,但實際上這是不許確的,咱們以前提到的數據響應,都是經過數據的改變去驅動 DOM 視圖的變化,而雙向綁定除了數據驅動 DOM 外, DOM 的變化反過來影響數據,是一個雙向關係,在 Vue 中,咱們能夠經過 v-model 來實現雙向綁定。html

在Vue中體現出雙向綁定做用的方式有兩種,在分析以前咱們先介紹這兩種使用方式有什麼區別。vue

1)v-model 屬性java

2).sync 修飾符git

v-model

2.2.0+ 新增github

一個組件上的 v-model 默認會利用名爲 value 的 prop 和名爲 input 的事件,可是像單選框、複選框等類型的輸入控件可能會將 value 特性用於不一樣的目的。model 選項能夠用來避免這樣的衝突:express

ChildBox.vueapp

<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.vuedom

<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函數改成emit('update:checked',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">
複製代碼

動態綁定了 inputvalue 指向了 messgae 變量,而且在觸發 input 事件的時候去動態把 message 設置爲目標值,這樣實際上就完成了數據雙向綁定了,因此說 v-model 實際上就是語法糖。

ustbhuangyi 在Vue.js 技術揭祕中也詳細的介紹v-model在組件中的實現原理,這裏就不過多的陳述了。

總結

咱們瞭解到 v-model 是 Vue 雙向綁定的真正實現,但本質上就是一種語法糖,它便可以支持原生表單元素,也能夠支持自定義組件。在組件的實現中,咱們是能夠配置子組件接收的 prop 名稱,以及派發的事件名稱。

最後有一個問題 v-model.sync 均可以實現數據雙向綁定的效果,那到底用哪一種更合理呢?歡迎回復闡述你的觀點。

祝學習進步

鄧文斌

2019年3月21日

相關文章
相關標籤/搜索