亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

虛擬Dom詳解 - (二)

sevi_stuo / 2787人閱讀

摘要:第一篇文章中主要講解了虛擬基本實(shí)現(xiàn),簡(jiǎn)單的回顧一下,虛擬是使用數(shù)據(jù)描述的一段虛擬節(jié)點(diǎn)樹,通過函數(shù)生成其真實(shí)節(jié)點(diǎn)。并添加到其對(duì)應(yīng)的元素容器中。在創(chuàng)建真實(shí)節(jié)點(diǎn)的同時(shí)并為其注冊(cè)事件并添加一些附屬屬性。

第一篇文章中主要講解了虛擬DOM基本實(shí)現(xiàn),簡(jiǎn)單的回顧一下,虛擬DOM是使用json數(shù)據(jù)描述的一段虛擬Node節(jié)點(diǎn)樹,通過render函數(shù)生成其真實(shí)DOM節(jié)點(diǎn)。并添加到其對(duì)應(yīng)的元素容器中。在創(chuàng)建真實(shí)DOM節(jié)點(diǎn)的同時(shí)并為其注冊(cè)事件并添加一些附屬屬性。

虛擬Dom詳解 - (一)

在上篇文章中也曾經(jīng)提到過,當(dāng)狀態(tài)變更的時(shí)候用修改后的新渲染的的JavaScript對(duì)象和舊的虛擬DOMJavaScript對(duì)象作對(duì)比,記錄著兩棵樹的差異,把差別反映到真實(shí)的DOM結(jié)構(gòu)上最后操作真正的DOM的時(shí)候只操作有差異的部分的更改。然而上篇文章中也只是簡(jiǎn)簡(jiǎn)單單的提到過一句卻沒有進(jìn)行實(shí)質(zhì)性的實(shí)現(xiàn),這篇文章主要講述一下虛擬DOM是如何做出更新的。那就開始吧...O(∩_∩)O

在虛擬DOM中實(shí)現(xiàn)更新的話是使用DIFF算法進(jìn)行更新的,我想大多數(shù)小伙伴都應(yīng)該聽說過這個(gè)詞,DIFF是整個(gè)虛擬DOM部分最核心的部分,因?yàn)楫?dāng)虛擬DOM節(jié)點(diǎn)狀態(tài)發(fā)生改變以后不可能去替換整個(gè)DOM節(jié)點(diǎn)樹,若是這樣的話會(huì)出現(xiàn)打兩個(gè)DOM操作,無非是對(duì)性能的極大影響,真的如此的話還不如直接操作DOM來的實(shí)際一些。

第一篇文章中是通過render對(duì)虛擬DOM節(jié)點(diǎn)樹進(jìn)行渲染的,但是在render函數(shù)中只做了一件事情,只是對(duì)虛擬DOM進(jìn)行了新建也就是初始化工作,其實(shí)回過頭來想一下,無論是新建操作還是修改操作,都應(yīng)該通過render函數(shù)來做,在react中所有的DOM渲染都是通過其中的render函數(shù)完成的,那么也就得出了這個(gè)結(jié)論。

//  渲染虛擬DOM
//    虛擬DOM節(jié)點(diǎn)樹
//    承載DOM節(jié)點(diǎn)的容器,父元素
function render(vnode,container) {
  //  首次渲染
  mount(vnode,container);
};

既然更新和創(chuàng)建操作都是通過render函數(shù)來做的,在方法中又應(yīng)該如何區(qū)分當(dāng)前的操作到底是新建還是更新呢?畢竟在react我們并沒有給出明確的標(biāo)識(shí)來告訴其方法,當(dāng)前是進(jìn)行的哪個(gè)操作。在執(zhí)行render函數(shù)的時(shí)候有兩個(gè)參數(shù),一個(gè)是傳入的vnode節(jié)點(diǎn)樹,還有一個(gè)就是承載真實(shí)DOM節(jié)點(diǎn)的容器,其實(shí)我們可以把其虛擬DOM節(jié)點(diǎn)樹掛載在其容器中,若容器中存在其節(jié)點(diǎn)樹則是更新操作,反之則是新建操作。

//  渲染虛擬DOM
//    虛擬DOM節(jié)點(diǎn)樹
//    承載DOM節(jié)點(diǎn)的容器,父元素
function render(vnode, container) {

  if (!container.vnode) {
    //  首次渲染
    mount(vnode, container);
  } else {
    //  舊的虛擬DOM節(jié)點(diǎn)
    //  新的DOM節(jié)點(diǎn)
    //  承載DOM節(jié)點(diǎn)的容器
    patch(container.vnode, vnode, container);
  }
  container.vnode = vnode;
};

既然已經(jīng)確定了現(xiàn)在的render函數(shù)所需要進(jìn)行的操作了,那么接下來就應(yīng)該進(jìn)行下一步操作了,如果想要做更新的話必須要知道如下幾個(gè)參數(shù),原有的虛擬DOM節(jié)點(diǎn)是什么樣的,新的虛擬DOM又是什么樣的,上一步操作中我們已經(jīng)把原有的虛擬DOM節(jié)點(diǎn)已經(jīng)保存在了父容器中,直接使用即可。

//  更新函數(shù)
//    舊的虛擬DOM節(jié)點(diǎn)
//    新的DOM節(jié)點(diǎn)
//    承載DOM節(jié)點(diǎn)的容器
function patch(oldVNode, newVNode, container) {
  //  新節(jié)點(diǎn)的VNode類型
  let newVNodeFlag = newVNode.flag;
  //  舊節(jié)點(diǎn)的VNode類型
  let oldVNodeFlag = oldVNode.flag;
  //  如果新節(jié)點(diǎn)與舊節(jié)點(diǎn)的類型不一致
  //  如果不一致的情況下,相當(dāng)于其節(jié)點(diǎn)發(fā)生了變化
  //  直接進(jìn)行替換操作即可
  //  這里判斷的是如果一個(gè)是 TEXT 一個(gè)是 Element
  //  類型判斷
  if (newVNodeFlag !== oldVNodeFlag) {
    replaceVNode(oldVNode, newVNode, container);
  }
  //  由于在新建時(shí)創(chuàng)建Element和Text的時(shí)候使用的是兩個(gè)函數(shù)進(jìn)行操作的
  //  在更新的時(shí)候也是同理的
  //  也應(yīng)該針對(duì)不同的修改進(jìn)行不同的操作
  //  如果新節(jié)點(diǎn)與舊節(jié)點(diǎn)的HTML相同
  else if (newVNodeFlag == vnodeTypes.HTML) {
    //  替換元素操作
    patchMethos.patchElement(oldVNode, newVNode, container);
  }
  //  如果新節(jié)點(diǎn)與舊節(jié)點(diǎn)的TEXT相同
  else if (newVNodeFlag == vnodeTypes.TEXT) {
    //  替換文本操作
    patchMethos.patchText(oldVNode, newVNode, container);
  }
}
//  更新VNode方法集
const patchMethos = {
    //  替換文本操作
    //    舊的虛擬DOM節(jié)點(diǎn)
    //    新的DOM節(jié)點(diǎn)
    //    承載DOM節(jié)點(diǎn)的容器
    patchText(oldVNode,newVNode,container){
        //  獲取到el,并將 oldVNode 賦值給 newVNode
        let el = (newVNode.el = oldVNode.el);
        //  如果 newVNode.children 不等于 oldVNode.children
        //  其他情況就是相等則沒有任何操作,不需要更新
        if(newVNode.children !== oldVNode.children){
            //  直接進(jìn)行替換操作
            el.nodeValue = newVNode.children;
        }
    }
};
//  替換虛擬DOM
function replaceVNode(oldVNode, newVNode, container) {
  //  在原有節(jié)點(diǎn)中刪除舊節(jié)點(diǎn)
  container.removeChild(oldVNode.el);
  //  重新渲染新節(jié)點(diǎn)
  mount(newVNode, container);
}

上述方法簡(jiǎn)單的實(shí)現(xiàn)了對(duì)Text更新的一個(gè)替換操作,由于Text替換操作比較簡(jiǎn)單,所以這里就先實(shí)現(xiàn),僅僅完成了對(duì)Text的更新是遠(yuǎn)遠(yuǎn)不夠的,當(dāng)Element進(jìn)行操作的時(shí)也是需要更新的。相對(duì)來說Text的更新要比Element更新要簡(jiǎn)單很多的,Element更新比較復(fù)雜所以放到了后面,因?yàn)楸容^重要嘛,哈哈~

首先想要進(jìn)行Element替換之前要確定哪些Data數(shù)據(jù)進(jìn)行了變更,然后才能對(duì)其進(jìn)行替換操作,這樣的話需要確定要更改的數(shù)據(jù),然后替換掉原有數(shù)據(jù),才能進(jìn)行下一步更新操作。

//  更新VNode方法集
const patchMethos = {
    //  替換元素操作
    //    舊的虛擬DOM節(jié)點(diǎn)
    //    新的DOM節(jié)點(diǎn)
    //    承載DOM節(jié)點(diǎn)的容器
    patchElement(oldVNode,newVNode,container){
        //  如果 newVNode 的標(biāo)簽名稱與 oldVNode 標(biāo)簽名稱不一樣
        //  既然標(biāo)簽都不一樣則直接替換就好了,不需要再進(jìn)行其他多余的操作
        if(newVNode.tag !== oldVNode.tag){
            replaceVNode(oldVNode,newVNode,container);
            return;
        }
        //  更新el
        let el = (newVNode.el = oldVNode.el);
        //  獲取舊的Data數(shù)據(jù)
        let oldData = oldVNode.data;
        //  獲取新的Data數(shù)據(jù)
        let newData = newVNode.data;
        //  如果新的Data數(shù)據(jù)存在
        //  進(jìn)行更新和新增
        if(newData){
            for(let attr in newData){
                let oldVal = oldData[attr];
                let newVal = newData[attr];
                domAttributeMethod.patchData(el,attr,oldVal,newVal);
            }
        }
        //  如果舊的Data存在
        //  檢測(cè)更新
        if(oldData){
            for(let attr in oldData){
                let oldVal = oldData[attr];
                let newVal = newData[attr];
                //  如果舊數(shù)據(jù)存在,新數(shù)據(jù)中不存在
                //  則表示已刪除,需要進(jìn)行更新操作
                if(oldVal && !newVal.hasOwnProperty(attr)){
                    //  既然新數(shù)據(jù)中不存在,則新數(shù)據(jù)則傳入Null
                    domAttributeMethod.patchData(el,attr,oldVal,null);
                }
            }
        }
    }
};
//  dom添加屬性方法
const domAttributeMethod = {
  //  修改Data數(shù)據(jù)方法
  patchData (el,key,prv,next){
    switch(key){
      case "style":
        this.setStyle(el,key,prv,next);
        //  添加了這里,看我看我  (●"?"●)
        //  添加遍歷循環(huán)
        //  循環(huán)舊的data
        this.setOldVal(el,key,prv,next);
        break;
      case "class":
        this.setClass(el,key,prv,next);
        break;
      default :
        this.defaultAttr(el,key,prv,next);
        break;
    }
  },
  //  遍歷舊數(shù)據(jù)
  setOldVal(el,key,prv,next){
    //  遍歷舊數(shù)據(jù)
    for(let attr in prv){
        //  如果舊數(shù)據(jù)存在,新數(shù)據(jù)中不存在
        if(!next.hasOwnProperty(attr)){
            //  直接賦值為字符串
            el.style[attr] = "";
        }
    }
  },
  //  修改事件注冊(cè)方法
  addEvent(el,key,prev,next){
    //  添加了這里,看我看我  (●"?"●)
    //  prev 存在刪除原有事件,重新綁定新的事件
    if(prev){
      el.removeEventListener(key.slice(1),prev);
    }
    if(next){
      el.addEventListener(key.slice(1),next);
    }
  }
}

上面的操作其實(shí)只是替換Data部分,但是其子元素沒有進(jìn)行替換,所以還需要對(duì)子元素進(jìn)行替換處理。替換子元素有共分為6種情況:

舊元素只有一個(gè)

舊元素為空

舊元素為多個(gè)

新元素只有一個(gè)

新元素為空

新元素為多個(gè)

//  更新VNode方法集
const patchMethos = {
    //  替換元素操作
    //    舊的虛擬DOM節(jié)點(diǎn)
    //    新的DOM節(jié)點(diǎn)
    //    承載DOM節(jié)點(diǎn)的容器
    patchElement(oldVNode,newVNode,container){
        //  如果 newVNode 的標(biāo)簽名稱與 oldVNode 標(biāo)簽名稱不一樣
        //  既然標(biāo)簽都不一樣則直接替換就好了,不需要再進(jìn)行其他多余的操作
        if(newVNode.tag !== oldVNode.tag){
            replaceVNode(oldVNode,newVNode,container);
            return;
        }
        //  更新el
        let el = (newVNode.el = oldVNode.el);
        //  獲取舊的Data數(shù)據(jù)
        let oldData = oldVNode.data;
        //  獲取新的Data數(shù)據(jù)
        let newData = newVNode.data;
        //  如果新的Data數(shù)據(jù)存在
        //  進(jìn)行更新和新增
        if(newData){
            for(let attr in newData){
                let oldVal = oldData[attr];
                let newVal = newData[attr];
                domAttributeMethod.patchData(el,attr,oldVal,newVal);
            }
        }
        //  如果舊的Data存在
        //  檢測(cè)更新
        if(oldData){
            for(let attr in oldData){
                let oldVal = oldData[attr];
                let newVal = newData[attr];
                //  如果舊數(shù)據(jù)存在,新數(shù)據(jù)中不存在
                //  則表示已刪除,需要進(jìn)行更新操作
                if(oldVal && !newVal.hasOwnProperty(attr)){
                    //  既然新數(shù)據(jù)中不存在,則新數(shù)據(jù)則傳入Null
                    domAttributeMethod.patchData(el,attr,oldVal,null);
                }
            }
        }
        //  添加了這里
        //  更新子元素
        //      舊子元素類型
        //      新子元素類型
        //      舊子元素的children
        //      新子元素的children
        //      el元素,容器
        this.patchChildren(
            oldVNode.childrenFlag,
            newVNode.childrenFlag,
            oldVNode.children,
            newVNode.children,
            el,
        );

    },
    //  更新子元素
    //      舊子元素類型
    //      新子元素類型
    //      舊子元素的children
    //      新子元素的children
    //      el元素,容器
    patchChildren(...arg){
        let [oldChildrenFlag,newChildrenFlag,oldChildren,newChildren,container] = arg;
        switch(oldChildrenFlag){
            //  如果舊元素的子元素為一個(gè)
            case childTeyps.SINGLE:
                this.upChildSingle(...arg);
                break;
            //  如果舊元素的子元素為空
            case childTeyps.EMPTY:
                this.upChildEmpty(...arg);
                break;
            //  如果舊元素的子元素為多個(gè)
            case childTeyps.MULTIPLE:
                this.upChildMultiple(...arg);
                break;
        }
    },

    upChildSingle(...arg){
        let [oldChildrenFlag,newChildrenFlag,oldChildren,newChildren,container] = arg;
        //  循環(huán)新的子元素
        switch(newChildrenFlag){
            //  如果新元素的子元素為一個(gè)
            case childTeyps.SINGLE:
                patch(oldChildren,newChildren,container);
                break;
            //  如果新元素的子元素為空
            case childTeyps.EMPTY:
                container.removeChild(oldChildren.el);
                break;
            //  如果新元素的子元素多個(gè)
            case childTeyps.MULTIPLE:
                container.removeChild(oldChildren.el);
                for(let i = 0;i

上面代碼比較亂,因?yàn)榍短琢硕鄬友h(huán),大致邏輯就是使用上述六種情況一一對(duì)接配對(duì)并且使用其對(duì)應(yīng)的解決方案。

上述六中情況,switch匹配邏輯:

新數(shù)據(jù) 舊數(shù)據(jù)
舊元素只有一個(gè) 新元素只有一個(gè)
舊元素只有一個(gè) 新元素為空
舊元素只有一個(gè) 新元素為多個(gè)
舊元素為空 新元素只有一個(gè)
舊元素為空 新元素為空
舊元素為空 新元素為多個(gè)
舊元素為多個(gè) 新元素只有一個(gè)
舊元素為多個(gè) 新元素為空
舊元素為多個(gè) 新元素為多個(gè)

最為復(fù)雜的就是最后一種情況,新舊元素各為多個(gè),然而對(duì)于這一部分reactvue的處理方式都是不一樣的。以下借鑒的是reactdiff算法。

在進(jìn)行虛擬DOM替換時(shí),當(dāng)元素之間的順序沒有發(fā)生變化則原有元素是不需要進(jìn)行任何改動(dòng)的,也就是說,若原有順序是123456,新順序?yàn)?b>654321則他們之間的順序發(fā)生了變化這個(gè)時(shí)候需要對(duì)其進(jìn)行變更處理,若其順序出現(xiàn)了插入情況192939495969在每個(gè)數(shù)字后面添加了一個(gè)9,其實(shí)這個(gè)時(shí)候也是不需要進(jìn)行更新操作的,其實(shí)他們之間的順序還是和原來一致,只是添加了一些元素值而已,如果變成了213456,這是時(shí)候只需要改變12就好,其他的是不需要做任何改動(dòng)的。 接下來需要添加最關(guān)鍵的邏輯了。

//  更新VNode方法集
//  添加 oldMoreAndNewMore 方法
const patchMethos = {
  upChildMultiple(...arg) {
    let [oldChildrenFlag, newChildrenFlag, oldChildren, newChildren, container] = arg;
    //  循環(huán)新的子元素
    switch (newChildrenFlag) {
      //  如果新元素的子元素為一個(gè)
      case childTeyps.SINGLE:
        for (let i = 0; i < oldChildren.length; i++) {
          // 遍歷刪除舊元素
          container.removeChild(oldChildren[i].el);
        }
        // 添加新元素
        mount(newChildren, container);
        break;
      //  如果新元素的子元素為空
      case childTeyps.EMPTY:
        for (let i = 0; i < oldChildren.length; i++) {
          // 刪除所有子元素  
          container.removeChild(oldChildren[i].el);
        }
        break;
      //  如果新元素的子元素多個(gè)
      case childTeyps.MULTIPLE:
        // 修改了這里 (●"?"●) 
        this.oldMoreAndNewMore(...arg);
        break;
  },
  oldMoreAndNewMore(...arg) {
    let [oldChildrenFlag, newChildrenFlag, oldChildren, newChildren, container] = arg;
    let lastIndex = 0;
    for (let i = 0; i < newChildren.length; i++) {
      let newVnode = newChildren[i];
      let j = 0;
      //  新的元素是否找到
      let find = false;
      for (; j < oldChildren.length; j++) {
        let oldVnode = oldChildren[j];
        //  key相同為同一個(gè)元素
        if (oldVnode.key === newVnode.key) {
          find = true;
          patch(oldVnode, newVnode, container);
          if (j < lastIndex) {
            if(newChildren[i-1].el){
              //  需要移動(dòng)
              let flagNode = newChildren[i-1].el.nextSibling;
              container.insertBefore(oldVnode.el, flagNode);
            }
            break;
          }
          else {
            lastIndex = j;
          }
        }
      }
      // 如果沒有找到舊元素,需要新增
      if (!find) {
        // 需要插入的標(biāo)志元素
        let flagNode = i === 0 ? oldChildren[0].el : newChildren[i-1].el;
        mount(newVnode, container, flagNode);
      }
      //  移除元素
      for (let i = 0; i < oldChildren.length; i++) {
        //  舊節(jié)點(diǎn)
        const oldVNode = oldChildren[i];
        //  新節(jié)點(diǎn)key是否在舊節(jié)點(diǎn)中存在
        const has = newChildren.find(next => next.key === oldVNode.key);
        if (!has) {
          // 如果不存在刪除
          container.removeChild(oldVNode.el)
        }
      }
    }
  }
};
//  修改mount函數(shù)
//      flagNode    標(biāo)志node 新元素需要插入到哪里
function mount(vnode, container, flagNode) {
  //  所需渲染標(biāo)簽類型
  let { flag } = vnode;
  //  如果是節(jié)點(diǎn)
  if (flag === vnodeTypes.HTML) {
    //  調(diào)用創(chuàng)建節(jié)點(diǎn)方法
    mountMethod.mountElement(vnode, container, flagNode);
  } //  如果是文本
  else if (flag === vnodeTypes.TEXT) {
    //  調(diào)用創(chuàng)建文本方法
    mountMethod.mountText(vnode, container);
  };
};
//  修改mountElement
const mountMethod = {
  //  創(chuàng)建HTML元素方法
  //    修改了這里 (●"?"●) 添加 flagNode 參數(shù)
  mountElement(vnode, container, flagNode) {
    //  屬性,標(biāo)簽名,子元素,子元素類型
    let { data, tag, children, childrenFlag } = vnode;
    //  創(chuàng)建的真實(shí)節(jié)點(diǎn)
    let dom = document.createElement(tag);
    //  添加屬性
    data && domAttributeMethod.addData(dom, data);
    //  在VNode中保存真實(shí)DOM節(jié)點(diǎn)
    vnode.el = dom;
    //  如果不為空,表示有子元素存在
    if (childrenFlag !== childTeyps.EMPTY) {
      //  如果為單個(gè)元素
      if (childrenFlag === childTeyps.SINGLE) {
        //  把子元素傳入,并把當(dāng)前創(chuàng)建的DOM節(jié)點(diǎn)以父元素傳入
        //  其實(shí)就是要把children掛載到 當(dāng)前創(chuàng)建的元素中
        mount(children, dom);
      } //  如果為多個(gè)元素
      else if (childrenFlag === childTeyps.MULTIPLE) {
        //  循環(huán)子節(jié)點(diǎn),并創(chuàng)建
        children.forEach((el) => mount(el, dom));
      };
    };
    //  添加元素節(jié)點(diǎn)  修改了這里 (●"?"●)
    flagNode ? container.insertBefore(dom, flagNode) : container.appendChild(dom);
  }
}

最終使用:

const VNODEData = [
    "div",
    {id:"test",key:789},
    [
      createElement("p",{
        key:1,
        style:{
          color:"red",
          background:"pink"
        }
      },"節(jié)點(diǎn)一"),
      createElement("p",{
        key:2,
        "@click":() => console.log("click me!!!")
      },"節(jié)點(diǎn)二"),
      createElement("p",{
        key:3,
        class:"active"
      },"節(jié)點(diǎn)三"),
      createElement("p",{key:4},"節(jié)點(diǎn)四"),
      createElement("p",{key:5},"節(jié)點(diǎn)五")
    ]
];
let VNODE = createElement(...VNODEData);
render(VNODE,document.getElementById("app"));

const VNODEData1 = [
    "div",
    {id:"test",key:789},
    [
      createElement("p",{
        key:6
      },"節(jié)點(diǎn)六"),
      createElement("p",{
        key:1,
        style:{
          color:"red",
          background:"pink"
        }
      },"節(jié)點(diǎn)一"),
      createElement("p",{
        key:5
      },"節(jié)點(diǎn)五"),
      createElement("p",{
        key:2
      },"節(jié)點(diǎn)二"),
      createElement("p",{
        key:4
      },"節(jié)點(diǎn)四"),
      createElement("p",{
        key:3,
        class:"active"
      },"節(jié)點(diǎn)三")
    ]
];

setTimeout(() => {
  let VNODE = createElement(...VNODEData1);
  render(VNODE,document.getElementById("app"));
},1000)

上面代碼用了大量的邏輯來處理其中使用大量計(jì)算,會(huì)比較兩棵樹之間的同級(jí)節(jié)點(diǎn)。這樣就徹底的降低了復(fù)雜度,并且不會(huì)帶來什么損失。因?yàn)樵趙eb應(yīng)用中不太可能把一個(gè)組件在DOM樹中跨層級(jí)地去移動(dòng)。

在計(jì)算中會(huì)盡可能的引用之前的元素,進(jìn)行位置替換,其實(shí)無論是React還是Vue在渲染列表的時(shí)候需要給其元素賦值一個(gè)key屬性,因?yàn)樵谶M(jìn)行diff算法時(shí),會(huì)優(yōu)先使用其原有元素,進(jìn)行位置調(diào)整,也是對(duì)性能優(yōu)化的一大亮點(diǎn)。

結(jié)語

本文也只是對(duì)diff算法的簡(jiǎn)單實(shí)現(xiàn),也許不能滿足所有要求,React的基本實(shí)現(xiàn)原理則是如此,希望這篇文章能對(duì)大家理解diff算法有所幫助。

非常感謝大家用這么長(zhǎng)時(shí)間來閱讀本文章,文章中代碼篇幅過長(zhǎng),若有錯(cuò)誤請(qǐng)?jiān)谠u(píng)論區(qū)指出,我會(huì)及時(shí)做出改正。

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

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

相關(guān)文章

  • 虛擬Dom詳解 - (一)

    摘要:為此也做了一些學(xué)習(xí)簡(jiǎn)單的侃一侃虛擬到底是什么虛擬詳解二什么是虛擬虛擬首次產(chǎn)生是框架最先提出和使用的,其卓越的性能很快得到廣大開發(fā)者的認(rèn)可,繼之后也在其核心引入了虛擬的概念。所謂的虛擬到底是什么也就是通過語言來描述一段代碼。 隨著Vue和React的風(fēng)聲水起,伴隨著諸多框架的成長(zhǎng),虛擬DOM漸漸成了我們經(jīng)常議論和討論的話題。什么是虛擬DOM,虛擬DOM是如何渲染的,那么Vue的虛擬Dom...

    ashe 評(píng)論0 收藏0
  • vue-cli 構(gòu)建 vue 項(xiàng)目詳解

    摘要:打開瀏覽器輸入,會(huì)看到構(gòu)建的項(xiàng)目的主頁目錄結(jié)構(gòu)使用編輯器打開推薦使用,下面具體看看目錄結(jié)構(gòu)在中,根據(jù)我們?cè)跇?gòu)建項(xiàng)目的時(shí)候的選項(xiàng),有以下幾個(gè)命令。 構(gòu)建一個(gè) vue 項(xiàng)目最簡(jiǎn)單的方式就是使用腳手架工具 vue-cli 。前端的三大框架都有自己的腳手架工具,其作用就是用配置好的模板迅速搭建起一個(gè)項(xiàng)目工程來,省去自己配置 webpack 配置文件的基本內(nèi)容,大大降低了初學(xué)者構(gòu)建項(xiàng)目的難度。這...

    JeOam 評(píng)論0 收藏0
  • Vue指令詳解

    摘要:添加事件偵聽器時(shí)使用模式。只當(dāng)事件是從偵聽器綁定的元素本身觸發(fā)時(shí)才觸發(fā)回調(diào)。只當(dāng)點(diǎn)擊鼠標(biāo)右鍵時(shí)觸發(fā)只當(dāng)點(diǎn)擊鼠標(biāo)中鍵時(shí)觸發(fā)以模式添加偵聽器,減少額外的監(jiān)聽,提高性能表示永遠(yuǎn)不會(huì)調(diào)用。記住,指令函數(shù)能夠接受所有合法的表達(dá)式。 思維導(dǎo)圖 showImg(https://segmentfault.com/img/bVbphXZ?w=1920&h=2408); 指令 v-for 關(guān)于key 官方...

    itvincent 評(píng)論0 收藏0
  • Vue指令詳解

    摘要:添加事件偵聽器時(shí)使用模式。只當(dāng)事件是從偵聽器綁定的元素本身觸發(fā)時(shí)才觸發(fā)回調(diào)。只當(dāng)點(diǎn)擊鼠標(biāo)右鍵時(shí)觸發(fā)只當(dāng)點(diǎn)擊鼠標(biāo)中鍵時(shí)觸發(fā)以模式添加偵聽器,減少額外的監(jiān)聽,提高性能表示永遠(yuǎn)不會(huì)調(diào)用。記住,指令函數(shù)能夠接受所有合法的表達(dá)式。 思維導(dǎo)圖 showImg(https://segmentfault.com/img/bVbphXZ?w=1920&h=2408); 指令 v-for 關(guān)于key 官方...

    malakashi 評(píng)論0 收藏0
  • Vue指令詳解

    摘要:添加事件偵聽器時(shí)使用模式。只當(dāng)事件是從偵聽器綁定的元素本身觸發(fā)時(shí)才觸發(fā)回調(diào)。只當(dāng)點(diǎn)擊鼠標(biāo)右鍵時(shí)觸發(fā)只當(dāng)點(diǎn)擊鼠標(biāo)中鍵時(shí)觸發(fā)以模式添加偵聽器,減少額外的監(jiān)聽,提高性能表示永遠(yuǎn)不會(huì)調(diào)用。記住,指令函數(shù)能夠接受所有合法的表達(dá)式。 思維導(dǎo)圖 showImg(https://segmentfault.com/img/bVbphXZ?w=1920&h=2408); 指令 v-for 關(guān)于key 官方...

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

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

0條評(píng)論

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