vuejs设计与实现-权衡的艺术

87 阅读3分钟

权衡的艺术

框架设计里到处都体现了权衡的艺术

1.1 命令式和声明式

命令式关注过程:

const div = document.querySelector('#app');
div.innerText = 'hello world';
div.addEventListener('click', () => { ... });

声明式关注结果:

<div @click="() => { ... }">hello world</div>

1.2 性能与可维护性的权衡

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

  • 命令式代码的更新性能消耗 = 直接修改消耗的性能(需要用户自己找出差异的心智负担)
  • 声明式代码的更新性能消耗 = 直接修改消耗的性能 + 找出差异的性能消耗

框架本身就是为了封装命令式代码才实现了面向用户的声明式. 目的是通过声明式提升可维护性的同时让性能损失最小化.

1.3 虚拟dom的性能到底如何

  • innerHTML字符串模板: 将字符串解析成dom树(销毁所有旧dom并创建新dom)
  • 虚拟dom: 通过js对象, 比较新旧差异(diff)并更新
  • 原声js: 直接进行精准的dom操作
innerHTML虚拟dom原声js
心智负担中等心智负担小心智负担大
性能差可维护性强可维护性差
性能不错性能高

1.4 运行时和编译时

  • 运行时
    const obj = {
        tag: 'div',
        children: [{
            tag: 'span',
            children: 'hello world'
        }]
    }
    
    Render(obj, document.body)
    
    通过Render函数将数据直接渲染成dom元素, 没有其他额外的步骤.
  • 运行时+编译时
    // 运行时编译(JIT即时编译)/构建时编译(AOT预编译)
    const html = `
    <div>
        <span>hello world</span>
    </div>`
    // 通过Compiler得到树形结构的数据对象
    const obj = Compiler(html)
    Render(obj, document.body)
    
    通过编译的过程, 分析用户提供的内容. 知道哪些内容可能会改变, 哪些永远不会改变. 将结果传递给Render函数, 可以做进一步的优化.
  • 编译时
    <div>
        <span>hello world</span>
    </div>`
    
    ⬇️编译(不支持任何运行时内容, 代码需要通过编译器编译才能运行)
    const div = document.createElement('div')
    const span = document.createElement('span')
    span.innerText = 'hello world'
    div.appendChild(span)
    document.body.appendChild(div)
    
    不需要任何运行时, 可以分析用户提供的内容直接编译成可执行的js代码, 因此性能可能更好但有损灵活. 用户提供的内容需经过编译才能用.

总结

  1. 命令式与声明式的差异. 命令式理论上可以做到极致优化, 但要承受巨大的心智负担; 而声明式能有效减轻心智负担,但性能上有一定的牺牲. 框架设计者要尽量使性能损耗最小化
  2. 虚拟dom的性能: 虚拟dom的意义在于找出差异的性能消耗最小化. 声明式的更新性能消耗=找出差异的性能消耗+直接修改的性能消耗
  3. vue是一个编译时+运行时的框架, 它在灵活性的基础上通过编译手段分析用户提供的内容, 从而进一步提升更新性能.

《vuejs设计与实现》 1.权衡的艺术