Skip to content

引言

在第1章中,我们阐述了框架设计是权衡的艺术,这里面存在取舍,例如性能与可维护性之间的取舍、运行时与编译时之间的取舍等。 在第2章中,我们详细讨论了框架设计的几个核心要素,有些要素是框架设计者必须要考虑的,另一些要素则是从专业和提升开发体验的角度考虑的。 框架设计讲究全局视角的把控,一个项目就算再大,也是存在一条核心思路的,并围绕核心展开。 本章我们就从全局视角了解 Vue.js 3 的设计思路、工作机制及其重要的组成部分。我们可以把这些组成部分当作独立的功能模块, 看看它们之间是如何相互配合的。在后续的章节中,我们会深入各个功能模块了解它们的运作机制。

Vue.js 3 是一个声明式的 UI 框架,意思是说开发者在使用 Vue.js 3 开发页面时是声明式地描述 UI 的。 思考一下,如果让你设计一个声明式的 UI 框架,你会怎么设计呢?为了搞清楚这个问题,我们需要了解编写前端页面都涉及哪些内容,具体如下:

  • DOM 元素:例如是 div 标签还是 a 标签。
  • 属性:如 a 标签的 href 属性,再如 id、class 等通用属性。
  • 事件:如 click、keydown 等。
  • 元素的层级结构:DOM 树的层级结构,既有子节点,又有父节点。

那么,如何声明式地描述上述内容呢?这是框架设计者需要思考的问题。其实方案有很多。拿 Vue.js 3 来说,相应的解决方案是:

  • 使用与 HTML 标签一致的方式来描述 DOM 元素,例如描述一个div 标签时可以使用 <div></div>
  • 使用与 HTML 标签一致的方式来描述属性,例如 <div id="app"></div>
  • 使用 : 或 v-bind 来描述动态绑定的属性,例如 <div :id="dynamicId"></div>
  • 使用 @ 或 v-on 来描述事件,例如点击事件 <div @click="handler"></div>
  • 使用与 HTML 标签一致的方式来描述层级结构,例如一个具有 span 子节点的 div 标签 <div><span></span></div>

可以看到,在 Vue.js 中,哪怕是事件,都有与之对应的描述方式。用户不需要手写任何命令式代码,这就是所谓的声明式地描述 UI

除了上面这种使用模板来声明式地描述 UI 之外,我们还可以用 JS 对象来描述,代码如下所示:

js
const title = {
  // 标签名称
  tag: 'h1',
  // 标签属性
  props: {
    onClick: handler
  },
  // 子节点
  children: [
    { tag: 'span' }
  ]
}

对应到 Vue.js 模板,其实就是:

html
<h1 @click="handler"><span></span></h1>

那么,使用模板和 JS 对象描述 UI 有何不同呢?答案是:使用 JS 对象描述 UI 更加灵活。举个例子,假如我们要表示一个标题, 根据标题级别的不同,会分别采用 h1~h6 这几个标签,如果用 JS 对象来描述,我们只需要使用一个变量来代表 h 标签即可:

js
// h 标签的级别
let level = 3
const title = {
  tag: `h${level}`, // h3 标签
}

可以看到,当变量 level 值改变,对应的标签名字也会在 h1 和 h6 之间变化。但是如果使用模板来描述,就不得不穷举:

html
<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 对象灵活。而使用 JS 对象来描述 UI 的方式,其实就是所谓的虚拟 DOM。现在大家应该觉得虚拟 DOM 其实也没有那么神秘了吧。 正是因为虚拟 DOM 的这种灵活性,Vue.js 3 除了支持使用模板描述 UI 外,还支持使用虚拟 DOM 描述 UI。 其实我们在 Vue.js 组件中手写的渲染函数就是使用虚拟 DOM 来描述 UI 的,如以下代码所示:

js
import { h } from 'vue'

export default {
  render() {
    return h('h1', { onClick: handler }) // 虚拟 DOM
  }
}

有的读者可能会说,这里是 h 函数调用呀,也不是 JS 对象啊。其实 h 函数的返回值就是一个对象,其作用是让我们编写虚拟 DOM 变得更加轻松。 如果把上面 h 函数调用的代码改成 JS 对象,就需要写更多内容:

js
export default {
  render() {
    return {
      tag: 'h1',
      props: { onClick: handler }
    }
  }
}

如果还有子节点,那么需要编写的内容就更多了,所以 h 函数就是一个辅助创建虚拟 DOM 的工具函数,仅此而已

🔔 提示

在 Vue 中,createElement 函数(简称 h 函数)是用于辅助创建虚拟 DOM 对象的工具函数。它接收一些参数(如标签名、属性、子节点等), 并返回一个表示 DOM 结构的虚拟 DOM 对象。通过使用 h 函数,开发者可以以编程的方式构建虚拟 DOM 树,而不是直接操作真实 DOM。

“h 函数就是一个辅助创建虚拟 DOM 的工具函数”这句话的意思是,h 函数在 Vue 中起到了一个辅助作用,帮助开发者更方便地创建和组织虚拟 DOM 结构。 当 Vue 需要渲染或更新组件时,它会首先使用 h 函数创建虚拟 DOM,然后将其转换为真实 DOM,并在必要时进行高效的更新。 简单地说,h 函数就是一个用于创建虚拟 DOM 的辅助工具,在 Vue 中起到了连接虚拟 DOM 和真实 DOM 的关键作用。

另外,这里有必要解释一下什么是组件的渲染函数。一个组件要渲染的内容是通过渲染函数来描述的,也就是上面代码中的 render 函数, Vue.js 会根据组件的 render 函数的返回值拿到虚拟 DOM,然后就可以把组件的内容渲染出来了。

如有转载或CV请标注本站原文地址