Vue雙向繫結原理實現——觀察者模式
阿新 • • 發佈:2018-11-06
前言
Vue 框架是一種 MVVM 框架,它有一個很大的特點就是資料雙向繫結,在開發過程中我們只需要操作 Model ,而不需要修改 View ,使用起來 VR 因吹斯汀。但是它的實現原理並不複雜,主要是運用了設計模式中的觀察者模式,也可以說是加了鉤子函式。下面用原生 JS 實現一下。
程式碼實現
建立模板
建立一個 html 模板,包含一個 <input> 和一個 <span> 標籤,我們要實現的目標就是讓 v-model 中的 message 與 v-bind 中的 message 資料繫結。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="myapp">
<input v-model="message"/><br>
<span v-bind="message"></span>
</div>
</body>
</html>
建立物件
首先,我們要先建立一個 model 物件,並新增屬性 message,然後通過 querySelectorAll 獲取包含 v-model 屬性且值為 “message” 的所有物件,儲存在陣列 models 中,然後遍歷陣列獲取使用者的輸入,並賦值給定義的 model。
var model = {
message : ""
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].onkeyup = function () {
model[this.getAttribute("v-model")] = this.value;
}
}
實現原理
接下來,通過 defineProperty 來定義model 物件屬性,這裡主要新增 set 和 get 方法,這兩個方法是實現雙向繫結的關鍵。定義這兩個方法後,當我們為 model 賦值時,會自動呼叫 set 方法;當獲取 model 的值時,會自動呼叫 get 方法。因此,我們可以通過 set 方法,當檢測到使用者輸入時,在給 model 賦值之前,先給綁定了此資料的 v-bind 資料賦值,再給 v-model 物件賦值,最終給 model 物件賦值。程式碼如下:
// 觀察者模式 / 鉤子函式
// defineProperty 來定義一個物件的某個屬性
Object.defineProperty(model,"message",{
set:function (newValue) {
var binds = myapp.querySelectorAll("[v-bind=message]");
for (var i = 0; i < binds.length; i++) {
binds[i].innerHTML = newValue;
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].value = newValue;
};
this.value = newValue;
},
get:function () {
return this.value;
}
})
如此,就完成了最簡單的資料雙向繫結。但是在這樣的資料繫結還是有很多問題,Vue 中的原始碼實現也要更加複雜,下一篇部落格會對資料繫結做一下封裝,讓他的實用性變得更強。
全部程式碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="myapp">
<input v-model="message"/><br>
<span v-bind="message"></span>
</div>
<!--JavaScript-->
<!--<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>-->
<script type="text/javascript">
var model = {
message : ""
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].onkeyup = function () {
model[this.getAttribute("v-model")] = this.value;
}
}
// 觀察者模式 / 鉤子函式
// defineProperty 來定義一個物件的某個屬性
Object.defineProperty(model,"message",{
set:function (newValue) {
var binds = myapp.querySelectorAll("[v-bind=message]");
for (var i = 0; i < binds.length; i++) {
binds[i].innerHTML = newValue;
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].value = newValue;
};
this.value = newValue;
},
get:function () {
return this.value;
}
})
</script>
</body>
</html>