umi项目性能优化

本文以umi2.x项目作为示例进行项目优化,其他项目优化方向也是如此,可能配置上有一点差异。

目录

  1. umi获取配置文件
  2. 启动项目时的优化
  3. 优化文件
  4. 动态加载
  5. 文件缓存

一、umi 获取配置文件

umi 的配置文件路径为 config/config.js。其中包含了umi的配置与webpack的配置,具体配置项,请查看umi官方文档。

在配置项 chainWebpack 上能够获取到整个 webpack 的配置文件,为了方便调试,可以在每次运行时将文件保存到根目录上:

chainWebpack(config, { webpack }) {
    fs.writeFile('./webpackConfig.txt', config.toString(), () => {});
}

二、启动项目时的优化

1. 缓存文件,加速项目重启速度

webpack 的 DLLPlugin 能够缓存一些 bundles 文件,在项目启动时不需要再重新编译,极大提高启动速度。web 课堂项目,使用 DLLPlugin 之后,构建时间从55s左右减少到25s左右。
umi 中打开 DLLPlugin 方式如下:

plugins: [
    [
        'umi-plugin-react',
        {
            dll: true,
        },
    ]
]

此处注意,由于dll缓存node_modules中的依赖,所以当node_modules中的文件有更新时,并不会重新打包。若有实时更新依赖包的需要,请先关闭此配置。

2. 分配更多v8内存,确保大项目能正常启动

在Node中通过JavaScript使⽤内存时只能使⽤部分内存(64位系统:1.4
GB,32位系统:0.7 GB)。
如果前端项⽬比较庞⼤,Webpack编译时就会占⽤很多的系统资源,如
果超出了V8引擎对Node默认的内存限制⼤⼩时则启动失败。若碰到此情况,可以手动设置 node 启动参数 --max-old-space-size,分配更多的内存。
设置方式如下;

方案一

"large-start": "node --max-old-space-size=8192 ./node_modules/.bin/umi start",
"large-build": "node --max-old-space-size=8192 ./node_modules/.bin/umi build",

方案二

"large-start":"cross-env NODE_OPTIONS=--max-old-space-size=4096 npm
start",
"large-build":"cross-env NODE_OPTIONS=--max-old-space-size=4096 npm run
build"

同理,可配置 large-fat-build 、large-analyze 等启动命令。

3. 解决 babel-plugin-react-css-modules 与 cssLoader 在 windows 上协作异常问题

在 windows 上同时使用以上两个工具,会导致不能正确加载样式。原因为 windows 上路径分隔符为'',mac 上分隔符为'/',babel-plugin-react-css-modules会⾃动对''进行转换,⽽cssloader不会,所以⽤于计算hash
的相对路径不⼀致,导致计算结果匹配不上。
解决思路是,手动兼容cssloader的路径,同步两个工具用于计算hash的值。

config.js

const GENERATE_PATH = '[path]___[name]__[local]___[hash:base64:5]';
function generateScopedName(localName, filePath) {
    const relativePath = path.relative(process.cwd(), filePath);
    return genericNames(GENERATE_PATH)(localName, relativePath);
}

export default {
    cssLoaderOptions: {
        getLocalIdent: function(context, _, localName) {
            return generateScopedName(localName, context.resourcePath);
        },
    },
    extraBabelPlugins: [
        [
            'react-css-modules',
            {
                exclude: 'node_modules',
                generateScopedName: GENERATE_PATH,
                filetypes: {
                    '.less': {
                        syntax: 'postcss-less',
                    },
                },
            },
        ],
    ],
}

三、优化文件

1. 图片大小优化

在前端项目中加入imagemin-linter工具,自动在提交图片前进行压缩。

2. 清除无用文件

项目中,可能存在不会被用到的文件,这部分文件应该被清除。umi 项目可通过设置环境变量 ANALYZE=1 开启 webpack analyze。大部分项目中,以下文件大概率可以清除。

momentjs 国际化

moment 默认会导入国际化文件,一般只需要中文或者英文语言,其他语言可暂时不导入,之后根据需要导入。配置如下:

chainWebpack(config, { webpack }) {
    config
        .plugin('moment-locale')
        .use(webpack.ContextReplacementPlugin, [/moment[\/\\]locale$/, /en|zh-cn/]);
}

这里不使用 umi ignoreMomentLocale 的原因是,ignoreMomentLocale 会去除所有国际化文件,而实际项目中,可能会有两个语言供配置。所以此处使用了 ContextReplacementPlugin。

moment 导入国际化文件位置为 moment/src/lib/locale/locales loadLocale

lodashjs 国际化文件

官⽅⽂档推荐使⽤ babel-plugin-lodash 和 lodash-webpack-plugin。
babel-plugin-lodash 对代码中的 import _ from 'lodash'或者import { add } from 'lodash/fp'进⾏编译优化。确保引⼊最⼩的包。
lodash-webpack-plugin⽤于精简lodash,去除了⼀些特性,如果需要⽤到,则需⼿动开启。具体可参照lodash-webpack-plugin。

配置方式如下:
安装babel-plugin-lodash后配置:

extraBabelPlugins: [
    ['lodash'],
],

lodash-webpack-plugin配置为:

import lodashModuleReplacementPlugin from 'lodash-webpack-plugin';
chainWebpack(config, { webpack }) {
    config.plugin('lodash-shrink').use(lodashModuleReplacementPlugin, [
        {
            collections: true,
            paths: true,
            shorthands: true,
        },
    ]);
},

注:lodash优化效果有限:随着系统的增⻓,引⼊的第三⽅库越来越多。⽽第三⽅库中,有可能使⽤require('lodash')引⼊lodash,导致我们项⽬中对lodash的优化⽆效。所以,可以不⽤太过于纠结lodash的优化。

@antd-design icons/lib

antd3.x全量导⼊@ant-design/icons/lib/dist,可通过配置alias,指向本地⽂件,本地⽂件再按需引⼊。

alias: {
    '@ant-design/icons/lib/dist$': path.resolve(__dirname,'../src/helpers/antdIcons.js'),
},

src/helpers/antdIcons.js

// 调⽤antd的内置图标,需要在此处载⼊
export { default as LoadingOutline } from '@antdesign/icons/lib/outline/LoadingOutline';

四、动态加载

动态加载技术可以让模块只再需要使用时再请求载入,提升页面响应速度。

webpack 4.x已支持使用 splitchunksPlugin 进行代码分块。以下内容基于 webpack 4.x。

代码上,使用 import() 函数进行动态加载,用法如下:

import(/* webpackChunkName: 'edit-enow'*/ '@enow/editenow').then(({ default: Enow }) => {
    new Enow()
}

其中,webpackChunkName 指定分块后生成的 chunk 名字。

注:使用 babel 时,需要引入 babel-plugin-syntax-dynamic-import 插件,避免 babel 讲 import() 转为 require() 导致动态加载无效。umi 项目已自动配置该插件。

webpack 4.x 默认的 splitChunks 配置如下:

splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
        default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
        }
    }
}

可以看到,默认会将 node_modules 中的异步请求中的第三方依赖独立打成一个个 vendors chunk,其它打包为 default chunk。

与其相关的属性还有一个 chunkFilename,默认为[id].js,id 从0开始。可手动配置为 [name].js。
当chunkFilename为 [name].js 时,最终 chunk 文件名为,[cacheGroupsName] + automaticNameDelimiter + [chunkFilename]。
当chunkFilename为 [id].js 时,最终 chunk 文件名为 chunkFilename。

umi增加了一个name字段,使所有的node_modules依赖包会打在一个上,导致vendors包变得很大。在有大依赖包的情况下,该配置很不人性化。设置配置解除该限制:config.optimization.splitChunks({ name: true })

chunk为initial时 name为true,name为入口文件名称,同一个入口文件的依赖打包为同一个chunk在(名字加上cachegroups)。name为false时,name为id。 name为string时,无论入口,全部打包为同一个chunk。

splitchunks 最重要的配置为 cacheGroups,基本上如何分包就是在这里进行配置的。
cacheGroups 只要有一个属性没被覆盖,就会使用默认值。 即只要一个属性没有被覆盖,就默认使用以上取值。

splitchunks的配置项

五、文件缓存

文件缓存分为两部分,一部分是src目录下的文件缓存,另一部分时public目录下的静态资源缓存。

src 文件

src 文件的缓存比较容易处理,由于能在打包时生成文件的hash值,所以理论上可以设置这些文件的缓存时间无限。使用强缓存。

public 文件

该目录下的文件有以下特点:

  1. 发生更改时,文件名一般不会变化
  2. 不能使用hash值生成最终文件,因为代码里的引用路径为字符串,而不是将其作为依赖导入

根据以上特点,协商缓存是比较合理的办法。协商缓存有两种方式,一种是根据文件hash的缓存:ETag、If-None-Match,另外一种是根据时间的缓存:LastModified,If-Modified-Since。

由于没有对⽂件进⾏秒级的更改,选择第二种即可。

配置如下:

umi

chainWebpack(config, { webpack }) {
    config.plugin('copy-public').tap(() => [
        [
            {
                from: 'public',
                to: 'public',
                toType: 'dir',
            },
        ],
    ]);
}

egg

config.static = {
    dir: [
        {
            prefix: '/public',
            dir: path.join(appInfo.baseDir, 'app/view/public'),
            maxAge: 0,
        },
        {
            prefix: '/',
            dir: path.join(appInfo.baseDir, 'app/view/'),
            maxAge: 31536000, // dev设置为0
        },
    ],
    gzip: true, // dev 去除
};

设置maxAge为0,或者cacheControl为no-cache开启协商缓存。
更改完之后,引⽤public⽂件需要加/public/前缀。

你可能感兴趣的:(umi项目性能优化)