使用虚拟 dom 渲染页面 《vue.js 设计与实现》

 使用 js 对象描述 ui 更加灵活。假如我们要根据级别不同采用不同的标签。js 对象描述的话,只需要一个变量代表 h 标签即可。

// 当变量改变时,标签也会变化。
let level = 3
const title = {
  tag: `h${level}` //h3
}
<h1 v-if="level === 1"></h1>
<h2 v-else-if="level === 2"></h2>
<h3 v-else-if="level === 3"></h3>
<h4 v-else-if="level === 4"></h4>
<h5 v-else-if="level === 5"></h5>
<h6 v-else-if="level === 6"></h6>

  这远没有 js 对象灵活。其实就是所谓的虚拟 dom.vuejs 3 除了支持使用模板描述 ui 外,还支持使用虚拟 dom 描述 ui.其实我们在 vue.js 组建中手写的渲染函数就是使用虚拟 dom 来描述 ui 的。

import { h } from 'vue'

export default {
  render() {
    return h('h1', { onClick: handler })
  }
}

  渲染器的工作原理其实很简单,归根到底,都是使用一些我们熟悉的 DOM 操作 API 来完成渲染工作。渲染器的精髓都在更新节点的阶段。组件就是一组 dom 元素的封装,这组 dom 元素就是组件要渲染的内容,因此我们可以定义一个函数来代表组件。组件的返回值也是虚拟 dom,它代表组件要渲染的内容。搞清楚啦组件的本质,我们就可以定义用虚拟 dom 来描述组件来。

const MyComponent = function () {
  return {
    tag: 'div',
    props: {
      onClick: () => alert('hello')
    },
    children: 'click me'
  }
}

const vnode = {
  tag: MyComponent
}

function renderer(vnode, container) {
  if (typeof vnode.tag === 'string') {
    // vnode描述的是标签元素
    mountElement(vnode, container)
  } else if (typeof vnode.tag === 'function') {
    // vnode描述的是组件
    mountComponent(vnode, container)
  }
}

function mountElement(vnode, container) {
  const el = document.createElement(vnode.tag)

  for (const key in vnode.props) {
    if (/^on/.test(key)) {
      el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key])
    }
  }

  if (typeof vnode.children === 'string') {
    el.appendChild(document.createTextNode(vnode.children))
  } else if (Array.isArray(vnode.children)) {
    vnode.children.forEach((child) => renderer(child, el))
  }

  container.appendChild(el)
}

  无论是手写虚拟 dom(渲染函数)还是使用模板,都属于声明式描述 ui,并且 vue.js 同时支持这两种描述 ui 的方式。编译器和渲染器一样,只是一段程序而已,不过它们的工作内容不同。编译器的作用其实就是将模版编译为渲染函数。

  无论使用模版还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟 dom 渲染为真实 dom,这就是模版的工作原理,也是 vue.js 渲染页面的流程。

  编译器能识别出哪些是静态属性,哪些是动态属性,在生成代码的时候完全可以附带这些信息:

render(){
  return {
    tag:'div',
    props:{
      id:'foo',
      class:cls
    },
    patchFlag:1 // 假设数字1代表class是动态的
  }
}

  在生成的虚拟dom对象中多处啦一个patchFlags属性,假设1代表class是动态的,这样渲染器看到这个标志时就知道。省去啦寻找变更点的工作,性能就提升啦。编译器和渲染器之间时存在信息交流的,它们互相配合使得性能进一步提升,而它们之间交流的媒介就是虚拟dom对象。一个虚拟dom对象中回包含多种数据字段,每个字段都代表一定的含义。

posted @ 2023-01-12 15:03  艾路  阅读(129)  评论(0编辑  收藏  举报