一個簡化版Vue助你理解Vue原理

好多人看完我的這個文章對它的理解還是只是知道了大概原理,但是對具體的Vue雙向綁定的實現(xiàn)很模糊,因此就出了這篇文章,供大家參考希望可以得到收獲亦歉,以下是主要代碼邏輯,先陳述一下這一過程都需要什么:

需要有一個接收Vue實例配置項的構造函數(shù)SimpleVue畅哑,給他加兩個原型方法分別是observe()和compile()肴楷,再構造出一個訂閱器watcher,給他加一個更新視圖方法

  • observe():用來劫持并監(jiān)聽數(shù)據(jù)變化的數(shù)據(jù)監(jiān)聽器荠呐,有變化就會通知下文中的訂閱器watcher
  • compile():節(jié)點DOM解析器赛蔫,用來獲取和解析每一個節(jié)點及其指令,根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher
  • watcher():訂閱器watcher泥张,用來接收屬性值的相關數(shù)據(jù)的變化通知濒募,調(diào)用自身原型方法update從而更新視圖

由于Vue就是一個MVVM的框架理念,所以就要通過Object.defineProperty()方法來劫持并監(jiān)聽所有屬性值相關的數(shù)據(jù)圾结,看看它是否變化,如有變化則通知訂閱器watcher看是否需要視圖更新齿诉,這一過程就是我們的數(shù)據(jù)監(jiān)聽器observe的工作任務筝野,由于數(shù)據(jù)和訂閱器是一對多的關系,所以通知訂閱器的時候需要把數(shù)據(jù)對應的訂閱器的集合都放在一個oWatcherObj對象中粤剧,接下來需要一個節(jié)點DOM解析器compile歇竟,主要用來迭代遞歸獲取和解析每一個節(jié)點及其指令,根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher抵恋,實例化watcher就會接到數(shù)據(jù)變化的通知焕议,進而實現(xiàn)VM更新視圖

template:

<div id="simpleVue">
    <button yf-on:click="copy">戳我</button>
    <div>
        <textarea yf-model="name"></textarea>
        <div yf-text="name"></div>
    </div>
    <hr>
    <button yf-on:click="show">顯示/隱藏</button>
    <div yf-if="isShow">
        <input type="text" yf-model="webSite">
        <div yf-text="webSite"></div>
    </div>
</div>

SimpleVue構造:

class SimpleVue { // 簡化版Vue實例的構造 用來接收實例的配置項
    constructor(options) {
        this.$el = document.querySelector(options.el);
        this.$data = options.data;
        this.$methods = options.methods;
        this.oWatcherObj = {}; // 所有屬性值相關的數(shù)據(jù)對應的訂閱器的集合都放在該對象中
        this.observe(); // 調(diào)用數(shù)據(jù)監(jiān)聽器對屬性值相關的數(shù)據(jù)進行劫持并監(jiān)聽
        this.compile(this.$el); // 對該DOM節(jié)點進行解析
    }
    observe() { // 數(shù)據(jù)監(jiān)聽器 用來劫持并監(jiān)聽屬性值相關數(shù)據(jù)的變化 如有變化則通知訂閱器watcher
        for (let key in this.$data) {
            let value = this.$data[key];
            this.oWatcherObj[key] = []; // 初始化該數(shù)據(jù)的訂閱器 數(shù)據(jù)和訂閱器的關系是一對多
            let oWatcherObj = this.oWatcherObj[key];
            Object.defineProperty(this.$data, key, { // 關鍵方法 可以修改對象身上的默認屬性值的ES5方法 下面用到的是ES中兩大屬性中的訪問器屬性,有以下四種描述符對象
                configurable: false, // 該狀態(tài)下的屬性描述符不能被修改和刪除
                enumerable: false, // 該狀態(tài)下的屬性描述符中的屬性不可被枚舉
                get() { // 屬性值相關的數(shù)據(jù)讀取函數(shù)
                    return value;
                },
                set(newVal) { // 屬性值相關的數(shù)據(jù)寫入函數(shù)
                    if (newVal !== value) {
                        value = newVal;
                        oWatcherObj.forEach((obj) => {
                            obj.update(); // 通知和該數(shù)據(jù)相關的所有訂閱器
                        });
                    }
                }
            });
        }
    }
    compile(el) { // 節(jié)點DOM解析器 用來獲取和解析每一個節(jié)點及其指令 根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher
        let nodes = el.children;
        for (let i = 0; i < nodes.length; i++) { // 迭代同級所有節(jié)點
            let node = nodes[i];
            if (node.children.length > 0) {
                this.compile(node); // 遞歸所有子節(jié)點
            }
            if (node.hasAttribute('yf-on:click')) { // 節(jié)點中如存在該指令則執(zhí)行以下操作
                let eventAttrVal = node.getAttribute('yf-on:click');
                node.addEventListener('click', this.$methods[eventAttrVal].bind(this.$data)); // 綁定獲取到的指令對應的數(shù)據(jù)所觸發(fā)的方法
            }
            if (node.hasAttribute('yf-if')) {
                let ifAttrVal = node.getAttribute('yf-if');
                this.oWatcherObj[ifAttrVal].push(new Watcher(this, node, "", ifAttrVal)); // 給該指令對應的數(shù)據(jù)創(chuàng)建訂閱器放在該數(shù)據(jù)對應的訂閱器數(shù)組里
            }
            if (node.hasAttribute('yf-model')) {
                let modelAttrVal = node.getAttribute('yf-model');
                node.addEventListener('input', ((i) => { // 前方高能:此處有閉包請繞行!!! i的問題
                    this.oWatcherObj[modelAttrVal].push(new Watcher(this, node, "value", modelAttrVal));
                    return () => {
                        this.$data[modelAttrVal] = nodes[i].value; // 將該指令所在節(jié)點的值扔給該指令的數(shù)據(jù)
                    }
                })(i));
            }
            if (node.hasAttribute('yf-text')) {
                let textAttrVal = node.getAttribute('yf-text');
                this.oWatcherObj[textAttrVal].push(new Watcher(this, node, "innerText", textAttrVal));
            }
        }
    }
}

訂閱器構造:

class Watcher { // 訂閱器構造 用來接收屬性值的相關數(shù)據(jù)的變化通知 從而更新視圖
    constructor(...arg) {
        this.vm = arg[0];
        this.el = arg[1];
        this.attr = arg[2];
        this.val = arg[3];
        this.update(); // 初始化訂閱器時更新一下視圖
    }
    update() { // 將收到的新的數(shù)據(jù)更新在視圖中從而實現(xiàn)真正的VM
        if (this.vm.$data[this.val] === true) {
            this.el.style.display = 'block';
        } else if (this.vm.$data[this.val] === false) {
            this.el.style.display = 'none';
        } else {
            this.el[this.attr] = this.vm.$data[this.val];
        }
    }
}

Shortcuts

希望大家閱讀完本文可以有所收獲,因為能力有限弧关,掌握的知識也是不夠全面盅安,歡迎大家提出來一起分享唤锉!謝謝O(∩_∩)O~

歡迎訪問我的GitHub,喜歡的可以star别瞭,項目隨意fork窿祥,支持轉(zhuǎn)載但要下標注,同時恭候:個人博客

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝙寨,一起剝皮案震驚了整個濱河市晒衩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌墙歪,老刑警劉巖听系,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異虹菲,居然都是意外死亡靠胜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門届惋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來髓帽,“玉大人,你說我怎么就攤上這事脑豹≈2兀” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵瘩欺,是天一觀的道長必盖。 經(jīng)常有香客問我,道長俱饿,這世上最難降的妖魔是什么歌粥? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮拍埠,結(jié)果婚禮上失驶,老公的妹妹穿的比我還像新娘。我一直安慰自己枣购,他們只是感情好嬉探,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棉圈,像睡著了一般涩堤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上分瘾,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天胎围,我揣著相機與錄音,去河邊找鬼。 笑死白魂,一個胖子當著我的面吹牛汽纤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碧聪,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼冒版,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逞姿?” 一聲冷哼從身側(cè)響起辞嗡,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滞造,沒想到半個月后续室,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡谒养,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年挺狰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片买窟。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡丰泊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出始绍,到底是詐尸還是另有隱情瞳购,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布亏推,位于F島的核電站学赛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吞杭。R本人自食惡果不足惜盏浇,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芽狗。 院中可真熱鬧绢掰,春花似錦、人聲如沸童擎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柔昼。三九已至,卻和暖如春炎辨,著一層夾襖步出監(jiān)牢的瞬間捕透,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乙嘀,地道東北人末购。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像虎谢,于是被迫代替她去往敵國和親盟榴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353