通俗的解釋下Vite能用來干嘛?是怎么回事?
本文轉(zhuǎn)載自微信公眾號(hào)「秋風(fēng)的筆記」,作者藍(lán)色的秋風(fēng)。轉(zhuǎn)載本文請(qǐng)聯(lián)系秋風(fēng)的筆記公眾號(hào)。
最近在B乎看到了這么一個(gè)問題,能不能通俗地講 Vite 到底是用來干嘛的,一開始覺得這個(gè)問題沒什么意思,因?yàn)?Vite 這個(gè)話題有太多的人講了。
但是我看了看其他的回答,大部分都會(huì)從 Vite 的特性,ES Modules 去講整件事情,然后還是會(huì)時(shí)不時(shí)要和 webpack 去做比較。
而且我又仔細(xì)看了看題主的疑問。
我也陷入了深深的思考,到底是大家學(xué)習(xí)一些新知識(shí)的時(shí)候急迫了呢?還是說前端常常容易把一些簡(jiǎn)單的東西復(fù)雜化,容易形成套娃式的知識(shí)?又或者是知識(shí)太零散了,分不清到底是哪些是有關(guān)系的?
探索Vite
我打開了 Vite 的官網(wǎng),他的標(biāo)語是 「 下一代前端開發(fā)與構(gòu)建工具」,因?yàn)橐话銟?biāo)語需要言簡(jiǎn)意賅地表達(dá)出它的意思,所以會(huì)用最精簡(jiǎn)的去概括(也是為了宣傳)。我認(rèn)為確實(shí)也沒有啥毛病,但是對(duì)于一些新手而且,確實(shí)這句話不知所云,官網(wǎng)也沒有足夠清楚的圖,如果是一些不了解的人,確實(shí)也容易迷失。
那么這句話到底要表達(dá)出什么意思呢?我用通俗的話表述了一遍(可能不一定準(zhǔn)確,僅代表個(gè)人的理解):
目前大部分瀏覽器已經(jīng)支持了ESM(ES Modules)模塊的方式了,因此我寫了一個(gè)轉(zhuǎn)化工具,可以讓一些瀏覽器不支持的格式(.vue/.svelte/.ts)以及不支持的語法(最新的es語法/特性)讓瀏覽器支持,它將會(huì)成為趨勢(shì)。
很多人會(huì)從 ES Modules 、Dev Server 、Vue 集成度高、速度快啊各種方向來講解它,更像是在說它的優(yōu)點(diǎn),我覺得有點(diǎn)神話它了,所以才讓它變得那么神秘(迷惑)?
而在我看來 Vite 就是一個(gè)轉(zhuǎn)化器,而 Webpack 就是 模塊器 + 轉(zhuǎn)化器。
轉(zhuǎn)化器的用途就是,將一些瀏覽器無法解析的文件,轉(zhuǎn)化成可以被瀏覽器解析的 js 文件,Vite 做的核心就是這個(gè)。什么用 Vite 快啊,都是瀏覽器自己的功勞,瀏覽器統(tǒng)一了模塊化方案,Vite 只是吃了一波瀏覽器的性能紅利而已。
用一張圖來描述就是(這里我盜用了 Webpack 的官方圖,改了改)
Vite 就是把所有的資源都轉(zhuǎn)化成了 js 的形式去引入,因?yàn)闉g覽器只支持 js 文件的 ES Modles 方式,畢竟 ES Modules 屬于 ECMAscript 規(guī)范,當(dāng)然只能是適用于 js 了。而整個(gè)模塊化過程都是瀏覽器的功勞。
這里再來看看 Webpack 的整體流程圖,如果你對(duì) Webpack 了解,可能能加深印象,但是不了解 webpack 也沒關(guān)系。
由于 Webpack 要自己的模塊化方式,因此需要將所有的資源都打包成一個(gè) js,這個(gè)圖很形象地解釋了 Webpack 的作用。(對(duì)于不了解 Webpack 的也沒有關(guān)系,知道 Vite 的圖就夠了。)
我舉一個(gè)例子,可能能讓你更加清楚地去理解 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 裝一個(gè) Live Server , 然后啟動(dòng)這個(gè) html, 然后我們隨便改點(diǎn)東西,可以看到,更新速度的非???。
也許你會(huì)說,我文件數(shù)量太少了,沒事,我們這次來整活20個(gè)文件。
- 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 本質(zhì)是撥除了 webpack 模塊化功能后的一個(gè)轉(zhuǎn)化器。
但是盡管瀏覽器解決了模塊化的依賴,依舊是有兩個(gè)問題:
但是沒辦法支持一些樣式/文件(css/ttf/jpg...)資源的 import 語法
無法支持.ts/.vue/.svelte 等模板語法(或者高級(jí)特性)的直接引用
所以,才會(huì)有我們看到 Vite 仿佛又做了很多事情,因?yàn)?Vite 能夠去加載 .ts/.vue/.svelte 等文件, 它整合了很多插件去做這些轉(zhuǎn)化工作,將所有的資源都轉(zhuǎn)化成瀏覽器可識(shí)別的 js 的方式去導(dǎo)入,將 css 文件經(jīng)過包裝,轉(zhuǎn)化為一個(gè) js 文件等等。
剩余的就是原文件中的內(nèi)容替換,因?yàn)轭愃朴谙竦谌桨械馁Y源沒辦法直接引入,需要做一層替換,例如一下代碼就被轉(zhuǎn)化成了這樣。
在編譯的時(shí)候需要去替換我們實(shí)際寫代碼的地址,去讓瀏覽器加載,然后為了不讓瀏覽器加載文件太多,還要將第三方包導(dǎo)成一個(gè)模塊,然后還有熱更新功能(這部分功能稍微復(fù)雜一些)。并且為了能夠在生產(chǎn)環(huán)境打包(Tree shaking / 壓縮啊,等等之前常規(guī)的優(yōu)化),使用了 Rollup ,不僅提供了 ESM 的打包方式,以及你需要的其他模塊化方式(umd/amd/cmd/iife)。
因此核心是簡(jiǎn)單的,但是相關(guān)的生態(tài)想要好用,卻是要花大量的精力,Vite 團(tuán)隊(duì)也是花了大力氣去解決了周邊的生態(tài)問題,各種插件的適配啊等等。
首創(chuàng)的ESM
而首次提出利用瀏覽器原生ESM能力的工具并非是Vite,而是一個(gè)叫做Snowpack的工具(可以看我這篇文章 《模塊化系列》snowpack,提高10倍打包速度。)。前身是@pika/web,從1.x版本開始更名為Snowpack。
Snowpack在其官網(wǎng)是這樣進(jìn)行自我介紹的:“Snowpack是一種閃電般快速的前端構(gòu)建工具,專為現(xiàn)代Web設(shè)計(jì)。它是開發(fā)工作流程較重,較復(fù)雜的打包工具(如Webpack或Parcel)的替代方案。Snowpack利用JavaScript的模塊化方式(稱為ESM)來避免不必要的工作并保持流暢的開發(fā)體驗(yàn)”。
為此,Pika團(tuán)隊(duì)開發(fā)并維護(hù)了兩個(gè)技術(shù)體系:構(gòu)建相關(guān)的Snowpack和造福大眾的Skypack。其中 skypack 上還有很多特殊處理過的 ES Modules 形式的包(例如 React 等)直接用來調(diào)用,由于那些包原先是不支持 ES Modules 形式,他們單獨(dú)維護(hù)了 ES Modules 版本。
看完了 ES Modules 的現(xiàn)狀以及 Vite 的本質(zhì),我們就再來把模塊化來回顧一下,這樣整個(gè)時(shí)間線就完整了以及我們的開發(fā)方式變化到現(xiàn)在,Web 真的做出了巨大的努力。
模塊化簡(jiǎn)史
把時(shí)間回退到2006年,這個(gè)時(shí)候 「jQuery」 剛呱呱落地,那個(gè)時(shí)候雖然沒有模塊化,使用 jQuery 相比傳統(tǒng)那樣寫已經(jīng)提高極大的速度,當(dāng)然雖然已經(jīng)很方便了,單還是阻擋不了愛研究的程序員們。
在2009年的時(shí)候 「CommonJS」 誕生了,但是 「CommonJS」 由于有兩個(gè)重要問題沒能得到解決,所以遲遲不能推廣到瀏覽器上。(1.由于外層沒有 function 包裹,被導(dǎo)出的變量會(huì)暴露在全局中。2.在服務(wù)端 require 一個(gè)模塊,只會(huì)有磁盤 I/O,所以同步加載機(jī)制沒什么問題;但如果是瀏覽器加載,一是會(huì)產(chǎn)生開銷更大的網(wǎng)絡(luò) I/O,二是天然異步,就會(huì)產(chǎn)生時(shí)序上的錯(cuò)誤。)中間百家爭(zhēng)鳴(「AMD、CMD、UMD」)一直到2016年5月,經(jīng)過了兩年的討論,ECMAScript 6.0 終于正式通過決議,成為了國(guó)際標(biāo)準(zhǔn)。在這一標(biāo)準(zhǔn)中,首次引入了 import 和 export 兩個(gè) JavaScript 關(guān)鍵字,并提供了被稱為 「ES Module」 的模塊化方案。在 JavaScript 出生的第 21 個(gè)年頭里,JavaScript 終于迎來了屬于自己的模塊化方案。而在這期間想要使用模塊化,只能通過打包工具來解決。
有了標(biāo)準(zhǔn)之后,也不是能立馬讓所有設(shè)備都支持 「ES Module」 因?yàn)闉g覽器的推進(jìn)是一個(gè)漫長(zhǎng)的過程,不像服務(wù)端,如果做一個(gè)升級(jí),只需要對(duì)服務(wù)端升級(jí),而瀏覽器的升級(jí)伴隨著電腦/手機(jī)等一系列的因素,因素非常不可控,因?yàn)橛脩艨偸强梢杂卸喾N多樣的選擇,「ES Modules(ESM)」 是 JavaScript 官方的標(biāo)準(zhǔn)化模塊系統(tǒng),而它這一走,卻在標(biāo)準(zhǔn)化的道路上已經(jīng)花費(fèi)了近 10 年的時(shí)間。在2018 年 5 月 Firefox 60 發(fā)布之后,所有的主流瀏覽器就都支持 「ESM」 了。直到現(xiàn)在,「ES Module」 還并不能真正地用在生產(chǎn)環(huán)境使用,還是需要轉(zhuǎn)化成以舊的方式(非ESM方式)。
寫在最后
當(dāng)回答完這個(gè)問題的時(shí)候,不禁會(huì)想,前端的發(fā)展過程中卻是會(huì)有一些新瓶裝舊酒的東西,然后神話它,然后讓小白覺得它很高大上,讓人懼怕,然后大佬就會(huì)覺得這個(gè)東西很簡(jiǎn)單,也不愿意去拆解它,是否我們需要轉(zhuǎn)化一些思考,當(dāng)我們講一個(gè)東西的時(shí)候,剝離那些高大上的詞匯,做一些更加親民的解釋?當(dāng)然我不否則這些新的工具帶來的便利以及背后的付出,但是親民是否也是一種方式,或許會(huì)變得更加美好?答案我也不得而知,本文只是作出了自己的一個(gè)思考,如有錯(cuò)誤請(qǐng)大家批評(píng)指出。
參考
https://segmentfault.com/a/1190000039370642