【umi】 从umi讨论区学到一些性能优化

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 是面向中后台的框架

参考

相关推荐
IT_陈寒1 天前
JavaScript开发者必知的5个性能杀手,你踩了几个坑?
前端·人工智能·后端
跟着珅聪学java1 天前
Electron 精美菜单设计
运维·前端·数据库
日光倾1 天前
【Vue.js 入门笔记】闭包和对象引用
前端·vue.js·笔记
一只程序熊1 天前
UniappX 未找到 “video“ 组件,已自动当做 “view“ 组件处理。请确保代码正确,或重新生成自定义基座后再试。
前端
林小帅1 天前
【笔记】xxx 技术分享文档模板
前端
雾岛心情1 天前
【HTML&CSS】HTML为文字添加格式和内容
前端·css·html
心.c1 天前
如何在项目中减少 XSS 攻击
前端·xss
Rsun045511 天前
Vue相关面试题
前端·javascript·vue.js
TON_G-T1 天前
前端包管理器(npm、yarn、pnpm)
前端
卤炖阑尾炎1 天前
Web 技术基础与 Nginx 网站环境部署全解析
前端·nginx·microsoft