原生JS模擬Vue雙向數(shù)據(jù)綁定

童鞋們都應(yīng)該知道Vue2.x很重要的特性(數(shù)據(jù)雙向綁定、虛擬DOM)瘸恼,所以在面試的時(shí)候就經(jīng)常有面試官問(wèn)小白同學(xué)說(shuō)數(shù)據(jù)雙向綁定原理是什么劣挫,虛擬DOM是什么,給我實(shí)現(xiàn)一個(gè)唄东帅。所以給大家展示一個(gè)最簡(jiǎn)的雙向綁定的案例压固,也參考了網(wǎng)上一些資料。至于虛擬DOM的實(shí)現(xiàn)后面再說(shuō)吧靠闭,網(wǎng)上資料也很多很全因?yàn)楫吘箁eact都出來(lái)好久了帐我。(都知道網(wǎng)上資料一大堆坎炼,大都不著調(diào)。)直接上代碼@辜Rス狻!

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="msg">
        <span>{{msg}}</span>
    </div>
    <script src="mvvm/watcher.js"></script>
    <script src="mvvm/observer.js"></script>
    <script src="mvvm/compile.js"></script>
    <script src="mvvm/index.js"></script>
    <script>
        let vm = new Vue({
            el: "app",
            data: {
                msg: 'hello world'
            }
        })
    </script>
</body>
</html>
  • index.js
// 這個(gè)文件為入口文件芬为,也就是Vue的構(gòu)造函數(shù)
function Vue(options) {
    // 傳遞過(guò)來(lái)的對(duì)象
    this.data = options.data;
    this.id = options.el;
    // 1.第一步先將data中所有的數(shù)據(jù)進(jìn)行監(jiān)聽(tīng)
    //   這個(gè)函數(shù)一般使用遞歸的方式完成所有屬性的監(jiān)聽(tīng)
    observer(this.data, this);

    // 2.第二步將所有DOM節(jié)點(diǎn)的翻譯出來(lái)抢肛,也就是說(shuō)將v-model,
    //  {{}}等翻譯成你想要的數(shù)據(jù)碳柱。其次還有將v-model的數(shù)據(jù)進(jìn)行監(jiān)聽(tīng)捡絮,使用觀察者模式,完成雙向綁定
    getAllNode(document.getElementById(this.id), this);
}
  • observer.js
// 定義一個(gè)pubsub,這個(gè)作用是將所有的觀察者加入其中莲镣,
// 并且出發(fā)事件
function pubsub() {
    this.subs = [];
}

pubsub.prototype = {
    // 將需要觀察的數(shù)據(jù)加入subs中
    addSub: function(sub){
        this.subs.push(sub);
    },
    // 執(zhí)行觀察的數(shù)據(jù)上綁定的事件update事件福稳。
    pub: function(){
        console.log(this.subs);
        this.subs.forEach(function(sub){
            sub.update();
        })
    }
}

// 將數(shù)據(jù)都進(jìn)行監(jiān)聽(tīng)
function active(obj, key, val) {
    var pubsub1 = new pubsub();
    Object.defineProperty(obj.data, key, {
        // getter,如果獲取數(shù)據(jù)時(shí)瑞侮,會(huì)判斷是有需要觀察的數(shù)據(jù)的圆,如果有就添加到subs中,沒(méi)有不添加
        // Pubsub是一個(gè)全局的變量半火,這個(gè)變量必須是全局才能判斷是有需要觀察的數(shù)據(jù)
        get() {
            if(Pubsub.target) {
                // 添加訂閱
                pubsub1.addSub(Pubsub.target);
            }
            return val;
        },
        set(newVal) {
            // 如果數(shù)據(jù)被setter越妈,那么就涉及到及時(shí)的更新數(shù)據(jù),
            // 這時(shí)只需要進(jìn)行發(fā)布事件钮糖,觀察的數(shù)據(jù)就會(huì)執(zhí)行update函數(shù)來(lái)執(zhí)行更新操作
            if(val == newVal) {
                return;
            }
            val = newVal;

            // 發(fā)布
            pubsub1.pub();
        }
    })
}

// 監(jiān)聽(tīng)data中所有的數(shù)據(jù)
function observer(obj, vm) {
    // obj = data
    // vm  = 實(shí)例對(duì)象
    for(var key in obj) {
        active(vm, key, obj[key]);
    }
}
  • comiple.js
// 獲取到所有節(jié)點(diǎn)梅掠,并且進(jìn)行翻譯
function getAllNode(node, vm) {
    console.log(vm);
    var length = node.childNodes.length;
    for(var i = 0; i < length; i++) {
        compile(node.childNodes[i], vm)
    }
}

// 翻譯
function compile(node, vm) {
    // 匹配到{{}},將其中的值進(jìn)行觀察。
    var reg = /\{\{(.*)\}\}/;
    // 如果節(jié)點(diǎn)存在并且節(jié)點(diǎn)類型為1時(shí)(查看一下為1時(shí)一般都是什么節(jié)點(diǎn))
    if(node!=undefined && node.nodeType == 1) {
        // 獲取到節(jié)點(diǎn)的屬性
        var attr = node.attributes;
        if(attr.length) {
            // 對(duì)節(jié)點(diǎn)的屬性循環(huán)處理
            for(var i = 0; i < attr.length; i++) {
                // 如果為v-model時(shí)店归,進(jìn)行處理
                if(attr[i].nodeName == "v-model") {
                    // 獲取到v-model里面的寫的變量名
                    var name = attr[i].nodeValue;
                    // 給該input增加事件處理阎抒,如果內(nèi)容改變,并及時(shí)更新data中的數(shù)據(jù)
                    node.addEventListener('input', function(e) {
                        vm.data[name] = e.target.value;
                    })

                    // 修改dom上的數(shù)據(jù)消痛,并移除指令
                    console.log(vm.data[name]);
                    node.value = vm.data[name];
                    node.removeAttribute('v-model')
                }
            }
        }else {
            // 匹配到{{}}的dom節(jié)點(diǎn)
            if(reg.test(node.outerText)) {
                var name = RegExp.$1;
                // 拿到變量的名稱
                name = name.trim();

                // 將變量加入到watcher中
                new Watcher(vm, node, name)
            }
        }
    }
}
  • watcher.js
// 全局的變量且叁,這個(gè)變量來(lái)控制是否當(dāng)前有觀察的數(shù)據(jù)
var Pubsub = {
    target: null
}
// 觀察者定義
function Watcher(vm, node, name) {
    Pubsub.target = this;
    this.name = name;
    this.node = node;
    this.vm = vm;
    // 將數(shù)據(jù)觀察時(shí)就要進(jìn)行更新操作一次
    this.update();
    Pubsub.target = null;
}

Watcher.prototype = {
    // 將更新的數(shù)據(jù)渲染到頁(yè)面中去
    update() {
        this.node.innerHTML = this.vm.data[this.name];
    }
}

大家想測(cè)試,就將五個(gè)文件的代碼復(fù)制下來(lái)秩伞,運(yùn)行一下逞带。
運(yùn)行的同時(shí)給大家配一張圖,讓大家容易理解纱新。(這張是盜圖展氓,不知哪位大神用visio畫的,這里我就直接引用了)


原理圖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怒炸,一起剝皮案震驚了整個(gè)濱河市带饱,隨后出現(xiàn)的幾起案子毡代,更是在濱河造成了極大的恐慌阅羹,老刑警劉巖勺疼,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異捏鱼,居然都是意外死亡执庐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門导梆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)轨淌,“玉大人,你說(shuō)我怎么就攤上這事看尼〉蒺模” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵藏斩,是天一觀的道長(zhǎng)躏结。 經(jīng)常有香客問(wèn)我,道長(zhǎng)狰域,這世上最難降的妖魔是什么媳拴? 我笑而不...
    開(kāi)封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮兆览,結(jié)果婚禮上屈溉,老公的妹妹穿的比我還像新娘。我一直安慰自己抬探,他們只是感情好子巾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著小压,像睡著了一般砰左。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上场航,一...
    開(kāi)封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天缠导,我揣著相機(jī)與錄音,去河邊找鬼溉痢。 笑死僻造,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的孩饼。 我是一名探鬼主播髓削,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼镀娶!你這毒婦竟也來(lái)了立膛?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宝泵,沒(méi)想到半個(gè)月后好啰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡儿奶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年框往,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闯捎。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡椰弊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓤鼻,到底是詐尸還是另有隱情秉版,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布茬祷,位于F島的核電站沐飘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏牲迫。R本人自食惡果不足惜耐朴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盹憎。 院中可真熱鬧筛峭,春花似錦、人聲如沸陪每。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)檩禾。三九已至挂签,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盼产,已是汗流浹背饵婆。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留戏售,地道東北人侨核。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像灌灾,于是被迫代替她去往敵國(guó)和親搓译。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 有人說(shuō)锋喜,生活是什么些己?每個(gè)人看法不一樣,而我認(rèn)為生活來(lái)源于每個(gè)人的心里。 你有沒(méi)有一個(gè)想要生活好的心段标,這...
    林霜閱讀 98評(píng)論 0 0
  • 文|紀(jì)不了 -1- 你走過(guò)這么多的路怀樟,看過(guò)那么多的云。那你愛(ài)過(guò)一個(gè)人嗎盆佣? 起初怕他知道又怕他不知道往堡,用盡一切方法吸...
    紀(jì)不了閱讀 821評(píng)論 6 5
  • 很多朋友問(wèn)我,寫新體詩(shī)究竟要不要加標(biāo)點(diǎn)符號(hào)共耍?根據(jù)我自己的體會(huì)虑灰,詩(shī)歌的標(biāo)點(diǎn)符號(hào)其實(shí)也是一種寫作工具痹兜,有時(shí)要用...
    暗香夜話閱讀 837評(píng)論 0 1
  • 孔夫子本則感嘆說(shuō)明人性自私的一面,容易嚴(yán)以待人寬以待己字旭,古來(lái)今往人性不變对湃。 既然從古到今都一樣遗淳,也不必動(dòng)不動(dòng)就發(fā)些...
    海水藍(lán)閱讀 154評(píng)論 0 0