在开发环境下,vite的启动时间远远高于webpack,特别是首次启动,做到了真正的按需加载,开发体验好于webpack; 目的:开发环境加入vite,生产环境不变 react项目切webpack(开发环境增加vite) 例子项目简介:xpp-query-fe 技术栈:react-17,ts,react脚手架(内置webpack@5.73.0,customize-cra) 实现了一个记录时间的插件@xes/output-plugin-time,下面的时间记录基于该插件,已发布在npm包上 非首次启动平均时间4秒(存在cache情况下) 热更新2444毫秒 首次构建时间100886ms,有cache情况下再次构建时间54402ms 引入vite后 构建时间: 20.928s,相比首次提升80%,有cache提升61% 启动时间555ms,相对于非首次启动时间提升87% 热更新时间500ms,相对提高了79% 转化步骤 安装vite-npm i vite 根目录下新建vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path' export default defineConfig({ server: { port: 3001, }, plugins: [react()], resolve: { // 路径相关配置项 alias: [ { find: '@', replacement: path.resolve(__dirname, 'src'), }, { find: 'src', replacement: path.resolve(__dirname, 'src'), }, ], extensions: ['.js', '.json', '.ts', '.tsx', '.jsx', '.less'], }, }); 新建一个main.js文件,拷贝src里面的index.tsx的内容进去(不做也可以,引入的script标签src就为index.tsx) 拷贝public里面的index.html,放在最外层,文件中的%PUBLIC_URL%字段(公共URL路径)去掉, 加入<script type="module" src="src/index.tsx"></script> vite的入口文件index.html在最外层,并且index.html 中的 URL 将被自动转换,因此不再需要 %PUBLIC_URL% 占位符了 思考:可以实现一个方法,将public文件夹里面的文件拷贝并且经过处理后放置在目录最外层 import fs from 'fs' import path from 'path'
function createHtml() { var htmlPath = path.resolve(__dirname, 'public'); // 目录最外层 var outPath = path.resolve(__dirname); var webpackHtml = path.join(htmlPath, 'index.html'); var viteHtml = path.join(outPath, 'index.html');
// exists弃用,existsSync不使用回调
// 存在的话就返回
if (fs.existsSync(viteHtml)) return
if (fs.existsSync(webpackHtml)) {
var content = fs.readFileSync(webpackHtml, 'utf-8');
content = content.replace(/%PUBLIC_URL%/g, '');
var bq = '<script type="module" src="src/index.tsx"></script>';
var bodyIndex = content.lastIndexOf('</body>');
content = content.slice(0, bodyIndex)+bq+content.slice(bodyIndex)
fs.writeFileSync(viteHtml, content, 'utf-8');
console.log(`${outPath}成功创建外层html`);
} else {
console.error(`${htmlPath}路径没有html`);
}
}
export default createHtml package.json文件里面的script,加入启动vite脚手架命令(保留webpack命令) 图片: https://yach-doc-shimo.zhiyinlou.com/uploader/f/dfRDcBAPIqDBoVEH.png?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MjU4Nzk1OTAsImZpbGVHVUlEIjoibG9xZVdvWWU1YVRicnJBbiIsImlhdCI6MTcyNTg3MjM5MCwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjozNTE2MjcwfQ.G_IyndlORJDcMfHHCGX94toVKnz2XzVb0aMf4_uA-cg 这个时候启动的话,会报下图错误 图片: https://yach-doc-shimo.zhiyinlou.com/uploader/f/C8no6X5NlVuPLVN7.png?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MjU4Nzk1OTAsImZpbGVHVUlEIjoibG9xZVdvWWU1YVRicnJBbiIsImlhdCI6MTcyNTg3MjM5MCwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjozNTE2MjcwfQ.G_IyndlORJDcMfHHCGX94toVKnz2XzVb0aMf4_uA-cg 主要原因是开发环境,vite使用esbuild对代码进行转化(符合es),react文件的后缀结尾可以为js,jsx,ts,tsx,这些文件可能都包含jsx语法。 esbuild对jsx语法文件要求是后缀结尾的为jsx或者tsx,对于js,ts里面包含jsx语法的文件会报错。可增加其对应的后缀结尾 图片: https://yach-doc-shimo.zhiyinlou.com/uploader/f/5cMNCmVEDi6FDdDV.png?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MjU4Nzk1OTAsImZpbGVHVUlEIjoibG9xZVdvWWU1YVRicnJBbiIsImlhdCI6MTcyNTg3MjM5MCwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjozNTE2MjcwfQ.G_IyndlORJDcMfHHCGX94toVKnz2XzVb0aMf4_uA-cg 解决方法,在vue.config里面加入上图代码 环境变量设置 图片: https://yach-doc-shimo.zhiyinlou.com/uploader/f/b1ysvn9IF4Z7FWob.png?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MjU4Nzk1OTAsImZpbGVHVUlEIjoibG9xZVdvWWU1YVRicnJBbiIsImlhdCI6MTcyNTg3MjM5MCwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjozNTE2MjcwfQ.G_IyndlORJDcMfHHCGX94toVKnz2XzVb0aMf4_uA-cg 上图报错为环境两个两个脚手架暴露和访问环境变量的方式不同 react脚手架中环境变量访问通过process.env,且环境变量必须以 REACT_APP_ 为前缀,vite里面则为import.meta.env,所以需要替换(开发环境为import.meta.env) vite默认抛出以前缀开头为 VITE_的环境变量,REACT_APP_开头的环境变量使用import.meta.env是取不到的 可以在vite.config.ts中,加入选项envPrefix,并且加入前缀'REACT_APP_'即可,这样就不用改变原来的声明的环境变量 import.meta.env,ts类型检查会有报错,在tsconfig.json里面加入"types": ["vite/client"]、 思考:可实现一个方法,在转译代码的时候,替换该process.env,并且设置前缀 function vitePluginEnvTransform() { return { config: function () { return { //支持前缀REACT_APP_ envPrefix: 'REACT_APP_' }, // 对应webpack的loader,转译的时候将process.env转化为import.meta.env transform: function (code, id) { if (id.includes('node_modules')) return code;
return code.replace(/process\.env\./g,'import.meta.env.');
},
}; }
export default vitePluginEnvTransform;
图片: https://yach-doc-shimo.zhiyinlou.com/uploader/f/xJZIgaxmoWCJ6irp.png?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MjU4Nzk1OTAsImZpbGVHVUlEIjoibG9xZVdvWWU1YVRicnJBbiIsImlhdCI6MTcyNTg3MjM5MCwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjozNTE2MjcwfQ.G_IyndlORJDcMfHHCGX94toVKnz2XzVb0aMf4_uA-cg 注意:vite不支持require语法 使用vite-plugin-require-transform插件 import requireTransform from 'vite-plugin-require-transform' requireTransform({ fileRegex: /src/.*.[tj]sx?$/, })
分支switchVite,脚手架vite和webpack并存 可以将vite.config.ts里面的配置写进vitePluginEnvTransform方法里面 然后将两个方法写进一个插件里面,这样实现了一个简单通用的react&webpack项目增加vite脚手架的插件@xes/react-webpack-to-vite-env import { defineConfig, PluginOption } from 'vite'; import react from '@vitejs/plugin-react'; //支持在module里面使用require import requireTransform from 'vite-plugin-require-transform'
import createHtml from '@xes/react-webpack-to-vite-env/createHtml.js' import vitePluginEnvTransform from '@xes/react-webpack-to-vite-env/envVite.js'
export default defineConfig({ plugins: [createHtml(),react(), vitePluginEnvTransform() as PluginOption, requireTransform({ fileRegex: /src/.*.[tj]sx?$/, }),], }); //webpack:"@testing-library/jest-dom": "^4.2.4"需要将该包降级才能build