Skip to content
On this page
---

title: Vue.js 设计与实现 - 1 | 权衡的艺术 date: 2022-07-02 tags:

  • 书籍阅读 categories:
  • vue

总结

  1. 视图层框架:命令式、声明式
  2. 声明式的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗
  3. 对比了原生js操作DOM、虚拟DOM和innerHTML三者的性能,这三者性能无法简单下结论。需要结合页面大小、变更部分大小、行为是创建页面还是更新页面去判断。从结果而言虚拟DOM性能还不错
  4. 运行时 + 编译时,保证了不错的灵活度,性能也不赖。

1. 命令式和声明式

视图层框架:

  • 命令式:jQeury,关注过程。
    自然语言可以和代码一一对应。
    js
    $(#app) // 获取div
      .text('hello') // 设置文本内容
      .on('click', () => {alert('ok')}) // 绑定点击事件
    
  • 声明式:Vue,关注结果。
    vue为我们提供了结果,封装了过程,其内部的实现是命令式,而暴露给用户的是声明式。
    封装了命令式代码才实现了面向用户的声明式.
    html
    <div @click="() => alert('ok')">hello</div>
    

2. 性能和可维护性的权衡

结论:声明式代码的性能 不优于 命令式代码的性能

优缺点:

  • 命令式:性能好,维护性差。实际开发中难以写出性能最优的命令式代码(费精力)。
  • 声明式:性能差,维护性好。

性能消耗计算:

  • 命令式:直接修改性能消耗
  • 声明式:找出差异的性能消耗 + 直接修改的性能消耗

框架设计目的:保持可维护性的同时,让性能损失最小化。所以使用声明式,且尽可能减少【找出差异的性能消耗】。
找出差异的性能消耗可以使用虚拟DOM,只要虚拟DOM的性能足够优秀,那声明式的性能就能无限接近接近命令式的性能。

3. 虚拟DOM

结论:js的性能消耗远 小于 DOM操作的性能消耗

想要性能好,可以在js上找出差异,尽可能减少DOM操作的性能消耗:虚拟DOM
且尽可能优化虚拟DOM,尽量减少【找出差异的性能消耗】。

声明式视图框架Vue,便是使用虚拟DOM

虚拟DOM 和 innerHTML 的区别

  • innerHTML创建页面的性能:HTML字符串拼接的计算量(在js中拼接) + 销毁/创建所有真实DOM的计算量
    特点:全量更新. 只要HTML文本有一点更改,范围内的所有DOM元素就得销毁重新生成

  • 虚拟DOM的性能:( 创建JS对象的计算量 + Diff算法计算量 ) + 更新真实DOM的计算量
    特点:找到需要修改的元素,仅仅更新该元素.

更新页面时,性能比较:

innerHTML < 虚拟DOM < 原生js Dom操作        
性能差      性能不错      性能强
心智负担中  心智负端小    心智负担重
            可维护性强    可维护性差

4 运行时、编译时

框架设计

  • 纯运行时
  • 运行时 + 编译时
  • 纯编译时

4.1 纯运行时

js
const obj = {
  tag: 'div',
  children: [
    {tag: 'span', children: 'hello word'}
  ]
}

function Render(obj, root) {
  const el = document.createElement(obj.tag)
  if(typeof obj.children === 'string') {
    const text = document.createTextNode(obj.children)
    el.appendChild(text)
  } else if(obj.children) {
    obj.children.forEach(child => Render(child, el))
  }

  root.appendChild(el)
}

Render(obj, document.body)

上面的代码在运行时,通过调用Render函数,传递一个树的数据结构,便可以创建对应的HTML添加到文档中,这便是 纯运行时 框架。
如果觉得这个树的数据结构不够直观,想用类似HTML标签的方式描述树的数据结构,需要引入编译器。
编译器Compiler可以将HTML描述编译成树的数据结构,这样便可以继续调用Render函数

js
const html = `
<div>
  <span>hello word</span>
</div>
`

const obj = Compiler(html) // 编译器编译,获取属性数据结构
Render(obj, document.body)

当然,这里还是纯运行时,或者叫运行时编译。由于多调用了Compiler,对比直接调用Render,会消耗更多性能。

4.2 运行时 + 编译时

我们可以通过构建工具,在构建时便执行Compiler程序将用户提供的内容编译好,等到运行时就无需编译了,对性能非常好。
这便是运行时 + 编译时。

4.3 纯编译时

既然Compiler编译器可以将HTML描述文本编译成树的数据结构,同样它也可以将HTML描述文本直接转成命令行代码。
直接转成命令行代码,跳过了Render函数,性能可能会更好。

4.4 优缺点

纯运行时:

  • 灵活度最高,都在运行时执行代码,没有编译过程定下来固定的东西。
  • 没有编译过程,没法提前分析用户提供的内容,性能没有其它两个优越

运行时 + 编译时:Vue3

  • 有编译过程,可以提前分析用户提供内容,分析出哪些未来会改变,哪些不会从而提前编译好提高运行时的性能。
  • 具有不错的灵活度。

纯编译时:Svelte

  • 由于不需要任何运行时,直接编译成可执行的命令式代码,性能可能是最好的。
  • 但有损灵活度,且内容必须编译后才能使用。
  • 跟命令式代码一样,难以写出最理想化的命令式代码,框架真实性能可能达不到理想高度。

疑问

  1. 感觉有点零散,知识点连不到一起
  2. 为什么说纯编译时有损灵活度,且内容必须编译后才能使用。我们现在用的框架Vue React等不都有webpack脚手架等去编译吗。这算缺点的化 为什么在运行时+编译时不提这个缺点。
  3. 纯编译为什么灵活度差?