文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue.js设计与实现-框架设计的核心要素

2024-12-02 03:41

关注

1、写在前面

在前面文章中,了解到框架的设计是一种权衡的艺术,是需要宏观把握各方面因素从而尽可能完善。其实,框架设计比想象中的复杂,并不是只有功能实现就完事,需要考虑如何提供给使用者构建产物、如何让其快速定位问题,需要有良好的使用交互体验。

2、提升用户体验

衡量框架是否优秀的重要指标,就是看它的开发体验是否达到预期,需要为用户提供良好直观的交互指引和错误警示,帮助用户快速理解和错误定位。

3、控制代码体积

在进行项目开发中,在实现相同功能代码越简洁、越少,在进行打包压缩体积后,在浏览器渲染加载资源的时间更少,就能够换取提升用户的使用体验。在前面提到要给用户完善丰富的警告信息,那么也就意味着编写更多的代码,这就违背了控制代码体积的初衷。

对此,Vue3框架在设计源码时,采用_DEV_进行判断是否为开发环境,从而让一些代码只在开发时进行调用,也会构建对应的开发资源。在Vue.js中采用的打包工具是rollup,_DEV_ 是使用插件进行预定义的,在进行资源输出时用于区分生产环境还是开发环境。

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

当在构建生产环境的资源时,_DEV_的值为false,这时候上面的这段代码将永远不会进行执行,这样就成为了dead code,在构建资源时就会被打包工具所移除(Tree-shaking)。

4、Tree-shaking

事实上,在我们实际项目开发中,有些Vue.js内置的组件压根没有用不上,那么在构建生产环境的资源时就不能让它出现,也就是Tree-shaking。Tree-shaking概念是由rollup推广普及的,指的是消除那些永远不会被执行的代码,即排除dead code。

实现Tree-shaking功能,前提是依赖于文件的模块必须是ESM(ES Module),即文件的静态结构是ESM。

在rollup中Tree-shaking是如何工作的呢?

demo文件结构:

|-- home
| |__ package.json
| |__ ping.js
| |__ chuan.js

安装rollup:

yarn add rollup -D

ping.js和chuan.js文件内容:

// ping.js
export function foo(obj){
obj && obj.foo
}
export function bar(obj){
obj && obj.bar
}
export function fun(obj){
obj && obj.fun
}

注意,我们在chuan.js文件中并没有导入ping.js中的bar函数。

import {foo, fun} from "./ping"
// 告知rollup这是个纯函数,不会产生任何副作用,可以放心tree-shaking
foo()
fun()

使用命令进行构建,以chuan.js文件作为打包的入口,输出ESM的文件模块,打包后的输出文件是bundle.js。

npx roolup chuan.js -f esm -o bundle.js

在执行命令打包成功过后,我们可以输出的bundle.js文件中只包含fun函数的相关代码。而bar函数因为入口文件没有导入,不会打包进去,而foo函数前面加了代码,即告知rollup这是个纯函数,不会产生任何副作用,可以放心tree-shaking。这样,打包后的文件就只剩fun函数了。

//bundle.js
function fun(obj){
obj && obj.fun;
}
fun();

什么是tree-shaking的副作用呢?

经常提到的副作用其实就是:当调用函数时,会对外部产生影响,在rollup中如果函数调用时产生了副作用,就不能将其移除,因为会潜在的影响其他代码。

打包工具是怎么知道哪些代码可以放心移除呢?

rollup提供代码,可以告知rollup这是个纯函数,不会产生任何副作用,可以放心tree-shaking。上面代码片段中,在foo函数前添加,就可以做个标记告知rollup可以对其tree-shaking,打包后的bundle.js文件也对应将代码进行移除。其实可以用于任何代码,不单单是函数。

在Vue.js框架的源码中,我们可以发现大量的,但其实这并不会对使用者产生心智负担,因为通常产生副作用的代码都是模块内顶级调用的,而没有被顶级调用的代码是不会产生副作用的。

什么是顶级调用?

fun();//顶级调用
function fun(obj){
obj && obj.fun;
}
function foo(){
fun();//函数中调用
}

5、构建产物

Vue.js会不同的环境输出不同的包,vue.global.js用于开发环境,vue.global.prod.js用于生产环境,在构建产物时,会根据不同的需求输出不同的构建产物。

<body>
<script src="./vue.js"></script>
<script>
const {createApp} = Vue;
//...
</script>
</body>

首先用户直接可以在html页面中使用script引入框架使用,根据不同需求构建不同产物,需要输出一种IIFE(立即调用的函数表达式)格式的资源。

const Vue = (function(){
//...
exports.createApp = createApp;
//...
return exports
})()

这样,在使用