成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

自己實(shí)現(xiàn)MVVM(Vue源碼解析)

?xiaoxiao, / 3045人閱讀

摘要:無論是雙向綁定還是單向綁定,都是符合思想的??戳说脑创a后不難發(fā)現(xiàn)的雙向綁定的實(shí)現(xiàn)也就是在表單元素上添加了事件,可以說雙向綁定是單向綁定的一個(gè)語法糖。

前言

本文會(huì)帶大家手動(dòng)實(shí)現(xiàn)一個(gè)雙向綁定過程(僅僅涵蓋一些簡單的指令解析,如:v-text,v-model,插值),當(dāng)然借鑒的是Vue1的源碼,相信大家在閱讀完本文后對(duì)Vue1會(huì)有一個(gè)更好的理解,源代碼放到了github,由于本人水平有限,理解不到位的地方還請(qǐng)大家指出。

MVVM

MVVM使開發(fā)可以更加關(guān)注于數(shù)據(jù),減少了很大的工作量,也使代碼可讀性,可維護(hù)性更高,MVVM核心的思想就是視圖是狀態(tài)的函數(shù):View = ViewModel(Model),所以當(dāng)Model發(fā)生改變時(shí),ViewModel會(huì)來操作View來怎么做,而非是自己寫代碼來做。無論是雙向綁定還是單向綁定,都是符合MVVM思想的。Vue提倡的是雙向綁定,也就是允許View到Model的變化,其實(shí)這個(gè)場景出現(xiàn)在的也就是表單操作上,看個(gè)例子,例子中分別利用了Vue和React實(shí)現(xiàn)了一下表單value變化,影響頁面與其相關(guān)的dom節(jié)點(diǎn)發(fā)生變化,可以發(fā)現(xiàn)的是雙向綁定的Vue是inputvalue發(fā)生變化則h1innerText就發(fā)生了變化,變化是由View->Model,而提倡單向數(shù)據(jù)流的React需要手動(dòng)監(jiān)聽事件,事件觸發(fā)后,更改Model的值,從而使inputvalue發(fā)生了變化??戳薞ue的源碼后不難發(fā)現(xiàn)Vue的雙向綁定的實(shí)現(xiàn)也就是在表單元素上添加了input事件,可以說雙向綁定是單向綁定的一個(gè)語法糖。

實(shí)現(xiàn)思路

上圖是一個(gè)大體的流程,下面按照流程來實(shí)現(xiàn)下:

利用observer對(duì)data進(jìn)行了監(jiān)聽,并且提供訂閱某個(gè)數(shù)據(jù)項(xiàng)的變化的能力

這點(diǎn)的實(shí)現(xiàn),需要借助的是Object.defineProperty()來為對(duì)象的屬性綁定get/set特性(由于利用了Object.defineProperty(),所以Vue不支持ie8),observer需要將data的所有屬性都綁定get/set,很容易想到的就是利用遞歸來實(shí)現(xiàn),具體代碼就不貼出,請(qǐng)參見這里。

利用Compile對(duì)模板進(jìn)行解析

這點(diǎn)實(shí)現(xiàn)的是將我們的模板轉(zhuǎn)化為html,過程中會(huì)將數(shù)據(jù)與View中的節(jié)點(diǎn)相關(guān)聯(lián)起來,最終會(huì)將編譯好的html頁面替換到頁面上。首先來看解析,首先從根節(jié)點(diǎn)開始,根據(jù)不同的節(jié)點(diǎn)類型采用不同的解析方式:

function compileNode(node, vm) {
    const type = node.nodeType;
    if (type === 1 && !isScript(node)) {
        compileElement(node, vm);
    } else if (type === 3 && node.data.trim()) {
        compileTextNode(node, vm);
    } else {
        return null;
    }
}

對(duì)于文本節(jié)點(diǎn)來說,可能存在情況只有兩種

與數(shù)據(jù)不相關(guān)不用操作

含有插值,需要與數(shù)據(jù)進(jìn)行關(guān)聯(lián)

{{}}文本插值

{{{}}}html插值

利用下面正就可以將插值找出:

/{{{(.*?)}}}|{{(.*?)}}/g

采用下面函數(shù)來對(duì)文本節(jié)點(diǎn)的內(nèi)容解析:

function parseText(node) {
    var text = node.wholeText;
    if (!tagRE.test(text)) {
        return void 0;
    }
    const tokens = [];
    var lastIndex = tagRE.lastIndex = 0,
        match, index, html, value;
    while (match = tagRE.exec(text)) {
        index = match.index;
        if (index > lastIndex) {
            tokens.push({
                value: text.slice(lastIndex, index)
            })
        }
        html = htmlRE.test(match[0]);
        value = html ? match[1] : match[2];
        tokens.push({
            value: value,
            tag: true,
            html: html
        });
        lastIndex = index + match[0].length;
    }
    if (lastIndex < text.length) {
        tokens.push({
            value: text.slice(lastIndex)
        })
    }
    return tokens;
}

返回了tokens,里面存儲(chǔ)了每一個(gè)塊內(nèi)容,一個(gè)插值or一個(gè)普通文本,tag來標(biāo)記是否為插值,html來標(biāo)記是否為純html插值。遍歷返回的tokens,根據(jù)不同的類型,來采用不同的方式將其添加到其父節(jié)點(diǎn)上:

function compileTextNode(node, vm) {
    const tokens = parseText(node);
    if (tokens == null) return void 0;
    var frag = document.createDocumentFragment();
    tokens.forEach(token => {
        var el;
        if (token.tag) {
            if (token.html) {
                el = document.createDocumentFragment();
                el.$parent = node.parentNode;
                el.$oneTime = true;
                dirCollection["html"](el, vm, token.value);
            } else {
                el = document.createTextNode(" ");
                dirCollection["text"](el, vm, token.value);
            }
        } else {
            el = document.createTextNode(token.value);
        } 
        el && frag.appendChild(el);
    });
    return replace(node, frag);
}

dirCollection是一個(gè)指令集合,也就是決定了如何初始化以及如何更新該節(jié)點(diǎn)。對(duì)于nodeType1的節(jié)點(diǎn)來說,指令全部存儲(chǔ)在其屬性中,遍歷屬性,假若指令中含有v-html,v-model,v-text,則停止遍歷其子樹,直接將調(diào)用相應(yīng)指令即可,否則,則需要遍歷其子節(jié)點(diǎn),對(duì)其子節(jié)點(diǎn)應(yīng)用compileNode進(jìn)行解析:

function compileNodeList(nodes, vm) {
    for (let val of nodes) {
        compileNode(val, vm);
    }
}
function compileElement(node, vm) {
    var flag = false;
    const attrs = Array.prototype.slice.call(node.attributes);
    attrs.forEach((val) => {
        const name = val.name,
            value = val.value;
        if (dirRE.test(name)) {
            var dir;
            // 事件指令
            if (
                (dir = name.match(eventRE)) && 
                (dir = dir[1])
            ) {
                dirCollection["eventDir"](node, dir, vm, value);
            } else {
                dir = name.match(dirRE)[1];
                dirCollection[dir](node, vm, value);
            }
            // 指令中為v-html or v-text or v-model終止遞歸
            flag = flag || 
                name === vhtml || 
                name === vtext;    
            node.removeAttribute(name);
        }    
    });
    const childs = node.childNodes;
    if (!flag && childs && childs.length) {
        compileNodeList(childs, vm);
    }
}

dirCollections中還會(huì)做的就是將數(shù)據(jù)與View的dom節(jié)點(diǎn)相關(guān)聯(lián),利用的就是DepWatcher,頁面上每一個(gè)與數(shù)據(jù)相關(guān)聯(lián)的節(jié)點(diǎn)都含有一個(gè)Watcher,當(dāng)數(shù)據(jù)發(fā)生變化是Watcher用于計(jì)算,是否需要更新該節(jié)點(diǎn);數(shù)據(jù)的每一個(gè)屬性都有一個(gè)Dep,當(dāng)該屬性發(fā)生變化時(shí),Dep會(huì)通知與該數(shù)據(jù)相關(guān)聯(lián)的Watcher來進(jìn)行計(jì)算是否需要更新對(duì)應(yīng)頁面。Dep代碼,Watcher代碼。

異步更新隊(duì)列

異步更新隊(duì)列,是一個(gè)優(yōu)化,將更新dom的操作變?yōu)楫惒降?,放到下一個(gè)事件循環(huán)來做,這樣做可以減少不必要的dom更新,看下面情況:

vm.value++;
vm.value++;
vm.value++;

三次數(shù)據(jù)改變,假若同步更新的話,則每次數(shù)據(jù)改變會(huì)立即更新dom,而異步更新的話,可以先將更新推入一個(gè)隊(duì)列中,由于是異步,也可以保證每一個(gè)Watcher只被推入到一次,這樣就避免了不必要的更新,異步更新主要利用的是nextTick,這個(gè)函數(shù)會(huì)優(yōu)先使用Promise,不兼容則利用MutationObserver,再不兼容的話會(huì)利用setTimeout。

寫在后面

看過了Vue的源碼不得不感嘆Vue的優(yōu)美,而Vue2又增加了虛擬dom,這樣就可以做到服務(wù)端渲染,給了我們更多的可能!

這篇博客最好配合著源碼來看,關(guān)于源碼歡迎star

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/91287.html

相關(guān)文章

  • JavaScript從初級(jí)往高級(jí)走系列————MVVM-Vue

    摘要:原文博客地址如何理解如何實(shí)現(xiàn)是否解讀過的源碼與框架的區(qū)別實(shí)現(xiàn)實(shí)現(xiàn)獨(dú)立初始化實(shí)例兩者的區(qū)別數(shù)據(jù)和視圖的分離,解耦開放封閉原則,對(duì)擴(kuò)展開放,對(duì)修改封閉在中在代碼中操作視圖和數(shù)據(jù),混在一塊了以數(shù)據(jù)驅(qū)動(dòng)視圖,只關(guān)心數(shù)據(jù)變化, 原文博客地址:https://finget.github.io/2018/05/31/mvvm-vue/ MVVM 如何理解 MVVM 如何實(shí)現(xiàn) MVVM 是否解讀過 ...

    codercao 評(píng)論0 收藏0
  • 剖析Vue原理&實(shí)現(xiàn)雙向綁定MVVM

    摘要:所以無需太過介懷是實(shí)現(xiàn)的單向或雙向綁定。監(jiān)聽數(shù)據(jù)綁定更新函數(shù)的處理是在這個(gè)方法中,通過添加回調(diào)來接收數(shù)據(jù)變化的通知至此,一個(gè)簡單的就完成了,完整代碼。 本文能幫你做什么?1、了解vue的雙向數(shù)據(jù)綁定原理以及核心代碼模塊2、緩解好奇心的同時(shí)了解如何實(shí)現(xiàn)雙向綁定為了便于說明原理與實(shí)現(xiàn),本文相關(guān)代碼主要摘自vue源碼, 并進(jìn)行了簡化改造,相對(duì)較簡陋,并未考慮到數(shù)組的處理、數(shù)據(jù)的循環(huán)依賴等,也...

    melody_lql 評(píng)論0 收藏0
  • JavaScript 進(jìn)階之深入理解數(shù)據(jù)雙向綁定

    摘要:當(dāng)我們的視圖和數(shù)據(jù)任何一方發(fā)生變化的時(shí)候,我們希望能夠通知對(duì)方也更新,這就是所謂的數(shù)據(jù)雙向綁定。返回值返回傳入函數(shù)的對(duì)象,即第一個(gè)參數(shù)該方法重點(diǎn)是描述,對(duì)象里目前存在的屬性描述符有兩種主要形式數(shù)據(jù)描述符和存取描述符。 前言 談起當(dāng)前前端最熱門的 js 框架,必少不了 Vue、React、Angular,對(duì)于大多數(shù)人來說,我們更多的是在使用框架,對(duì)于框架解決痛點(diǎn)背后使用的基本原理往往關(guān)注...

    sarva 評(píng)論0 收藏0
  • 剖析Vue實(shí)現(xiàn)原理 - 如何實(shí)現(xiàn)雙向綁定mvvm(轉(zhuǎn)載)

    摘要:接下來要看看這個(gè)訂閱者的具體實(shí)現(xiàn)了實(shí)現(xiàn)訂閱者作為和之間通信的橋梁,主要做的事情是在自身實(shí)例化時(shí)往屬性訂閱器里面添加自己自身必須有一個(gè)方法待屬性變動(dòng)通知時(shí),能調(diào)用自身的方法,并觸發(fā)中綁定的回調(diào),則功成身退。 本文能幫你做什么?1、了解vue的雙向數(shù)據(jù)綁定原理以及核心代碼模塊2、緩解好奇心的同時(shí)了解如何實(shí)現(xiàn)雙向綁定為了便于說明原理與實(shí)現(xiàn),本文相關(guān)代碼主要摘自vue源碼, 并進(jìn)行了簡化改造,...

    nemo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<