vite 為什么快


一、ES module 減少服務啟動時間

import { foo } from './other-module'

由於大多數現代瀏覽器都支持上面的 ES module 語法,所以在開發階段,我們就不必對其進行打包,這節省了大量的服務啟動時間。另外,vite 按需加載當前頁面所需文件,一個文件一個http請求,進一步減少啟動時間。

二、緩存減少頁面更新時間

每個文件通過 http 頭緩存在瀏覽器端,當編輯完一個文件,只需讓此文件緩存失效。當基於 ES module 進行熱更新時,僅需更新失效的模塊,這使得更新時間不隨包的增大而增大。

vite 怎么做到的?

當我們執行yarn dev時,vite-cli 會在本地啟動一個 koa 服務:

export function createServer(config) {
  ...
  const app = new Koa()
  const server = resolveServer(config, app.callback())
  ...
  // 加載插件
  ...

  const listen = server.listen.bind(server)
  server.listen = (async (port, ...args) => {
    if (optimizeDeps.auto !== false) {
      await require('../optimizer').optimizeDeps(config)
    }
    return listen(port, ...args)
  })
  ...

  return server;
}

監聽端口,執行其他服務之前,會執行optimizeDeps方法,即優化依賴。vite 文檔將這部分優化叫做依賴預打包Dependency Pre-Bundling,這么做的理由有兩個:一是將非 ES module轉化為可被瀏覽器導入的 ESM;二是將 ESM 依賴的多個內部模塊轉化為一個模塊,以減少瀏覽器請求從而提升頁面加載速度。

按需加載

上面我們提到,vite通過按需加載減少等待時間,這是如何做到的?所謂一生二,二生三,三生萬物。一切的根源就是服務啟動后,我們訪問的初始地址http://localhost:3000/。首頁,瀏覽器根據這個 url 發出第一個請求,通過serverPluginServeStatic.ts插件獲取到位於項目根目錄的index.html文件,再通過serverPluginHtml.ts向其插入<script type="module">import "/vite/client"</script>,最終我們看到第一個請求返回的內容如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <script type="module">import "/vite/client"</script>

  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

我們先看<script type="module" src="/src/main.js"></script>,瀏覽器向我們的本地服務器請求這個文件,koa 插件serverPluginModuleRewrite.ts會重寫main.js源文件的 import 的解析方式:

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

上面的內容經過插件處理后,將以下內容返回給瀏覽器:

import { createApp } from '/@modules/vue.js'
import App from '/src/App.vue'
import '/src/index.css?import'

這里文檔npm-dependency-resolving也有部分提及。瀏覽器解析此文件,依次發送三個請求:

當瀏覽器發送第二個請求,koa 插件serverPluginVue.ts會編譯單文件組件 App.vue,並打上 etag(用於緩存)后再返回給瀏覽器(A流程)。瀏覽器再解析此文件並更新頁面,重復這個過程(發送請求、服務端響應、瀏覽器解析)直到不再發送請求。

組件熱更新

vue 組件熱更新,由serverPluginVue.tsserverPluginHmr.tsclient.ts三個部分共同完成。其中client.ts會被發送到瀏覽器端,位於服務端的 serverPluginHmr.ts 通過 websocket 和 client.ts 進行通信。

我們以vite-cli默認模板項目舉例,假設更改了App.vue文件內容,此時:

  • 1)serverPluginVue.ts 使用 serverPluginHmr.ts 提供的 send 方法向 client.ts 所在的瀏覽器端發送數據,即數據通過 websockt 從服務端推送到瀏覽器,數據內容如下:

    {
      "type": "vue-reload",
      "path": "/src/App.vue",
      "changeSrcPath": "/src/App.vue",
      "timestamp": 1609815863830
    }
    
  • 2) client.ts 收到數據后,執行import('/src/App.vue?t=1609815863830')

  • 3)接着瀏覽器發起請求:http://localhost:3000/src/App.vue?t=1609815863830,時間戳的作用是使url緩存失效

  • 4)最后,執行重復上面的A流程。
    經過上述步驟,頁面得以更新。當然,更新的類型還有full-reloadjs-update以及樣式更新等,但總體流程都框定在以上步驟,這里就不展開了。

p.s.以上內容搭配vite源碼再看更易理解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM