一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 1 天,点击查看活动详情。
首先
大家好,我是小欲望。写博客目的让不知道此技术的人,知道有这个东西,具体是做什么,如何使用它。
这篇讲我看Vite官网延伸的知识。第一次听到vite还是在Vue3发布的那段时间,只知道说使用它,可以增加开发体验,快速启动。但是认知还是十分的模糊不清,因为只是听说。现在打算认真的看一遍Vite的官方文档。
本来不知道写这个的意义,因为大家看我文章还不如去看官网呢。但是这里可以让你们知道我是怎么学习的。最主要还是当作自己的笔记啦。哈哈哈。
为什么选Vite
这个是官网的第一个标题。这里介绍了 现实问题(缓慢的服务启动、缓慢的更新)、为什么生产环境仍需要打包等等之类的(不想抄了)。
第一句
Vite的第一句话
在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发
我就思考,浏览器什么时候开始支持ES 模块了。
因此就看到这篇文章: 浏览器已原生支持 ES 模块,这对前端开发来说意味着什么?
主流浏览器开始原生支持ES模块是在2017年。
什么是模块化
// a.js
export const aaa = 1
export default function sum (a, b) {
return a + b
}
// b.js
import { aaa } from './a.js'
console.log(aaa)
浏览器支持ES模块有什么改变
在遥远的记忆中,刚开始学习html的时候,外部引入一个js 文件就需要加上type="text/javascript"
<script type="text/javascript" src='./a.js'></script>
defer 与 async的区别
// a.js
try {
console.log(_.VERSION)
}catch {
console.log('Loadsh Not Available')
}
console.log(document.body ? 'Yes' : 'No')
- 未加属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>
<script type="text/javascript" src='./a.js'></script>
</head>
<body>
</body>
</html>
// 结果: 4.17.10 No
- async
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" async></script>
<script type="text/javascript" src='./a.js' async></script>
</head>
<body>
</body>
</html>
// 结果: Loadsh Not Available Yes
- defer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" defer></script>
<script type="text/javascript" src='./a.js' defer></script>
</head>
<body>
</body>
</html>
// 结果: 4.17.10. Yes
总结:
从第一个看出浏览器解析器是按照顺序执行,当遇到Script标签会去加载js文件,由JS解释器执行Js代码。这样就阻塞了浏览器的解析。因此有一个之前的优化: 把脚本放在 body 底部
当使用async 属性的时候,异步加载文件,但是不保证顺序,谁先加载完成,JS解析器就执行先到的js代码。但是不阻塞浏览器的解析。
使用defer 属性,从结果就可以看出,这个是保证了执行顺序。不阻塞浏览器的解析。
type='module'
// a.js
import { bb } from './b.js'
console.log(bb)
// b.js
export const bb = 111
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src='./a.js' defer></script>
</head>
<body>
</body>
</html>
这样浏览器就会报错Uncaught SyntaxError: Cannot use import statement outside a module
修改type='module'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type='module' src='./a.js' defer></script>
</head>
<body>
</body>
</html>
可以看到也是加载了b.js文件
结合上面defer 和 async的代码 看下结果:
// a.js
import { bb } from './b.js'
try {
console.log(_.VERSION)
}catch {
console.log('Loadsh Not Available')
}
console.log(document.body ? 'Yes' : 'No')
console.log(bb)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js" defer></script>
<script type='module' src='./a.js'></script>
</head>
<body>
</body>
</html>
// 结果 4.17.10 Yes 111
说明type='module'
默认是defer属性,当然如果需要修改成async,只需要加入即可。
小总结:
后面还有些细节可以参考那篇文章,这里我们就知道了浏览器的模块化就是会根据你的import
而加载对应的文件。没有模块化的时候就是打包整合成一个js文件(b.js 代码加到 a.js中)。
开发时
启动阶段
现在开始看看两张图的区别:
第一张: 先执行把所有的ES模块文件打包成bundle,启动服务。缺点:当项目过大 模块过多,打包耗时就久。
第二张:先启动服务,当需要哪些执行文件就加载所需的。思考到一个问题:如果所需文件过多是不是影响进入的加载速度?(Vite 肯定有对应处理,我还没读到)
但是Vite前期也是会有做一些包的处理:预构建依赖 如loash下面的包如何都按需导入的话就很影响性能。
更新阶段
其实感觉都是大同小异的,之后再写一篇博客(水一篇)。
为什么生产环境仍需打包
虽然使用了ESM的模块导入,但是会导致请求文件过多,增加了额外的网络往返。即使使用HTTP2(多路复用、服务端推送和头部压缩等优势)也是十分低效。最好还是需要对代码进行tree-shaking、压缩、chunk分割、懒加载等优化手段。
总结
- 讲了什么是模块化
- defer 和 async 属性的区别
- Vite的模块化加载的优势。
- 为什么生产还需要打包。
最后,祝大家都有光明的未来。