本文基於 vite@0.7.0 編寫,可能與目前代碼不符,若有疑問歡迎郵件或評論溝通
vite 是一個基於 Vue3 單文件組件的非打包開發服務器css
vite 在開發的時候沒有打包的過程,ES 模塊源碼直接傳輸給瀏覽器,瀏覽器使用自帶的 <script module>
進行解析支持,經過 HTTP 請求進行每次 import,開發服務器攔截請求和對須要轉換的代碼進行轉換。html
例如:*.vue
文件會在發回瀏覽器以前進行編譯vue
這樣操做有許多優點:node
導入本地 ES 模塊可能會引起深層的導入鏈路,整個頁面從新加載會比依賴打包的開發服務器略慢。然而這是一個本地開發服務器,這部分增長的時間和實際編譯的時間相比應該很是小(編譯的文件會被緩存在內存中)webpack
vite 的編譯本質上仍是的 Node.js 中進行,從技術上講它能夠支持打包工具能支持的各類代碼轉換,沒有什麼能夠阻止你將代碼包用於生產,實際上,vite 提供了vite build
的腳本用於這個操做,所以不會在生產環境中遭遇到網絡流爆炸的問題git
當前 vite 尚處於實驗性階段,不適合用於生產環境,但但願有一天能作到這個目標github
本地 ES 模塊導入不支持以下的導入方式web
import { createApp } from 'vue'
瀏覽器
默認狀況下將會致使一個錯誤,vite 在 js 文件中檢測到這種狀況將會將其改寫爲@modules/{package-name}
,在這些特殊的路徑下,vite 執行如下的方式找到正確的文件緩存
vue
有特殊的處理,你不須要安裝這個模塊,若是須要使用特殊的版本,vite 將會使用node_modules
內部的模塊包web_modules
目錄存在,將會使用它node_modules
中查找*.vue
文件將會獲得開箱即用的替換功能*.js
須要提供相似於 webpack HMR 的 API import { foo } from "./foo.js"; import { hot } from "@hmr"; foo(); hot.accept("./foo.js", ({ foo }) => { // the callback receives the updated './foo.js' module foo(); });
安裝模塊便可在 *.vue
中使用
<style lang="scss"> /* use scss */ </style>
執行 vite build
,當前支持 --root
和 --cdn
兩個參數
可使用 API 定製開發服務器,vite 支持插件形式擴展,能夠定製化訪問 vite 內部的 koa 實例和增長相關的中間件
http://localhost:3000/
首屏頁面<div id="app"></div> <script type="module"> import { createApp } from "/@modules/vue"; // 此模塊中包含相關熱加載邏輯 import App from "./App.vue"; // 此文件爲SFC主模板 createApp(App).mount("#app"); // 渲染模版 </script>
http://localhost:3000/App.vue
主模板import { updateStyle } from "/@hmr"; // 加載更新style方法 const __script = { data: () => ({ count: 0 }) }; updateStyle("c44b8200-0", "/App.vue?type=style&index=0"); __script.__scopeId = "data-v-c44b8200"; import { render as __render } from "/App.vue?type=template"; // 加載template模板 __script.render = __render; __script.__hmrId = "/App.vue"; __script.__file = "/Users/shoyuf/work/vite-app/App.vue"; export default __script;
/@hmr
更新邏輯console.log("[vite] connecting..."); const socket = new WebSocket(`ws://${location.host}`); // Listen for messages socket.addEventListener("message", ({ data }) => { const { type, path, id, index, timestamp } = JSON.parse(data); switch (type) { case "connected": // 鏈接成功 console.log(`[vite] connected.`); break; case "vue-reload": // 當script改變的狀況下,須要從新加載 import(`${path}?t=${timestamp}`).then(m => { __VUE_HMR_RUNTIME__.reload(path, m.default); console.log(`[vite] ${path} reloaded.`); }); break; case "vue-rerender": // 當template改變的狀況下,須要從新渲染 import(`${path}?type=template&t=${timestamp}`).then(m => { __VUE_HMR_RUNTIME__.rerender(path, m.render); console.log(`[vite] ${path} template updated.`); }); break; case "vue-style-update": // 當css改變狀況下更新style updateStyle(id, `${path}?type=style&index=${index}&t=${timestamp}`); console.log( `[vite] ${path} style${index > 0 ? `#${index}` : ``} updated.` ); break; case "vue-style-remove": // css改變後移除舊的css引用 const link = document.getElementById(`vite-css-${id}`); if (link) { document.head.removeChild(link); } break; case "js-update": // js 模塊更新從新加載 const update = jsUpdateMap.get(path); if (update) { update(timestamp); console.log(`[vite]: js module reloaded: `, path); } else { console.error( `[vite] got js update notification but no client callback was registered. Something is wrong.` ); } break; case "full-reload": // 導入鏈進入死衚衕,須要進行頁面從新加載 location.reload(); } }); // ping server socket.addEventListener("close", () => { console.log(`[vite] server connection lost. polling for restart...`); setInterval(() => { new WebSocket(`ws://${location.host}`).addEventListener("open", () => { location.reload(); }); }, 1000); }); export function updateStyle(id, url) { const linkId = `vite-css-${id}`; let link = document.getElementById(linkId); if (!link) { link = document.createElement("link"); link.id = linkId; link.setAttribute("rel", "stylesheet"); link.setAttribute("type", "text/css"); document.head.appendChild(link); } link.setAttribute("href", url); } const jsUpdateMap = new Map(); export const hot = { accept(importer, deps, callback) { jsUpdateMap.set(importer, timestamp => { if (Array.isArray(deps)) { Promise.all(deps.map(dep => import(dep + `?t=${timestamp}`))).then( callback ); } else { import(deps + `?t=${timestamp}`).then(callback); } }); } };
/App.vue?type=template
主模板 HTML 部分import { createVNode as _createVNode, toDisplayString as _toDisplayString, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from "/@modules/vue"; const _withId = _withScopeId("data-v-c44b8200"); _pushScopeId("data-v-c44b8200"); const _hoisted_1 = _createVNode( // 建立Virtual DOM "h1", null, "Hello Vite + Vue 3!", -1 /* HOISTED */ ); const _hoisted_2 = _createVNode( "p", null, "Edit ./App.vue to test hot module replacement (HMR).", -1 /* HOISTED */ ); _popScopeId(); export const render = _withId(function render(_ctx, _cache) { // 渲染函數 return ( _openBlock(), _createBlock( _Fragment, null, [ _hoisted_1, _hoisted_2, _createVNode("p", null, [ _createVNode( "span", null, "Count is: " + _toDisplayString(_ctx.count), 1 /* TEXT */ ), _createVNode( "button", { onClick: _cache[1] || (_cache[1] = $event => _ctx.count++) }, "increment" ) ]) ], 64 /* STABLE_FRAGMENT */ ) ); });
/App.vue?type=style&index=0
主模板 css 部分,包括 scopedIdh1[data-v-c44b8200] { color: #4fc08d; } h1[data-v-c44b8200], p[data-v-c44b8200] { font-family: Arial, Helvetica, sans-serif; }
ws://localost:3000/
執行熱替換的數據交互,與/@hmr
相聯Example:
{ path: "/App.vue", timestamp: 1588242356511, type: "vue-reload" }
type 與@hmr
的相關方法一致