技术债务来自旧项目的依赖过久,React 16 + antd 3 + webpack 4。React 新版本的批处理优化了,antd 最新已经由 css-in-js 重写了,不用再配置 less 等预先编译器。旧版本的 webpack 在 nodejs 20 会出现 openssl 的报错,需要配置以下环境变量才能正常运行,长期看,这些项目依赖已经不符合长期迭代。
NODE_OPTIONS=--openssl-legacy-provider
首先我们要检查依赖版本,针对核心工具库依赖升级,把打包工具从 webpack 切换成为 vite。最后总结老旧项目的升级收益。
一、检查依赖工具
1.1 vscode 插件:Version Lens
推荐,vscode插件查看 package.json 中各依赖最新的版本,你可以挑取核心依赖专门进行升级。也可以利用 knip 剪枝掉没用的 dependencies。
version lens
1.2 taze
该工具是命令行工具,它可以按照规则批量升级依赖。它可以按照 sermer 版本号规则,利用其major/minor/patch 的规则,升级版本号,更有把握地批量升级。
二、 React版本升级
2.1 React批渲染的差异
旧代码存在不规范的 case:“在 promise 当中设置 setState,然后立即获取 this.state。”这类的逻辑是能够在 react 16 跑通的,但是 react 18 已经优化了批处理的逻辑,所以 setState 会合并,this.state 由于 setState 是异步的,所以不能获取设置后的状态。
修改方法:绕过 setState,异步直接导出数据。
const fetchApi = async () => {
const resp = await requestApi();
this.setState({data: resp.data});
return resp.data;
}
const init = async () => {
const resp = await fetchApi();
// 修改前
console.log(this.state.data);
// 修改后
console.log(resp);
setState...
}
2.2 React dom 根节点初始化方式改变
利用 createRoot 进行根节点的初始化。
// 修改前
ReactDOM.render(<App />, document.querySelector('#root'));
// 修改后
const root = createRoot(document.querySelector('#root'));
root.render(<App />);
三、antd版本升级
3.1 Form 表单升级
-
-
-
Form.Item 替代 getFieldDecorator
3.2 Menu Select 等组件升级
-
-
将 Select.Option 挪到 options 属性
3.3 Icon 升级
antd 4 开始,将 Icon 独立出来,所有要替换引用。
// 修改前
type="default"
icon="sync"
size="default"
className={styles.refreshButton}
onClick={() => loadData(moduleId)}
/>
// 修改后
<Button
type="default"
icon={<SyncOutlined />}
size="middle"
className={styles.refreshButton}
onClick={() => loadData(moduleId)}
/>
3.4 其他修改
移除 babel-plugin-import,参考从 v4 到 v5 - Ant Design (antgroup.com)。
四、 webpack 迁移到 vite
前面都是属于运行时内容调整,调整完回归一遍。后面属于编译时的调整,也涉及到一些 commonjs 写法的调整,可以参考《
记一次webpack迁移至vite的艰难优化
》
4.1 require 换成 dynamic import
首当其冲的是,require 语法在 esm 是不可用的。目标在于将 require 语法转换为 dynamic import。
该业务仓库中存在配置问题引入采用 require 语法,问题是 require 是同步加载,而 dynamic import 采用异步加载。这一块代码的修改要细致验证一下。
4.2 全局环境变量注入
旧代码用有全局环境变量注入,例如:
process.env.APP_REGION
在 vite config 当中,全局注入一些全局的值,保证已有逻辑能够正常运作。
// vite.config.ts
export default defineConfig(() => {
return {
define: {
'process.env.APP_REGION': process.env.APP_REGION,
}
}
});
4.3 lodash 替换成为 lodash-es
lodash 是 antd 的重要依赖,全量引入 lodash 体积较大,而且是 cjs 语法,所以利用 vite alias 替换它的别名,指向 lodash-es。
// vite.config.ts
export default defineConfig(() => {
return {
alias: {
lodash: 'lodash-es',
}
}
});
五、收益总结
5.1 优化效果
|
首屏最大 js 包体积
|
首屏最大 js gzip
|
FCP
|
LCP
|
修改前
|
1550kb
|
541 kb
|
1.4s
|
1.8s
|
修改后
|
1001kb
|
369kb
|
0.8 s
|
1.8s
|
收益
|
减小32%
|
减小32%
|
提升43%
|
没变化
|