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/前缀。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342