webpack以一个默认的入口文件作为打包的起点,将所有的资源依赖关系记录,形成一个依赖关系树状图,根据这个树状图引入这些相关的资源。引入的资源形成一个chunk(代码块),分别将这些chunk进行处理(例如将es6转化,将sass预处理为css等,这些操作统一称为打包),将打包好的文件输出为静态资源bundle
五个核心概念
-
Entry指示
webpack以哪个文件为打包的起点,并分析构建好内部资源依赖图。 -
Output指示
webpack将打包后的资源bundle输出到哪里,以及如何命名。 -
Loader让
webpack能够去处理那些非js代码(webpack本身只理解js代码),如同翻译官的角色。loader模块下载后,课配置直接使用。 -
Plugins可以用于执行范围更广泛的任务,包括从打包优化和压缩,到重新定义环境中的变量等。
plugins插件下载后,还需引入才可配置使用。 -
Modedevelopment:能让代码本地调试运行的环境production:能让代码优化上线的运行环境
webpack能处理js/json资源,不能处理css/img等其他资源;打包的生产环境或开发环境代码,ES6模块化已经编译为浏览器能识别的模块化;打包的生产环境比开发环境多一个js压缩的过程。
webpack.config.js
指示webpack需要进行的工作,运行webpack指令时,会加载文件内的配置。所有构建工具都是基于nodejs平台运行的,默认采用commonjs标准模块化。
css文件编译处理
style-loader:创建style标签,将已转化为js的样式资源插入,然后添加到head中生效;css-loader:将css文件转化为commonjs模块加载到js中,模块内容为样式字符串;less-loader:将less文件编译成css文件,需要依赖less插件。
{
test: /\.css$/, //匹配文件
use: [ //use中loader执行顺序从后往前
'style-loader', //创建style标签,将js中的样式资源插入,然后添加到head中生效
'css-loader', //将css文件变成commonjs模块加载到js中,模块内容为样式字符串
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader', //将less文件编译成css文件,需要依赖less插件
]
},
html文件编译处理
html-webpack-plugin:不添加任何配置,默认会创建一个空的html,自动引入打包输出的所有资源(js/css);如果需要有结构的html文件,可以进行自定义配置。
new HtmlWebpackPlugin({
template: './src/index', //复制./src/index.html文件,并自动引入打包输出的所有资源
})
图片资源编译处理
使用url-loader(依赖file-loader,默认处理不了html中的img图片,webpack5不再使用)处理图片资源,limit配置表示图片大小小于8kb的情况下,会通过base64处理转换成字符串方式。base64可以减少请求数量,减轻服务器压力,但同时图片体积会更大,文件请求速度会更慢。
使用html-loader处理html文件的img图片,该插件负责引入img图片,从而能被url-loader进行处理。由于url-loader是使用es6模块处理文件,而html-loader使用commonJs引入图片,解析会出问题:[object Module],此时url-loader应使用esModule配置。
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[name]_[hash:10].[ext]', //name为图片文件名;hash:10取图片hash的前10位;ext取文件原来的扩展名
esModule: false, //关闭url-loader的es6模块化,使用commonJs解析
}
},
{
test: /\.html$/,
loader: 'html-loader'
}
在webpack5中我们使用assets-module,url-loader在这个版本中已经被废弃。
{
test: /\.(png|jpe?g|gif)/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024,
}
},
generator: {
filename: 'img/[name]_[hash:10].[ext]'
}
}
其他资源编译处理
{
exclude: /\.(css|jpg|png|gif|html|less|js)$/,
loader: 'file-loader',
options: {
name: '[name]_[hash:10].[ext]'
}
}
devServer
开发服务器,用来做自动化,能够自动编译,自动打开浏览器,自动刷新浏览器等。只会在内存中编译打包,不会有任何输出,output的配置将不再有用,终止服务器后,内存中存在的编译包将会自动删除。启动devServer指令为:webpack-dev-server(需下载该插件)
devServer: {
contentBase: resolve(__dirname, 'build'), //需要运行的项目的文件目录(构建后的目录),webpack5不用配置该选项(该选项已被删除)
compress: true, //启动gzip压缩,让项目体积更小,启动更快
port: 9006, //开发服务器的端口号
open: true, //自动打开浏览器
}
打包文件优化
mini-css-extract-plugin插件可以将样式文件从js代码中抽离出来,单独成一个文件目录,通过link标签引入。将css单独提取出来,可以减小js文件大小
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
{
test: /\.css$/, //匹配文件
use: [ //use中loader执行顺序从后往前
// 'style-loader',
MiniCssExtractPlugin.loader, //取代style-loader,提取出css代码
'css-loader',
]
},
plugins: [
new MiniCssExtractPlugin({
filename: './css/built.css', //对输出的css文件进行重命名,注意该路径为相对路径
})
]
css兼容性处理
使用postcss,依赖于插件postcss-loader与postcss-preset-env(帮助postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式,可以使兼容性精确到某一个浏览器版本。默认是找生产环境browserslist,若要找development环境,需要设置node环境变量:process.env.NODE_ENV = development)
"browserslist": { //package.json
"development": [
"last 1 chrome version", //兼容至少最近的一个chrome浏览器版本
"last 1 safari version"
],
"production": [
">0.2%", //大于99.8%的浏览器
"not dead", //不要已经废弃的浏览器版本
"not op_mini all", //不要op_mini浏览器,webpack5已经删除
]
}
{ //webpack.config.js
test: /\.css$/, //匹配文件
use: [ //use中loader执行顺序从后往前
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss', //固定写法
plugins: () => [
require('postcss-preset-env')()
]
}
},
{ //webpack5用法
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
},
}
]
},
css压缩
使用插件optimize-css-assets-webpack-plugin,webpack.config.js中直接调用插件,默认的配置已经足够。
js语法检查
语法检查只针对js文件,使用eslint-loader插件(依赖于eslint库),应该只检查自己的源代码,排除其他插件代码。需要在package.json中eslintConfig设置具体的检查规则。推荐使用airbnb风格指南,指示如何规范的编写js代码。运用airbnb可以通过使用插件eslint-config-airbnb(包含react风格建议)或eslint-config-airbnb-base(不包含react风格建议)。
eslint-config-airbnb依赖于插件eslint,eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-jsx-a11y
eslint-config-airbnb-base依赖于插件eslint,eslint-plugin-import
{
test: /\.js$/,
exclude: /node_modules/,
enforce: 'pre', //优先执行
loader: 'eslint-loader',
options: {
fix: true, //自动修复代码格式问题
}
}
//eslint-disable-next-line 不检查下一行代码
{ //packag.json
"eslintConfig": {
"extends": "airbnb-base" //使用该包进行语法检查
}
}
js兼容性处理
使用bable-loader插件,依赖于插件@babel/core与@babel/preset-env,只能转换基本语法,如promise不能转换。
{
test: /\.js/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], //指示babel做怎样的兼容性处理
}
}
全部js兼容性处理还需插件@babel/polyfill,直接在js文件中引入import '@babel/polyfill'。该插件会将所有兼容性代码全部引入,导致文件体积太大。
按需引入需要做兼容性处理的代码,需要借助corejs,需要下载插件@babel/core。使用这种方法就不能同时使用第二种方法。
{ //有报错
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage', //按需下载
corejs: {
version: 3, //指定corejs版本
},
targets: { //指定兼容性做到哪个版本浏览器
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17',
}
}
]
]
}
},
webpack性能优化
开发环境性能优化
-
优化打包构建速度
HMR:hot module replacement(热模块替换/模块热替换),一个模块发生变化,只会重新打包这一个模块(而不是打包整个模块),不会重新刷新浏览器,极大提升构建速度。devServer配置中添加hot: true就可以使用该功能。样式文件由于style-loader内部实现了HMR功能,可以实现热更新。因此在开发环境中应该使用style-loader,使样式文件打包更快;html文件默认不支持该功能,由于项目中只存在一个,html文件发生变化的时候也只需要更新这一个文件,因此不需要HMR功能;js文件默认也不支持该功能,若要实现该功能(只能处理非入口js文件),则需要添加代码:if(module.hot) { //为true则表示HMR功能为开启状态 module.hot.accept('./print.js', () => { //module.hot.accept监听print.js文件,一旦该文件发生变化,其他模块不会重新打包构建,只会再次执行当前这个回调函数 print(); }) } -
优化代码调试
source-map:一种提供源代码到构建后代码映射的技术,如果构建后代码出错了,通过映射,可以追踪到源代码错误位置。使用方式:在webpack.config.js中添加配置devtool: 'source-map'cheap-module-source-map中module表示会将loader的source-map也加入。开发环境推荐eval-source-map更快,生产环境推荐source-map
生产环境性能
-
优化打包构建速度
one-of可以在webpack构建的时候,相应文件只用匹配一个规则,此是有一个问题,相同类型文件不能有两个规则来处理。解决方法是,将处理相同文件的多个规则移出one-of。rules: [{}, {oneOf: []}]babel缓存:配置
babel-loader规则选项cacheDirectory为true,表示在第二次构建时, 会读取之前的缓存。使第二次构建速度更快。 -
优化代码运行的性能
文件资源缓存:让代码上线运行缓存更好使用
hash:每次webpack构建时,都会生成一个唯一的hash值,将js和css文件名加上hash值,可以使每次构建后都能拿到最新的代码。但由于webpack每次构建都会生成一个新的hash值,导致所有的缓存都会失效。chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,hash值就一样。chunk:由入口文件加上入口文件中引入的js或css文件形成一个chunk。由于css是在js中被引入的,因此同属于一个chunk,所以在其中部分模块发生变化时,也会使这一个chunk相关的所有缓存失效。cotenthash:根据文件的内容生成相应的hash值,不同文件的hash值一定不一样,从而实现,当前文件内容更新,只更新当前文件的hash值,获取最新文件代码,其他文件还是来自于缓存。tree shaking去除无用代码,减少代码体积。在使用ES6模块 化,
webpack中mode为production时,会自动开启该动能。如果配置sideEffects: false表示所有代码都没有副作用,都可以进行tree shaking,这样可能会导致直接引入的代码,例如css引入被去除。配置sideEffects: [*.css],改类型文件将不会执行tree shaking。代码分割
可以自动将
node_modules中代码单独打包成一个chunk最终输出;自动分析多入口chunk中,有没有公共的文件,如果有,会打包成单独的一个chunk;import动态导入语法,能将某个文件单独打包,实现懒加载。{ optimization: { splitChunks: { chunks: 'all', } } }
多进程打包
使用thread-loader模块,开启多进程打包。在thread-loader配置后的loader将会开启过进程打包。但进程启动大约需要600ms,进程通信也有一定的开销,因此只有工作消耗时间比较长(js代码比较多),才需要多进程打包。
{
loader: 'thread-loader',
options: {
worker: 2
}
}