webpack实战——打包优化【上】

2020-10-27 11:06:30 浏览数 (1)

前言

本篇介绍一些webpack优化的配置方法,目的有二:

  1. 打包速度更快
  2. 输出资源更小

“注意:在软件工程领域有一条十分重要的功能经验,不要过早优化。在项目初期不要看到一个可以优化的点就去做优化,这样极有可能会增加尤其开发及维护的复杂度,并且从整体效果看,优化效果不会太理想。

1. HappyPack

“HappyPack是一个通过多线程来提升webpack打包速度的工具

1.1 工作原理

在打工过程中,非常耗时的一个工作是使用loader将各种资源进行转译处理,例如常见的使用babel-loader将ES6 语法代码转译为ES5等。代码转移的工作流程如下:

  1. 从配置中获取打包入口;
  2. 匹配loader规则,并对入口模块进行转译;
  3. 对转译后的模块进行依赖查找;
  4. 对新找到的模块重复步骤2)和3),直到没有新的依赖模块。

从上述步骤中可以看出,从步骤2)到步骤4)其实是一个递归的过程,webpack需要一步步地获取更下一级的资源然后逐个进行转译。为什么逐个?问题就在于webpack是单线程的。而HappyPack便将这里作为切入口,它的核心特性是可以开启多个线程,并行的对不同模块进行转译,这样便更加充分的利用计算机资源来提升打包速度。

1.2 工程目标

HappyPack显然更加适用于转译任务比较繁重的工程,当我们把类似babel-loadre,ts-loader等迁移到HappyPack之上后,一般会有比较不错的效果,而对于sass-loader等本身消耗时间并不太多的功能则效果一般。

1.3 单个loader优化

以babel-loader为例:

代码语言:javascript复制
// 初始webpack.config.js
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    presets: ['react'],
                }
            }
        ]
    }
}

使用HappyPack:

代码语言:javascript复制
// 使用HappyPack:
const HappyPack = require('happypack');
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                loader: 'happypack/loader'
            }
        ]
    },
    plugin: [
        new HappyPack({
            loaders: [
                {
                    loader: 'babel-loader',
                    options: {
                        presets: ['react']
                    }
                }
            ]
        })
    ]
}

在module.rules中,我们使用happypack/loader替换掉了原来的babel-loader,而在plugins中添加了HappyPack插件,将原来的bebel-loader及其配置插入进去即可。

1.4 多个loader优化

在使用HappyPack对多个loader进行优化时,需要为每一个loader配置一个id,否则HappyPack无法知道rules与plugins的对应关系

这里以babel-loader及ts-loader为例:

代码语言:javascript复制
// webpack.config.js
const HappyPack = require('happypack');
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                loader: 'happypack/loader?id=js'
            },
            {
                test: /.ts$/,
                exclude: /node_modules/,
                loader: 'happypack/loader?id=ts'
            }
        ]
    },
    plugin: [
        new HappyPack({
            id: 'js',
            loaders: [
                {
                    loader: 'babel-loader',
                    options: {
                        // babel配置
                    }
                }
            ]
        }),
        new HappyPack({
            id: 'ts',
            loaders: [
                {
                    loader: 'ts-loader',
                    options: {
                        // ts配置
                    }
                }
            ]
        })
    ]
}

除上述配置之外,可以查阅官方文档查看更多的配置,例如线程数,debug模式等。

2. 缩小打包作用域

从宏观角度看,提升性能的方式总结为两种:

  1. 增加资源:使用更多的CPU和内存,用更多的计算能力来缩短任务执行时间;
  2. 缩小范围:针对任务本身,去除冗余流程,不做重复性工作或使其简单化;

而上面我们所了解的HappyPack明显属于增加资源,那接下来介绍从缩小范围的几个方案。

2.1 exclude 和 include

在前面章节(预处理器(loader)【上篇】)中,介绍过exclude和include,在配置loader的时候一般都会对其进行配置。对于JS来说,一般需要把node_modules目录排除掉,另外当exclude和include规则有重叠部分时,exclude优先级更高。

那对于此处,我们使用include让babel-loader只生效于源码目录:

代码语言:javascript复制
// webpack.config.js
...
module: {
    rules: [
        test: /.js$/,
        include: /src/scripts/,
        loader: 'babel-loader'
    ]
}

2.2 noParse

有些库我们希望webpack完全不要去解析,那此时可以使用noParse对其进行忽略,如:

代码语言:javascript复制
// webpack.config.js
// ...
module.exports = {
    noParse: {
        // fullPath是绝对路径,如 /User/me/app/webpack/noparse/lib/lodash.js
        return /lib/.test(fullPath);
    }
}

如上配置将忽略所有lib目录下的资源解析。

2.3 IgnorePlugin

exclude和include是确定loader的规则范围,noParse是不去解析但仍会打包到bundle中,那接下来介绍一个插件——IgnorePlugin,他可以完全排除一些模块,被排除的模块即使被引用也不会被打包进资源文件中。一般作用于排除一些库相关文件。

一个由库产生的额外资源我们用不到但没办法去掉时,可以考虑使用此方法处理。

例,一个日期时间处理的相关插件Moment.js,为了做本地化会加载许多语言包,但一般我们只会用到本地的语言包而不会使用其他地区的语言包,而语言包会占用很大体积,这时可以使用IgnorePlugin来做处理:

代码语言:javascript复制
// webpack.config.js
...
plugins: [
    new webpack.IgnorePlugin({
        resourceRegexp: /^./locale$/, // 匹配资源文件
        contextRegExp: /moment$/    // 匹配检索目录
    })
]

2.4 Cache

我们在使用某些loader时会有一个cache的配置项,用来在编译代码后同时保存一份缓存,在执行下一次编译前会优先检查源码文件是否有变化,没有则直接使用缓存结果,也就是上次编译的结果。这样一来,只有在发生变化时编译变化了的文件,对于整体而言也属于一种优化处理。

小结

本篇从多线程打包缩小打包作用域两个方面入手,对webpack打包层面做出优化。下一篇描述从动态链接库思想死代码检测方面继续深入探究打包层面的深度优化。

0 人点赞