最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2017年09月27日 10:35:10   作者:小溪流  
本篇文章主要介紹了Vue2.x的虛擬DOM diff原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言

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

實現(xiàn)VNode

src/core/vdom/Vnode.js

export class VNode{
 constructor (
  tag, //標簽名
  children,//孩子[VNode,VNode],
  text, //文本節(jié)點
  elm //對應的真實dom對象
 ){
  this.tag = tag;
  this.children = children
  this.text = text;
  this.elm = elm;
 }
}
export function createTextNode(val){
 //為什么這里默認把elm置為undefined,不直接根據(jù)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類, 創(chuàng)建節(jié)點分為兩類,一類為text節(jié)點,一類非text節(jié)點

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

<ul>

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

先實現(xiàn)不用diff把Vnode渲染到頁面中來

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

先來實現(xiàn)一個工具函數(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); //渲染頁面
console.timeEnd('start');

這里我們要實現(xiàn)一個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節(jié)點的根一致 開始diff
  patchVnode(oldVnode,vnode)
 }else{
  //這里就是不借助diff的實現(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é)點
  if(isDef(oldCh) && isDef(ch)){
   //都有字節(jié)點
   if(oldCh !== ch){
    //更新children
    updateChildren(elm,oldCh,ch,removeOnly);
   }
  }else if(isDef(ch)){
   //新的有子節(jié)點,老的沒有
   if(isDef(oldVnode.text)){
    nodeOps.setTextContent(elm,'');
   }
   //添加子節(jié)點
   addVnodes(elm,null,ch,0,ch.length-1)
  }else if(isDef(oldCh)){
   //老的有子節(jié)點,新的沒有
   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){
  //老的提前相遇,添加新節(jié)點中沒有比較的節(jié)點
  refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx+1].elm
  addVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx)
 }else{
  //新的提前相遇 刪除多余的節(jié)點
  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)){
  // 非文本節(jié)點
  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)
  }
 }
}

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • v-for中動態(tài)校驗el-form表單項的實踐

    v-for中動態(tài)校驗el-form表單項的實踐

    在項目開發(fā)中,我們經常會遇到表單保存的功能,本文主要介紹了v-for中動態(tài)校驗el-form表單項的實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧<BR>
    2022-05-05
  • el-form的label和表單自適應填滿一行且靠左對齊方式

    el-form的label和表單自適應填滿一行且靠左對齊方式

    這篇文章主要介紹了el-form的label和表單自適應填滿一行且靠左對齊方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Vue-cli proxyTable 解決開發(fā)環(huán)境的跨域問題詳解

    Vue-cli proxyTable 解決開發(fā)環(huán)境的跨域問題詳解

    本篇文章主要介紹了Vue-cli proxyTable 解決開發(fā)環(huán)境的跨域問題詳解,非常具有實用價值,需要的朋友可以參考下
    2017-05-05
  • vue 獲取視頻時長的實例代碼

    vue 獲取視頻時長的實例代碼

    這篇文章主要介紹了vue 獲取視頻時長的實例代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08
  • Vue2.0實現(xiàn)將頁面中表格數(shù)據(jù)導出excel的實例

    Vue2.0實現(xiàn)將頁面中表格數(shù)據(jù)導出excel的實例

    本篇文章主要介紹了Vue2.0實現(xiàn)將頁面中表格數(shù)據(jù)導出excel的實例,非常具有實用價值,感興趣的同學可以了解一下
    2017-08-08
  • Vue自定義表單內容檢查rules實例

    Vue自定義表單內容檢查rules實例

    這篇文章主要介紹了Vue自定義表單內容檢查rules實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Vue基礎語法知識梳理上篇

    Vue基礎語法知識梳理上篇

    這篇文章主要介紹了Vue基礎語法知識梳理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-12-12
  • 詳解Element-UI中上傳的文件前端處理

    詳解Element-UI中上傳的文件前端處理

    這篇文章主要介紹了詳解Element-UI中上傳的文件前端處理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • vue如何將導航欄、頂部欄設置為公共頁面

    vue如何將導航欄、頂部欄設置為公共頁面

    這篇文章主要介紹了vue如何將導航欄、頂部欄設置為公共頁面問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Vue實現(xiàn)省市區(qū)三級聯(lián)動el-select組件的示例代碼

    Vue實現(xiàn)省市區(qū)三級聯(lián)動el-select組件的示例代碼

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)省市區(qū)三級聯(lián)動el-select組件的方法,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的的可以參考一下
    2023-02-02

最新評論

青岛市| 祥云县| 白水县| 本溪市| 河北省| 东阳市| 德令哈市| 都安| 诏安县| 佛学| 济阳县| 汉沽区| 宿州市| 固原市| 东城区| 客服| 马公市| 张家川| 星子县| 新兴县| 奇台县| 宝坻区| 腾冲县| 岐山县| 西乌珠穆沁旗| 无棣县| 茶陵县| 竹溪县| 朝阳县| 鞍山市| 潼关县| 惠安县| 瑞安市| 车致| 遂溪县| 雷州市| 东兴市| 岳阳市| 太仆寺旗| 宁陕县| 虹口区|