赞
踩
随着项目的迭代,项目越来越大,依赖越来越多,冷启动和热更新的速度越来越慢,打包后的项目体积也逐渐膨胀;为了优化开发体验和用户体验,需要对项目进行webpack构建优化,此次优化主要涉及构建速度和构建体积的优化。
以下是项目原carco.config.js
配置
const FileManagerPlugin = require("filemanager-webpack-plugin");
const Webpack = require("webpack");
const fs = require("fs");
const path = require("path");
const { name, version } = require("./package.json");
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
const appPath = resolveApp(".");
const appBuild = resolveApp("build");
process.env.PORT = 3000;
// 环境信息
const env = process.env.REACT_APP_TARGET_ENV;
let source = `${appPath}/config.dev.js`;
if (env === "test") {
source = `${appPath}/config.test.js`;
} else if (env === "pre") {
source = `${appPath}/config.pre.js`;
} else if (env === "pro") {
source = `${appPath}/config.pro.js`;
}
module.exports = {
reactScriptsVersion: "react-scripts" /* (default value) */,
plugins: [
{
plugin: {
overrideDevServerConfig: ({ devServerConfig }) => {
return {
...devServerConfig,
headers: {
"Access-Control-Allow-Origin": "*",
},
};
},
},
},
],
webpack: {
alias: {
"@": path.resolve(__dirname, "src"),
"@components": path.resolve(__dirname, "src/components"),
"@containers": path.resolve(__dirname, "src/containers"),
"@constants": path.resolve(__dirname, "src/constants"),
"@utils": path.resolve(__dirname, "src/utils"),
"@routes": path.resolve(__dirname, "src/routes"),
"@assets": path.resolve(__dirname, "src/assets"),
"@styles": path.resolve(__dirname, "src/styles"),
"@services": path.resolve(__dirname, "src/services"),
"@mocks": path.resolve(__dirname, "src/mocks"),
"@hooks": path.resolve(__dirname, "src/hooks"),
"@stories": path.resolve(__dirname, "src/stories"),
},
// configure: { /* Any webpack configuration options: https://webpack.js.org/configuration */ },
configure: (webpackConfig, { env, paths }) => {
// 配置扩展扩展名
webpackConfig.resolve.extensions = [...webpackConfig.resolve.extensions, ...[".scss", ".css"]];
if (env !== "development") {
webpackConfig.plugins = webpackConfig.plugins.concat(
new FileManagerPlugin({
events: {
onEnd: {
mkdir: [`zip/${name}/dist`, `zip/${name}/template`],
copy: [
{
source: `${appPath}/conf${
process.env.REACT_APP_TARGET_ENV === "dev" ? ".development.js" : ".production.js"
}`,
destination: `${appBuild}/config.js`,
},
{
source: source,
destination: `${appBuild}/config.js`,
},
{
source: `${path.resolve("build")}`,
destination: `zip/${name}/dist`,
},
{
source: path.resolve("template"),
destination: `zip/${name}/template`,
},
],
archive: [
{
source: `zip`,
destination: path.relative(__dirname, `./${name}-${version}-SNAPSHOT.tar.gz`),
format: "tar",
options: {
gzip: true,
gzipOptions: {
level: 1,
},
globOptions: {
nomount: true,
},
},
},
],
delete: ["zip"],
},
},
runTasksInSeries: true,
}),
);
}
webpackConfig.output.library = `${name}-[name]`;
webpackConfig.output.libraryTarget = "umd";
// webpackConfig.output.jsonpFunction = `webpackJsonp_${name}`;
webpackConfig.output.globalObject = "window";
return webpackConfig;
},
},
};
在优化之前,我们需要了解一些量化分析的工具,使用它们来帮助我们分析需要优化的点。
webpackbar可以在打包时实时显示打包进度。
配置也很简单,如果是在webpack.config.js
中,直接在plugins数组中加入即可;
const WebpackBar = require('webpackbar');
module.exports = {
plugins: [
...
new WebpackBar()
]
}
如果是在craco.config.js
中需要在webpack
属性下的plugins
里加入:
const WebpackBar = require('webpackbar');
module.exports = {
webpack:{
plugins: [
...
new WebpackBar()
]
}
}
加入这个插件之后就可以在打包的时候看到打包的进度了。
使用speed-measure-webpack-plugin
可以看到每个loader
和plugin
的耗时情况。
和普通插件的使用略有不同,需要用它的wrap
方法包裹整个webpack
配置项。
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
entry: './src/main.js',
...
})
对于carco
而言,需要在webpack
前使用wrap
方法包裹住即可;
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = {
reactScriptsVersion: "react-scripts" /* (default value) */,
webpack: smp.wrap({
alias:{},
})
};
打包后,在命令行的输出信息如下,我们可以看出哪些loader
和plugin
耗时比较久,然后对其进行优化
比如现在我们项目冷启动的时间是4分零4秒
生产环境打包需要的时间是2分34秒
这个构建的时间还是挺久的。
webpack-bundle-analyzer
以可视化的方式让我们直观地看到打包的bundle
中到底包含哪些模块内容,以及每一个模块的体积大小。我们可以根据这些信息去分析项目结构,调整打包配置,进行优化。
在plugins
数组中加入该插件。构建完成后,默认会在http://127.0.0.1:8888/
展示分析结果。
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
plugins: [
...
new BundleAnalyzerPlugin()
]
}
一般我们在本地开发环境不需要分析打包体积,故craco.config.js
中进行如下配置:
const WebpackBar = require("webpackbar");
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const smp = new SpeedMeasurePlugin();
process.env.PORT = 3000;
module.exports = {
reactScriptsVersion: "react-scripts" /* (default value) */,
webpack: smp.wrap({
configure: (webpackConfig, { env, paths }) => {
if (env !== "development") {
webpackConfig.plugins = webpackConfig.plugins.concat(
new BundleAnalyzerPlugin({
analyzerMode: "server",
analyzerHost: "127.0.0.1",
analyzerPort: 8888,
openAnalyzer: true, // 构建完打开浏览器
reportFilename: path.resolve(__dirname, `analyzer/index.html`),
}),
);
}
},
plugins: [
new WebpackBar(),
],
}),
};
webpack-bundle-analyzer
会计算出模块文件在三种情形下的大小:
babel-loader
转换ES6->ES5
、UglifyJsPlugin
压缩等等)Gzip
压缩的大小使用speed-measure-webpack-plugin
和webpack-bundle-analyzer
本身也会增加打包时间(webpack-bundle-analyzer特别耗时
),所以建议这两个插件只在开发分析时使用,而在生产环境实际使用中去掉。
可以看到,我们的项目打包压缩后还有1.03MB
大小
运行在Node.js
之上的 Webpack
是单线程的,就算有多个任务同时存在,它们也只能一个一个排队执行。当项目比较复杂时,构建就会比较慢。如今大多数CPU
都是多核的,我们可以借助一些工具,充分释放 CPU
在多核并发方面的优势。
比较常见的方案有HappyPack
、thread-loader
,由于happypack
的作者已经没有这个项目进行维护了,我们这里选择thread-loader
。
可以看到,HappyPack
最后一次更新是四年前,确实很久远了。
thread-loader
使用起来很简单,就是把它放置在其它loader
之前,如下所示。放置在这个thread-loader
之后的 loaders
会运行在一个单独的worker池
中。
module.exports = {
module:{
rules:[
{
test: /\.js$/,
use: ['thread-loader','babel-loader']
}
]
},
}
对于craco
来说,项目的loader
都已经经过封装了,所以我们需要用到craco
提供的一些工具方法来实现给原有loader
加上loader
的能力;
const { addBeforeLoaders, loaderByName } = require("@craco/craco");
configure: (webpackConfig, { env, paths }) => {
addBeforeLoaders(webpackConfig, loaderByName("style-loader"), "thread-loader");
addBeforeLoaders(webpackConfig, loaderByName("style-loader"), "cache-loader");
}
可以看到使用thread-loader
之后生产环境构建用了1分58秒
,构建速度提升了36秒钟
使用thread-loader
之后开发环境冷启动时间将至了1分18秒
,足足减少了两分46秒
利用缓存可以提升二次构建速度(下面的对比图都是二次构建的速度)。使用缓存后,在node_modules
中会有一个.cache
目录,用于存放缓存的内容。
在一些性能开销较大的 loader
之前添加此cache-loader
,以将结果缓存到磁盘中。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['cache-loader','babel-loader']
}
]
}
}
可以看到生产环境二次构建速度提升了6秒钟
开发环境热更新时间为8秒钟
对babel-loader
使用缓存,也可以不借助cache-loader
,直接在babel-loader
后面加上?cacheDirectory=true
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory=true']
}
]
}
}
hard-source-webpack-plugin
用于开启模块的缓存。
这插件很多文章都有推荐,感觉很不错的样子,用起来也很简单,只需要做如下配置:
const HardSourceWebpackPlugin = require("hard-source-webpack-plugin")
module.exports = {
plugins:[
new HardSourceWebpackPlugin()
]
}
然鹅,你以为真的会这么简单吗?
实际在使用的过程中会报错:
Cannot find module 'webpack/lib/DependenciesBlockVariable'
笔者也是百思不得其解啊,去Google
也没有人遇到这种问题。
不得已,只能去hard-source-webpack-plugin
的github
上看issue
,发现其实有人遇到这个问题的,他们的解决方案就是降低webpack
的版本,可笔者这里没办法这么做,因为都集成在craco
里了,根本不可能降版本。
可能是因为hard-source-webpack-plugin
这个插件太久没更新,webpack版本落后导致的吧,特意查了一下,这个插件最后一次更新是在四年前,所以这个插件是没法使用了。
持久化缓存算得上是 Webpack 5
最令人振奋的特性之一,它能够将首次构建结果持久化到本地文件系统,在下次执行构建时跳过一系列解析、链接、编译等非常消耗性能的操作,直接复用 module
、chunk
的构建结果。使用持久化缓存后,构建性能有巨大提升。
那么,为什么开启持久化缓存之后构建性能会有如此巨大的提升呢?一言蔽之,Webpack5
会将首次构建出的 Module
、Chunk
、ModuleGraph
等对象序列化后保存到硬盘中,后面再运行的时候就可以跳过一些耗时的编译动作,直接复用缓存信息。
使用:
module.exports = {
cache: {
// 缓存类型,支持 'memory' | 'filesystem',需要设置 filesystem 才能开启持久缓存
type: "filesystem",
// 缓存文件存放的路径,默认为 node_modules/.cache/webpack
cacheDirectory: path.resolve(__dirname, ".temp_cache"),
// 是否输出缓存处理过程的详细日志,默认为 false
profile: false,
// 缓存失效时间,默认值为 5184000000
maxAge: 5184000000,
},
}
对于craco
来说可以使用如下方式配置:
configure: (webpackConfig, { env, paths }) => {
// 开启持久化缓存
webpackConfig.cache.type = "filesystem";
return webpackConfig;
}
开启缓存后二次构建时间为1分51秒
,快了7秒钟
开发环境冷启动直接降为7.3秒
持久化缓存详细配置可参考官方文档
通常来说,loader
会处理符合匹配规则的所有文件。比如babel-loader
,会遍历项目中用到的所有js
文件,对每个文件的代码进行编译转换。而node_modules
里的js
文件基本上都是转译好了的,不需要再次处理,所以我们用 include/exclude
来帮我们避免这种不必要的转译。
module.exports = {
module:{
rules:[
{
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/
//或者 include: [path.resolve(__dirname, 'src')]
}
]
},
}
include
直接指定查找文件夹,比exclude
效率更高,更能提升构建速度。
由于craco
封装了webpack
配置,不太好直接修改rules
,故采用如下方案:
configure: (webpackConfig, { env, paths }) => {
// 缩小loaders处理范围
webpackConfig.module.rules.forEach((rule) => {
rule.include = path.resolve(__dirname, "src");
});
return webpackConfig;
}
reslove.extensions
配置resolve.extensions
用于配置在尝试过程中用到的后缀列表,如果这个列表越长,或者正确的后缀在越后面,就会造成尝试的次数越多,所以resolve.extensions
的配置也会影响到构建的性能。 在配置resolve.extensions
时你需要遵守以下几点,以做到尽可能的优化构建性能:
require('./data')
写成require('./data.json')
。craco
默认的reslove.extensions
配置是[ '.web.mjs', '.mjs', '.web.js', '.js', '.web.ts', '.ts', '.web.tsx', '.tsx', '.json', '.web.jsx', '.jsx' ]
,对于我这个项目而言,这个配置是有点冗余和不合理的,因为我这个项目主要是用ts
写的,源码中有大量的tsx
和ts
文件,所以把extensios
做出如下配置优化:
// 配置扩展扩展名
webpackConfig.resolve.extensions = [".tsx", ".ts", ".jsx", ".js", ".scss", ".css"];
之后再打包构建,会发现构建速度有了小幅度提升。
以下是webpack
官网对noParse
的解释:防止 webpack
解析那些任何与给定正则表达式相匹配的文件。忽略的文件中 不应该含有 import, require, define
的调用,或任何其他导入机制,忽略大型的 library
可以提高构建性能。
对于项目中有引用jquery
或者lodash
这种不需要解析的第三方库而言,忽略对这些第三方库的解析也会略微提升构建性能。
webpack
配置:
module.exports = {
//...
module: {
noParse: /jquery|lodash/,
},
};
craco
中的配置:
configure: (webpackConfig, { env, paths }) => {
// jquery不需要解析;笔者实测,lodash需要解析,可能和我用的版本有关系
webpackConfig.module.noParse = /jquery/;
return webpackConfig;
}
常用的js代码压缩插件有:uglifyjs-webpack-plugin
和 terser-webpack-plugin
在webpack4中,生产环境默认开启代码压缩。我们也可以自己配置去覆盖默认配置,来完成更定制化的需求。
v4.26.0
版本之前,webpack
内置的压缩插件是uglifyjs-webpack-plugin
,从v4.26.0版本开始,换成了terser-webpack-plugin
。我们这里也以terser-webpack-plugin
为例,和普通插件使用不同,在optimization.minimizer
中配置压缩插件
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true, //开启并行压缩,可以加快构建速度
sourceMap: true, //如果生产环境使用source-maps,则必须设置为true
})
]
}
}
构建时间降到了1分33秒
source-map
优化对于生产环境不需要生成soure-map
映射文件的项目可以优化devtool
选项,来优化构建;对于carco
来说可以同时在生产环境移除source-map-loader
;
// 生产环境移除source-map-loader
removeLoaders(webpackConfig, loaderByName("source-map-loader"));
优化之后生产环境打包只需要1分16秒
,缩短了近一半的时间。
分离第三方库和业务代码中的基础库,可以避免单个bundle.js
体积过大,加载时间过长。并且在多页面构建中,还能减少重复打包。
SplitChunks
插件是什么呢?
简单的来说就是 Webpack
中一个提取或分离代码的插件,主要作用是提取公共代码,防止代码被重复打包,拆分过大的js
文件,合并零散的js
文件。
使用配置:
// splitChunks打包优化
webpackConfig.optimization.splitChunks = {
...webpackConfig.optimization.splitChunks,
cacheGroups: {
commons: {
chunks: "all",
// 将两个以上的chunk所共享的模块打包至commons组。
minChunks: 2,
name: "commons",
priority: 80,
},
},
};
使用SplitChunks
后项目体积减少到了到了995kb
动态import
的作用主要减少首屏资源的体积,非首屏的资源在用到的时候再去请求,从而提高首屏的加载速度。一个常见的例子就是单页面应用的路由管理(比如vue-router
)
{
path: '/list',
name: 'List',
component: () => import('../views/List.vue')
},
不是直接import
组件(import List from '../views/List.vue'
),那样会把组件都打包进同一个bundle
。而是动态import
组件,凡是通过import()
引用的模块都会打包到独立的bundle
,使用到的时候再去加载。对于功能复杂,又不是首屏必须的资源都推荐使用动态import
。
使用ES6
的import/export
语法,并且使用下面的方式导入导出你的代码,而不要使用export default
。
// util.js 导出
export const a = 1
export const b = 2
export function afunc(){}
或
export { a, b, afunc }
// index.js 导入
import { a, b } from './util.js'
console.log(a,b)
那么在mode:production
生产环境,就会自动开启tree-shaking
,移除没有使用到的代码,上面例子中的afunc
函数就不会被打包到bundle
中。
对于antd
这种第三方库,开启按需加载;lodash
也可以开启按需加载
安装相关插件:
npm install babel-plugin-import babel-plugin-lodash --save-dev
配置babel
:
module.exports = {
babel: {
plugins: [
// antd按需加载
[
"import",
{
libraryName: "antd",
libraryDirectory: "es",
style: "css",
},
],
// lodash按需加载
"lodash",
],
}
}
dayjs
替换antd
的moment
众所周知moment
的体积远远大于dayjs
的体积,而antd
默认使用的是moment
,这在无形中也增加了项目打包的体积,好在antd
官方提供了一个webpack
插件方便我们将moment
替换为dayjs
;(为什么替换成dayjs
而不是date-fn
呢?主要是因为dayjs
的api
和moment
几乎一样,基本没有切换成本)。
替换步骤如下:
npm install antd-dayjs-webpack-plugin -D
webpack
配置文件中设置插件const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
module.exports = {
// ...
plugins: [
new AntdDayjsWebpackPlugin()
]
};
详细配置可参考官方插件
注意:在craco.config.js
中需要配置在webpack
的插件中
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
其实第三方库代码基本都是成熟的,不用作什么处理。因此,我们可以将项目的第三方库代码分离出来。
常见的处理方式有三种:
Externals
可以避免处理第三方库,但每一个第三方库都得在html
文档中增加一个script
标签来引入,一个页面过多的js
文件下载会影响网页性能,而且有时我们只使用第三方库中的一小部分功能,用script
标签全量引入不太合理。
SplitChunks
在每一次构建时都会重新构建第三方库,不能有效提升构建速度。
这里推荐使用DllPlugin
和DLLReferencePlugin
(配合使用),它们是webpack
的内置插件。DllPlugin
会将不频繁更新的第三方库单独打包,当这些第三方库版本没有变化时,就不需要重新构建。
使用方法:
1.使用DllPlugin
打包第三方库
2.使用DLLReferencePlugin
引用manifest.json
,去关联第1步中已经打好的包
webpack
配置文件webpack.dll.js
用于打包第三方库// webpack.dll.js
const path = require('path');
const { DllPlugin } = require('webpack');
module.exports = {
mode: 'production',
entry: {
vendor: ['react', 'react-dom', 'react-router-dom'], // 需要统一打包的类库
},
output: {
filename: '[name].dll.js', //name必须要和output.library一致
path: path.resolve(__dirname, 'public/lib'),
library: '[name]',
},
plugins: [
new DllPlugin({
name: '[name]',
path: path.resolve(__dirname, 'public/lib/[name].json'), //manifest.json的存放位置
}),
],
};
package.json
文件中的scripts
属性下新增如下命令"dll": "webpack --config webpack.dll.js",
然后在控制台运行npm run dll
即可将第三方库一起打包好;之后如果没有需要更新第三方库的需求都不需要再打包了
webpack
配置,去关联第1步中已经打好的包const { DllReferencePlugin } = require('webpack');
const manifest = require('./public/lib/vendor.json');
module.exports = {
// 这是craco.config.js中的配置,如果是webpack.config.js中则不需要加webpack这一层
webpack:{
plugins:[new DllReferencePlugin({ manifest })]
}
}
html
中引入第一步打包好的vendor.dll.js
,引入的顺序需要在业务代码的前面,如果不引入或引入顺序错误的话会报错vendor is undefined
。开启gzip
压缩,可以减小文件体积。在浏览器支持gzip
的情况下,可以加快资源加载速度。服务端和客户端都可以完成gzip
压缩,服务端响应请求时压缩,客户端应用构建时压缩。但压缩文件这个过程本身是需要耗费时间和CPU
资源的,如果存在大量的压缩需求,会加大服务器的负担。
所以可以在构建打包时候就生成gzip
压缩文件,作为静态资源放在服务器上,接收到请求后直接把压缩文件返回。
使用webpack
生成gzip
文件需要借助compression-webpack-plugin
,使用配置如下:
const CompressionWebpackPlugin = require("compression-webpack-plugin")
module.exports = {
plugins: [
new CompressionWebpackPlugin({
test: /\.(js|css)$/, //匹配要压缩的文件
algorithm: "gzip"
})
]
}
打包完成后除了生成打包文件外,还会额外生成 .gz
后缀的压缩文件。可以看出,gzip
压缩文件的体积比未压缩文件的体积小很多。
但是!需要服务器支持
今天就到这里啦!
以下是craco.config.js
的完整配置:
const FileManagerPlugin = require("filemanager-webpack-plugin");
const WebpackBar = require("webpackbar");
const { DllReferencePlugin } = require("webpack");
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const TerserPlugin = require("terser-webpack-plugin");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const { addBeforeLoaders, removeLoaders, loaderByName } = require("@craco/craco");
const fs = require("fs");
const path = require("path");
const { name, version } = require("./package.json");
const manifest = require("./public/lib/vendor.json");
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
const appPath = resolveApp(".");
const appBuild = resolveApp("build");
const smp = new SpeedMeasurePlugin();
process.env.PORT = 3000;
// 环境信息
const env = process.env.REACT_APP_TARGET_ENV;
let source = `${appPath}/config.dev.js`;
if (env === "test") {
source = `${appPath}/config.test.js`;
} else if (env === "pre") {
source = `${appPath}/config.pre.js`;
} else if (env === "pro") {
source = `${appPath}/config.pro.js`;
}
module.exports = {
reactScriptsVersion: "react-scripts" /* (default value) */,
babel: {
plugins: [
// lodash按需加载
"lodash",
],
loaderOptions: {
// babel-loader开启缓存
cacheDirectory: true,
},
},
plugins: [
{
plugin: {
overrideDevServerConfig: ({ devServerConfig }) => {
return {
...devServerConfig,
headers: {
"Access-Control-Allow-Origin": "*",
},
};
},
overrideWebpackConfig: ({ webpackConfig, context: { env } }) => {
if (env !== "development") {
// 缩小生产环境所有loaders的检索范围
webpackConfig.module.rules[0].oneOf.forEach((rule) => {
rule.include = path.resolve(__dirname, "src");
});
} else {
// 缩小本地开发环境所有loaders的检索范围
webpackConfig.module.rules[0].include = path.resolve(__dirname, "src");
webpackConfig.module.rules[1].oneOf.forEach((rule, index) => {
rule.include = path.resolve(__dirname, "src");
// 本地开发环境babel-loader比较耗时,故加上thread-loader
if (index === 3) {
const babelLoader = {
loader: rule.loader,
options: rule.options,
};
rule.use = ["thread-loader", babelLoader];
delete rule.loader;
delete rule.options;
}
});
}
return {
...webpackConfig,
};
},
},
},
],
webpack: smp.wrap({
alias: {
"@": path.resolve(__dirname, "src"),
"@components": path.resolve(__dirname, "src/components"),
"@containers": path.resolve(__dirname, "src/containers"),
"@constants": path.resolve(__dirname, "src/constants"),
"@utils": path.resolve(__dirname, "src/utils"),
"@routes": path.resolve(__dirname, "src/routes"),
"@assets": path.resolve(__dirname, "src/assets"),
"@styles": path.resolve(__dirname, "src/styles"),
"@services": path.resolve(__dirname, "src/services"),
"@mocks": path.resolve(__dirname, "src/mocks"),
"@hooks": path.resolve(__dirname, "src/hooks"),
"@stories": path.resolve(__dirname, "src/stories"),
},
// configure: { /* Any webpack configuration options: https://webpack.js.org/configuration */ },
configure: (webpackConfig, { env }) => {
// 配置扩展扩展名优化
webpackConfig.resolve.extensions = [".tsx", ".ts", ".jsx", ".js", ".scss", ".css", ".json"];
// 作为子应用接入微前端的打包适配,不接入微前端可以不需要
webpackConfig.output.library = `${name}-[name]`;
webpackConfig.output.libraryTarget = "umd";
webpackConfig.output.globalObject = "window";
// splitChunks打包优化
webpackConfig.optimization.splitChunks = {
...webpackConfig.optimization.splitChunks,
cacheGroups: {
commons: {
chunks: "all",
// 将两个以上的chunk所共享的模块打包至commons组。
minChunks: 2,
name: "commons",
priority: 80,
},
},
};
// 开启持久化缓存
webpackConfig.cache.type = "filesystem";
// 生产环境打包优化
if (env !== "development") {
webpackConfig.plugins = webpackConfig.plugins.concat(
new FileManagerPlugin({
events: {
onEnd: {
mkdir: [`zip/${name}/dist`, `zip/${name}/template`],
copy: [
{
source: source,
destination: `${appBuild}/config.js`,
},
{
source: `${path.resolve("build")}`,
destination: `zip/${name}/dist`,
},
{
source: path.resolve("template"),
destination: `zip/${name}/template`,
},
],
archive: [
{
source: `zip`,
destination: path.relative(__dirname, `./${name}-${version}-SNAPSHOT.tar.gz`),
format: "tar",
options: {
gzip: true,
gzipOptions: {
level: 1,
},
globOptions: {
nomount: true,
},
},
},
],
delete: ["zip"],
},
},
runTasksInSeries: true,
}),
new BundleAnalyzerPlugin({
analyzerMode: "server",
analyzerHost: "127.0.0.1",
analyzerPort: 8889,
openAnalyzer: true, // 构建完打开浏览器
reportFilename: path.resolve(__dirname, `analyzer/index.html`),
}),
new CompressionWebpackPlugin({
test: /\.(js|ts|jsx|tsx|css|scss)$/, //匹配要压缩的文件
algorithm: "gzip",
}),
);
webpackConfig.optimization.minimizer = [
new TerserPlugin({
parallel: true, //开启并行压缩,可以加快构建速度
}),
];
// 生产环境关闭source-map
webpackConfig.devtool = false;
// 生产环境移除source-map-loader
removeLoaders(webpackConfig, loaderByName("source-map-loader"));
} else {
addBeforeLoaders(webpackConfig, loaderByName("style-loader"), "thread-loader");
addBeforeLoaders(webpackConfig, loaderByName("style-loader"), "cache-loader");
}
return webpackConfig;
},
plugins: [new WebpackBar(), new DllReferencePlugin({ manifest })],
}),
};
声明:掘金同名账号guxin_duyin
是我本人,在掘金上发布此文章不是抄袭。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。