帶你手寫vnode到renderDom

前言

本文分爲css

  • vnode樹的構建
  • render 的渲染
  • 數據變動後的diff
  • 拿到變動後的patch

項目搭建

npm i -g create-react-app
create-react-app my-app
cd my-app
npm start
複製代碼

而且把src中其餘文件都刪除,留下index.js就能夠接下來了前端

vnode 的構建

首先要肯定咱們vnode對象的屬性; type:標籤名,attrs:屬性名,children:子元素node

實例: react

vnode

那麼根據上面咱們須要的vnode結果,咱們去寫建立vnode的函數 createElement(type, attrs, children)git

src/index.js

import { createElement } from './element'
let virtualDom = createElement('ui', { class: 'li-group' }, [
  createElement('li', { class: 'item' }, ['a']),
  createElement('li', { class: 'item' }, ['b'])
])

複製代碼

接下來咱們開始具體去寫createElement函數,咱們先在src下新建一個element.js文件github

createElement - src/element.js

src/elment.js

class Element {
  constructor(type, attrs, children) {
    this.type = type
    this.attrs = attrs
    this.children = children
  }
}

function createElement(type, attrs, children) {
  return new Element(type, attrs, children)
}

export { createElement }
複製代碼

如今咱們已經能夠從上面打印出virtualDom的數據結構了,那麼接下來就是將virtualDom經過render函數去構建成真實的dom算法

render - src/elment.js

render(virtualDom, target) target:添加的目標節點npm

src/index.js

import { render } from './element'
let el = render(virtualDom)
複製代碼
src/element.js

function render(virtualDom){
  const { type, attrs, children } = virtualDom
  let el = document.createElement(type)

  // 掛載屬性
  for(let key in attrs){
    setAttr(el, key, attrs[key])
  }
  // 建立子節點
  children.forEach(child=>{
    if(child instanceof Element){
      // 節點
      el.appendChild(render(child))
    }else{
      // 文本
      let textNode = document.createTextNode(child)
      el.appendChild(textNode)
    }
  })
  return el
}

function setAttr(node, key, value){
  switch(key){
    case 'value': // input textarea
      if(node.tagName.toUpperCase() === 'INPUT' || node.tagName.toUpperCase() === 'TEXTAREA'){
        node[key] = value
      } else{
        node.setAttribute(key, value)
      }
      break;
    case 'style':
      node.style.cssText = value
      break;
    default:
      node.setAttribute(key, value)
      break;
  }
}
複製代碼

那麼如今咱們終於能夠將Dom渲染到頁面中了bash

renderDom - src/element.js

src/index.js

import { renderDom } from './element'
renderDom(el, document.getElementById('root'))
複製代碼
src/element.js

function renderDom(el, target){
  target.appendChild(el)
}
複製代碼

上半部分已經好了,咱們仍是動手完成一下成果把

github: github.com/XueMary/my-…數據結構

先整理一下剩下的內容

vnode已經完成了,那麼接下來就還有數據變動後的 diff 算法 和 diff 算法計算出的變動內容 patch。 數據變動後會產生新的vnode, diff會對比兩份 vnode 查到其中的變動,再將這些變動經過patch函數去修改真實dom

再來打破一下大多數人對虛擬dom的理解

網上不少人說虛擬動效率高,性能好。

開始分析了:

一、什麼虛擬dom通過diff算法實現了dom的最小變化,局部刷新(這句話每錯),可是和性能不要緊,你手動去操做dom,看看控制檯是否是也只有你改變的dom元素刷新了,手工操做自己就是最小變化,框架還要通過diff,那麼框架爲何要diff,由於不diff,那他就得把整個頁面出現渲染了。

二、另外一種說法就更唬人點了。什麼js線程和渲染線程是兩個部門,跨部門交流固然耗性能,js去更新 虛擬dom是js和js間的溝通,搞的好像更新了虛擬dom樹後不用去操做真實dom同樣了。比手工操做還多了一遍操做

那麼虛擬dom到底爲何存在。

回到框架自己,如今前端是數據驅動視圖,是爲了讓使用者只關注數據自己,對dom的增刪改查就都被隱藏在了框架中,框架自己也須要這麼一份vnode的描述,來講明如今的node結構是怎麼的,否則他也不知道哪裏被改變了,也就沒法幫咱們去修改真實dom了

這纔是維護一份虛擬dom的做用

下文:juejin.im/post/5cfa71…

相關文章
相關標籤/搜索