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

資訊專欄INFORMATION COLUMN

【React進(jìn)階系列】從零開始手把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(三)

qqlcbb / 988人閱讀

摘要:函數(shù)依次做了這幾件事調(diào)用函數(shù),對(duì)比新舊兩個(gè),根據(jù)兩者的不同得到需要修改的補(bǔ)丁將補(bǔ)丁到真實(shí)上當(dāng)計(jì)數(shù)器小于等于的時(shí)候,將加,再繼續(xù)下一次當(dāng)計(jì)數(shù)器大于的時(shí)候,結(jié)束下面我們來實(shí)現(xiàn)函數(shù)和函數(shù)。

上集回顧

【React進(jìn)階系列】從零開始手把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(二)

上集我們實(shí)現(xiàn)了首次渲染從JSX=>Hyperscript=>VDOM=>DOM的過程,今天我們來看一下當(dāng)數(shù)據(jù)變動(dòng)的時(shí)候怎么更新DOM,也就是下圖的右半邊部分。

改寫view()
function view(count) { 
  const r = [...Array(count).keys()]
  return 
    { r.map(n =>
  • item {(count * n).toString()}
  • ) }
}

我們的view函數(shù)接收一個(gè)參數(shù)count,變量r表示從0到count-1的一個(gè)數(shù)組。假如count=3, r=[0, 1, 2]。ul的className的值有三種可能:list-0, list-1, list-2。li的數(shù)量取決于count。

改寫render()
function render(el) {
  const initialCount = 0

  el.appendChild(createElement(view(initialCount)))
  setTimeout(() => tick(el, initialCount), 1000)
}

function tick(el, count) {
  const patches = diff(view(count + 1), view(count))
  patch(el, patches)

  if(count > 5) { return }
  setTimeout(() => tick(el, count + 1), 1000)
}

render函數(shù)有兩個(gè)修改,首先調(diào)用view()的時(shí)候傳入count=0。其次,寫了一個(gè)定時(shí)器,1秒后悔執(zhí)行tick函數(shù)。tick函數(shù)接收兩個(gè)參數(shù),el代表節(jié)點(diǎn)元素,count是當(dāng)前計(jì)數(shù)值。

tick函數(shù)依次做了這幾件事:

調(diào)用diff函數(shù),對(duì)比新舊兩個(gè)VDOM,根據(jù)兩者的不同得到需要修改的補(bǔ)丁

將補(bǔ)丁patch到真實(shí)DOM上

當(dāng)計(jì)數(shù)器小于等于5的時(shí)候,將count加1,再繼續(xù)下一次tick

當(dāng)計(jì)數(shù)器大于5的時(shí)候,結(jié)束

下面我們來實(shí)現(xiàn)diff函數(shù)和patch函數(shù)。

我們先列出來新舊兩個(gè)VDOM對(duì)比,會(huì)有哪些不同。在index.js文件的最前面聲明一下幾個(gè)常量。

const CREATE = "CREATE"   //新增一個(gè)節(jié)點(diǎn)
const REMOVE = "REMOVE"   //刪除原節(jié)點(diǎn)
const REPLACE = "REPLACE"  //替換原節(jié)點(diǎn)
const UPDATE = "UPDATE"    //檢查屬性或子節(jié)點(diǎn)是否有變化
const SET_PROP = "SET_PROP"  //新增或替換屬性
const REMOVE_PROP = "REMOVE PROP"  //刪除屬性
diff()
function diff(newNode, oldNode) {
   if (!oldNode) {
     return { type: CREATE, newNode }
   }

   if (!newNode) {
     return { type: REMOVE }
   }

   if (changed(newNode, oldNode)) {
     return { type: REPLACE, newNode }
   }

   if (newNode.type) {
     return {
       type: UPDATE,
       props: diffProps(newNode, oldNode),
       children: diffChildren(newNode, oldNode)
     }
   }
}

假如舊節(jié)點(diǎn)不存在,我們返回的patches對(duì)象, 類型為新增節(jié)點(diǎn);

假如新節(jié)點(diǎn)不存在,表示是刪除節(jié)點(diǎn);

假如兩者都存在的話,調(diào)用changed函數(shù)判斷他們是不是有變動(dòng);

假如兩者都存在,且changed()返回false的話,判斷新節(jié)點(diǎn)是否是VDOM(根據(jù)type是否存在來判斷的,因?yàn)閠ype不存在的話,newNode要么是空節(jié)點(diǎn),要么是字符串)。假如新節(jié)點(diǎn)是VDOM,則返回一個(gè)patches對(duì)象,類型是UPDATE,同時(shí)對(duì)props和children分別進(jìn)行diffProps和diffChildren操作。

下面我們一次看一下changed, diffProps, diffChildren函數(shù)。

changed()
function changed(node1, node2) {
  return typeof(node1) !== typeof(node2) ||
         typeof(node1) === "string" && node1 !== node2 ||
         node1.type !== node2.type
}

檢查新舊VDOM是否有變動(dòng)的方法很簡(jiǎn)單,

首先假如數(shù)據(jù)類型都不一樣,那肯定是變動(dòng)了;

其次假如兩者的類型都是純文本,則直接比較兩者是否相等;

最后比較兩者的類型是否相等。

diffProps()
function diffProps(newNode, oldNode) {
  let patches = []

  let props = Object.assign({}, newNode.props, oldNode.props)
  Object.keys(props).forEach(key => {
    const newVal = newNode.props[key]
    const oldVal = oldNode.props[key]
    if (!newVal) {
      patches.push({type: REMOVE_PROP, key, value: oldVal})
    }

    if (!oldVal || newVal !== oldVal) {
      patches.push({ type: SET_PROP, key, value: newVal})
    }
  })

  return patches
}

比較新舊VDOM的屬性的變化,并返回相應(yīng)的patches。

首先我們采用最大可能性原則,將新舊VDOM的所有屬性都合并賦值給一個(gè)新的變量props

遍歷props變量的所有Keys,依次比較新舊VDOM對(duì)于這個(gè)KEY的值

假如新值不存在,表示這個(gè)屬性被刪除了

假如舊值不存在,或者新舊值不同,則表示我們需要重新設(shè)置這個(gè)屬性

diffChildren()
function diffChildren(newNode, oldNode) {
  let patches = []

  const maximumLength = Math.max(
    newNode.children.length,
    oldNode.children.length
  )
  for(let i = 0; i < maximumLength; i++) {
    patches[i] = diff(
      newNode.children[i],
      oldNode.children[i]
    )
  }

  return patches
}

同樣采用最大可能性原則,取新舊VDOM的children的最長(zhǎng)值作為遍歷children的長(zhǎng)度。然后依次比較新舊VDOM的在相同INDEX下的每一個(gè)child。

這里需要強(qiáng)烈注意一下
為了簡(jiǎn)化,我們沒有引入key的概念,直接比較的是相同index下的child。所以假如說一個(gè)列表ul有5項(xiàng),分別是li1, li2, li3, li4, li5; 如果我們刪掉了第一項(xiàng),新的變成了li2, li3, li4, li5。那么diffchildren的時(shí)候,我們會(huì)拿li1和li2比較,依次類推。這樣一來,本來只是刪除了li1, 而li2, li3, li4, li5沒有任何變化,我們得出的diff結(jié)論卻是[li替換,li2替換, li3替換, li4替換, li5刪除]。所以react讓大家渲染列表的時(shí)候,必須添加Key。

截止到現(xiàn)在,我們已經(jīng)得到了我們需要的補(bǔ)丁。下面我們要將補(bǔ)丁Patch到DOM里。

patch()
function patch(parent, patches, index = 0) {
  if (!patches) {
    return
  }

  const el = parent.childNodes[index]
  switch (patches.type) {
    case CREATE: {
      const { newNode } = patches
      const newEl = createElement(newNode)
      parent.appendChild(newEl)
      break
    }
    case REMOVE: {
      parent.removeChild(el)
      break
    }
    case REPLACE: {
      const {newNode} = patches
      const newEl = createElement(newNode)
      return parent.replaceChild(newEl, el)
      break
    }
    case UPDATE: {
      const {props, children} = patches
      patchProps(el, props)
      for(let i = 0; i < children.length; i++) {
        patch(el, children[i], i)
      }
    }
  }
}

首先當(dāng)patches不存在時(shí),直接return,不進(jìn)行任何操作

利用childNodes和Index取出當(dāng)前正在處理的這個(gè)節(jié)點(diǎn),賦值為el

開始判斷補(bǔ)丁的類型

當(dāng)類型是CREATE時(shí),生成一個(gè)新節(jié)點(diǎn),并append到根節(jié)點(diǎn)

當(dāng)類型是REMOVE時(shí),直接刪除當(dāng)前節(jié)點(diǎn)el

當(dāng)類型是REPLACE時(shí),生成新節(jié)點(diǎn),同時(shí)替換掉原節(jié)點(diǎn)

當(dāng)類型是UPDATE時(shí),需要我們特殊處理

調(diào)用patchProps將我們之前diffProps得到的補(bǔ)丁渲染到節(jié)點(diǎn)上

遍歷之前diffChildren得到的補(bǔ)丁列表,再依次遞歸調(diào)用patch

最后我們?cè)傺a(bǔ)充一下patchProps函數(shù)

patchProps
function patchProps(parent, patches) {
  patches.forEach(patch => {
    const { type, key, value } = patch
    if (type === "SET_PROP") {
      setProp(parent, key, value)
    }
    if (type === "REMOVE_PROP") {
      removeProp(parent, key, value)
    }
  })
}

function removeProp(target, name, value) { //@
  if (name === "className") {
    return target.removeAttribute("class")
  }

  target.removeAttribute(name)
}

這個(gè)就不用我解釋了,代碼很直觀,setProp函數(shù)在上一集我們已經(jīng)定義過了。這樣一來,我們就完成了整個(gè)數(shù)據(jù)更新導(dǎo)致DOM更新的完整過程。
npm run compile后打開瀏覽器查看效果,你應(yīng)該看到是一個(gè)背景顏色在不同變化,同時(shí)列表項(xiàng)在逐漸增加的列表。

完結(jié)撒花

至此,我們的VDOM就全部完成了。系列初我提出的那幾個(gè)問題不知道你現(xiàn)在是否有了答案。有答案的童鞋可以在文章評(píng)論區(qū)將你的見解跟大家分享一下。分析全面且準(zhǔn)確的會(huì)收到我的特殊獎(jiǎng)勵(lì)。

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

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

相關(guān)文章

  • React進(jìn)階系列從零開始把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(一)

    摘要:可實(shí)際上并不是創(chuàng)造的,將這個(gè)概念拿過來以后融會(huì)貫通慢慢地成為目前前端最炙手可熱的框架之一。則是將再抽象一層生成的簡(jiǎn)化版對(duì)象,這個(gè)對(duì)象也擁有上的一些屬性,比如等,但它是完全脫離于瀏覽器而存在的。所以今天我要手把手教大家怎么從零開始實(shí)現(xiàn)。 假如你的項(xiàng)目使用了React,你知道怎么做性能優(yōu)化嗎?你知道為什么React讓你寫shouldComponentUpdate或者React.PureCo...

    PumpkinDylan 評(píng)論0 收藏0
  • React進(jìn)階系列從零開始把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(二)

    摘要:上集回顧從零開始手把手教你實(shí)現(xiàn)一個(gè)一上一集我們介紹了什么是,為什么要用,以及我們要怎樣來實(shí)現(xiàn)一個(gè)。完成后,在命令行中輸入安裝下依賴。最后返回這個(gè)目標(biāo)節(jié)點(diǎn)。明天,我們迎接挑戰(zhàn),開始處理數(shù)據(jù)變動(dòng)引起的重新渲染,我們要如何新舊,生成補(bǔ)丁,修改。 上集回顧 從零開始手把手教你實(shí)現(xiàn)一個(gè)Virtual DOM(一)上一集我們介紹了什么是VDOM,為什么要用VDOM,以及我們要怎樣來實(shí)現(xiàn)一個(gè)VDOM...

    dendoink 評(píng)論0 收藏0
  • Vue.js最佳實(shí)踐(五招讓你成為Vue.js大師)

    摘要:但如果你想更加高效地使用來開發(fā),成為大師,那下面我要傳授的這五招你一定得認(rèn)真學(xué)習(xí)一下了。雖然損失了一丟丟性能,但避免了無限的。所以我們需要設(shè)置,這些默認(rèn)行為將會(huì)被去掉以上兩點(diǎn)的優(yōu)化才能成功。陸續(xù)可能還會(huì)更新一些別的招數(shù),敬請(qǐng)期待。 本文面向?qū)ο笫怯幸欢╒ue.js編程經(jīng)驗(yàn)的開發(fā)者。如果有人需要Vue.js入門系列的文章可以在評(píng)論區(qū)告訴我,有空就給你們寫。 對(duì)大部分人來說,掌握Vue.j...

    CocoaChina 評(píng)論0 收藏0
  • javascript知識(shí)點(diǎn)

    摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評(píng)論0 收藏0
  • 前方來報(bào),八月最新資訊--關(guān)于vue2&3的最佳文章推薦

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

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

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

0條評(píng)論

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