vue單向數(shù)據(jù)綁定和雙向數(shù)據(jù)綁定方式
一、總結(jié)
vue中有2種數(shù)據(jù)綁定的方式:
- 單向數(shù)據(jù)綁定(v-bind):數(shù)據(jù)只能從data流向頁面;
- 雙向數(shù)據(jù)綁定(v-model):數(shù)據(jù)不僅能從data流向頁面,還可以從頁面流向data;
備注:
- 雙向數(shù)據(jù)綁定一般都應(yīng)用在表單類(輸入類)元素上 (如:input、select等);
- v-model:value可以簡寫為v-model,因為v-model默認收集的就是value的值;
二、分析
- 單向數(shù)據(jù)綁定:就是把
Model綁定到View,當(dāng)我們用JavaScript代碼更新Model時,View就會自動更新雙向綁定就很容易聯(lián)想到了; - 雙向數(shù)據(jù)綁定:在單向綁定的基礎(chǔ)上,用戶更新了
View,Model的數(shù)據(jù)也自動被更新了

三、 雙向綁定的原理
Vue 是數(shù)據(jù)雙向綁定的框架,雙向綁定由三個重要部分構(gòu)成:
- 數(shù)據(jù)層(Model):應(yīng)用的數(shù)據(jù)及業(yè)務(wù)邏輯
- 視圖層(View):應(yīng)用的展示效果,各類UI組件
- 業(yè)務(wù)邏輯層(ViewModel):框架封裝的核心,它負責(zé)將數(shù)據(jù)與視圖關(guān)聯(lián)起來
理解ViewModel,它的主要職責(zé)就是:
- 數(shù)據(jù)變化后更新視圖
- 視圖變化后更新數(shù)據(jù)
它還有兩個主要部分組成
- 監(jiān)聽器(Observer):對所有數(shù)據(jù)的屬性進行監(jiān)聽
- 解析器(Compiler):對每個元素節(jié)點的指令進行掃描跟解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)
四、實現(xiàn)雙向綁定
- new vue()首先執(zhí)行初始化,對data執(zhí)行響應(yīng)式處理,這個過程發(fā)生在observe中;
- 同時對模板執(zhí)行編譯,找到其中動態(tài)綁定的數(shù)據(jù),從data中獲取并初始化視圖,這個過程發(fā)生在Compile中;
- 同時定義一個更新函數(shù)和Watcher,將來對應(yīng)數(shù)據(jù)變化時Watcher會調(diào)用更新函數(shù);
- 由于data中的某個key在一個視圖中可能出現(xiàn)多次,所以每個key都需要一個管家Dep來管理多個Watcher
- 將來data中數(shù)據(jù)一旦發(fā)生變化,會首先找到對應(yīng)的Dep,通知所有Watcher執(zhí)行更新函數(shù);

五、實現(xiàn)
先來一個構(gòu)造函數(shù):執(zhí)行初始化,對data執(zhí)行響應(yīng)化處理
class Vue {
constructor(options) {
this.$options = options;
this.$data = options.data;
// 對data選項做響應(yīng)式處理
observe(this.$data);
// 代理data到vm上
proxy(this);
// 執(zhí)行編譯
new Compile(options.el, this);
}
} 對data選項執(zhí)行響應(yīng)化具體操作
function observe(obj) {
if (typeof obj !== "object" || obj == null) {
return;
}
new Observer(obj);
}
class Observer {
constructor(value) {
this.value = value;
this.walk(value);
}
walk(obj) {
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key]);
});
}
} 編譯Compile
對每個元素節(jié)點的指令進行掃描跟解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)

class Compile {
constructor(el, vm) {
this.$vm = vm;
this.$el = document.querySelector(el); // 獲取dom
if (this.$el) {
this.compile(this.$el);
}
}
compile(el) {
const childNodes = el.childNodes;
Array.from(childNodes).forEach((node) => { // 遍歷子元素
if (this.isElement(node)) { // 判斷是否為節(jié)點
console.log("編譯元素" + node.nodeName);
} else if (this.isInterpolation(node)) {
console.log("編譯插值?本" + node.textContent); // 判斷是否為插值文本 {{}}
}
if (node.childNodes && node.childNodes.length > 0) { // 判斷是否有子元素
this.compile(node); // 對子元素進行遞歸遍歷
}
});
}
isElement(node) {
return node.nodeType == 1;
}
isInterpolation(node) {
return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
}
} 收集依賴
視圖中會用到data中某key,這稱為依賴。同一個key可能出現(xiàn)多次,每次都需要收集出來用一個Watcher來維護它們,此過程稱為依賴收集多個Watcher需要一個Dep來管理,需要更新時由Dep統(tǒng)一通知;

實現(xiàn)思路
- defineReactive是為每一個key創(chuàng)建一個Dep實例;
- 初始化視圖時讀取某個key,例如name1,創(chuàng)建一個Wathcher1;
- 由于觸發(fā)name1的getter方法,便將Wathcher1添加到name1的對應(yīng)的Dep中;
- 當(dāng)name1更新,setter觸發(fā)時,便可通過對應(yīng)Dep通知其管理所有Watncer更新;
// 負責(zé)更新視圖
class Watcher {
constructor(vm, key, updater) {
this.vm = vm
this.key = key
this.updaterFn = updater
// 創(chuàng)建實例時,把當(dāng)前實例指定到Dep.target靜態(tài)屬性上
Dep.target = this
// 讀一下key,觸發(fā)get
vm[key]
// 置空
Dep.target = null
}
// 未來執(zhí)行dom更新函數(shù),由dep調(diào)用的
update() {
this.updaterFn.call(this.vm, this.vm[this.key])
}
} 聲明Dep
class Dep {
constructor() {
this.deps = []; // 依賴管理
}
addDep(dep) {
this.deps.push(dep);
}
notify() {
this.deps.forEach((dep) => dep.update());
}
} 創(chuàng)建watcher時觸發(fā)getter
class Watcher {
constructor(vm, key, updateFn) {
Dep.target = this;
this.vm[this.key];
Dep.target = null;
}
} 依賴收集,創(chuàng)建Dep實例
function defineReactive(obj, key, val) {
this.observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher實例
return val;
},
set(newVal) {
if (newVal === val) return;
dep.notify(); // 通知dep執(zhí)行更新方法
},
});
} 最后
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue項目中Element UI組件未注冊的問題原因及解決方法
在 Vue 項目中使用 Element UI 組件庫時,開發(fā)者可能會遇到一些常見問題,例如組件未正確注冊導(dǎo)致的警告或錯誤,本文將詳細探討這些問題的原因、解決方法,以及如何在需要時撤銷相關(guān)操作,需要的朋友可以參考下2025-01-01
el-element中el-table表格嵌套el-select實現(xiàn)動態(tài)選擇對應(yīng)值功能
這篇文章主要給大家介紹了關(guān)于el-element中el-table表格嵌套el-select實現(xiàn)動態(tài)選擇對應(yīng)值功能的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-01-01
vue使用動態(tài)添加路由(router.addRoutes)加載權(quán)限側(cè)邊欄的方式
這篇文章主要介紹了vue使用動態(tài)添加路由(router.addRoutes)加載權(quán)限側(cè)邊欄的方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
淺談vue中使用圖片懶加載vue-lazyload插件詳細指南
本篇文章主要介紹了淺談vue中使用圖片懶加載vue-lazyload插件詳細指南,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-10-10

