<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答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
        主站蜘蛛池模板: 亚洲精品又粗又大又爽A片| 久久精品国产亚洲77777| 国产偷国产偷亚洲高清人| 毛片免费在线视频| 亚洲日韩精品国产一区二区三区| 4hu四虎最新免费地址| 亚洲伊人久久大香线蕉| 两性刺激生活片免费视频| 2017亚洲男人天堂一| 亚洲人成色77777在线观看大| 亚洲国产精品无码久久98| 又爽又黄无遮挡高清免费视频| 成在线人直播免费视频| 久久国产成人亚洲精品影院 | 日本免费观看网站| 亚洲愉拍一区二区三区| 国产一级淫片a免费播放口之| 一区二区免费在线观看| 亚洲精品成人网站在线观看 | 亚洲电影在线免费观看| 小说区亚洲自拍另类| 国产成人精品曰本亚洲79ren| 99精品视频在线观看免费| 亚洲最大黄色网站| 四虎成人免费网站在线| 免费福利资源站在线视频| 亚洲韩国—中文字幕| 国产免费内射又粗又爽密桃视频| 国产AV无码专区亚洲AV男同 | 亚洲伊人色一综合网| 国产成人精品高清免费| 免费国产午夜高清在线视频| 亚洲嫩草影院在线观看| 免费在线黄色网址| 91人人区免费区人人| 欧洲亚洲国产精华液| 久久被窝电影亚洲爽爽爽 | h在线看免费视频网站男男| 1区1区3区4区产品亚洲| 免费在线观看黄网站| 24小时免费看片|