一、背景
三年前的老项目,技术栈是react + webpack4,融合了很多模块,项目巨大无比,每次更改代码都得等待20s左右,实在有点怀疑人生。接手项目的第一天就想有机会一定要优化,随着对项目的了解和工作节奏的适应,最近开始着手优化项目热加载。
二、webpack4的优化
已经做的优化
- DllPlugin
- babel-loader设置缓存
尝试做的优化
- react-hot-loader
- hard-source-webpack-plugin
- react-refresh
试了若干种方案后,效果甚微;后来就某个问题场外求助时,被安利了esbuild和Vite,看了esbuild首页夸张的数据对比,抱着将信将疑的态度尝试了Vite(真香)。
三、迁移至Vite
1. vite简介
Vite对于第三方库使用esbuild预构建依赖,Esbuild 使用 Go 编写,速度比以 JavaScript 编写的打包器预构建依赖快 10-100 倍;以原生ESM方式提供源码,没有了webpack的编译过程,对webpack可以说是降维打击...
webpack团队成员的中文回复..
2. 迁移步骤
2.1 根目录创建 Vite.config.js,完整配置:
import { defineConfig } from 'vite';
import reactRefresh from '@vitejs/plugin-react-refresh';
import antdDayjs from 'antd-dayjs-vite-plugin';
function ttt (){
}
export default defineConfig({
plugins: [
reactRefresh(),
// 处理antd日期组件只展示今天的问题
antdDayjs({ preset: 'antdv3' })
],
base: './',
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
alias: [
{ find: '@src', replacement: ’xxxx },
{ find: 'react-native', replacement: 'react-native-web' },
{ find: '@ant-design/icons/lib/dist', replacement: '@ant-design/icons/lib/index.es.js' }
]
},
optimizeDeps: {
// exclude: ['bizcharts']
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true
}
}
},
server: {
port: 3000,
host: 'localhost',
open: '/',
proxy: {
'/api': {
target: 'xxx',
changeOrigin: true
}
}
},
define: {
'process.env': {
// 计划只在开发环境使用,写死了dev
NODE_ENV: 'development'
}
}
});
复制代码
2.2 复制index.html至根目录
添加 <script type="module" src="/src/Main.jsx"></script>
2.3 package.json 添加启动命令
"start:Vite": "Vite"
至此Vite配置已告一段落,可以尝试跑下 "start:Vite": "Vite",会发现各种报错,下面开始填坑之旅。
2.4为开发环境为vite,线上保持webpack的兼容处理
2.4 让webpack处理 import.meta.globEager 和 import.meta.glob
test: /.(js|mjs|jsx|ts|tsx)$/,
use: [
{ loader: require.resolve('@open-wc/webpack-import-meta-loader') },
{
loader: require.resolve('babel-loader'),
options: {
presets: ['babel-preset-vite'],
plugins: [
[require('@babel/plugin-syntax-import-meta')]
]
}
}
]
复制代码
3. 遇到的问题
3.1 Cannot find module 'worker_threads'
node ≥ 12
3.2 Unknown theme type: undefined, name: undefined
alias: {
'@ant-design/icons/lib/dist': '@ant-design/icons/lib/index.es.js'
},
复制代码
3.3 require is not defined,
如backgroundimage:url(${require('common/Asset/icon${item.index_page}.svg')})
可以写个获取图片的工具方法
const getImageUrl = imageName => {
// 因为import.meta.url会加上本模块所在的目录 src/utils,所以加上../../
return `${new URL(`../../src/${imagePath}`, import.meta.url).href}`;
};
backgroundimage:`url(${getImageUrl(`${item.index_page}.svg`)})`
复制代码
3.4 error: No matching export in "node_modules/@antv/util/esm/index.js" for import "max"
@antv/util 升级到 2.0.17
3.5 The requested module 'xxx' does not provide an export named 'xx'
exports 改为export
3.6 Failed to fetch dynamically imported module
改用import.meta.glob
3.7 process is not defined
// vite.config.js
define: {
'process.env': {
NODE_ENV: 'development'
}
}
复制代码
3.8 antd的日期组件只显示了今天
Vite.config.js添加 plugins: [antdDayjs({ preset: 'antdv3' })] (非v3版本不用preset参数)
3.9 import.meta.glob webpack build 报错
npm install --save-dev babel-preset-vite @babel/plugin-syntax-import-meta
presets: ['babel-preset-vite'],
plugins: ['@babel/plugin-syntax-import-meta']
复制代码
3.10 import.meta.url webpack build 报错
添加@open-wc/webpack-import-meta-loader
3.11 Dynamic require of "xxx.css" is not supported
改了 yimi-form-components 的样式。。。。
// require("antd/lib/table/style/index.css");
import "antd/lib/table/style/index.css"
复制代码
3.12 The requested module '/node_modules/.vite/react-intl.js?v=94a2fd86' does not provide an export named 'intlShape'
这个问题很奇怪,'intlShape'是react-intl的一个interface,在vite编译后的文件里确实没有,于是想看是哪个组件使用了,一个个试了发现是有的组件引用了'intlShape'但是没有使用,把这样的'intlShape'删除后项目就可以启动了..
3.13 GET http://localhost:9021/@id/dayjs/moment net::ERR_ABORTED 404 (Not Found)
组件中有 import moment from 'moment/moment';
改为 import moment from 'moment‘;
即可
3.14 打开页面后发出了2000多个http请求,完全渲染完成依然需要9s
查看了下请求的文件,大多是swagger生成的services文件,因services是通过一个文件export出来的,所以使用的时候就是引用了所有的services文件,查看了services下确实有1800多个文件。
看了vite文档中有介绍:
一些包将它们的 ES 模块构建作为许多单独的文件相互导入。例如,lodash-es 有超过 600 个内置模块!当我们执行 import { debounce } from 'lodash-es' 时,浏览器同时发出 600 多个 HTTP 请求!尽管服务器在处理这些请求时没有问题,但大量的请求会在浏览器端造成网络拥塞,导致页面的加载速度相当慢。通过预构建 lodash-es 成为一个模块,我们就只需要一个 HTTP 请求了!
所有可以把类似的模块加入预构建,比如这里的services,还有翻译相关的
optimizeDeps: {
include: ['@src/services', '@src/Locales/zh_CN', '@src/Locales/en_US']
}
复制代码
3.15 vite一直reload,new dependencies .. 的提示
由于动态import,预构建可能在项目启动后继续发生,但避免vite.config.js手动配置多个依赖,使用vite-plugin-optimize-persist插件可以把配置写到package.json\
// vite.config.ts
import OptimizationPersist from 'vite-plugin-optimize-persist'
import PkgConfig from 'vite-plugin-package-config'
plugins: [
PkgConfig(),
OptimizationPersist()
]
复制代码
四、效果
webpack | Vite | |
---|---|---|
第一次启动 | 45s | 20s |
第二次启动 | 45s | 1s |
热加载 | 20s | 1s |
五、总结
计划是对webpack做些优化的,但试了多种方法都没啥效果,所以不如直接上Vite,投入的时间差不多(后面陆续填了好多坑),但收获的开发体验是极致的。