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