webpack4配置到优化到原理(上)
前言
-
前端繁荣发展,工程化已经成为高级前端工程师的必不可少的条件之一,打包构建的发展从
grunt
,fis
,glup
到rollup
,webpack
,Parcel
,技术手段千变万化, -
但其实不论任何一项技术或工具,都有五个阶段,
- 简单使用(菜鸟)
- 熟练掌握(老鸟)
- 弄清原理(高手)
- 改造优化(大牛)
- 创新超越(大神)
这五个阶段越往后是越艰难,但是你越是往后深入就越能透过表象看清它的本质,以在这快速变化的技术手段中站稳,以不变应万变
webpack现在是前端打包构建最流行的工具,那么我们就来好好了解一下它(webpack ^4.42.1)
首先梳理下本文要讲到的内容
-
核心概念
- entry(打包入口)
- output(打包后文件的处理)
- loader(对各种资源的处理)
- plugin (利用插件对webpack进行扩展和增强)
- mode(针对开发环境和生成环境的区分处理)
-
其他常用配置
- devServer(热更新)
- resolve(模块解析)
- optimization(优化)
- devtool(源码调试)
-
优化手段
- stats分析
- 速度分析
- 体积分析
- tree-shaking
- scope-hoisting
- 多进程构建
- 构建中断处理
- 并行压缩
- 预编译资源模块
- 提升二次构建速度
- css的tree-shaking
- webpack图片压缩
- 动态polufill
-
配置总结
-
webpack原理
-
loader编写
-
plugin编写
下面逐个介绍
一. 核心概念
1.entry(打包入口)
定义打包的入口
- webpack是一个模块打包器,他会把一切资源都当作是模块,模块之间存在依赖关系
- 入口指示使用哪些模块,根据依赖关系,构成了依赖树,如下图所示
- 对依赖树进行遍历,最终生成打包后资源
使用示例
// 简写
module.exports = {
entry: './src/index.js',
}
// 多入口
module.exports = {
entry: {
index: './src/index.js',
list: './src/index.js',
},
}
2. output(输出)
编译后文件输出到磁盘的相关配置
// 简写
module.exports = {
output: {
filename: '[name]_[chunkhash:8].js' //单个文件名可直接指定,多入口利用占位符保证文件名统一
path: path.join(__dirname, '../dist') // 写入文件磁盘路径
publicPath: 'http://cdn.example.com/assets/'
//资源使用 CDN ,给所有文件引入模版文件时加上路径前缀
},
}
占位符
- [name]
- 入口名称
- [id]
- 内部chunk id,例如0,1,2
- [hash]
- 所有文件哈希值相同,只要改变内容跟之前的不一致,所有哈希值都改变
- [chunkhash]
- 不同的entry生成不同的chunkhash
- 同一个模块,就算将js和css分离,其哈希值也是相同的,修改一处,js和css哈希值都会变
- [contenthash]
- 文件内容不一样,产生的哈希值就不一样
- [hash:8]
- 默认生成20位hash,可自定义截取位数
3. loader(资源解析转换)
webpack 原生只支持js 和json,利用loader,对不同文件类型支持,转换成有效的模块 简单示例
module.exports = {
module: {
rules:[
{
test: /\/.txt$/, // 指定匹配规则
use: 'babel-loader' // 指定使用的loader名称
}
]
}
}
下面介绍几种文件类型的处理以及常用的loader
- 解析ES6和JSX
babel-loader
:js默认是不支持es6 和jsx语法的,.babelrc
文件: 设置具体支持的属性方法
{
test: /\.(j|t)sx?$/,
use: 'babel-loader',
exclude: /node_modules/
},
- 解析CSS 项目里使用css文件
style-loader
:将样式通过style
标签插入模版文件的head当中css-loader
: 用于加载.css文件 并且转换成commonjs 对象
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
],
},
- 解析less
less-loader
:less转换成css,- 其他同上
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
options: { // 可配置属性,修改变量的值,一般利用来修改UI库的主题样式,以下是antd主题样式配置
modifyVars: {
'@primary-color': '#ec7259',
},
javascriptEnabled: true,
},
},
],
},
- 图片和字体解析
file-loader
:解析图片, 字体等url-loader
:也可处理图片和字体,,并可设置较小资源自动base64
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'static/img/[name].[hash:8].[ext]',// [ext] 文件的后缀名
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 8192,
name: 'static/fonts/[name].[hash:8].[ext]',
},
},
- 移动端适配
px2rem-loader
: 把px转换成rem,配合lib-flexible使用
{
loader: 'px2rem-loader',
options: {
remUnit: 75, // 1rem=多少像素
remPrecision: 8, // rem的小数点后位数
}
}
- css前缀补齐
postcss-loader
:用于浏览器适配,某些css3属性浏览器不支持需要加前缀,它会自动针对不同浏览器加不同的属性前缀
{
loader: 'postcss-loader',
options: {
plugins: () => [autoprefixer()],
},
},
4. plugin (利用插件对webpack进行扩展和增强)
- 解决loader无法完成的事
- 因为插件可以携带参数/选项,所以要向
plugins
属性传入new
实例 简单示例
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugin: {
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
}
}
下面介绍几种常用的plugin
- 页面打包
new HtmlWebpackPlugin({
filename: '../dist/template/index.html', // 指定生成的模版文件名及路径
template: path.join(__dirname, '../src/template/index.html'), // 指定要使用的模版文件
inject: true, // 指定的chunk会自动注入html文件中
chunks: ['index'], //指定生成的html要使用的chunk
minify: { // 代码的最小化输出
collapseWhitespace: true, // 删除空格,但是不会删除SCRIPT、style和textarea中的空格
preserveLineBreaks: false, // 是否保留换行符
minifyCSS: true, // css压缩
minifyJS: true, // js压缩
removeComments: true, // 删除注释,但是会保留script和style中的注释
},
}),
- 文件清理
- 每次打包文件到dist,首先要清理dist内部文件,或直接删除dist文件夹,防止文件重复 我们可利用
rimraf dist
- 但此方式不太优雅,我们可以使用clean-webpack-plugin,清理dist内部文件
new CleanWebpackPlugin(),
- css剥离
- css代码默认打包在js文件中,但有时候css变了,js没变,或者相反,这是不利于缓存的,
- 我们可以把css剥离出来单独生成文件,去做缓存处理
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
- css压缩
- 既然css单独剥离出来,就要做压缩,
new OptimizeCssAsssetePlugin({
assetNameRegExp: /\.css$/g, //文件匹配
cssProcessor: require('cssnano') // cssnano 压缩和优化的css插件
}),
- 基础库分离
- 我们常把一些不太变化的静态资源放在cdn上,然后在模版文件里引入
- 在webpack也提供了插件支持,可直接配置插入
- 假如我们分离react和react-dom
举例:
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
plugins: [
new HtmlWebpackExternalsPlugin({
externals:[
{
module: 'react',
entry: 'https://cdn.cn/16.8.0/react.min.js',
global: 'React',
},
{
module: 'react-dom',
entry: 'https://cdn.cn/16.8.0/react-dom.min.js',
global: 'ReactDOM',
}
]
}),
5. mode(针对开发环境和生成环境的区分处理)
- mode:对应三个属性,
development
: 开发模式,production
: 生产模式,none
: 无
webpack会针对不同环境直接做一些优化工作,例如production模式下会进行,tree-shaking
和scope-hosting
下面优化会详细介绍
二. 常用配置
6. devServer(热更新)
- 简介
-
远古时期,我们做前端开发时,写一个html文件,在浏览器打开它查看效果,修改时,需手动更新,
-
后来我们使用热更新,webpack低版本,不提供热更新的支持,我们使用插件
http-proxy-middleware
和webpack-hot-middleware
,实现热更新,配置比较麻烦 -
最后webpack把热更新集成在内部,就成了devServer
简单配置如下
devServer: {
historyApiFallback: true, // 单页面程序 刷新浏览器会出现404,原因是它通过这个路径(比如: /search/list)来访问后台,所以会出现404,而把historyApiFallback设置为true那么所有的路径都执行index.html
host: '127.0.0.1', // 域名
open: true, //支持自动打开浏览器
hot: true, // 模块热替换,在前端代码变动的时候无需整个刷新页面,只把变化的部分替换掉
inline: false, // inline选项会为入口页面添加“热加载”功能,即代码改变后重新加载页面
port: 8080, // 端口
proxy: proxyConfig._proxy, // 代理后端服务,举例:可本地调试测试接口
before(app) { // 其他中间件之前, 提供执行自定义中间件
apiMocker(app, path.resolve('./mocks/mock.js'), // 举例:可用来做mock数据
proxyConfig._proxy);
},
},
- 原理
首先来看下简单的流程示意图

webpack compile
将JS编译成bundle.jsHMR server
将热更新文件输出给HMR Runtime,HMR -> HotModuleReplacement(热模块替换)Bundle server
提供文件在浏览器的访问HMR Runtime
注入浏览器,更新文件的变化,使浏览器 和 服务器建立一个链接(websocket)bundle.js
构建输出的文件
启动阶段
-
1 -> 2 -> 3
-
初始代码经过webpack compiler编译进行打包
-
编译好的文件传输给bundle server, 它就相当于一个服务器,它使文件以server的方式让浏览器访问
-
files
->webpack Compiler
->Bundle Sever
->bundle.js
更新阶段
-
1 -> 4 -> 5 -> 6
-
file文件发生变化,经过webpack compiler编译
-
编译好的文件传输给HMR server,通知浏览器端的HMR Runtime(通常以JSON形式传输)
-
HMR Runtime 更新代码,实现无刷新改变页面内容
-
files
->webpack Compiler
->HMR server
->HMR Runtime
->code
7. resolve(模块解析)
- 设置模块如何被解析
介绍几个常用的属性用法
alias
创建 import 或 require 的别名,来确保模块引入变得更简单extensions
自动解析确定的扩展mainFileds
当从 npm 包中导入模块时,决定在 package.json 中使用哪个字段导入模块moudles
告诉 webpack 解析模块时应该搜索的目录
举例:
// webpack 配置文件
resolve: {
alias: {
Util: path.resolve(__dirname, 'src/util/'),
},
mainFileds: ['main'],
extensions: ['.js', '.jsx', '.json'],
moudles: [path.resolve(__dirname, 'node_modules')]
},
//业务文件 component.js
import Utility from '../../util/utility.js';
// 简化写法(不用写文件路径前缀,也不用写引用文件的扩展名)
import Utility from 'Util/utility';
8. optimization(优化)
- webpack 4 开始,会根据你选择的 mode 来执行不同的优化,不过所有的优化还是可以手动配置和重写 下面介绍几种常用的优化
- 提取公共资源
- 项目多页面的时候,大多数页面使用的基础库或依赖都是一样的,这时每个页面都单独打包一份,对资源是一种浪费,打包后体积较大,页面加载时间长,
- 所以我们可以把公共资源提取出来单独打包,访问多页面时利用缓存机制,只加载一次,达到优化目的
splitChunks
,代替之前的CommonsChunkPlugin,公共资源分离vendors
chunks属性特别说明
- async 对异步引入的文件分离(默认)
- initial 对同步引入的文件分离
- all 对所有匹配的文件分离 不论是同步还是异步我们都希望分离出来,所以推荐使用 all
- 公共文件分离
- 一些公共的工具函数类文件,我们可以通过限制被调用的次数来决定是否把它分离出来
- 利用
splitChunks
公共文件分离commons
- 提取webpack的模块化信息清单
- 模块信息清单在每次有模块变更(hash 变更)时都会变更, 所以把这部分代码单独打包出来, 配合后端缓存策略,
- 避免某个模块的变化导致包含在模块化信息中的模块缓存失效
- 具体使用
runtimeChunk
举例:
optimization: {
runtimeChunk: {
name: 'manifest',
},
splitChunks: {
minSize: 50000 // 分离的包的体积大小
cacheGroups: {
vendors: {
test: /(react|react-dom)/, //正则匹配要分离的文件
name: 'vendors',
chunks: 'all', // 确定对何种引入方式的文件进行分离
minChunks: 1, // 最小使用的次数
priority: 10, // 多个缓存组时,需要有优先级排列,优先使用哪个进行分离
},
commons: { // 分离公共文件
name: 'commons',
chunks: 'all',
minChunks: 2,
priority: 5,
},
},
},
},
9. devtool(源码调试)
- 控制是否生成,以及如何生成 source map
- 我们要进行一个配置以方便我们在测试环境进行问题定位,源码调试的增强
关键字定义
eval
模块都使用 eval() 包裹执行,并且都有 //@ sourceURL(指向的是原文件index.js,调试的时候,根据sourceUrl找到的index.js文件的)source map
产生.map文件(这个map文件会和原始文件做一个映射,调试的时候,就是通过这个.map文件去定位原来的代码位置的 )cheap
不包含列的信息,(假如代码运行出现了错误,控制台报出了,error,我们点击定位到具体源码的时候,就只能定位到行,而不能定位到具体的列)inline
.map文件作为dataUrl嵌入到打包文件,而不单独生成- moudle 包含loader的sourcemap(调试的代码不会被转换,会保留原始代码语法)
几种关键字进行组合就形成了具体的用法
不同用法对构建速度是有影响的,基本情况你越清晰容易的看到原始的代码,构建速度就越慢,调试的方便性和构建速度上大家可以自己权衡一下
共有13种用法,详细的请看官方文档
举例
// 开发环境
devtool: 'cheap-module-eval-source-map' // 原始源代码(仅限行)
// 生产环境,一般不进行设置
三. 优化手段
1. 优化分析 stats
构建统计信息
使用举例
// 构建完成后会生成json文件,显示构建的一些信息,时间,各模块的体积等
scripts: {
'build: stats': 'webpack --config build/webpack.prod.config.js --json > stats.json',
}
2. 速度分析
- 分析每个插件和loader的耗时情况 使用举例
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasureWebpackPlugin();
module.exports = smp.wrap(merge(webpackConfigBase, webpackConfigProd));

图中我们看到了每个插件和loader的耗时情况, 如果耗时较长,会以红字提示,我们就可以具体分析那个地方为什么时间长,可以用别的插件替换之类的去做构建速度优化
3. 体积分析
- 以图形大小的形式,更加直观的看到各个模块插件所占用的体积
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins: [
new BundleAnalyzerPlugin({
analyzerPort: 8919 //打包构建后体积分析图展示的窗口
}),
],
运行打包命令,体积分析示意图会自动打开在8919窗口

图中我们可以看到moment插件占用的空间很大,我们可以对它进行优化
- 减小体积(忽略语言包)
- 我们可以看到为了支持国际化,moment里包含了很多语言包,我们可以利用webpack内置的插件忽略它,在需要的时候按需引入
new webpack.IgnorePlugin(/\.\/locale/, /moment/),
// compoent
import 'moment/locale/zh-cn';
moment.locale('zh-cn');
- 替换插件
- 我们可以用更为轻量的dayjs插件进行替换,它的大小仅为2k
4. tree-shaking
- 字面意思是摇晃树,就是把树上坏掉的叶子摇下来,就是死码清除(即没有被用到的代码)
- 某个模块或文件,的某个方法被用到了,整个模块都会被打包都bundle文件中去,tree-shaking会把没有用到的方法去除,在uglify阶段清除
- 仅支持es6语法 webpack 4 中设置 production 默认开启了此项优化
Dead Code(什么是死码呢?)
- 代码不会被执行
- 执行结果不会被用到
- 代码只会影响死变量
ES6 module 特点:
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是 immutable的
ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础,让死码清除成为了可能
静态分析就是不执行代码,仅仅从字面的意思上对代码进行分析,ES6之前的模块化,比如我们可以动态require一个模块,只有执行后才知道引用的什么模块,这个就不能通过静态分析去做优化,特别说明import()
动态引入也是不支持的
5. scope-hoisting
- 问题 webpack构建后存在大量的闭包代码
- 大量函数闭包包裹代码,导致体积增大
- 运行代码时函数作用域变多,消耗更多的内存
- 举例
引用的文件tools.js
export default 'Hello World';
入口文件index.js
import str from './tools';
console.log(str);
未开启scope-hoisting
编译后文件
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
console.log(_tools__WEBPACK_IMPORTED_MODULE_0__["default"]);
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ('Hello World');
/***/ })
可以看到
- 0表示index模块
- 1表示tools模块
- 两个模块就存在两块函数闭包代码,真实的场景会有更多模块
开启scope-hoisting
编译后文件
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ var tools = ('Hello World');
console.log(tools);
/***/ })
可以看到
- 函数声明由两个变成了一个,tools.js 中定义的内容被直接注入到了 index.js 对应的模块中
- 优点 使用scope-hoisting
- 代码体积更小,因为函数申明语句会产生大量代码;
- 代码在运行时因为创建的函数作用域更少了,内存开销也随之变小。
- 原理
- 将所有模块的代码按照引用顺序,放在一个函数作用域内,
- 重命名一些变量防止命名冲突
- 小结
- webapack4 中mode设置production后也是默认开启的此项优化
- 使用非 ES6 模块或使用异步 import() 也不会把模块放到同一个函数作用域中去
- 还要注意得是,只合并被引用了一次的模块,引用多次的还是分成多个闭包,减少代码的冗余度
6. 多进程构建
- webpack构建是一个涉及文件的读写的过程,如果项目非常复杂,构建时间就会加长,
- 而webpack运行在nodejs上是单线程模型,同一时间只能处理一个任务
- 我们是否可以让webpack同时进行多任务处理呢
happypack
和thread loader
给我们提供了方案
由于happypack作者不再维护此项目,同时两者原理大致一致,我们就主要介绍
thread loader
- thread loader由官方提供
- thread loader放在最上面,就会在最后执行,之前的loader会在一个单独的worker池中运行,
- 每个 worker 都是一个单独的有 600ms 限制的 node.js 进程,从而实现了多进程的构建,降低构建时间 举例
rules: [
{
test: /.js$/,
use: [
{
loader: 'thread-loader',
options: {
workers: 3, // 产生的 worker 的数量,默认是 cpu 的核心数
}
}
]
},
]
注意:thread loader 只有在项目庞大复杂的时候才能显著的凸显效果,如果是中小型项目没有必要使用
日志上报
7. 构建异常,中断处理
- 构建过程中,有时会出现构建异常报错的情况,我们可以通过某些方法捕获,以及自定义一些逻辑
plugins: [
// 主动捕获构建错误
function () {
this.hooks.done.tap('done', (stats) => {
if (stats.compilation.errors
&& process.argv.indexOf('--watch' == -1)) {
console.log('error', stats.compilation.errors);
// 可以做一个构建系统的日志,在此处上报错误原因
process.exit(12); // 自定义错误code码
}
});
},
],
8. 并行压缩
- webpack4 推荐使用
terser-webpack-plugin
开启 parallel -
uglify-webpack-plugin
也支持并行压缩,但不支持es6,不做具体介绍,有兴趣的同学自行查询
optimization: {
minimizer: [
new TerserPluginWebpack({
parallel: 4, // 开启 不主动指定的话,默认数值是当前电脑cpu数量的2倍减1
})
],
}
9. 分包,预编译资源模块
- 问题 之前的分包存在问题
- externals 分包会打出太多的script标签
- splitchunk 分包需要一个分析的时间
- 解决
- 预编译资源模块
- 将react,react-dom redux 等基础包和业务基础包打包成一个文件
- 方法
dll-plugin
进行分包,dllreferenceplugin
对mainfest.json(对分离包的描述)引用,将预编译的模块加载进来- 利用
add-asset-html-webpack-plugin
把生成文件插入模版
举例
// package.json
"scripts": {
"dll": "webpack --config build/webpack.dll.js" // 打包前只需执行一次,只要基础包不变则不需要执行
},
// webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: { // 指定要分离的包
library: [ // 基础包
'react',
'react-dom',
],
},
output: {
filename: "[name]_[hash].dll.js",
path: path.resolve(__dirname, './library'),
library: "[name]_[hash]", //包名称 注意此名需和下面的DllPlugin,name名一致
},
plugins: [
new webpack.DllPlugin({
name: "[name]_[hash]",
path: path.resolve(__dirname, './library/[name].json')
})
]
}
// webpack.prod.js
plugins: [
...
// 将给定的 JS文件添加到 webpack 配置的文件中,并插入到模版文件中
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, '../build/library/*.dll.js'),
}),
//
new webpack.DllReferencePlugin({
manifest: require('../build/library/library.json')
}),
]
- 总结 【1】使用范围
- 引用但是不会修改的npm包 【2】优点:
- 多个包打在了一起
- DllPlugin 将包含大量复用模块且不会频繁更新的库进行编译,只需要编译一次,提升打包速度
10. 提升二次构建以后的速度
- 缓存插件
hard-source-webpack-plugin
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
plugins: [
...
new HardSourceWebpackPlugin()
]
]}
总结
- dll 虽然提升打包速度,但是配置复杂
- 并且
vue-cli
和create-react-app
中并没有使用dll - 所以如果只是为了提升打包速度,可以使用
hard-source-webpack-plugin
替换dll
- 但如果想把将多个npm包打成一个公共包,dll还是有点用的
11. css的tree-shaking(Remove unused CSS)
- 清除 css无用代码
- 早期PurifyCSSPlugin,它主要的作用是可以去除没有用到的CSS代码,类似JS的Tree Shaking,现已不再维护
- 现在使用purgecss-webpack-plugin,配合mini-css-extract-plugin使用
举例
const PATHS = {
src: path.join(__dirname, '../src')
}
plugin: [
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), // 注意是绝对路径匹配
}),
]
12. webpack 图片压缩
- 使图片压缩更加自动化
- 基于node库的imagemin
- 优点
- 支持定制选项,
- 可引入第三方插件
- 支持多个图片格式
- 具体用image-webpack-loader 举例
{
test: /.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name:'[name]_[hash:8].[ext]'
}
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
},
],
},
13 动态polyfill服务
- 问题
- babel-polyfill 打包时占资源比重较大
- 能否按需加载呢
- polyfill-service
- 获取浏览器的useragent判断支持情况,
- 动态的返回浏览器不支持的新特性
- 官方提供了cdn
https://polyfill.io/v3/polyfill.min.js
- 也可以基于官方自建polyfill服务,更加自由的配置属性(比如,指定只判断promise的支持程度)
例如
https://polyfill.alicdn.com/polyfill.min.js?features=Promise
- 注意
- 国内浏览器众多复杂,某些浏览器私自修改了useragent,导致判断不准确
- 我们可以判断执行错误时加载回全部polyfill,进行一个降级处理
四. 配置总结
我们以实际目标为导向收集整理一下常用webpack配置要做什么事情
1. 基础配置
- 解析js
- 解析css
- 解析less
- 解析图片
- 解析字体
- 前缀补齐
- 移动端适配
- 目录清理
- 页面打包
- css抽离
- 异常主动捕获
2.提高构建速度
- resolve缩小文件的搜索范围
- 使用DllPlugin减少基础模块编译次数
- thread loader 多进程构建
- terser并行压缩
- hard-source-webpack-plugin提升二次构建速度
- dll分包,预编译资源模块
3.减小构建体积
- 公共资源分离
- Tree Shaking
- scope-hoisting
- js,css,字体,图片压缩
- 动态polyfill
- css的tree-shaking
- 代码分割,按需引入,import动态加载
4.提升开发体验
- sourcemap源码调试
- devserver热更新
- 友好错误提示
5. 保证稳定安全
- 代码压缩混淆
小结
虽然说了很多关于webpack的配置和优化,但我们还是要根据项目的复杂程度,公司&项目的具体情况,处理遗留代码的难度,来选择性的处理, 有时如果强行使用反而得不偿失
总结
当然最后要记住一切的技术都只是工具,都要以赋能业务,价值产出为目标,打包工具的目标就是
- 提高工作效率,
- 提升开发体验,
- 提升用户体验,
- 保证稳定安全
参考
转载自:https://juejin.cn/post/6844904138044604430