为什么选择Vite的学习

654 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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')
  1. 未加属性
<!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
  1. 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
  1. 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>

image.png 可以看到也是加载了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中)。

开发时

启动阶段

现在开始看看两张图的区别:

image.png

image.png

第一张: 先执行把所有的ES模块文件打包成bundle,启动服务。缺点:当项目过大 模块过多,打包耗时就久。

第二张:先启动服务,当需要哪些执行文件就加载所需的。思考到一个问题:如果所需文件过多是不是影响进入的加载速度?(Vite 肯定有对应处理,我还没读到)

但是Vite前期也是会有做一些包的处理:预构建依赖 如loash下面的包如何都按需导入的话就很影响性能。

更新阶段

其实感觉都是大同小异的,之后再写一篇博客(水一篇)。

为什么生产环境仍需打包

虽然使用了ESM的模块导入,但是会导致请求文件过多,增加了额外的网络往返。即使使用HTTP2(多路复用、服务端推送和头部压缩等优势)也是十分低效。最好还是需要对代码进行tree-shaking、压缩、chunk分割、懒加载等优化手段。

总结

  1. 讲了什么是模块化
  2. defer 和 async 属性的区别
  3. Vite的模块化加载的优势。
  4. 为什么生产还需要打包。

最后,祝大家都有光明的未来。

参考文章

浏览器已原生支持 ES 模块,这对前端开发来说意味着什么?

Vite官网