import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' import vitePluginImp from 'vite-plugin-imp' import path from 'path' const isProduction = process.env.NODE_ENV === 'production' // https://vitejs.dev/config/ export default defineConfig({ // 項目根目錄 root: process.cwd(), // 在生產中服務時的基本公共路徑 base: isProduction ? './' : '', // 配置中指明將會把 serve 和 build 時的模式都覆蓋掉,serve 時默認 'development',build 時默認 'production' mode: 'development', // 在開發時會被定義為全局變量,而在構建時則是靜態替換 define: '', // 靜態資源服務的文件夾 publicDir: 'assets', resolve: { // 目錄別名 alias: { '@': path.resolve(__dirname, '/src'), }, }, // CSS 預處理器 css: { preprocessorOptions: { scss: { // additionalData: `$injectedColor: orange;` additionalData: "@import './src/assets/style/mixin.scss';" } , less: { javascriptEnabled: true } }, postcss: { plugins: [ require('autoprefixer') ] } }, // server: { // 是否自動打開瀏覽器 open: true, // 服務器主機名,如果允許外部訪問,可設置為"0.0.0.0" host: '0.0.0.0', // 服務器端口號 port: 56438, // 設為 true ,若端口已被占用則會直接退出,而不是嘗試下一個可用端口 strictPort: false, // 為開發服務器配置 CORS cors: true, // 設置為 true 強制使依賴預構建 force: true, // 代理 proxy: { '/api': { target: 'http://xxx.xxx.xx', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } , } , // build build: { // 壓縮 minify: "esbuild", assetsDir: "", outDir: `./dist/${process.env.VITE_ENV}`, // 進行壓縮計算 brotliSize: false }, ssr: false, // 將要用到的插件數組 plugins: [ vue(), vitePluginImp({ libList: [ { libName: 'vant', style(name) { if (/CompWithoutStyleFile/i.test(name)) { // This will not import any style file return false } return `vant/es/${name}/style/index.js` } }, { libName: 'element-plus', style: (name) => { return `element-plus/lib/theme-chalk/${name}.css` } } ] }), require('autoprefixer') ] })
https://cn.vitejs.dev/config/#resolve-conditions
Vite插件是什么
使用Vite插件可以擴展Vite能力,比如解析用戶自定義的文件輸入,在打包代碼前轉譯代碼,或者查找第三方模塊。
Vite插件的形式
Vite
插件擴展自Rollup
插件接口,只是額外多了一些Vite
特有選項。
Vite
插件是一個擁有名稱、創建鈎子(build hook)或生成鈎子(output generate hook)的對象。
如果需要配置插件,它的形式應該是一個接收插件選項,返回插件對象的函數。
范例:加載一個不存在的虛擬模塊
創建vite-plugin-my-example.js
export default function myExample () { return { name: 'my-example', // 名稱用於警告和錯誤展示 resolveId ( source ) { if (source === 'virtual-module') { return source; // 返回source表明命中,vite不再詢問其他插件處理該id請求 } return null; // 返回null表明是其他id要繼續處理 }, load ( id ) { if (id === 'virtual-module') { return 'export default "This is virtual!"'; // 返回"virtual-module"模塊源碼 } return null; // 其他id繼續處理 } }; }
插件鈎子
通用鈎子
開發時,Vite dev server
創建一個插件容器按照Rollup
調用創建鈎子的規則請求各個鈎子函數。
下面鈎子會在服務器啟動時調用一次:
options
替換或操縱rollup
選項buildStart
開始創建
下面鈎子每次有模塊請求時都會被調用:
下面鈎子會在服務器關閉時調用一次:
Vite特有鈎子
- config: 修改Vite配置
- configResolved:Vite配置確認
- configureServer:用於配置dev server
- transformIndexHtml:用於轉換宿主頁
- handleHotUpdate:自定義HMR更新時調用
范例:鈎子調用順序測試
export default function myExample () { // 返回的是插件對象 return { name: 'hooks-order', // 初始化hooks,只走一次 options(opts) { console.log('options', opts); }, buildStart() { console.log('buildStart'); }, // vite特有鈎子 config(config) { console.log('config', config); return {} }, configResolved(resolvedCofnig) { console.log('configResolved'); }, configureServer(server) { console.log('configureServer'); // server.app.use((req, res, next) => { // // custom handle request... // }) }, transformIndexHtml(html) { console.log('transformIndexHtml'); return html // return html.replace( // /<title>(.*?)<\/title>/, // `<title>Title replaced!</title>` // ) }, // 通用鈎子 resolveId ( source ) { if (source === 'virtual-module') { console.log('resolvedId', source); return source; } return null; }, load ( id ) { if (id === 'virtual-module') { console.log('load'); return 'export default "This is virtual!"'; } return null; }, transform(code, id) { if (id === 'virtual-module') { console.log('transform'); } return code }, }; }
鈎子調用順序
插件順序
- 別名處理Alias
- 用戶插件設置
enforce: 'pre'
- Vite核心插件
- 用戶插件未設置
enforce
- Vite構建插件
- 用戶插件設置
enforce: 'post'
- Vite構建后置插件(minify, manifest, reporting)
插件編寫實操
實現一個mock服務器vite-plugin-mock
實現思路是給開發服務器實例(connect)配一個中間件,該中間件可以存儲用戶配置接口映射信息,並提前處理輸入請求,如果請求的url和路由表匹配則接管,按用戶配置的handler返回結果。
創建plugins/vite-plugin-mock.js
import path from 'path' let mockRouteMap = {}; function matchRoute(req) { let url = req.url; let method = req.method.toLowerCase(); let routeList = mockRouteMap[method]; return routeList && routeList.find((item) => item.path === url); } function createRoute(mockConfList) { mockConfList.forEach((mockConf) => { let method = mockConf.type || 'get'; let path = mockConf.url; let handler = mockConf.response; let route = { path, method: method.toLowerCase(), handler }; if (!mockRouteMap[method]) { mockRouteMap[method] = []; } console.log('create mock api: ', route.method, route.path); mockRouteMap[method].push(route); }); } function send(body) { let chunk = JSON.stringify(body); // Content-Length if (chunk) { chunk = Buffer.from(chunk, 'utf-8'); this.setHeader('Content-Length', chunk.length); } // content-type this.setHeader('Content-Type', 'application/json'); // status this.statusCode = 200; // respond this.end(chunk, 'utf8'); } export default function (options = {}) { options.entry = options.entry || './mock/index.js'; if (!path.isAbsolute(options.entry)) { options.entry = path.resolve(process.cwd(), options.entry); } return { configureServer: function ({ app }) { const mockObj = require(options.entry); createRoute(mockObj); const middleware = (req, res, next) => { let route = matchRoute(req); if (route) { console.log('mock request', route.method, route.path); res.send = send; route.handler(req, res); } else { next(); } }; app.use(middleware); }, }; }
export default function vitePlugin () { // 定義vite插件唯一id const virtualFileId = '@my-virtual-plugin' // 返回插件對象 return { // 必須的,將會顯示在 warning 和 error 中 name: 'vite-plugin', // *以下鈎子函數按照實際執行順序排列* /** * config 可以在被解析之前修改 Vite 配置 * Vite獨有鈎子 * https://cn.vitejs.dev/guide/api-plugin.html#config * @param config vite配置信息 * @param env 描述配置環境的變量 */ config: (config, env) => ({}), /** * configResolved 解析 Vite 配置后調用,使用這個鈎子讀取和存儲最終解析的配置 * Vite獨有鈎子 * https://cn.vitejs.dev/guide/api-plugin.html#configresolved * @param config vite配置信息 */ configResolved: config => ({}), /** * options 替換或操作傳遞給rollup.rollup()的選項 * 通用鈎子 * https://rollupjs.org/guide/en/#options * @param options rollup配置信息 */ options: options => ({}), /** * configureServer 用於配置開發服務器 * Vite獨有鈎子 * https://cn.vitejs.dev/guide/api-plugin.html#configureserver * @param server ViteDevServer配置信息 * https://cn.vitejs.dev/guide/api-javascript.html#vitedevserver */ configureServer: server => ({}), /** * buildStart 在每個rollup.rollup()構建時被調用 * 通用鈎子 * https://rollupjs.org/guide/en/#buildstart * @param options rollup配置信息 */ buildStart: options => ({}), /** * 此時 Vite dev server is running */ /** * transformIndexHtml 轉換 index.html 的專用鈎子 * Vite獨有鈎子 * https://cn.vitejs.dev/guide/api-plugin.html#transformindexhtml * @param html html字符串 * @param ctx 轉換上下文; 在開發期間會額外暴露ViteDevServer實例; 在構建期間會額外暴露Rollup輸出的包 */ transformIndexHtml: (html, ctx) => ({}), /** * resolveId 用戶自定義解析器 * 通用鈎子 會在每個傳入模塊請求時被調用 * https://rollupjs.org/guide/en/#resolveid * @param source 源導入者 例子: import { foo } from '../bar.js', '../bar.js' 為source * @param importer 導入者所在文件絕對路徑 */ resolveId: (source, importer) => ({}), /** * load 用戶自定義加載器 * 通用鈎子 會在每個傳入模塊請求時被調用 * https://rollupjs.org/guide/en/#load * @param id 同resolveId source */ load: id => ({}), /** * transform 可以用來轉換單個模塊 * 通用鈎子 會在每個傳入模塊請求時被調用 * https://rollupjs.org/guide/en/#transform * @param code 模塊代碼 * @param id 同resolveId source */ transform: (code, id) => ({}) } }
下一集:手寫vite