成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

從0行代碼開(kāi)始學(xué)習(xí)Vue2.x的虛擬DOM diff原理

justCoding / 2589人閱讀

摘要:前言經(jīng)??吹街v解的虛擬原理的,但很多都是在原代碼的基礎(chǔ)上添加些注釋等等,這里從行代碼開(kāi)始實(shí)現(xiàn)一個(gè)的虛擬實(shí)現(xiàn)標(biāo)簽名孩子文本節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)對(duì)象為什么這里默認(rèn)把置為,不直接根據(jù)用把賦值而要等后面時(shí)候再賦值呢定義一個(gè)類(lèi),創(chuàng)建節(jié)點(diǎn)分為兩類(lèi),一類(lèi)為節(jié)點(diǎn)

前言

經(jīng)??吹街v解Vue2的虛擬Dom diff原理的,但很多都是在原代碼的基礎(chǔ)上添加些注釋等等,這里從0行代碼開(kāi)始實(shí)現(xiàn)一個(gè)
Vue2的虛擬DOM

實(shí)現(xiàn)VNode
src/core/vdom/Vnode.js

export class VNode{
    constructor (
        tag, //標(biāo)簽名
        children,//孩子[VNode,VNode],
        text, //文本節(jié)點(diǎn)
        elm //對(duì)應(yīng)的真實(shí)dom對(duì)象
    ){
        this.tag = tag;
        this.children = children
        this.text = text;
        this.elm = elm;
    }
}
export function createTextNode(val){
    //為什么這里默認(rèn)把elm置為undefined,不直接根據(jù)tag 用document.createElement(tagName)把elm賦值?而要等后面createElm時(shí)候再賦值呢?
    return new VNode(undefined,undefined,String(val),undefined)
}
export function createCommentNode(tag,children){
    if(children){
        for(var i=0;i

定義一個(gè)Vnode類(lèi), 創(chuàng)建節(jié)點(diǎn)分為兩類(lèi),一類(lèi)為text節(jié)點(diǎn),一類(lèi)非text節(jié)點(diǎn)

src/main.js

import {VNode,createCommentNode} from "./core/vdom/vnode"
var newVonde = createCommentNode("ul",[createCommentNode("li",["item 1"]),createCommentNode("li",["item 2"]),createCommentNode("li",["item 3"])])

在main.js就可以根據(jù)Vnode 生成對(duì)應(yīng)的Vnode對(duì)象,上述代碼對(duì)應(yīng)的dom表示

  • item1
  • item2
  • item3

先實(shí)現(xiàn)不用diff把Vnode渲染到頁(yè)面中來(lái)

為什么先來(lái)實(shí)現(xiàn)不用diff渲染Vnode的部分,這里也是為了統(tǒng)計(jì)渲染的時(shí)間,來(lái)表明一個(gè)道理
并不是diff就比非diff要開(kāi),虛擬DOM并不是任何時(shí)候性能都比非虛擬DOM 要快

先來(lái)實(shí)現(xiàn)一個(gè)工具函數(shù),不熟悉的人可以手工敲下代碼 熟悉下

// 真實(shí)的dom操作
src/core/vdom/node-ops.js

export function createElement (tagName) {
  return document.createElement(tagName)
}

export function createTextNode (text) {
  return document.createTextNode(text)
}

export function createComment (text) {
  return document.createComment(text)
}

export function insertBefore (parentNode, newNode, referenceNode) {
  parentNode.insertBefore(newNode, referenceNode)
}

export function removeChild (node, child) {
  node.removeChild(child)
}

export function appendChild (node, child) {
  node.appendChild(child)
}

export function parentNode (node) {
  return node.parentNode
}

export function nextSibling (node) {
  return node.nextSibling
}

export function tagName (node) {
  return node.tagName
}

export function setTextContent (node, text) {
  node.textContent = text
}

export function setAttribute (node, key, val) {
  node.setAttribute(key, val)
}
src/main.js

import {VNode,createCommentNode} from "./core/vdom/vnode"
import patch from "./core/vdom/patch"


var container = document.getElementById("app");
var oldVnode = new VNode(container.tagName,[],undefined,container);
var newVonde = createCommentNode("ul",[createCommentNode("li",["item 1"]),createCommentNode("li",["item 2"]),createCommentNode("li",["item 3"])])


console.time("start");
patch(oldVnode,newVonde); //渲染頁(yè)面
console.timeEnd("start");

這里我們要實(shí)現(xiàn)一個(gè)patch方法,把Vnode渲染到頁(yè)面中

src/core/vdom/patch.js

import * as nodeOps from "./node-ops"
import VNode from "./vnode"


export default function patch(oldVnode,vnode){
    let isInitialPatch = false;
    if(sameVnode(oldVnode,vnode)){
        //如果兩個(gè)Vnode節(jié)點(diǎn)的根一致  開(kāi)始diff
        patchVnode(oldVnode,vnode)
    }else{
        //這里就是不借助diff的實(shí)現(xiàn)
        const oldElm = oldVnode.elm;
        const parentElm = nodeOps.parentNode(oldElm);
        createElm(
            vnode,
            parentElm,
            nodeOps.nextSibling(oldElm)
        )
        if(parentElm != null){
            removeVnodes(parentElm,[oldVnode],0,0)
        }
    }
    return vnode.elm;
}
function patchVnode(oldVnode,vnode,removeOnly){
    if(oldVnode === vnode){
        return
    }
    const elm = vnode.elm = oldVnode.elm
    const oldCh = oldVnode.children;
    const ch = vnode.children

    if(isUndef(vnode.text)){
        //非文本節(jié)點(diǎn)
        if(isDef(oldCh) && isDef(ch)){
            //都有字節(jié)點(diǎn)
            if(oldCh !== ch){
                //更新children
                updateChildren(elm,oldCh,ch,removeOnly);
            }
        }else if(isDef(ch)){
            //新的有子節(jié)點(diǎn),老的沒(méi)有
            if(isDef(oldVnode.text)){
                nodeOps.setTextContent(elm,"");
            }
            //添加子節(jié)點(diǎn)
            addVnodes(elm,null,ch,0,ch.length-1)
        }else if(isDef(oldCh)){
            //老的有子節(jié)點(diǎn),新的沒(méi)有
            removeVnodes(elm,oldCh,0,oldCh.length-1)
        }else if(isDef(oldVnode.text)){
            //否則老的有文本內(nèi)容 直接置空就行
            nodeOps.setTextContent(elm,"");
        }
    }else if(oldVnode.text !== vnode.text){
        //直接修改文本
        nodeOps.setTextContent(elm,vnode.text);
    }
}

function updateChildren(parentElm,oldCh,newCh,removeOnly){
     //這里認(rèn)真讀下,沒(méi)什么難度的,不行的話(huà) 也可以搜索下圖文描述這段過(guò)程的

    let oldStartIdx = 0;
    let newStartIdx =0;
    let oldEndIdx = oldCh.length -1;
    let oldStartVnode = oldCh[0];
    let oldEndVnode = oldCh[oldEndIdx];
    let newEndIdx = newCh.length-1;
    let newStartVnode = newCh[0]
    let newEndVnode = newCh[newEndIdx]
    let refElm;
    const canMove = !removeOnly
    while(oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx){
        if(isUndef(oldStartVnode)){
            oldStartVnode = oldCh[++oldStartIdx]
        }else if(isUndef(oldEndVnode)){
            oldEndVnode = oldCh[--oldEndIdx]
        }else if(sameVnode(oldStartVnode,newStartVnode)){
            patchVnode(oldStartVnode,newStartVnode)
            oldStartVnode = oldCh[++oldStartIdx]
            newStartVnode = newCh[++newStartIdx]
        }else if(sameVnode(oldEndVnode,newEndVnode)){
            patchVnode(oldEndVnode,newEndVnode)
            oldEndVnode = oldCh[--oldEndIdx];
            newEndVnode = newCh[--newEndIdx];
        }else if(sameVnode(oldStartVnode,newEndVnode)){
            patchVnode(oldStartVnode,newEndVnode);
            //更換順序
            canMove && nodeOps.insertBefore(parentElm,oldStartVnode.elm,nodeOps.nextSibling(oldEndVnode.elm))
            oldStartVnode = oldCh[++oldStartIdx]
            newEndVnode = newCh[--newEndIdx]
        }else if(sameVnode(oldEndVnode,newStartVnode)){
            patchVnode(oldEndVnode,newStartVnode)
            canMove && nodeOps.insertBefore(parentElm,oldEndVnode.elm,oldStartVnode.elm)
            oldEndVnode = oldCh[--oldEndIdx]
            newStartVnode = newCh[++newStartIdx]
        }else{
            createElm(newStartVnode,parentElm,oldStartVnode.elm)
            newStartVnode = newCh[++newStartIdx];
        }
    }

    if(oldStartIdx > oldEndIdx){
        //老的提前相遇,添加新節(jié)點(diǎn)中沒(méi)有比較的節(jié)點(diǎn)
        refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx+1].elm
        addVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx)
    }else{
        //新的提前相遇  刪除多余的節(jié)點(diǎn)
        removeVnodes(parentElm,oldCh,oldStartIdx,oldEndIdx)
    }
}
function removeVnodes(parentElm,vnodes,startIdx,endIdx){
    for(;startIdx<=endIdx;++startIdx){
        const ch = vnodes[startIdx];
        if(isDef(ch)){
            removeNode(ch.elm)
        }
    }
}

function addVnodes(parentElm,refElm,vnodes,startIdx,endIdx){
    for(;startIdx <=endIdx;++startIdx ){
        createElm(vnodes[startIdx],parentElm,refElm)
    }
}

function sameVnode(vnode1,vnode2){
    return vnode1.tag === vnode2.tag
}
function removeNode(el){
    const parent = nodeOps.parentNode(el)
    if(parent){
        nodeOps.removeChild(parent,el)
    }
}
function removeVnodes(parentElm,vnodes,startIdx,endIdx){
    for(;startIdx<=endIdx;++startIdx){
        const ch = vnodes[startIdx]
        if(isDef(ch)){
            removeNode(ch.elm)
        }
    }
}
function isDef (s){
    return s != null
}
function isUndef(s){
    return s == null
}
function createChildren(vnode,children){
    if(Array.isArray(children)){
        for(let i=0;i

這就是完整實(shí)現(xiàn)了

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88602.html

相關(guān)文章

  • 前方來(lái)報(bào),八月最新資訊--關(guān)于vue2&3最佳文章推薦

    摘要:哪吒別人的看法都是狗屁,你是誰(shuí)只有你自己說(shuō)了才算,這是爹教我的道理。哪吒去他個(gè)鳥(niǎo)命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰(shuí)和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...

    izhuhaodev 評(píng)論0 收藏0
  • Preact了解一個(gè)類(lèi)React框架是怎么實(shí)現(xiàn)(二): 元素diff

    摘要:本系列文章將重點(diǎn)分析類(lèi)似于的這類(lèi)框架是如何實(shí)現(xiàn)的,歡迎大家關(guān)注和討論。作為一個(gè)極度精簡(jiǎn)的庫(kù),函數(shù)是屬于本身的。 前言   首先歡迎大家關(guān)注我的掘金賬號(hào)和Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫(xiě)東西沒(méi)法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)?! ≈胺窒磉^(guò)幾篇關(guān)于React的文章: React技術(shù)內(nèi)幕: key帶來(lái)了什么 React技術(shù)內(nèi)幕: setState的秘密...

    張巨偉 評(píng)論0 收藏0
  • 2017-09-24 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選未來(lái)布局之星更快地構(gòu)建使用預(yù)解析以及深入的虛擬原理原來(lái)與是這樣阻塞解析和渲染的怎樣把網(wǎng)站升級(jí)到中文視頻從談函數(shù)式與響應(yīng)式編程葉俊星系列三之煙花效果實(shí)現(xiàn)掘金的故事解剖表情動(dòng)圖的構(gòu)成設(shè)計(jì)系列傳統(tǒng)遞歸和尾調(diào)用的實(shí)現(xiàn)前端架構(gòu)經(jīng) 2017-09-24 前端日?qǐng)?bào) 精選 未來(lái)布局之星Grid更快地構(gòu)建DOM: 使用預(yù)解析, async, defer 以及 preload_JavaScri...

    yuanzhanghu 評(píng)論0 收藏0
  • 淺析虛擬dom原理并實(shí)現(xiàn)

    摘要:虛擬原理流程簡(jiǎn)單概括有三點(diǎn)用模擬樹(shù),并渲染這個(gè)樹(shù)比較新老樹(shù),得到比較的差異對(duì)象把差異對(duì)象應(yīng)用到渲染的樹(shù)。下面是流程圖下面我們用代碼一步步去實(shí)現(xiàn)一個(gè)流程圖用模擬樹(shù)并渲染到頁(yè)面上其實(shí)虛擬,就是用對(duì)象結(jié)構(gòu)的一種映射,下面我們一步步實(shí)現(xiàn)這個(gè)過(guò)程。 背景 大家都知道,在網(wǎng)頁(yè)中瀏覽器資源開(kāi)銷(xiāo)最大便是DOM節(jié)點(diǎn)了,DOM很慢并且非常龐大,網(wǎng)頁(yè)性能問(wèn)題大多數(shù)都是有JavaScript修改DOM所引起的...

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

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

0條評(píng)論

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