<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          下一代前端開發(fā)利器——Vite(原理源碼解析)

          共 9395字,需瀏覽 19分鐘

           ·

          2022-03-09 12:49

          術(shù)????

          前言

          Hi,大家好!

          前段時間用Vue3搭建項目時看到同時推出的Vite,只當(dāng)它是一個新打包工具或者vue-cli的升級版,仍然選擇了用Webpack構(gòu)建項目。最近看了尤雨溪在VueConf上的演講視頻:《Vue3 生態(tài)進展和計劃》[1],感覺它確實解決了現(xiàn)階段前端工程化的一些痛點,也能體會到尤雨溪對Vite的重視和大力推廣的決心,再加上Vue本身的龐大用戶基數(shù),Vite確實有可能成為下一代前端構(gòu)建工具的突破口。

          本文將討論下Vite出現(xiàn)的背景,解決的痛點,核心功能的實現(xiàn),存在的意義和預(yù)期的未來。Vite本身并不復(fù)雜。中文官方文檔非常清晰簡潔,建議大家使用前仔細(xì)讀下文檔。


          大綱

          • 背景
          • 什么是Vite?
          • 基本用法
          • 實現(xiàn)原理
          • 源碼分析
          • 優(yōu)勢與不足
          • 與傳統(tǒng)構(gòu)建工具對比
          • 兼容性
          • 未來

          背景

          這里的背景介紹會從與Vite緊密相關(guān)的兩個概念的發(fā)展史說起,一個是JavaScript的模塊化標(biāo)準(zhǔn),另一個是前端構(gòu)建工具。

          共存的模塊化標(biāo)準(zhǔn)

          為什么JavaScript會有多種共存的模塊化標(biāo)準(zhǔn)?因為js在設(shè)計之初并沒有模塊化的概念,隨著前端業(yè)務(wù)復(fù)雜度不斷提高,模塊化越來越受到開發(fā)者的重視,社區(qū)開始涌現(xiàn)多種模塊化解決方案,它們相互借鑒,也爭議不斷,形成多個派系,從CommonJS開始,到ES6正式推出ES Modules規(guī)范結(jié)束,所有爭論,終成歷史,ES Modules也成為前端重要的基礎(chǔ)設(shè)施。

          • CommonJS:現(xiàn)主要用于Node.js([email protected]開始支持直接使用ES Module)
          • AMDrequire.js依賴前置,市場存量不建議使用
          • CMDsea.js就近執(zhí)行,市場存量不建議使用
          • ES Module:ES語言規(guī)范,標(biāo)準(zhǔn),趨勢,未來

          對模塊化發(fā)展史感興趣的可以看下《前端模塊化開發(fā)那點歷史》@玉伯[2],而Vite的核心正是依靠瀏覽器對ES Module規(guī)范的實現(xiàn)。

          發(fā)展中的構(gòu)建工具

          近些年前端工程化發(fā)展迅速,各種構(gòu)建工具層出不窮,目前Webpack仍然占據(jù)統(tǒng)治地位,npm 每周下載量達到兩千多萬次。下面是我按 npm 發(fā)版時間線列出的開發(fā)者比較熟知的一些構(gòu)建工具。

          當(dāng)前工程化痛點

          現(xiàn)在常用的構(gòu)建工具如Webpack,主要是通過抓取-編譯-構(gòu)建整個應(yīng)用的代碼(也就是常說的打包過程),生成一份編譯、優(yōu)化后能良好兼容各個瀏覽器的的生產(chǎn)環(huán)境代碼。在開發(fā)環(huán)境流程也基本相同,需要先將整個應(yīng)用構(gòu)建打包后,再把打包后的代碼交給dev server(開發(fā)服務(wù)器)。

          Webpack等構(gòu)建工具的誕生給前端開發(fā)帶來了極大的便利,但隨著前端業(yè)務(wù)的復(fù)雜化,js代碼量呈指數(shù)增長,打包構(gòu)建時間越來越久,dev server(開發(fā)服務(wù)器)性能遇到瓶頸:

          • 緩慢的服務(wù)啟動:大型項目中dev server啟動時間達到幾十秒甚至幾分鐘。

          • 緩慢的HMR熱更新:即使采用了 HMR 模式,其熱更新速度也會隨著應(yīng)用規(guī)模的增長而顯著下降,已達到性能瓶頸,無多少優(yōu)化空間。

          緩慢的開發(fā)環(huán)境,大大降低了開發(fā)者的幸福感,在以上背景下Vite應(yīng)運而生。


          什么是Vite?

          基于esbuild與Rollup,依靠瀏覽器自身ESM編譯功能, 實現(xiàn)極致開發(fā)體驗的新一代構(gòu)建工具!

          概念

          先介紹以下文中會經(jīng)常提到的一些基礎(chǔ)概念:

          • 依賴:指開發(fā)不會變動的部分(npm包、UI組件庫),esbuild進行預(yù)構(gòu)建。
          • 源碼:瀏覽器不能直接執(zhí)行的非js代碼(.jsx、.css、.vue等),vite只在瀏覽器請求相關(guān)源碼的時候進行轉(zhuǎn)換,以提供ESM源碼。

          開發(fā)環(huán)境

          • 利用瀏覽器原生的ES Module編譯能力,省略費時的編譯環(huán)節(jié),直給瀏覽器開發(fā)環(huán)境源碼,dev server只提供輕量服務(wù)。
          • 瀏覽器執(zhí)行ESM的import時,會向dev server發(fā)起該模塊的ajax請求,服務(wù)器對源碼做簡單處理后返回給瀏覽器。
          • Vite中HMR是在原生 ESM 上執(zhí)行的。當(dāng)編輯一個文件時,Vite 只需要精確地使已編輯的模塊失活,使得無論應(yīng)用大小如何,HMR 始終能保持快速更新。
          • 使用esbuild處理項目依賴,esbuild使用go編寫,比一般node.js編寫的編譯器快幾個數(shù)量級。

          生產(chǎn)環(huán)境

          • 集成Rollup打包生產(chǎn)環(huán)境代碼,依賴其成熟穩(wěn)定的生態(tài)與更簡潔的插件機制。

          處理流程對比

          Webpack通過先將整個應(yīng)用打包,再將打包后代碼提供給dev server,開發(fā)者才能開始開發(fā)。

          Vite直接將源碼交給瀏覽器,實現(xiàn)dev server秒開,瀏覽器顯示頁面需要相關(guān)模塊時,再向dev server發(fā)起請求,服務(wù)器簡單處理后,將該模塊返回給瀏覽器,實現(xiàn)真正意義的按需加載。


          基本用法

          創(chuàng)建vite項目

          $?npm?create?vite@latest

          選取模板

          Vite內(nèi)置6種常用模板與對應(yīng)的TS版本,可滿足前端大部分開發(fā)場景,可以點擊下列表格中模板直接在?StackBlitz[3]?中在線試用,還有其他更多的社區(qū)維護模板[4]可以使用。

          JavaScriptTypeScript
          vanillavanilla-ts
          vuevue-ts
          reactreact-ts
          preactpreact-ts
          litlit-ts
          sveltesvelte-ts

          啟動

          {
          ??"scripts":?{
          ????"dev":?"vite",?//?啟動開發(fā)服務(wù)器,別名:`vite dev`,`vite serve`
          ????"build":?"vite?build",?//?為生產(chǎn)環(huán)境構(gòu)建產(chǎn)物
          ????"preview":?"vite?preview"?//?本地預(yù)覽生產(chǎn)構(gòu)建產(chǎn)物
          ??}
          }

          實現(xiàn)原理

          ESbuild 編譯

          esbuild使用go編寫,cpu密集下更具性能優(yōu)勢,編譯速度更快,以下摘自官網(wǎng)的構(gòu)建速度對比:
          瀏覽器:“開始了嗎?”
          服務(wù)器:“已經(jīng)結(jié)束了。”
          開發(fā)者:“好快,好喜歡!!”

          image.png

          依賴預(yù)構(gòu)建

          • 模塊化兼容:如開頭背景所寫,現(xiàn)仍共存多種模塊化標(biāo)準(zhǔn)代碼,Vite在預(yù)構(gòu)建階段將依賴中各種其他模塊化規(guī)范(CommonJS、UMD)轉(zhuǎn)換 成ESM,以提供給瀏覽器。
          • 性能優(yōu)化:npm包中大量的ESM代碼,大量的import請求,會造成網(wǎng)絡(luò)擁塞。Vite使用esbuild,將有大量內(nèi)部模塊的ESM關(guān)系轉(zhuǎn)換成單個模塊,以減少import模塊請求次數(shù)。

          按需加載

          • 服務(wù)器只在接受到import請求的時候,才會編譯對應(yīng)的文件,將ESM源碼返回給瀏覽器,實現(xiàn)真正的按需加載。

          緩存

          • HTTP緩存:充分利用http緩存做優(yōu)化,依賴(不會變動的代碼)部分用max-age,immutable強緩存,源碼部分用304協(xié)商緩存,提升頁面打開速度。
          • 文件系統(tǒng)緩存:Vite在預(yù)構(gòu)建階段,將構(gòu)建后的依賴緩存到node_modules/.vite,相關(guān)配置更改時,或手動控制時才會重新構(gòu)建,以提升預(yù)構(gòu)建速度。

          重寫模塊路徑

          瀏覽器import只能引入相對/絕對路徑,而開發(fā)代碼經(jīng)常使用npm包名直接引入node_module中的模塊,需要做路徑轉(zhuǎn)換后交給瀏覽器。

          • es-module-lexer掃描 import 語法
          • magic-string重寫模塊的引入路徑
          //?開發(fā)代碼
          import?{?createApp?}?from?'vue'

          //?轉(zhuǎn)換后
          import?{?createApp?}?from?'/node_modules/vue/dist/vue.js'

          源碼分析

          Webpack-dev-server類似Vite同樣使用WebSocket與客戶端建立連接,實現(xiàn)熱更新,源碼實現(xiàn)基本可分為兩部分,源碼位置在:

          • vite/packages/vite/src/clientclient(用于客戶端)
          • vite/packages/vite/src/nodeserver(用于開發(fā)服務(wù)器)

          client 代碼會在啟動服務(wù)時注入到客戶端,用于客戶端對于WebSocket消息的處理(如更新頁面某個模塊、刷新頁面);server 代碼是服務(wù)端邏輯,用于處理代碼的構(gòu)建與頁面模塊的請求。

          簡單看了下源碼([email protected]),核心功能主要是以下幾個方法(以下為源碼截取,部分邏輯做了刪減):

          1. 命令行啟動服務(wù)npm run dev后,源碼執(zhí)行cli.ts,調(diào)用createServer方法,創(chuàng)建http服務(wù),監(jiān)聽開發(fā)服務(wù)器端口。
          //?源碼位置?vite/packages/vite/src/node/cli.ts
          const?{?createServer?}?=?await?import('./server')
          try?{
          ????const?server?=?await?createServer({
          ????????root,
          ????????base:?options.base,
          ????????...
          ????})
          ????if?(!server.httpServer)?{
          ????????throw?new?Error('HTTP?server?not?available')
          ????}
          ????await?server.listen()
          }
          1. createServer方法的執(zhí)行做了很多工作,如整合配置項、創(chuàng)建http服務(wù)(早期通過koa創(chuàng)建)、創(chuàng)建WebSocket服務(wù)、創(chuàng)建源碼的文件監(jiān)聽、插件執(zhí)行、optimize優(yōu)化等。下面注釋中標(biāo)出。
          //?源碼位置?vite/packages/vite/src/node/server/index.ts
          export?async?function?createServer(
          ????inlineConfig:?InlineConfig?=?{}
          ):?Promise<ViteDevServer>?
          {
          ????//?Vite?配置整合
          ????const?config?=?await?resolveConfig(inlineConfig,?'serve',?'development')
          ????const?root?=?config.root
          ????const?serverConfig?=?config.server

          ????//?創(chuàng)建http服務(wù)
          ????const?httpServer?=?await?resolveHttpServer(serverConfig,?middlewares,?httpsOptions)

          ????//?創(chuàng)建ws服務(wù)
          ????const?ws?=?createWebSocketServer(httpServer,?config,?httpsOptions)

          ????//?創(chuàng)建watcher,設(shè)置代碼文件監(jiān)聽
          ????const?watcher?=?chokidar.watch(path.resolve(root),?{
          ????????ignored:?[
          ????????????'**/node_modules/**',
          ????????????'**/.git/**',
          ????????????...(Array.isArray(ignored)???ignored?:?[ignored])
          ????????],
          ????????...watchOptions
          ????})?as?FSWatcher

          ????//?創(chuàng)建server對象
          ????const?server:?ViteDevServer?=?{
          ????????config,
          ????????middlewares,
          ????????httpServer,
          ????????watcher,
          ????????ws,
          ????????moduleGraph,
          ????????listen,
          ????????...
          ????}

          ????//?文件監(jiān)聽變動,websocket向前端通信
          ????watcher.on('change',?async?(file)?=>?{
          ????????...
          ????????handleHMRUpdate()
          ????})

          ????//?非常多的?middleware
          ????middlewares.use(...)
          ????
          ????//?optimize
          ????const?runOptimize?=?async?()?=>?{...}

          ????return?server
          }
          1. 使用chokidar[5]監(jiān)聽文件變化,綁定監(jiān)聽事件。
          //?源碼位置?vite/packages/vite/src/node/server/index.ts
          ??const?watcher?=?chokidar.watch(path.resolve(root),?{
          ????ignored:?[
          ??????'**/node_modules/**',
          ??????'**/.git/**',
          ??????...(Array.isArray(ignored)???ignored?:?[ignored])
          ????],
          ????ignoreInitial:?true,
          ????ignorePermissionErrors:?true,
          ????disableGlobbing:?true,
          ????...watchOptions
          ??})?as?FSWatcher
          1. 通過ws[6]來創(chuàng)建WebSocket服務(wù),用于監(jiān)聽到文件變化時觸發(fā)熱更新,向客戶端發(fā)送消息。
          //?源碼位置?vite/packages/vite/src/node/server/ws.ts
          export?function?createWebSocketServer(...){
          ????let?wss:?WebSocket
          ????const?hmr?=?isObject(config.server.hmr)?&&?config.server.hmr
          ????const?wsServer?=?(hmr?&&?hmr.server)?||?server

          ????if?(wsServer)?{
          ????????wss?=?new?WebSocket({?noServer:?true?})
          ????????wsServer.on('upgrade',?(req,?socket,?head)?=>?{
          ????????????//?服務(wù)就緒
          ????????????if?(req.headers['sec-websocket-protocol']?===?HMR_HEADER)?{
          ????????????????wss.handleUpgrade(req,?socket?as?Socket,?head,?(ws)?=>?{
          ????????????????????wss.emit('connection',?ws,?req)
          ????????????????})
          ????????????}
          ????????})
          ????}?else?{
          ????????...
          ????}
          ????//?服務(wù)準(zhǔn)備就緒,就能在瀏覽器控制臺看到熟悉的打印?[vite]?connected.
          ????wss.on('connection',?(socket)?=>?{
          ????????socket.send(JSON.stringify({?type:?'connected'?}))
          ????????...
          ????})
          ????//?失敗
          ????wss.on('error',?(e:?Error?&?{?code:?string?})?=>?{
          ????????...
          ????})
          ????//?返回ws對象
          ????return?{
          ????????on:?wss.on.bind(wss),
          ????????off:?wss.off.bind(wss),
          ????????//?向客戶端發(fā)送信息
          ????????//?多個客戶端同時觸發(fā)
          ????????send(payload:?HMRPayload)?{
          ????????????const?stringified?=?JSON.stringify(payload)
          ????????????wss.clients.forEach((client)?=>?{
          ????????????????//?readyState?1?means?the?connection?is?open
          ????????????????client.send(stringified)
          ????????????})
          ????????}
          ????}
          }
          1. 在服務(wù)啟動時會向瀏覽器注入代碼,用于處理客戶端接收到的WebSocket消息,如重新發(fā)起模塊請求、刷新頁面。
          //源碼位置?vite/packages/vite/src/client/client.ts
          async?function?handleMessage(payload:?HMRPayload)?{
          ??switch?(payload.type)?{
          ????case?'connected':
          ??????console.log(`[vite]?connected.`)
          ??????break
          ????case?'update':
          ??????notifyListeners('vite:beforeUpdate',?payload)
          ??????...
          ??????break
          ????case?'custom':?{
          ??????notifyListeners(payload.event?as?CustomEventName,?payload.data)
          ??????...
          ??????break
          ????}
          ????case?'full-reload':
          ??????notifyListeners('vite:beforeFullReload',?payload)
          ??????...
          ??????break
          ????case?'prune':
          ??????notifyListeners('vite:beforePrune',?payload)
          ??????...
          ??????break
          ????case?'error':?{
          ??????notifyListeners('vite:error',?payload)
          ??????...
          ??????break
          ????}
          ????default:?{
          ??????const?check:?never?=?payload
          ??????return?check
          ????}
          ??}
          }

          H5-Dooring

          優(yōu)勢

          • 快!快!非常快!!
          • 高度集成,開箱即用。
          • 基于ESM急速熱更新,無需打包編譯。
          • 基于esbuild的依賴預(yù)處理,比Webpack等node編寫的編譯器快幾個數(shù)量級。
          • 兼容Rollup龐大的插件機制,插件開發(fā)更簡潔。
          • 不與Vue綁定,支持React等其他框架,獨立的構(gòu)建工具。
          • 內(nèi)置SSR支持。
          • 天然支持TS。

          不足

          • Vue仍為第一優(yōu)先支持,量身定做的編譯插件,對React的支持不如Vue強大。
          • 雖然已經(jīng)推出2.0正式版,已經(jīng)可以用于正式線上生產(chǎn),但目前市場上實踐少。
          • 生產(chǎn)環(huán)境集成Rollup打包,與開發(fā)環(huán)境最終執(zhí)行的代碼不一致。

          與 webpack 對比

          由于Vite主打的是開發(fā)環(huán)境的極致體驗,生產(chǎn)環(huán)境集成Rollup,這里的對比主要是Webpack-dev-serverVite-dev-server的對比:

          • 到目前很長時間以來Webpack在前端工程領(lǐng)域占統(tǒng)治地位,Vite推出以來備受關(guān)注,社區(qū)活躍,GitHub star 數(shù)量激增,目前達到37.4K
          • Webpack配置豐富使用極為靈活但上手成本高,Vite開箱即用配置高度集成
          • Webpack啟動服務(wù)需打包構(gòu)建,速度慢,Vite免編譯可秒開
          • Webpack熱更新需打包構(gòu)建,速度慢,Vite毫秒響應(yīng)
          • Webpack成熟穩(wěn)定、資源豐富、大量實踐案例,Vite實踐較少
          • Vite使用esbuild編譯,構(gòu)建速度比webpack快幾個數(shù)量級

          兼容性

          • 默認(rèn)目標(biāo)瀏覽器是在script標(biāo)簽上支持原生 ESM 和 原生 ESM 動態(tài)導(dǎo)入
          • 可使用官方插件@vitejs/plugin-legacy,轉(zhuǎn)義成傳統(tǒng)版本和相對應(yīng)的polyfill

          未來探索

          • 傳統(tǒng)構(gòu)建工具性能已到瓶頸,主打開發(fā)體驗的Vite,可能會受到歡迎。
          • 主流瀏覽器基本支持ESM,ESM將成為主流。
          • ViteVue3.0代替vue-cli,作為官方腳手架,會大大提高使用量。
          • Vite2.0推出后,已可以在實際項目中使用Vite
          • 如果覺得直接使用Vite太冒險,又確實有dev server速度慢的問題需要解決,可以嘗試用Vite單獨搭建一套dev server

          相關(guān)資源

          官方插件

          除了支持現(xiàn)有的Rollup插件系統(tǒng)外,官方提供了四個最關(guān)鍵的插件

          • @vitejs/plugin-vue提供 Vue3 單文件組件支持
          • @vitejs/plugin-vue-jsx?提供 Vue3 JSX 支持(專用的 Babel 轉(zhuǎn)換插件)
          • @vitejs/plugin-react提供完整的 React 支持
          • @vitejs/plugin-legacy為打包后的文件提供傳統(tǒng)瀏覽器兼容性支持

          UI組件庫

          • Element UI[7]:支持 vite 引入

          相關(guān)鏈接

          • Vite官網(wǎng)[8]
          • Vue3 生態(tài)進展和計劃-尤雨溪[9]
          • Vite源碼解析[10]
          • Develop with Vite | Vite快速入門 - Anthony Fu ? Vue北京聚會 Day 13[11]

          參考資料

          [1]

          《Vue3 生態(tài)進展和計劃》:https://www.yuque.com/vueconf/mkwv0c/xqyxix

          [2]

          《前端模塊化開發(fā)那點歷史》:https://github.com/seajs/seajs/issues/588

          [3]

          StackBlitz:https://vite.new/

          [4]

          社區(qū)維護模板:https://github.com/vitejs/awesome-vite#templates

          [5]

          chokidar:https://www.npmjs.com/package/chokidar

          [6]

          ws:https://www.npmjs.com/package/ws

          [7]

          Element UI:https://element-plus.gitee.io/zh-CN/guide/quickstart.html#%E6%8C%89%E9%9C%80%E5%AF%BC%E5%85%A5

          [8]

          Vite官網(wǎng):https://cn.vitejs.dev/

          [9]

          Vue3 生態(tài)進展和計劃-尤雨溪:https://www.yuque.com/vueconf/mkwv0c/xqyxix

          [10]

          Vite源碼解析:http://vite.ssr-fc.com/

          [11]

          Develop with Vite | Vite快速入門 - Anthony Fu ? Vue北京聚會 Day 13:https://www.youtube.com/watch?v=xx8gEHet6n8

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区三区视频 | 精品第一页 | 苍井空和黑人最猛一次 | 伊人电影成人网 | 波多野结衣av中文字幕 |