Vue3源代码导出,打造出她们的Vue3架构
——————
浏览の地止:https://www.lexueit.com/4748.html
——————
Vue3源代码阐释(一)- 架构结构设计
架构结构设计里四处都充分体现了取舍的表演艺术。
当他们写作Vue3源代码时辨认出,整座架构会依照组件间展开分割,但组件间又并非互相分立的,而要互相关联,有提及亲密关系,做为两个架构结构斯特默,具体来说要做的是对架构总体展开结构设计,对各组件机能展开明晰的分割
陈述句与新闻稿式
具体来说快照层架构一般来说气氛陈述句与新闻稿式,她们各有优劣
陈述句架构特征是,比如JQuery是众所周知的陈述句架构
$(#app).text(hello world).on(click, () => { alert(hi)})// 设置文本内容hello world// 绑定点击事件可以看出代码本身描述的事是做事的过程,这符合他们的逻辑直觉
新闻稿式架构的特征是,比如Vue.js
<div @click=“() => alert(hi)”>hello world</div>
这段类html模板语法是vue实现与jq写法一样机能,只需看到结果,至于如何实现由vue.js帮他们处理
新闻稿式代码比陈述句代码多出了找出差异的性能消耗,Vue架构本身是封装了陈述句代码才实现了面向用户的新闻稿式,所以新闻稿式代码性能不会优于陈述句代码的性能,那么为什么Vue.js还会选择新闻稿式结构设计呢?原因在于新闻稿式代码可维护性更强,如果采用陈述句开发,需要开发者维护整座过程,包括手动完成DOM元素创建、更新、删除等工作,而新闻稿式展示的是开发者要的结果,看上去更加直观,至于过程,vuejs都为他们封装好了
这就充分体现了架构结构设计在可维护性与性能间的取舍,而架构结构设计核心是:在保持可维护性的同时让性能损失最小化
虚拟DOM的性能
新闻稿式代码更新性能 = 找出差异的性能消耗 + 直接修改的性能消耗
如果他们能够最小化找出差异的性能消耗,就能让新闻稿式代码无限接近陈述句代码性能。而所谓的虚拟DOM,是为了最小化找出差异这这一步性能消耗而出现的
运行时和编译时
架构结构设计有三种选择:纯运行时、运行时+编译时、纯编译
运行时 使用render函数将对象渲染成DOM元素const obj = {tag: div,children: [ {tag: span,children: hello world } ]}Render(obj, document.body)// 实现render函数functionRender(obj, root) {const el = document.createElement(obj.tag)if (typeof obj.children === string) {const text = document.createTextNode(obj.children) }elseif (obj.children) { obj.children.forEach((child) =>Render(child, el)) }}编译时<div><span>hello world</span></div>
// 编译
const obj = {
tag: div,
children: [
{
tag: span,
children: hello world
}
]
}
运行+编译时// 封装两个compiler函数,html结构转为对象const obj = compiler(html)// 再调用展开渲染Render(obj, document.body)控制架构代码的体积
架构的大小是衡量两个架构标准之一,在实现同样机能情况下,用到的代码越少越好,这样体积越小,浏览器加载资源时间就会越少 在vue3源代码中每两个warn函数调用都会配合__dev__常量的检查,比如:
if (__dev__ && !res) { warn(`failed to mount app: mount target selector “${container}” returned null` )}Vue.js在使用rollup.js对项目展开构建,这里的__dev__常量实际上是通过rollup.js插件配置来预定义的,其机能与webpack中DefinePlugin类似。
在打包制品时,__dev__常量会被替换成false,这段代码永远不会执行,在构建资源时候会被移除,最终也不会出现在产物中,这样就做到了在开发环境为用户提供友好的警告信息同事,不会增加生产环境代码体积
架构要做到良好的Tree-Shaking
上文中提到自定义常量__DEV__,能够在生产环境不包含用于打印警告的代码,但这还远远不够,Vue.js内建了很多组件,比如组件,如果他们没有用到该组件,那么他的代码是不需要包含在生产环境资源里的,那么如何做到的呢?这就不得不提到Tree-Shaking
什么是Tree-Shaking
这个概念因rollup.js而普及。简单的说,Tree-Shaking是消除永远不用的代码,也是排除dead code,现在不论是rollup还是webpack都支持Tree-Shaking
想要实现Tree-Shaking必须满足两个条件,组件必须是ESM(ES Module),因为Tree-Shaking是依赖ESM的的静态结构,
// input.jsimport { foo } from./utils.js foo()// utils.jsexportfunctionfoo(obj) { obj && obj.foo }exportfunctionbar(obj) { obj && obj.bar }执行如下命令展开构建:
npx rollup input.js -f esm -o bund.js
输出内容如下:
// bund.jsfunctionfoo(obj) { obj && obj.foo }输出产物没有包含bar函数,Tree-Shaking起了作用,因为没有用到bar函数,因此可以做为dead code被删除了,
但foo函数执行也没有什么意义,仅仅是读取了对象的值,所以执行了也没有什么必要,为什么rollup没有做为dead code移除呢?这就涉及到两个关键点-副作用
什么是副作用
当调用函数的时候对外部产生影响,比如修改了全局变量,但他们的方法仅仅是读取了对象的值,怎么会产生副作用,其实是可能的,如果obj对象是通过Proxy创建的代理对象,当他们读取对象属性时,就会触发代理对象get,在get夹子中是有可能产生副作用的。因此静态分析js代码很有难度,所以像rollup 提供两个机制,让他们很明确告诉rollup,“放心吧,这段代码不出出现副作用,可以移除”,可以修改input.js为:
import { foo } from./utils/*#__PURE__*/ foo()注意注释代码/#PURE/,其作用是告诉rollup,对于foo函数调用不会产生副作用,可以放心展开Tree-Shaking
在vue3源代码中,大量使用了该注释,比如:
exportconst isHTMLTag = /*#__PURE__*/makeMap(HTML_TAGS)错误处理
错误处理是架构开发过程中重要环节,架构错误处理机制好坏决定了用户应用程序的健壮性,还决定了用户开发处理错误的心智负担。
// utils.jsexportdefault {foo(fn) { fn && fn() } }该组件导出两个对象,其中foo属性是两个函数,接受两个回调函数做为参数,如果用户执行的时候出错了怎么办呢?第一种办法是用户自行处理,需要用户她们执行try…catch,这种会增加用户负担,假设导出有100方法,那就要写100次兼容性处理
第二种他们代替用户统一处理
// utils.jslet handlerError = nullexportdefault {foo(fn) { callWithErrorHandling(fn) }, registerErrorHandler(fn) { handlerError = fn } }functioncallWithErrorHandling(fn) {try { fn && fn() } catch(e) { handlerError(e) } }用户可以使用registerErrorHandler函数注册错误处理程序,然后再callWithErrorHandling函数内部捕获错误后,把错误传递给用户注册的错误处理程序