• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當前位置: 首頁 - 科技 - 知識百科 - 正文

    深入理解Vue2.x的虛擬DOM diff原理

    來源:懂視網 責編:小采 時間:2020-11-27 22:28:54
    文檔

    深入理解Vue2.x的虛擬DOM diff原理

    深入理解Vue2.x的虛擬DOM diff原理:前言 經常看到講解Vue2的虛擬Dom diff原理的,但很多都是在原代碼的基礎上添加些注釋等等,這里從0行代碼開始實現一個Vue2的虛擬DOM 實現VNode src/core/vdom/Vnode.js export class VNode{ constructor ( tag, //標簽名 c
    推薦度:
    導讀深入理解Vue2.x的虛擬DOM diff原理:前言 經常看到講解Vue2的虛擬Dom diff原理的,但很多都是在原代碼的基礎上添加些注釋等等,這里從0行代碼開始實現一個Vue2的虛擬DOM 實現VNode src/core/vdom/Vnode.js export class VNode{ constructor ( tag, //標簽名 c

    前言

    經常看到講解Vue2的虛擬Dom diff原理的,但很多都是在原代碼的基礎上添加些注釋等等,這里從0行代碼開始實現一個Vue2的虛擬DOM

    實現VNode

    src/core/vdom/Vnode.js

    export class VNode{
     constructor (
     tag, //標簽名
     children,//孩子[VNode,VNode],
     text, //文本節點
     elm //對應的真實dom對象
     ){
     this.tag = tag;
     this.children = children
     this.text = text;
     this.elm = elm;
     }
    }
    export function createTextNode(val){
     //為什么這里默認把elm置為undefined,不直接根據tag 用document.createElement(tagName)把elm賦值?而要等后面createElm時候再賦值呢?
     return new VNode(undefined,undefined,String(val),undefined)
    }
    export function createCommentNode(tag,children){
     if(children){
     for(var i=0;i<children.length;i++){
     var child = children[i];
     if(typeof child == 'string'){
     children[i] = createTextNode(child)
     }
     }
     }
     return new VNode(tag,children,undefined,null)
    }
    

    定義一個Vnode類, 創建節點分為兩類,一類為text節點,一類非text節點

    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就可以根據Vnode 生成對應的Vnode對象,上述代碼對應的dom表示

    <ul>
    
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
    </ul>
    

    先實現不用diff把Vnode渲染到頁面中來

    為什么先來實現不用diff渲染Vnode的部分,這里也是為了統計渲染的時間,來表明一個道理。并不是diff就比非diff要開,虛擬DOM并不是任何時候性能都比非虛擬DOM 要快

    先來實現一個工具函數,不熟悉的人可以手工敲下代碼 熟悉下

    // 真實的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); //渲染頁面
    console.timeEnd('start');
    
    

    這里我們要實現一個patch方法,把Vnode渲染到頁面中

    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)){
     //如果兩個Vnode節點的根一致 開始diff
     patchVnode(oldVnode,vnode)
     }else{
     //這里就是不借助diff的實現
     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)){
     //非文本節點
     if(isDef(oldCh) && isDef(ch)){
     //都有字節點
     if(oldCh !== ch){
     //更新children
     updateChildren(elm,oldCh,ch,removeOnly);
     }
     }else if(isDef(ch)){
     //新的有子節點,老的沒有
     if(isDef(oldVnode.text)){
     nodeOps.setTextContent(elm,'');
     }
     //添加子節點
     addVnodes(elm,null,ch,0,ch.length-1)
     }else if(isDef(oldCh)){
     //老的有子節點,新的沒有
     removeVnodes(elm,oldCh,0,oldCh.length-1)
     }else if(isDef(oldVnode.text)){
     //否則老的有文本內容 直接置空就行
     nodeOps.setTextContent(elm,'');
     }
     }else if(oldVnode.text !== vnode.text){
     //直接修改文本
     nodeOps.setTextContent(elm,vnode.text);
     }
    }
    
    function updateChildren(parentElm,oldCh,newCh,removeOnly){
     //這里認真讀下,沒什么難度的,不行的話 也可以搜索下圖文描述這段過程的
    
     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){
     //老的提前相遇,添加新節點中沒有比較的節點
     refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx+1].elm
     addVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx)
     }else{
     //新的提前相遇 刪除多余的節點
     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<children.length;i++){
     createElm(children[i],vnode.elm,null)
     }
     }
    }
    function createElm(vnode,parentElm,refElm){
     const children = vnode.children
     const tag = vnode.tag
     if(isDef(tag)){
     // 非文本節點
     vnode.elm = nodeOps.createElement(tag); // 其實可以初始化的時候就賦予
     createChildren(vnode,children);
     insert(parentElm,vnode.elm,refElm)
     }else{
     vnode.elm = nodeOps.createTextNode(vnode.text)
     insert(parentElm,vnode.elm,refElm)
     }
    }
    function insert(parent,elm,ref){
     if(parent){
     if(ref){
     nodeOps.insertBefore(parent,elm,ref)
     }else{
     nodeOps.appendChild(parent,elm)
     }
     }
    }
    
    

    這就是完整實現了

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    深入理解Vue2.x的虛擬DOM diff原理

    深入理解Vue2.x的虛擬DOM diff原理:前言 經常看到講解Vue2的虛擬Dom diff原理的,但很多都是在原代碼的基礎上添加些注釋等等,這里從0行代碼開始實現一個Vue2的虛擬DOM 實現VNode src/core/vdom/Vnode.js export class VNode{ constructor ( tag, //標簽名 c
    推薦度:
    標簽: 原理 VUE di
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 国产成人精品怡红院在线观看| 久久精品国产清自在天天线| 国产av无码专区亚洲国产精品| 无码精品人妻一区二区三区免费看| 国产精品嫩草影院久久| 2018国产精华国产精品| 自拍偷自拍亚洲精品被多人伦好爽| 国产精品久久久久一区二区三区 | 国产亚洲精品一品区99热| 一本久久a久久精品亚洲| 精品久久久久久国产免费了| 欧美亚洲精品在线| 亚洲AV无码乱码精品国产| 2023国产精品自拍| 2022国产精品自产拍在线观看| 午夜精品久久久久久99热| 99久久亚洲综合精品成人| 亚洲第一区精品日韩在线播放| 国产成人精品白浆久久69| 久久精品成人免费看| 国产精品午睡沙发系列| 久久国产精品无码HDAV| 日韩精品专区AV无码| 午夜精品久久久久久久久| 日韩精品无码中文字幕一区二区| 亚洲欧洲国产精品香蕉网| 日韩av无码久久精品免费| 精品无码久久久久久尤物| 国产综合色在线精品| 国产精品毛片无遮挡| 国产a精品视频| 丝袜美腿国产精品视频一区| 成人精品一区二区三区在线观看| 国产成人久久久精品二区三区| 午夜精品久视频在线观看| 国产成人高清精品免费观看| 国产系列高清精品第一页| 精品久久国产一区二区三区香蕉| 欧美精品三区| 亚洲精品无码Av人在线观看国产 | 国产一区精品|