1. 程式人生 > >Vue雙向繫結原理實現——觀察者模式

Vue雙向繫結原理實現——觀察者模式

前言

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>