从11884 得到一些启示
优化
1、 关于国际化优化
国际化是常见的需求, json文件可能会很大,采用动态加载的方式就会从umi.js的文件分出来,让umi.js的体积看起来没有那么大。
原先我们使用也是umi的国际化,后面因为需要从后端拉取数据,改成了i18next方案
2、图表或者编辑器, 组件手动懒加载出去?但是umi已经按页拆包了
3、 代码分割
css
- 类型:`{ jsStrategy: 'bigVendors' | 'depPerChunk' | 'granularChunks'; jsStrategyOptions: {} }`
- 默认值:`null`
本示例依赖安装
- bigVendors 是大 vendors 方案,会将 async chunk 里的 node_modules 下的文件打包到一起,可以避免重复。同时缺点是,1)单文件的尺寸过大,2)毫无缓存效率可言。
源码 packages/preset-umi/src/features/codeSplitting/codeSplitting.ts
php
// 大 vendors 方案
if (jsStrategy === 'bigVendors') {
memo.optimization.splitChunks({
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: 10,
name: 'vendors',
chunks: 'async',
...jsStrategyOptions,
},
},
});
}
- depPerChunk 和 bigVendors 类似,不同的是把依赖按 package name + version 进行拆分,算是解了 bigVendors 的尺寸和缓存效率问题。但同时带来的潜在问题是,可能导致请求较多。我的理解是,对于非大型项目来说其实还好,因为,1)单个页面的请求不会包含非常多的依赖,2)基于 HTTP/2,几十个请求不算问题。但是,对于大型项目或巨型项目来说,需要考虑更合适的方案。
是不是只有直接依赖呀
js
// 按 package name + version 进行拆分
if (jsStrategy === 'depPerChunk') {
memo.optimization.splitChunks({
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'async',
name(module: any) {
// e.g. node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es
const path = module.context.replace(/.pnpm[\\/]/, '');
const match = path.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/);
if (!match) return 'npm.unknown';
const packageName = match[1];
return `npm.${packageName
.replace(/@/g, '_at_')
.replace(/\+/g, '_')}`;
},
},
},
});
}
- granularChunks 在 bigVendors 和 depPerChunk 之间取了中间值,同时又能在缓存效率上有更好的利用。无特殊场景,建议用 granularChunks 策略。
js
// 结合了大vendor 和 按 package name + version 进行拆分两种方案, 果然复杂些
if (jsStrategy === 'granularChunks') {
const FRAMEWORK_BUNDLES = [
'react-dom',
'react',
// 'core-js',
// 'regenerator-runtime',
'history',
'react-router',
'react-router-dom',
'scheduler',
// TODO
// add renderer-react
];
memo.optimization.splitChunks({
cacheGroups: {
default: false,
defaultVendors: false,
framework: {
name: 'framework',
chunks: 'all',
test: new RegExp(
`[\\\\/]node_modules[\\\\/](${FRAMEWORK_BUNDLES.join(
`|`,
)})[\\\\/]`,
),
priority: 40,
enforce: true,
},
lib: {
test(module: any) {
return (
!isModuleCSS(module) &&
module.size() > 160000 &&
/node_modules[/\\]/.test(module.identifier())
);
},
name(module: any) {
const rawRequest =
module.rawRequest &&
module.rawRequest.replace(/^@(\w+)[/\\]/, '$1-');
if (rawRequest) {
return `${
// when `require()` a package with relative path,
// need remove leading `.` and `/`, otherwise will not found `.js` file
// e.g. require('../../lib/codemirror')
rawRequest.replace(/\./g, '_').replace(/\//g, '-')
}-lib`;
}
const identifier = module.identifier();
const trimmedIdentifier = /(?:^|[/\\])node_modules[/\\](.*)/.exec(
identifier,
);
const processedIdentifier =
trimmedIdentifier &&
trimmedIdentifier[1].replace(/^@(\w+)[/\\]/, '$1-');
return `${processedIdentifier || identifier}-lib`;
},
priority: 30,
minChunks: 1,
reuseExistingChunk: true,
chunks: 'async',
},
shared: {
name(_module: any, chunks: any) {
const cryptoName = crypto
.createHash('sha1')
.update(
chunks.reduce((acc: any, chunk: any) => {
return acc + chunk.name;
}, ''),
)
.digest('base64')
// replace `+=/` that may be escaped in the url
// https://github.com/umijs/umi/issues/9845
.replace(/\//g, '')
.replace(/\+/g, '-')
.replace(/=/g, '_');
return `shared-${cryptoName}`;
},
priority: 10,
minChunks: 2,
reuseExistingChunk: true,
chunks: 'async',
},
},
});
}
4、 把固定依赖变成 umd external 加载
好奇这种优化方案用的人多不?因为大家会担心节点不稳定,容易挂,反正说是这么说,没看过用过
js
externals: isProd ? {
react: 'window.React',
'react-dom': 'window.ReactDOM'
} : {},
scripts: isProd ? ['...', '...'] : []
上文说的编辑器组件也可以的吧
总结
其实关于优化还有很多,代码分割和splitchunks是常见的减少包体积的手段,还有比如ssr、图片webp等等这些,umi自己也说过,如果是to c,建议用next 框架,umi 是面向中后台的框架