本文转载自微信公众号「秋风的笔记」,作者蓝色的秋风。转载本文请联系秋风的笔记公众号。
最近在B乎看到了这么一个问题,能不能通俗地讲 Vite 到底是用来干嘛的,一开始觉得这个问题没什么意思,因为 Vite 这个话题有太多的人讲了。
但是我看了看其他的回答,大部分都会从 Vite 的特性,ES Modules 去讲整件事情,然后还是会时不时要和 webpack 去做比较。
而且我又仔细看了看题主的疑问。
我也陷入了深深的思考,到底是大家学习一些新知识的时候急迫了呢?还是说前端常常容易把一些简单的东西复杂化,容易形成套娃式的知识?又或者是知识太零散了,分不清到底是哪些是有关系的?
探索Vite
我打开了 Vite 的官网,他的标语是 「 下一代前端开发与构建工具」,因为一般标语需要言简意赅地表达出它的意思,所以会用最精简的去概括(也是为了宣传)。我认为确实也没有啥毛病,但是对于一些新手而且,确实这句话不知所云,官网也没有足够清楚的图,如果是一些不了解的人,确实也容易迷失。
那么这句话到底要表达出什么意思呢?我用通俗的话表述了一遍(可能不一定准确,仅代表个人的理解):
目前大部分浏览器已经支持了ESM(ES Modules)模块的方式了,因此我写了一个转化工具,可以让一些浏览器不支持的格式(.vue/.svelte/.ts)以及不支持的语法(最新的es语法/特性)让浏览器支持,它将会成为趋势。
很多人会从 ES Modules 、Dev Server 、Vue 集成度高、速度快啊各种方向来讲解它,更像是在说它的优点,我觉得有点神话它了,所以才让它变得那么神秘(迷惑)?
而在我看来 Vite 就是一个转化器,而 Webpack 就是 模块器 + 转化器。
转化器的用途就是,将一些浏览器无法解析的文件,转化成可以被浏览器解析的 js 文件,Vite 做的核心就是这个。什么用 Vite 快啊,都是浏览器自己的功劳,浏览器统一了模块化方案,Vite 只是吃了一波浏览器的性能红利而已。
用一张图来描述就是(这里我盗用了 Webpack 的官方图,改了改)
Vite 就是把所有的资源都转化成了 js 的形式去引入,因为浏览器只支持 js 文件的 ES Modles 方式,毕竟 ES Modules 属于 ECMAscript 规范,当然只能是适用于 js 了。而整个模块化过程都是浏览器的功劳。
这里再来看看 Webpack 的整体流程图,如果你对 Webpack 了解,可能能加深印象,但是不了解 webpack 也没关系。
由于 Webpack 要自己的模块化方式,因此需要将所有的资源都打包成一个 js,这个图很形象地解释了 Webpack 的作用。(对于不了解 Webpack 的也没有关系,知道 Vite 的图就够了。)
我举一个例子,可能能让你更加清楚地去理解 Vite 的真面目。
- // index.html
- <script type="module" src="main.js"></script>
- // main.js
- import { element, text } from './el.js';
- const container = element('div');
- const h1 = element('h1');
- const t = text('Hello ES Modules');
- h1.appendChild(t);
- container.appendChild(h1);
- document.body.appendChild(container);
- // el.js
- export function element(name) {
- return document.createElement(name);
- }
- export function text(data) {
- return document.createTextNode(data);
- }
在 VS Code 装一个 Live Server , 然后启动这个 html, 然后我们随便改点东西,可以看到,更新速度的非常快。
也许你会说,我文件数量太少了,没事,我们这次来整活20个文件。
- const fs = require('fs');
- const LENGTH = 20;
- new Array(LENGTH).fill(0).forEach((item, index) => {
- fs.writeFileSync(`child-${index}.js`, `
- export { child } from './child-${index+1}.js';
- `)
- })
- fs.writeFileSync(`child-${LENGTH}.js`, `
- import { element, text } from '../el.js';
- export function child() {
- const c = element('div');
- const t = text('child');
- c.appendChild(t);
- document.body.appendChild(c);
- }
- `)
所以我说,Vite 本质是拨除了 webpack 模块化功能后的一个转化器。
但是尽管浏览器解决了模块化的依赖,依旧是有两个问题:
但是没办法支持一些样式/文件(css/ttf/jpg...)资源的 import 语法
无法支持.ts/.vue/.svelte 等模板语法(或者高级特性)的直接引用
所以,才会有我们看到 Vite 仿佛又做了很多事情,因为 Vite 能够去加载 .ts/.vue/.svelte 等文件, 它整合了很多插件去做这些转化工作,将所有的资源都转化成浏览器可识别的 js 的方式去导入,将 css 文件经过包装,转化为一个 js 文件等等。
剩余的就是原文件中的内容替换,因为类似于像第三方包中的资源没办法直接引入,需要做一层替换,例如一下代码就被转化成了这样。
在编译的时候需要去替换我们实际写代码的地址,去让浏览器加载,然后为了不让浏览器加载文件太多,还要将第三方包导成一个模块,然后还有热更新功能(这部分功能稍微复杂一些)。并且为了能够在生产环境打包(Tree shaking / 压缩啊,等等之前常规的优化),使用了 Rollup ,不仅提供了 ESM 的打包方式,以及你需要的其他模块化方式(umd/amd/cmd/iife)。
因此核心是简单的,但是相关的生态想要好用,却是要花大量的精力,Vite 团队也是花了大力气去解决了周边的生态问题,各种插件的适配啊等等。
首创的ESM
而首次提出利用浏览器原生ESM能力的工具并非是Vite,而是一个叫做Snowpack的工具(可以看我这篇文章 《模块化系列》snowpack,提高10倍打包速度。)。前身是@pika/web,从1.x版本开始更名为Snowpack。
Snowpack在其官网是这样进行自我介绍的:“Snowpack是一种闪电般快速的前端构建工具,专为现代Web设计。它是开发工作流程较重,较复杂的打包工具(如Webpack或Parcel)的替代方案。Snowpack利用JavaScript的模块化方式(称为ESM)来避免不必要的工作并保持流畅的开发体验”。
为此,Pika团队开发并维护了两个技术体系:构建相关的Snowpack和造福大众的Skypack。其中 skypack 上还有很多特殊处理过的 ES Modules 形式的包(例如 React 等)直接用来调用,由于那些包原先是不支持 ES Modules 形式,他们单独维护了 ES Modules 版本。
看完了 ES Modules 的现状以及 Vite 的本质,我们就再来把模块化来回顾一下,这样整个时间线就完整了以及我们的开发方式变化到现在,Web 真的做出了巨大的努力。
模块化简史
把时间回退到2006年,这个时候 「jQuery」 刚呱呱落地,那个时候虽然没有模块化,使用 jQuery 相比传统那样写已经提高极大的速度,当然虽然已经很方便了,单还是阻挡不了爱研究的程序员们。
在2009年的时候 「CommonJS」 诞生了,但是 「CommonJS」 由于有两个重要问题没能得到解决,所以迟迟不能推广到浏览器上。(1.由于外层没有 function 包裹,被导出的变量会暴露在全局中。2.在服务端 require 一个模块,只会有磁盘 I/O,所以同步加载机制没什么问题;但如果是浏览器加载,一是会产生开销更大的网络 I/O,二是天然异步,就会产生时序上的错误。)中间百家争鸣(「AMD、CMD、UMD」)一直到2016年5月,经过了两年的讨论,ECMAScript 6.0 终于正式通过决议,成为了国际标准。在这一标准中,首次引入了 import 和 export 两个 JavaScript 关键字,并提供了被称为 「ES Module」 的模块化方案。在 JavaScript 出生的第 21 个年头里,JavaScript 终于迎来了属于自己的模块化方案。而在这期间想要使用模块化,只能通过打包工具来解决。
有了标准之后,也不是能立马让所有设备都支持 「ES Module」 因为浏览器的推进是一个漫长的过程,不像服务端,如果做一个升级,只需要对服务端升级,而浏览器的升级伴随着电脑/手机等一系列的因素,因素非常不可控,因为用户总是可以有多种多样的选择,「ES Modules(ESM)」 是 JavaScript 官方的标准化模块系统,而它这一走,却在标准化的道路上已经花费了近 10 年的时间。在2018 年 5 月 Firefox 60 发布之后,所有的主流浏览器就都支持 「ESM」 了。直到现在,「ES Module」 还并不能真正地用在生产环境使用,还是需要转化成以旧的方式(非ESM方式)。
写在最后
当回答完这个问题的时候,不禁会想,前端的发展过程中却是会有一些新瓶装旧酒的东西,然后神话它,然后让小白觉得它很高大上,让人惧怕,然后大佬就会觉得这个东西很简单,也不愿意去拆解它,是否我们需要转化一些思考,当我们讲一个东西的时候,剥离那些高大上的词汇,做一些更加亲民的解释?当然我不否则这些新的工具带来的便利以及背后的付出,但是亲民是否也是一种方式,或许会变得更加美好?答案我也不得而知,本文只是作出了自己的一个思考,如有错误请大家批评指出。
参考
https://segmentfault.com/a/1190000039370642