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

Vue中渲染系統(tǒng)模塊的實(shí)現(xiàn)詳解

 更新時(shí)間:2023年07月17日 16:02:35   作者:會(huì)說法語(yǔ)的豬  
想要實(shí)現(xiàn)一個(gè)簡(jiǎn)潔版的Mini-Vue框架,應(yīng)該包含三個(gè)模塊:分別是:渲染系統(tǒng)模塊、可響應(yīng)式系統(tǒng)模塊、應(yīng)用程序入庫(kù)模塊,本文主要介紹的是渲染系統(tǒng)模塊的實(shí)現(xiàn),需要的可以參考一下

我們想要實(shí)現(xiàn)一個(gè)簡(jiǎn)潔版的Mini-Vue框架,應(yīng)該包含三個(gè)模塊:分別是:渲染系統(tǒng)模塊、可響應(yīng)式系統(tǒng)模塊、應(yīng)用程序入庫(kù)模塊。

這篇就來寫一下渲染系統(tǒng)模塊。剩下兩個(gè)模塊后面有時(shí)間再更新。

vue渲染系統(tǒng)實(shí)現(xiàn),應(yīng)該包含三個(gè)功能,分別是:① h函數(shù),用于返回一個(gè)VNode對(duì)象;② mount函數(shù),用于將VNode轉(zhuǎn)為真實(shí)dom,并掛載到DOM上;③ patch函數(shù),類似于diff算法,用于對(duì)比兩個(gè)VNode,決定如何處理新的VNode

虛擬dom本身其實(shí)就是個(gè)javascript對(duì)象。這里會(huì)寫一些核心邏輯,大家如果看vue源碼的話,會(huì)發(fā)現(xiàn)里面有非常多的邊界情況的處理判斷。我們只需要知道核心邏輯就可以了,也就是下面所寫的。

下面寫的會(huì)有用到個(gè)判斷類型的方法,getObjType如下: 

也是最準(zhǔn)確的判斷數(shù)據(jù)類型的方法 

function getObjType(obj) {
  let toString = Object.prototype.toString
  let map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  }
  if (obj instanceof Element) {
    return 'element'
  }
  return map[toString.call(obj)]
}

h函數(shù) 

h函數(shù)其實(shí)非常簡(jiǎn)單,就是返回一個(gè)js對(duì)象 

/**
 * 虛擬dom本身就是個(gè)javascript對(duì)象
 * @param {String} tag 標(biāo)簽名稱
 * @param {Object} props 屬性配置
 * @param {String | Array} children 子元素
 * @returns 
 */
const h = (tag, props, children) => ({ tag, props, children })

可以看到,這就是個(gè)虛擬的dom樹 

mount函數(shù) 

mount函數(shù)負(fù)責(zé)將vnode轉(zhuǎn)為真實(shí)的dom,并掛載到指定容器下。 

/**
 * 將虛擬dom轉(zhuǎn)為真實(shí)dom并掛載到指定容器
 * @param {Object} vnode 虛擬dom
 * @param {Element} container 容器
 */
const mount = (vnode, container) => {
  const el = vnode.el = document.createElement(vnode.tag)
  if(vnode.props) {
    for(let key in vnode.props) {
      const value = vnode.props[key]
      if(key.startsWith('on')) {
        el.addEventListener(key.slice(2).toLowerCase(), value)
      } else {
        el.setAttribute(key, value)
      }
    }
  }
  if(vnode.children) {
    if(getObjType(vnode.children) === 'string') {
      el.textContent = vnode.children
    } else if(getObjType(vnode.children) === 'array') {
      vnode.children.forEach(item => {
        mount(item, el)
      })
    }
  }
  container.appendChild(el)
}

這里第三個(gè)參數(shù)只考慮了,string和array的情況,還有對(duì)象的情況相對(duì)少一些,就是我們平常的使用的插槽 

patch函數(shù) 

patch函數(shù)類似于diff算法,用于兩個(gè)VNode進(jìn)行對(duì)比,決定如何處理新的VNode

/**
 * 類似diff對(duì)比兩個(gè)vnode
 * @param {Object} n1 舊vnode
 * @param {Object} n2 新vnode
 */
const patch = (n1, n2) => {
  if(n1.tag !== n2.tag) {
    const n1parentEl = n1.el.parentElement
    n1parentEl.removeChild(n1.el)
    mount(n2, n1parentEl)
  } else {
    const el = n2.el = n1.el
    // 處理props
    const oldProps = n1.props || {}
    const newProps = n2.props || {}
    for(let key in newProps) {
      const oldValue = oldProps[key]
      const newValue = newProps[key]
      if(newValue !== oldValue) {
        if (key.startsWith('on')) {
          el.addEventListener(key.slice(2).toLowerCase(), newValue)
        } else {
          el.setAttribute(key, newValue)
        }
      }
    }
    for(let key in oldProps) {
      if(!(key in newProps)) {
        if (key.startsWith('on')) {
          const oldValue = oldProps[key]
          el.removeEventListener(key.slice(2).toLowerCase(), oldValue)
        } else {
          el.removeAttribute(key)
        }
      }
    }
    const oldChildren = n1.children || []
    const newChildren = n2.children || []
    if(getObjType(newChildren) === 'string') {
      if(getObjType(oldChildren) === 'string') {
        if(newChildren !== oldChildren) {
          el.textContent = newChildren
        }
      } else {
        el.innerHTML = newChildren
      }
    } else if(getObjType(newChildren) === 'array') {
      if(getObjType(oldChildren) === 'string') {
        el.innerHTML = ''
        newChildren.forEach(item => {
          mount(item, el)
        })
      } else if(getObjType(oldChildren) === 'array') {
        // oldChildren -> [vnode1, vnode2, vnode3]
        // newChildren -> [vnode1, vnode5, vnode6, vnode7, vnode8]
        // 前面有相同節(jié)點(diǎn)的元素進(jìn)行patch操作
        const commonLength = Math.min(newChildren.length, oldChildren.length)
        for(let i = 0; i < commonLength; i++) {
          patch(oldChildren[i], newChildren[i])
        }
        // 2. newChildren.length > oldChildren.length  多余的做掛載操作
        if(newChildren.length > oldChildren.length) {
          newChildren.slice(commonLength).forEach(item => {
            mount(item, el)
          })
        }
        // 3. newChildren.length < oldChildren.length  多余的做移除操作
        if(newChildren.length < oldChildren.length) {
          oldChildren.slice(commonLength).forEach(item => {
            el.removeChild(item.el)
          })
        }
      }
    }
  }
}

完整代碼 

render.js 

/**
 * Vue渲染系統(tǒng)實(shí)現(xiàn)
 * 1. h函數(shù),用于返回一個(gè)VNode對(duì)象;
 * 2. mount函數(shù),用于將VNode掛載到DOM上;
 * 3. patch函數(shù),用于對(duì)兩個(gè)VNode進(jìn)行對(duì)比,決定如何處理新的VNode;
 */
/**
 * 虛擬dom本身就是個(gè)javascript對(duì)象
 * @param {String} tag 標(biāo)簽名稱
 * @param {Object} props 屬性配置
 * @param {String | Array} children 子元素
 * @returns 
 */
const h = (tag, props, children) => ({ tag, props, children })
/**
 * 將虛擬dom轉(zhuǎn)為真實(shí)dom并掛載到指定容器
 * @param {Object} vnode 虛擬dom
 * @param {Element} container 容器
 */
const mount = (vnode, container) => {
  const el = vnode.el = document.createElement(vnode.tag)
  if(vnode.props) {
    for(let key in vnode.props) {
      const value = vnode.props[key]
      if(key.startsWith('on')) {
        el.addEventListener(key.slice(2).toLowerCase(), value)
      } else {
        el.setAttribute(key, value)
      }
    }
  }
  if(vnode.children) {
    if(getObjType(vnode.children) === 'string') {
      el.textContent = vnode.children
    } else if(getObjType(vnode.children) === 'array') {
      vnode.children.forEach(item => {
        mount(item, el)
      })
    }
  }
  container.appendChild(el)
}
/**
 * 類似diff對(duì)比兩個(gè)vnode
 * @param {Object} n1 舊vnode
 * @param {Object} n2 新vnode
 */
const patch = (n1, n2) => {
  if(n1.tag !== n2.tag) {
    const n1parentEl = n1.el.parentElement
    n1parentEl.removeChild(n1.el)
    mount(n2, n1parentEl)
  } else {
    const el = n2.el = n1.el
    // 處理props
    const oldProps = n1.props || {}
    const newProps = n2.props || {}
    for(let key in newProps) {
      const oldValue = oldProps[key]
      const newValue = newProps[key]
      if(newValue !== oldValue) {
        if (key.startsWith('on')) {
          el.addEventListener(key.slice(2).toLowerCase(), newValue)
        } else {
          el.setAttribute(key, newValue)
        }
      }
    }
    for(let key in oldProps) {
      if(!(key in newProps)) {
        if (key.startsWith('on')) {
          const oldValue = oldProps[key]
          el.removeEventListener(key.slice(2).toLowerCase(), oldValue)
        } else {
          el.removeAttribute(key)
        }
      }
    }
    const oldChildren = n1.children || []
    const newChildren = n2.children || []
    if(getObjType(newChildren) === 'string') {
      if(getObjType(oldChildren) === 'string') {
        if(newChildren !== oldChildren) {
          el.textContent = newChildren
        }
      } else {
        el.innerHTML = newChildren
      }
    } else if(getObjType(newChildren) === 'array') {
      if(getObjType(oldChildren) === 'string') {
        el.innerHTML = ''
        newChildren.forEach(item => {
          mount(item, el)
        })
      } else if(getObjType(oldChildren) === 'array') {
        // oldChildren -> [vnode1, vnode2, vnode3]
        // newChildren -> [vnode1, vnode5, vnode6, vnode7, vnode8]
        // 前面有相同節(jié)點(diǎn)的元素進(jìn)行patch操作
        const commonLength = Math.min(newChildren.length, oldChildren.length)
        for(let i = 0; i < commonLength; i++) {
          patch(oldChildren[i], newChildren[i])
        }
        // 2. newChildren.length > oldChildren.length  多余的做掛載操作
        if(newChildren.length > oldChildren.length) {
          newChildren.slice(commonLength).forEach(item => {
            mount(item, el)
          })
        }
        // 3. newChildren.length < oldChildren.length  多余的做移除操作
        if(newChildren.length < oldChildren.length) {
          oldChildren.slice(commonLength).forEach(item => {
            el.removeChild(item.el)
          })
        }
      }
    }
  }
}
function getObjType(obj) {
  let toString = Object.prototype.toString
  let map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  }
  if (obj instanceof Element) {
    return 'element'
  }
  return map[toString.call(obj)]
}

測(cè)試: 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./render.js"></script>
</head>
<body>
  <div id="app"></div>
  <script>
    const vnode = h('div', { class: 'wft' }, [
      h('h1', null, '標(biāo)題'),
      h('span', null, '哈哈哈')
    ])
    setTimeout(() => {
      mount(vnode, document.getElementById('app'))
    }, 1500)
    const newVNode = h('div', { class: 'new-wft', id: 'wft' }, [
      h('h3', null, '我是h3')
    ])
    setTimeout(() => {
      patch(vnode, newVNode)
    }, 4000)
  </script>
</body>
</html>

到此這篇關(guān)于Vue中渲染系統(tǒng)模塊的實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Vue渲染系統(tǒng)模塊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue 通過自定義指令回顧v-內(nèi)置指令(小結(jié))

    Vue 通過自定義指令回顧v-內(nèi)置指令(小結(jié))

    這篇文章主要介紹了Vue 通過自定義指令回顧v-內(nèi)置指令(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • antd Form組件表單在vue3中的使用方式

    antd Form組件表單在vue3中的使用方式

    這篇文章主要介紹了antd Form組件表單在vue3中的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • vue cli3適配所有端方案的實(shí)現(xiàn)

    vue cli3適配所有端方案的實(shí)現(xiàn)

    這篇文章主要介紹了vue cli3適配所有端方案的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • vuex 中輔助函數(shù)mapGetters的基本用法詳解

    vuex 中輔助函數(shù)mapGetters的基本用法詳解

    mapGetters輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性,在組件或界面中不使用mapGetter調(diào)用映射vuex中的getter,在組件或界面中使用mapGetter調(diào)用映射vuex中的getter,具體內(nèi)容跟隨小編一起通過本文學(xué)習(xí)吧 
    2021-07-07
  • Vue中ref、reactive、toRef、toRefs、$refs的基本用法總結(jié)

    Vue中ref、reactive、toRef、toRefs、$refs的基本用法總結(jié)

    這篇文章主要給大家介紹了關(guān)于Vue中ref、reactive、toRef、toRefs、$refs的基本用法,以及他們之家的區(qū)別,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • 一文詳解如何在Vue3中使用jsx/tsx

    一文詳解如何在Vue3中使用jsx/tsx

    本篇文章旨在帶領(lǐng)大家快速了解和使用?Vue?中的?JSX?語(yǔ)法,好讓大家在?Vue?中遇到或使用?JSX?的時(shí)候能很快入手,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-03-03
  • Vue中的偵聽器及使用場(chǎng)景

    Vue中的偵聽器及使用場(chǎng)景

    Vue中的偵聽器是一種響應(yīng)式機(jī)制,可以對(duì)指定的數(shù)據(jù)進(jìn)行監(jiān)聽,并在數(shù)據(jù)變化時(shí)執(zhí)行相應(yīng)的回調(diào)函數(shù)。常用于監(jiān)聽復(fù)雜數(shù)據(jù)類型的變化,如對(duì)象和數(shù)組。通過偵聽器,可以實(shí)現(xiàn)數(shù)據(jù)的自動(dòng)更新和邏輯處理等功能,提高代碼的可讀性和可維護(hù)性
    2023-05-05
  • vue如何動(dòng)態(tài)加載組件詳解

    vue如何動(dòng)態(tài)加載組件詳解

    組件是Vue.js最強(qiáng)大的功能之一,組件可以擴(kuò)展HTML元素,封裝可重用的代碼,下面這篇文章主要給大家介紹了關(guān)于vue如何動(dòng)態(tài)加載組件的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • 一次前端Vue項(xiàng)目國(guó)際化解決方案的實(shí)戰(zhàn)記錄

    一次前端Vue項(xiàng)目國(guó)際化解決方案的實(shí)戰(zhàn)記錄

    這篇文章主要給大家介紹了關(guān)于前端Vue項(xiàng)目國(guó)際化解決方案的實(shí)戰(zhàn)記錄,以上只是一部分Vue項(xiàng)目開發(fā)中遇到的典型問題和解決方案,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • mockjs+vue頁(yè)面直接展示數(shù)據(jù)的方法

    mockjs+vue頁(yè)面直接展示數(shù)據(jù)的方法

    這篇文章主要介紹了mockjs+vue頁(yè)面直接展示數(shù)據(jù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-12-12

最新評(píng)論

兴和县| 开阳县| 东宁县| 冷水江市| 安徽省| 凤冈县| 华宁县| 离岛区| 宕昌县| 清镇市| 抚顺市| 伊金霍洛旗| 汕尾市| 南投县| 横峰县| 富宁县| 门源| 岳池县| 长武县| 黄陵县| 三江| 许昌县| 梧州市| 万全县| 百色市| 盐津县| 克山县| 舞阳县| 库车县| 桃园县| 郯城县| 大连市| 芦溪县| 黄陵县| 大竹县| 武隆县| 韩城市| 广汉市| 绵阳市| 博兴县| 奉化市|