vite 插件钩子
- config 类型:async (config: Config, env: { command: string, mode: string }) => Promise<void | Config> 用途:修改或扩展 Vite 的配置。这个钩子在解析配置文件后、其他插件之前运行。
- configResolved 类型:async (config: ResolvedConfig) => Promise<void | ResolvedConfig> 用途:在配置完全解析和验证之后调用。你可以在这个钩子中访问所有解析后的配置选项。
- transform 类型:async (code: string, id: string) => Promise<string | { code: string, map: RawSourceMap } | null> 用途:转换文件内容。例如,你可以使用此钩子来编译 Vue 或其他框架的组件,或者应用 Babel 转换。
- transformIndexHtml 类型:async (html: string, url: URL) => Promise<string | null> 用途:转换 index.html 文件的内容。例如,你可以插入 script 标签或修改页面结构。
- resolveId 类型:async (source: string, importer?: string) => Promise<string | null | false> 用途:解析模块 ID。你可以在这里实现自定义的模块解析逻辑,例如支持特定的模块导入路径或后缀。
//业务代码
import A from "@A"
console.log(A())// 1
// ./add.ts
export default ()=>{
return 1
}
async resolveId(source: string, importer: any, options: any) {
console.log('source', source)
// 检查 rawId 是否匹配自定义别名
if (source === '@A') {
// 返回实际的文件路径
return resolve(__dirname, "./src/components/add.ts");
}
// 如果 rawId 不匹配,则返回 null 或 undefined,让其他插件继续处理
return null;
},
- load 类型:async (id: string) => Promise<string | null | void> 用途:加载模块内容。如果 Vite 无法直接加载某个模块,你可以在这里实现自定义的加载逻辑。
// 能拿到文件的路径,然后通过fs拿到内容
if (id.endsWith('.ts')) {
// 假设我们想要在每个 .ts 文件的开头添加一行 console.log
const originalCode = fs.readFileSync(id).toString()/* 获取原始代码的代码 */; // 注意:这里需要实际获取源代码的逻辑
const modifiedCode = `console.log('This file is loaded by Vite!');\n${originalCode}`;
return modifiedCode;
}
// 结果如下
console.log("This file is loaded by Vite!");
export default () => {
return 1;
};
-
configureServer 类型:(server: ViteDevServer) => void 用途:配置开发服务器。你可以在这里添加中间件、自定义 HMR 行为等。
-
transformRequest 类型:async (url: URL, options: TransformRequestOptions) => Promise<void | URL | { url: URL, headers?: HeadersInit }> 用途:在请求资源之前进行转换。你可以在这里修改请求的 URL、添加请求头或返回自定义的响应。
-
handleHotUpdate 类型:(ctx: HotUpdateContext) => void 用途:处理热模块替换(HMR)的更新。你可以在这里定义如何应用模块更新或自定义 HMR 的行为。
handleHotUpdate({ file, server }: any) {
console.log("handleHotUpdate",file )
// 检查是否是 JSON 文件更新
if (file.endsWith('a.json')) {
// 发送自定义事件通知客户端
server.ws.send({
type: 'custom',
event: 'json-file-updated',
path: file,
});
}
},
// 页面上
// 在需要监听 JSON 文件更新的组件或文件中
if (import.meta.hot) {
import.meta.hot.on('json-file-updated', (data) => {
// 处理 JSON 文件更新逻辑,比如重新加载数据、更新 UI 等
console.log('JSON 文件已更新:', data);
// ... 其他处理逻辑 ...
});
}
-
buildStart 类型:async (options: BuildOptions) => Promise 用途:在构建过程开始时调用。你可以在这里进行一些初始化工作,例如设置构建目录或加载额外的资源。
-
buildEnd 类型:async () => Promise 用途:在构建过程结束时调用。你可以在这里进行一些清理工作或生成额外的输出文件。
-
closeBundle 类型:async (outputOptions: RollupOutputOptions, bundle: RollupBundle, isWrite: boolean) => Promise 用途:在 Rollup 打包结束后,但在输出文件之前调用。你可以在这里对打包结果进行后处理或自定义输出格式。
备注:
vite 通过预构建将node_modules 放到.vite 缓存起来,而本地的业务代码vue、css等会放到vite内部实现的 ModuleGraph 的 内存中,里面有很多Map、Set 去实现缓存。
import fs from 'fs';
import { resolve } from 'path';
export default () => {
return {
name: "vite-my-plugin",
async config(config: any, env: any) {
console.error('config')
await clear()
await writeLog('config:' + JSON.stringify(config))
await writeLog('config:' + JSON.stringify(env))
return config
},
async configResolved(config: any) {
console.error('configResolved')
await writeLog('configResolved:' + JSON.stringify(config))
return config
},
async resolveId(source: string, importer: any, options: any) {
console.error('resolveId')
// 检查 rawId 是否匹配自定义别名
if (source === '@A') {
// 返回实际的文件路径
return resolve(__dirname, "./src/components/add.ts");
}
// 如果 rawId 不匹配,则返回 null 或 undefined,让其他插件继续处理
return null;
},
async load(id: string) {
console.error('load')
// if (id.endsWith('.ts')) {
// // 假设我们想要在每个 .js 文件的开头添加一行 console.log
// const originalCode = fs.readFileSync(id).toString()/* 获取原始代码的代码 */; // 注意:这里需要实际获取源代码的逻辑
// const modifiedCode = `console.log('This file is loaded by Vite!');\n${originalCode}`;
// return modifiedCode;
// }
},
transformRequest(url, options) {
console.log("transformRequest:", url, options)
// 假设我们想要给所有 .js 文件的请求添加一个自定义的查询参数
// if (url.endsWith('.js')) {
// const modifiedUrl = `${url}?customParam=true`;
// return { url: modifiedUrl };
// }
// 对于其他类型的文件,直接返回原 URL
return { url };
},
handleHotUpdate({ file, server }: any) {
console.log("handleHotUpdate",file )
// 检查是否是 JSON 文件更新
if (file.endsWith('a.json')) {
// 发送自定义事件通知客户端
server.ws.send({
type: 'custom',
event: 'json-file-updated',
path: file,
});
}
},
transform(source: any, id: any) {
console.error('transform')
if (process.env.NODE_ENV === 'development' && process.versions.node) {
const arr = id?.split("/")
try {
fs.writeFile(`./test/${arr[arr?.length - 1]}`, source, { encoding: 'utf8', flag: 'w' }, function (err) {
if (err) throw err;
});
} catch (error) {
// console.log(error)
}
}
return source
},
async transformIndexHtml(html: string, url: any) {
console.error('transformIndexHtml')
const scriptContent = `
console.log('Hello from dynamically inserted script啊啊啊!');
// 这里可以添加更多的JavaScript代码
`;
await writeLog('config:' + JSON.stringify(html))
// 使用模板字符串插入<script>标签
return html.replace('</body>', `
<script>
${scriptContent}
</script>
</body>
`);
},
buildStart({ command, mode }: any){
console.log('buildStart:', command, mode)
},
buildEnd(context){
console.error('buildEnd completed')
},
closeBundle(bundle) {
// bundle 对象可能包含构建结果的相关信息,但具体结构取决于 Vite 的版本
// config 是 Vite 的配置对象
// 打印构建完成消息
console.log('Vite 构建完成,开始执行 closeBundle 钩子中的操作...' );
// 在这里,你可以根据 bundle 或 config 对象中的信息执行自定义操作
// 例如,你可以访问输出目录、构建时间等信息
// 假设我们要访问输出目录(outputDir)
// 注意:closeBundle 钩子通常是同步的,但如果你需要执行异步操作,可以将其定义为 async 函数
},
}
}
const logPath = "./log.log";
const clear = async () => {
await fs.writeFileSync(logPath, "", { encoding: 'utf8', flag: 'w' });
}
const writeLog = async (log: string) => {
let content = ""
if (fs.existsSync(logPath)) {
content = fs.readFileSync(logPath).toString()
}
content += '\r\n' + log
await fs.writeFileSync(logPath, content, { encoding: 'utf8', flag: 'w' });
}
官方插件
@vitejs/plugin-vue 提供 Vue 3 单文件组件支持。
@vitejs/plugin-vue-jsx 提供 Vue 3 JSX 支持(通过 专用的 Babel 转换插件)。
@vitejs/plugin-vue2 提供对 Vue 2.7 的单文件组件支持。
@vitejs/plugin-vue2-jsx 提供对 Vue 2.7 JSX 对支持(通过 dedicated Babel transform)。
@vitejs/plugin-react 使用 esbuild 和 Babel,使用一个微小体积的包脚注可以实现极速的 HMR,同时提升灵活性,能够使用 Babel 的转换管线。在构建时没有使用额外的 Babel 插件,只使用了 esbuild。
@vitejs/plugin-react-swc 在开发时会将 Babel 替换为 SWC。在构建时,若使用了插件则会使用 SWC+esbuild,若没有使用插件则仅会用到 esbuild。对不需要非标准 React 扩展的大型项目,冷启动和模块热替换(HMR)将会有显著提升。
@vitejs/plugin-legacy 为打包后的文件提供传统浏览器兼容性支持。
vite-plugin-pwa 0 配置 用来生成 pwa 的插件