接上一篇文章:阅读《Vue.js设计与实现》 – 01
文章目录
- 第二章
- 提升用户的开发体验
- tips
- 控制框架代码的体积
- Tree-Shaking
- 副作用
- 框架应该输出怎样的构建产物?
- 注意
- 这两个文件有什么区别?
- 特性开关
- 如何实现?
- 处理错误
- TS支持
第二章
本书第二章,主要是告诉我们一个好的框架需要考虑的事情很多,包括:构建的产物是什么?产物构建模式?提升用户的开发体验?热更新?减少打包体积?可配置?……
这部分主要就是说:vue怎么样做到这些事情,以及vue怎么好的。属于自卖自夸,手动狗头!
提升用户的开发体验
衡量一个框架是否足够优秀的指标之一,就是看它的开发体验如何。
说白了就是出现问题提示我们程序员够不够清楚,别出个问题全是源码提示,一点进去直接跳进源码(说实话,菜鸟开发vue经常性这样,所以感觉有点自卖自夸),《Vue.js设计与实现》 中举例的是 createApp
挂载元素不存在时的一个 wran
,那确实提示得很清楚
这要是都提示不清楚,那也没必要说 vue 好用了,毕竟挂载上去是最重要的一点!
如果 Vue.js 内部不做任何处理,那么我们很可能得到的是 JavaScript 层面的错误信息,例如 Uncaught TypeError: Cannot read property 'xxx' of null
,根据此信息,我们很难知道问题出在哪里。
tips
我们在写代码打印 ref
对象时,如果不加 .value
就打印,打印出来的结构还是比较难看的,打印结果需要我们手动点击进去,且有很多我们不关注的无用信息。
但是其实浏览器允许我们编写自定义的 formatter,从而自定义输出形式。在 Vue.js3
的源码中,你可以搜索到名为 initCustomFormatter
的函数,该函数就是用来在开发环境下初始化自定义 formatter 的。
我们只需要设置一下goole控制台,即可优化打印结果,步骤如下:
1、打开控制台,点击如图按钮
2、找到控制台,开启 Enablecustom formatters
即可
设置之后的打印结果会变成
控制框架代码的体积
这部分主要讲的就是:框架在实现相同功能时,肯定是代码越少越好,这样体积更小,请求后加载速度就更快!但是这个和上一个违背了,因为要处理好报错,肯定就要加一堆不相关的逻辑。vue想办法解决了这个问题,就是区分环境,用rollup
里面的插件来设置一个Boolean
值(源码为__DEV__
常量 ),让开发环境有提示,生产环境因为rollup
的Tree-Shaking
,就不会打包进入生产环境,减小体积!
Tree-Shaking
什么是 Tree-Shaking
?在前端领域,这个概念因 rollup.js
而普及,指的就是消除那些永远不会被执行的代码,现在无论是 rollup.js
还是 webpack
,都支持 Tree-Shaking
。
想要实现 Tree-Shaking
,必须满足一个条件,即模块必须是ESM(ES Module)
,因为 Tree-Shaking
依赖 ESM
的静态结构。
这里《Vue.js设计与实现》举了一个rollup
打包的例子,一个input.js
文件
import { foo } from './utils.js'
一个utils.js
文件
export function foo(obj) {obj && obj.foo
}export function bar(obj) {obj && obj.bar
}
使用 rollup 打包后,打包文件只会留下 foo 函数,bar 函数会被Tree-Shaking
掉!
但是又发现其实 foo 也没有使用,只是引用了而已,按理说应该也要将其删除!
副作用
当调用函数的时候会对外部产生影响。这时你可能会说,上面的代码明显是读取对象的值,怎么会产生副作用呢?其实是有可能的,试想一下,如果 obj 对象是一个通过 Proxy 创建的代理对象,那么当我们读取对象属性时,就会触发代理对象的 get ,在 get 中是可能产生副作用的,例如:我们在 get 夹子中修改了某个全局变量。而到底会不会产生副作用,只有代码真正运行的时候才能知道,JavaScript 本身是动态语言,因此想要静态地分析哪些代码是永远不会被使用的很有难度。
因为静态地分析 JavaScript 代码很困难,所以像 rollup.js 这类工具都会提供一个机制,让我们能明确地告诉 rollup.js:“放心吧,这段代码不会产生副作用,你可以移除它。”
如何告诉rollup呢?使用/*#__PURE__*/
代码变成:
import {foo} from './utils'
/*#__PURE__*/ foo()
框架应该输出怎样的构建产物?
这里讲解了rollup
如何打包产物为你需要的格式,如果用户想直接通过script
直接引入那么就要设置
菜鸟这里直接搞成表格
需要的打包产物 | rollup设置(output/format) |
---|---|
script直接引入 | iife |
script使用module形式引入 | esm |
node中使用require引入 | cjs |
注意
这里第二种其实会产生两种文件,一种vue.esm-browser.js
,该文件是直接给 <script type="module">
使用的,还有一种vue.esm-bundler.js
,这个文件是开发时,给构建工具(vite 或者 webpack)使用的!
我们知道,无论是 rollup.js 还是 webpack,在寻找资源时,如果 package.json 中存在 module 字段,那么会优先使用 module 字段指向的资源来代替 main 字段指向的资源。
这两个文件有什么区别?
区别在于上文中的 __DEV__
常量。当构建用于<script>
标签的 ESM 资源时,如果是用于开发环境,那么__DEV__
会设置为 true;如果是用于生产环境,那么 __DEV__
常量会设置为 false,从而被 Tree-Shaking
移除。
但是当我们构建提供给打包工具的 ESM 格式的资源时,不能直接把 __DEV__
设置为 true或 false,而要使用 (process.env.NODE_ENV !=='production')
替换 __DEV__
常量。
特性开关
就是预留一些配置,用户通过true或者false(或者其他代码)来确定自己要不要那些功能,如果用户想要就在打包时留下那些功能代码,不想要就直接删除!
如何实现?
其实很简单,原理和上文提到的__DEV__
常量一样,本质上是利用 rollup.js 的预定义常量插件
来实现。
更具体的就想要读者去看书了,这里不好总结,感觉就是一整段,但是都拿过来就要被寄律师函了,手动狗头!
死道友不死贫道,手动狗头,大家可以看这个:https://juejin.cn/post/7091263773301800991?from=search-suggest#heading-17
处理错误
框架需要为用户提供统一的错误处理接口,这样用户可以通过注册自定义的错误处理函数来处理全部的框架异常。
说实话,菜鸟没有用过,所以不太清楚这个统一的错误处理接口是啥,然后菜鸟问了一下GPT,感觉这里统一的错误接口应该就是和js里面的顶层监听错误一个意思!类似:
GPT回答:
菜鸟感觉基本用处不是很大,读者想了解可以直接vue3官网搜:onErrorCaptured()
--> vue3的一个生命周期!
TS支持
一个常见的认知误区,即"使用 TS 编写框架和框架对 TS 类型支持友好是两件完全不同的事"。有时候为了让框架提供更加友好的类型支持,甚至要花费比实现框架功能本身更多的时间和精力。
菜鸟只是略懂ts,但是感觉ts让我们的心智负担更重而不是减少了!我们程序需要的是:检验变量类型,以确保逻辑正确,而不是在项目中一引入就一大堆报错,然后去找用什么类型才能让这个组件的类型是对的,以及一些不知道名字的库的类型是对的,感觉ts检测的重心应该在逻辑而不是检验为什么组件类型不对。
就像这本书说的:
有时候为了让框架提供更加友好的类型支持,甚至要花费比实现框架功能本身更多的时间和精力。