【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记(持续更新)

从高层设计的角度去探讨框架需要关注的问题。

参考:速读《Vue.js 设计与实现》 - 掘金 (juejin.cn)

系列目录:

标题 博客
第一篇:框架设计概览 【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记
第二篇:响应系统 【Vue.js设计与实现】第二篇:响应系统-阅读笔记
第三篇:渲染器 【Vue.js设计与实现】第三篇:渲染器-阅读笔记
第四篇:组件化 【Vue.js设计与实现】第四篇:组件化-阅读笔记
第五篇:编译器 【Vue.js设计与实现】第五篇:编译器-阅读笔记
第六篇:服务端渲染 【Vue.js设计与实现】第六篇:服务端渲染-阅读笔记

第一篇:框架设计概览

  • 第 1 章 权衡的艺术
  • 第 2 章 框架设计的核心要素
  • 第 3 章 Vue.js 3 的设计思路

文章目录

    • 第一章 权衡的艺术
      • 命令式和声明式
      • 性能与可维护性的权衡
      • 运行时和编译时
    • 第二章:框架设计的核心要素
      • `__DEV__`:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积
      • ` /*#__PURE__*/`与Tree Shaking
      • 框架应该输出怎样的构建产物
      • 错误处理
      • 良好的TypeScript类型支持
      • 总结
    • 第三章:Vue.js 3 的设计思路
      • 声明式地描述UI
      • 初识渲染器
      • 组件的本质
      • 模板的工作原理
      • 总结

第一章 权衡的艺术

框架的设计,本身就是一种权衡的艺术。

命令式和声明式

命令式关注过程,声明式关注结果

命令式:

const div=document.querySelector('#app') //获取div
div.innerText='hello world' //设置文本内容
div.addElementListener('click',()=>{alert('OK')}) //绑定事件

声明式:

<div @click="()=>alert('OK')">Hello worlddiv>

vue的内部是命令式的,而我们使用vue的时候是声明式的。

即,vue封装了命令式的过程,对外暴露出了声明式的结果

性能与可维护性的权衡

从性能的角度上看,命令式的性能>声明式的性能。

原因:

命令式的代码通过原生的JS实现,声明式的代码要实现同样功能时,还要再调用相同的命令式代码。
声明式代码的更新性能消耗 = 找出差异的性能消耗+直接修改的性能消耗.
所谓的虚拟 DOM,就是为了最小化找出差异这一步的性能消耗而出现的。

显然命令式的性能更高。此时vue还要对外暴露出声明式的接口,原因是声明式的可维护性,远大于命令式的可维护性

而且,vue在性能优化之下,它并不会比纯命令式的性能差太多。

在前端领域,用JS修改HTML的方式主要有3种:原生JS,innerHTML,虚拟DOM。

心智负担:虚拟DOM < innerHTML< 原生JS
性能:innerHTML < 虚拟DOM < 原生JS
可维护性:原生JS < innerHTML < 虚拟DOM

虚拟DOM的性能并不是最高的,但是vue依然选择虚拟DOM来进行渲染层的构架。

这也是性能与可维护性的权衡。

运行时和编译时

都是框架设计的一种方式,可单独出现,也可组合使用。

  • 运行时:runtime

利用render函数,把虚拟DOM转化为真实DOM的方式。

  • 编译时:compiler

把template模板中的内容,转化为真实DOM。
注意,存在编译过程,可以分析用户提供的内容。同时,没有运行时理论上性能会更好。

  • 运行时+编译时

过程分两步:

  1. 先把template模板转化成render函数,即编译时
  2. 再利用render函数,把虚拟DOM转化为真实DOM,即运行时

两者的结合,可以:

  • 在编译时:分析用户提供的内容
  • 在运行时,提供足够的灵活性

这也是vue的主要实现方式。

第二章:框架设计的核心要素

__DEV__:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积

有一个常量__DEV__,存在于所有的console.warn中:

if (__DEV__ && !res) {
	warn(
		`Failed to mount app: mount target selector "${container}"
    returned null.`
	);
}

在开发环境中__DEV__永远为true,在生产环境中__DEV__永远为false。永远不会执行的代码成为dead code,不会出现在最终产物中。

/*#__PURE__*/与Tree Shaking

Tree Shaking:消除那些永远不会被执行的代码。

想要实现Tree Shaking,模块必须是ESM。

Tree Shaking的关键点:副作用。如果一个函数调用会产生副作用,那么就不能将其移除。副作用就是,当调用函数的时候会对外部产生影响,例如修改了全局变量。
举个例子:

如果 obj 对象是一个通过 Proxy 创建的代理对象,那么当我们读取对象属性时,就会触发代理对象的 get 夹子(trap),在 get 夹子中是可能产生副作用的,例如我们在 get 夹子中修改了某个全局变量。而到底会不会产生副作用,只有代码真正运行的时候才能知道,JavaScript 本身是动态语言,因此想要静态地分析哪些代码是 dead code 很有难度

静态地分析 JavaScript 代码很困难,所以像 rollup.js 这类工具都会提供一个机制,让我们能明确地告诉 rollup.js:这段代码没有副作用,可以移除。

import {foo} from './utils'
/*#__PURE__*/ foo()

注释代码/*#__PURE__*/,就是告诉rollup.js,对于foo的调用不会产生副作用,可以Tree-Shaking。

实际上,通常产生副作用的代码都是模块内函数的顶级调用。

foo() // 顶级调用

function bar(){
	foo() // 函数内调用
}

可以看到,对于顶级调用来说,是可能产生副作用的;对于函数内调用来说,只要函数 bar 没有调用,那么 foo 函数的调用自然不会产生副作用
因此,在 Vue.js 3 的源码中,基本都是在一些顶级调用的函数上使用 /*#__PURE__*/ 注释。此注释也可以应用在语句上。

框架应该输出怎样的构建产物

用户能够使用

你可能感兴趣的:(文档/技术书籍,阅读笔记,vue.js,笔记,前端)