Vue數據雙向綁定的簡單實現

這篇仿Vue數據綁定的簡單實現,是我看了一些文章以後,把他們的代碼研究懂了以後寫出來的。固然,這個是簡單版本的,複雜版本的就...

若是隻是簡單實現一下Vue的數據綁定仍是很簡單的,只要將思路理清楚就能夠。寫的時候考慮的問題就是2點。
問題:
1)如何實現將數據層的數據顯示到視圖上
2)如何在視圖上的數據改變後,通知數據層相應的數據發生了變化
3)如何讓二者聯繫起來,當一個數據改變時,視圖上的全部數據層的數據進行更新
第一個問題要將數據層的數據顯示到視圖上,很明顯這個須要使用DOM操做,先判斷 el 下的子元素上的屬性是否有關鍵字(如: v-model、v-bind),若是有就將其與 data 中的key來進行對比,而後data裏的值渲染到頁面上。這裏須要用到遞歸,從而使el下的每一個層級的元素屬性都進行一次篩選。
第二個問題能夠經過在相應的元素(input)上進行事件監聽,當視圖上的數據發生了變化就改變相對應的數據成數據。這二者之間須要一個監聽器,從而使當一個改變時,另一個也跟着改變。
第三個問題實現須要利用發佈訂閱模式和ES5的Object.defineProperty()。首先要建立一個訂閱者,裏面有一個方法,使視圖層上的值等於數據層的數據。在進行DOM篩選時,將對應的數據、key、元素傳入訂閱者,在將這個訂閱者放在一個對象中,這個對象的key就是data的key。而後經過ES5的Object.defineProperty()來對data裏的數據進行數據劫持,當data裏的數據被從新設置,會觸發set函數,而後觸發監聽者。html

具體代碼以下:node

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
        <div id="app">
            <input type="text" v-model="name">
            <div>{{name}}</div>
        </div>
    <body>
        <script>
          window.onload = () => {
                new MyVue({
                    el: '#app',
                    data: {
                        name: 123
                    }
                })
            }
            
            function MyVue(option={}) {
                this.$el = document.querySelector(option.el);
                this.$data = option.data;
                this._watchTpl = {}; 
                this._observer(this.$data);
                this._compile(this.$el);
            }
    
            MyVue.prototype._observer = function (data) {
                let _this = this;
                Object.keys(data).forEach( (item, index) => {
                    let value = data[item];
                    this._watchTpl[item] = [];
                    Object.defineProperty( data, item, {
                        enumerable: true,
                        configurable: true,
                        get() {
                            return value;
                        },
                        set(newVal) {
                            if(newVal !== value) {
                                value= newVal;
                                _this._watchTpl[item].forEach((item) => {
                                    item.update();
                                })  
                            }
                        }
                    } )
                } )
            }
            MyVue.prototype._compile = function (el) {
                let nodeList = el.children;
                nodeList = Array.prototype.slice.call(nodeList);
                nodeList.forEach( (node, index) => {
                    if(node.children) {
                        this._compile(node);
                    }
                    if (node.getAttribute('v-model')) {
                        let key = node.getAttribute('v-model');
                        if(!this._watchTpl[key].length) {
                            this._watchTpl[key] = [];
                        }
                        this._watchTpl[key].push(new Watcher(node, key, this, 'value'))
                        node.addEventListener('input', () => {
                            this.$data[key] = node.value;
                        } )
                    }
    
                    let reg = /\{\{\s*([^}]+\S)\s*\}\}/g;
                    // 該正則表達式的含義  \s匹配任何空白字符,\S匹配任何非空白字符,*匹配任何包含零個或多個前導字符串,+匹配任何至少包含一個前導字符串  總的含義就是匹配以{{爲開頭,後面跟着零或多個空白符,}前面有至少一個非空白字符的以}爲結尾的字符串
                    let content = node.textContent;
                    if (reg.test(content)) {
                        // 這篇文章介紹了replace怎麼用,我之前沒怎麼研究過replace這個語法。 https://blog.csdn.net/linbilin_/article/details/57094602
                        content.replace(reg, (matched, placeholder) => {
                            if(!this._watchTpl[placeholder].length) {
                                this._watchTpl[placeholder] = [];
                            }
                            this._watchTpl[placeholder].push(new Watcher(node, placeholder, this, 'innerHTML'))
                        })
                    }
    
                } )
            }
            function Watcher (node, key, vm, type) {
                this.node = node;
                this.key = key;
                this.vm = vm;
                this.type = type;
                this.update();
            }
            Watcher.prototype.update = function () {
                this.node[this.type] = this.vm.$data[this.key];
            }
        </script>
    </body>
    
    </html>

這篇文章主要是參考於https://segmentfault.com/a/11... ,高難度的那個是https://www.cnblogs.com/canfo...正則表達式

相關文章
相關標籤/搜索