1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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更新时调用
范例:钩子调用顺序测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 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); }, }; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗