下一代前端構建工具Vite

1、背景

Vue做者尤雨溪在今年4月提出了一個由Vue3搭載的前端開發工具Vite。Vite主要提供了前端開發服務器的功能以及生產環境打包的功能,而其主要突破則是在前端開發服務器這一方面,提供了一種基於ES Module的快速的本地開發服務器。html

2、Vite簡介

2.1 什麼是Vite

在本文編輯時,Vite版本仍處於1.0.0-rc.9,還沒有正式發佈,而且Vite目前主要支持Vue3項目,尚不識別Vue2語法。下面是引用尤雨溪在微博上對Vite的介紹。前端

Vite,一個基於瀏覽器原生 ES imports 的開發服務器。利用瀏覽器去解析 imports,在服務器端按需編譯返回,徹底跳過了打包這個概念,服務器隨起隨用。同時不只有 Vue 文件支持,還搞定了熱更新,並且熱更新的速度不會隨着模塊增多而變慢。針對生產環境則能夠把同一份代碼用 rollup 打。雖然如今還比較粗糙,但這個方向我以爲是有潛力的,作得好能夠完全解決改一行代碼等半天熱更新的問題。vue

2.1 如何使用Vite

使用下面的命令便可快速搭建一個使用Vite做爲開發服務器的項目,使用十分方便,相似於Vue-cli。node

npm init vite-app <project-name> 
cd <project-name> 
npm install 
npm run dev
複製代碼

第一行命令的目的就是從npm倉庫拉取 create-vite-app這個包,而後全局安裝,最後使用它建立基於Vite的模板項目。webpack

3、Vite特性

官方文檔介紹,Vite主要有下面三個特性web

  1. 快速的冷啓動
  2. 及時的熱模塊更新
  3. 真正的按需加載 咱們對比 create-vite-appVue-cli建立的項目,來看一下Vite快在哪裏,下圖分別是使用Vite和 Vue-cli(webpack)啓動本地開發服務器的過程。

圖片

圖片

能夠看出Vite相對於Vue-cli(webpack)在本地服務器啓動時省略了打包步驟,於是作到了冷啓動秒開的效果,而且這個速度提高會隨着項目模塊增多而越發明顯。下面咱們就看一下Vite是如何實現其快速的特性。npm

4、Vite原理

Vite 是基於瀏覽器原生 ES imports 的開發服務器。於是咱們首先須要瞭解瀏覽器是如何支持ES Module的瀏覽器

4.1 ES Module

首先,咱們來看一下在瀏覽器如何使用ES Module。打開瀏覽器調試面板, 清空network,使用下面代碼在頁面動態插入一段 <script>代碼緩存

const script = document.createElement('script')
script.setAttribute('type', 'module');
script.innerHTML = 'import {test} from "./test.js"'
document.body.append(script)
複製代碼

圖片

在運行上述代碼後,瀏覽器向當前服務器目錄發送了 http://km.oa.com/test.js的請求。咱們都知道本地項目中咱們使用ES import會從文件系統讀取相應路徑的模塊,瀏覽器則是將模塊路徑轉換爲Url。服務器

圖片

瀏覽器解析ES module的過程如上圖所示。

  1. 識別帶有熟悉 type="module"<script>標籤
  2. 獲取並解析該標籤內的js內容。
  3. 識別 import語法,生成請求url,向服務器請求該地址的模塊

能夠說瀏覽器對於ES Module的支持實現了真正的按需加載,省略了前端打包的過程,對於減小首屏加載時間是有極大幫助的。可是咱們要在生產環境中使用它必須知道瀏覽器的支持度到底如何。

下面是一張caniuse中說明的瀏覽器對於 ES Module的靜態import語法的支持狀況。能夠看出除了IE外的主流瀏覽器基本上都支持了 ES Module的import語法。

圖片

那麼,對於不支持ES Module的瀏覽器,難道咱們就讓項目跑不起來嗎?

固然不是,在 script 標籤中使用 nomodule 屬性,能夠確保向後兼容。

圖片

像上圖同樣提供ES Module方式和非ES Module方式的代碼,對於支持ES Module的瀏覽器,其會忽視 nomodule類型的script,而對於沒法識別 Es Module的瀏覽器則會直接使用 nomodule的script代碼。所以咱們只需提供一份打包好的代碼,放在 nomodule標籤內就能夠實現向後兼容

值得注意的是,瀏覽器只能解析以’/’, ‘./’, 或 '…/'開頭的模塊路徑,對於像引用nodemodules中的模塊,好比像下面引用Vue的方式,瀏覽器沒法識別,會報錯。所以對於nodemodules的引用,須要另外處理,而Vite也給出瞭解決方案。

import Vue from 'vue'
複製代碼

4.2 Vite開發服務器如何使用ES Module

咱們啓動Vite本地開發服務器,用瀏覽器打開入口頁面,觀察瀏覽器的NetWork面板.以下圖所示,

  1. 瀏覽器加載了入口html,解析發現 main.js
  2. 發現 main.js中包含ES Module, 解析 import語法,發現有三個 import
  3. 根據 import,發出所依賴的模塊的Http請求
  4. 依次類推,邊解析邊請求。

圖片

圖片

對比源碼和網絡請求,咱們會發現網絡請求數明顯要多於源碼中 import的個數。多出的網絡請求主要是兩類

  1. .vue文件相關 對於一個Vue組件SFC(Single File Components),其主要包含三類代碼,模板、script、樣式。因爲瀏覽器是沒法識別vue文件的,一個vue文件會被拆分爲三個請求 .vue, .vue?type=template, .vue?type=style,這些都須要藉助Vite的本地服務器實現,具體實現方法下文會詳細闡述。
  2. 熱更新相關 咱們看到請求中有個 clent.js,還有websocket請求,這些都是爲熱更新服務的,而在代碼中插入創建websocket鏈接須要的 clent.js邏輯也是由Vite開發服務器實現的。

4.3 Vite 開發服務器模塊處理

對於瀏覽器不識別的node_module引用如何處理?對於 .vue文件如何處理都是由Vite開發服務器實現的。首先咱們看一下Vite開發服務器架構圖

圖片

Vite開發服務器是基於Koa框架的,利用Koa中間件實現模塊解析以及熱更新的主要功能。這一節咱們主要看一下Vite是如何處理模塊的。

nodemodules 模塊處理過程 對nodemodules的處理主要由中間件 serverPluginModuleRewrite完成,其主要過程以下

  1. 在 koa 中間件裏獲取請求 body
  2. 經過 es-module-lexer 解析資源 ast 拿到 import 的內容
  3. 判斷 import 的資源是不是絕對路徑,絕對視爲 npm 模塊
  4. 返回處理後的資源路徑:「vue」 => 「/@modules/vue」

vue文件處理過程 對vue組件的處理由 serverPluginVue來實現,其處理流程以下

  1. 分析請求路徑,是否包含查詢字段type
  2. 不包含type的請求視爲script內容請求, 返回類型爲 js
  3. 對於type爲template請求則返回.vue文件的 template內容的渲染函數,返回類型爲 js
  4. 對於type爲style的請求則返回.vue文件中 style標籤內樣式的動態插入函數

4.4 Vite 熱更新

圖片

如上圖所示,Vite熱更新也是基於Websocket。在Vite服務器啓動時,Vite利用中間件 serverPluginHtml在html中插入 client.js. 這個js文件主要用於在創建瀏覽器和Vite服務器之間的Websocket通訊。熱更新的步驟以下

  1. Vite服務器監聽本地文件更新
  2. 對比緩存中的文件和變更後的文件,組織更新內容
  3. 服務器經過PostMessage向瀏覽器通知更新消息,更新消息包含跟新類型,更新後模塊的最新地址,時間戳
  4. 瀏覽器請求熱更新文件
  5. 根據跟新類型處理返回的文件

clientjs監聽的更新消息類型

  • connected: WebSocket 鏈接成功
  • vue-reload: Vue 組件從新加載(當你修改了 script 裏的內容時)
  • vue-rerender: Vue 組件從新渲染(當你修改了 template 裏的內容時)
  • style-update: 樣式更新
  • style-remove: 樣式移除
  • js-update: js 文件更新
  • full-reload: fallback 機制,網頁重刷新

總結

Vite 提供了一個更快的開發環境服務器, 其實現原理基於ES模塊,經過開發環境去打包將構建時間從 O(n) 減小到 O(1), 其搭載Vue3發佈,藉助Vue生態,在將來有更普遍的使用場景。

原做者:李璐

未經贊成,禁止轉載!

相關文章
相關標籤/搜索