带你手写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…

相关文章
相关标签/搜索