欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解Vue中渲染器的簡(jiǎn)單實(shí)現(xiàn)

 更新時(shí)間:2024年05月13日 09:20:27   作者:Lumen丶  
渲染器用于完成渲染操作,比如在瀏覽器平臺(tái)上渲染器可以將虛擬DOM轉(zhuǎn)換為真實(shí)DOM,本文將通過(guò)一個(gè)簡(jiǎn)單例子來(lái)帶大家理解Vue中渲染器的工作過(guò)程,并通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下

一、渲染器

渲染器用于完成渲染操作,比如在瀏覽器平臺(tái)上渲染器可以將虛擬DOM轉(zhuǎn)換為真實(shí)DOM。

二、一個(gè)簡(jiǎn)單例子理解Vue中渲染器的工作過(guò)程

const { effect, ref } = VueReactivity

function renderer(domString, container) {
  container.innerHTML = domString
}

const count = ref(1)

effect(() => {
  renderer(`<h1>${count.value}</h1>`, document.getElementById('app'))
})

count.value++

我們通過(guò)@vue/reactivity引用vue的響應(yīng)式API,使用其中的effect(副作用函數(shù))和ref(生成響應(yīng)式數(shù)據(jù))使得countrenderer建立聯(lián)系,這樣當(dāng) count變化時(shí),會(huì)調(diào)用renderer處理渲染,將h1標(biāo)簽掛載到id為app的元素上。這就是一個(gè)簡(jiǎn)易的渲染器實(shí)現(xiàn)。

三、渲染器涉及的幾種操作: 掛載、更新、卸載

假設(shè)我們已經(jīng)有了一個(gè)渲染器renderer

const renderer = createRenderer()

有一個(gè)用于描述元素節(jié)點(diǎn)的數(shù)據(jù)vnode:類似下面的結(jié)構(gòu)

// type為類型,children為子節(jié)點(diǎn)
const vnode = { 
    type: 'h1', 
    children: 'hello' 
}

可以看到這是一個(gè)內(nèi)部有hello文本的<h1>節(jié)點(diǎn)

那么在渲染器實(shí)際工作過(guò)程中會(huì)有如下的幾種情況:

renderer.render(vnode, document.querySelector('#app'))

此時(shí)涉及到的操作是掛載,只需要將vnode變?yōu)镈OM元素放在app元素內(nèi)部即可

renderer.render(newVNode, document.querySelector('#app'))

由于已經(jīng)有舊的節(jié)點(diǎn)存在,所以不能簡(jiǎn)單的直接掛載,需要對(duì)新舊節(jié)點(diǎn)進(jìn)行對(duì)比,找到需要更新的部分再改變,此時(shí)的操作就是更新,為了處理更新,渲染器中需要有對(duì)應(yīng)的邏輯。

renderer.render(null, document.querySelector('#app')) 

新的節(jié)點(diǎn)為null則意味這渲染器需要清空app元素內(nèi)的內(nèi)容,此時(shí)涉及的操作是卸載

根據(jù)對(duì)以上三種情況的處理,可以將渲染器寫為:

function createRenderer() {

  function patch(n1, n2, container) {

  }

  function render(vnode, container) {
    if (vnode) {
      // 新 vnode 存在,將其與舊 vnode 一起傳遞給 patch 函數(shù)進(jìn)行打補(bǔ)丁
      patch(container._vnode, vnode, container)
    } else {
      if (container._vnode) {
        // 舊 vnode 存在,且新 vnode 不存在,說(shuō)明是卸載(unmount)操作
        // 只需要將 container 內(nèi)的 DOM 清空即可
        container.innerHTML = ''
      }
    }
    // 把 vnode 存儲(chǔ)到 container._vnode 下,即后續(xù)渲染中的舊 vnode
    container._vnode = vnode
  }
  
  return {
    render
  }
}

使用createRenderer重復(fù)上面三次渲染,分析過(guò)程:

// 首次渲染 
renderer.render(vnode, document.querySelector('#app'))

// 第二次渲染 
renderer.render(newVNode, document.querySelector('#app'))

//第三次渲染
renderer.render(null, document.querySelector('#app'))
  • 首次渲染: vnode被渲染,并存在container._vnode中;
  • 第二次渲染: 新舊vnode均存在,需要在patch函數(shù)中進(jìn)行更新;
  • 第三次渲染: 新的vnodenull 判斷舊節(jié)點(diǎn)container._vnode是否存在,若舊的vnode存在,則處理為卸載;

四、如何實(shí)現(xiàn)一個(gè)與平臺(tái)無(wú)關(guān)的渲染器:

一個(gè)通用的渲染器應(yīng)該是是與平臺(tái)無(wú)關(guān)的,即渲染器的渲染功能不能只在某一個(gè)平臺(tái)中有效。 為了實(shí)現(xiàn)這個(gè)目標(biāo),我們首先來(lái)實(shí)現(xiàn)一個(gè)基于瀏覽器平臺(tái)的渲染器,觀察在哪些步驟中使用到了和瀏覽器相關(guān)的API,之后可以考慮將這些API抽離,作為配置項(xiàng),這樣就可以實(shí)現(xiàn)一個(gè)通用的渲染器。

1.實(shí)現(xiàn)一個(gè)依賴瀏覽器API的渲染器

以之前的的vnode為例:

const vnode = { 
    type: 'h1', 
    children: 'hello' 
}
  • type為標(biāo)簽類型、
  • children為子元素。
    在以上實(shí)現(xiàn)的基礎(chǔ)上我們首先完善patch函數(shù),用于處理節(jié)點(diǎn)的掛載和更新情況
patch(container._vnode, vnode, container) 

通過(guò)之前處理的代碼,可以看到,patch函數(shù)會(huì)接受三個(gè)參數(shù),分別是 舊的節(jié)點(diǎn)、新的節(jié)點(diǎn)、以及容器, 我們?cè)谄渲袑?duì)舊節(jié)點(diǎn)參數(shù)進(jìn)行判斷,從而處理不同的操作:目前只分析掛載的情況:

  function patch(n1, n2, container) {
    if (!n1) {
      mountElement(n2, container)
    } else {
      //
    }
  }

如上代碼所示: 當(dāng)n1即舊節(jié)點(diǎn)不存在時(shí)證明是掛載操作,則直接調(diào)用mountElement對(duì)新的節(jié)點(diǎn)進(jìn)行掛載處理

  function mountElement(vnode, container) {
    const el = createElement(vnode.type)
    if (typeof vnode.children === 'string') {
      el.textContent = vnode.children
    }
    container.appendChild(el)
  }

mountElement中我們對(duì)掛載的邏輯進(jìn)行了簡(jiǎn)單的處理:

1. 使用createElement根據(jù)vnode中的type的值去創(chuàng)建對(duì)應(yīng)的DOM類型:vnode.type='h1'則el為<h1></h1>

2. 對(duì)vnode.children進(jìn)行判斷: 如果vnode.children為字符串類型則證明子節(jié)點(diǎn)是一個(gè)文本節(jié)點(diǎn),直接使用元素的textContent屬性設(shè)置元素。

3. 調(diào)用appendChild將處理好的元素添加到目標(biāo)容器中。由此掛載完成。

2.實(shí)現(xiàn)一個(gè)通用的渲染器

以上實(shí)現(xiàn)的渲染器對(duì)于瀏覽器的API是有依賴的,其中的appendChild、createElement、textContent都是需要瀏覽器環(huán)境才能執(zhí)行的API,如果要讓渲染器能夠通用,就需要去除對(duì)這些API的依賴。

解決辦法:將這些API作為配置項(xiàng)傳入渲染器創(chuàng)建函數(shù),這樣我們可以自己定義createRenderer使用哪些API去執(zhí)行渲染操作,從而使得渲染器的工作不再依賴于某一平臺(tái)。

const renderer2 = createRenderer({
  //創(chuàng)建元素
  createElement(tag) {
    return { tag }
  },
  //設(shè)置元素文本內(nèi)容
  setElementText(el, text) {
    console.log(`設(shè)置 ${JSON.stringify(el)} 的文本內(nèi)容:${text}`)
    el.text = text
  },
  //將元素插入目標(biāo)節(jié)點(diǎn)
  insert(el, parent, anchor = null) {
    parent.children = el
  }
})

我們將創(chuàng)建元素的邏輯和API放入createElement中,將設(shè)置文本內(nèi)容的API放入setElementText中, 將元素掛載到容器的API過(guò)程放入insert中。 再使用傳入的配置去調(diào)用mountElement

  function mountElement(vnode, container) {
    const el = createElement(vnode.type)
    if (typeof vnode.children === 'string') {
      setElementText(el, vnode.children)
    }
    insert(el, container)
  }

這樣我們可以通過(guò)傳入的createRenderer函數(shù)的options去配置,不同平臺(tái)中使用什么樣的方法去執(zhí)行元素的創(chuàng)建、元素內(nèi)容的處理以及元素掛載等操作。
由此整個(gè)渲染函數(shù)如下:

function createRenderer(options) {

  const {
    createElement,
    insert,
    setElementText
  } = options

  function mountElement(vnode, container) {
    const el = createElement(vnode.type)
    if (typeof vnode.children === 'string') {
      setElementText(el, vnode.children)
    }
    insert(el, container)
  }

  function patch(n1, n2, container) {
    if (!n1) {
      mountElement(n2, container)
    } else {
      //
    }
  }

  function render(vnode, container) {
		if (vnode) {
      // 新 vnode 存在,將其與舊 vnode 一起傳遞給 patch 函數(shù)進(jìn)行打補(bǔ)丁
      patch(container._vnode, vnode, container)
    } else {
      if (container._vnode) {
        // 舊 vnode 存在,且新 vnode 不存在,說(shuō)明是卸載(unmount)操作
        // 只需要將 container 內(nèi)的 DOM 清空即可
        container.innerHTML = ''
      }
    }
    // 把 vnode 存儲(chǔ)到 container._vnode 下,即后續(xù)渲染中的舊 vnode
    container._vnode = vnode
  }
  
  return {
    render
  }
}

由此一個(gè)簡(jiǎn)單的渲染器已經(jīng)實(shí)現(xiàn),但在實(shí)際情況下元素的掛載和更新還有更多的細(xì)節(jié)現(xiàn)需要處理,如元素上的屬性、class、事件如何被正確的掛載,如何提升渲染器更新的效率(diff算法)等,這些將在后續(xù)文章中進(jìn)行討論。

以上就是一文詳解Vue中渲染器的簡(jiǎn)單實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于Vue實(shí)現(xiàn)渲染器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論