Webpack 基础
项目初始化
npm init
安装依赖
npm install webpack webpack-cli -D
loader
- webpack 默认支持处理 JS 与 JSON 文件,必须借助 Loader 来对不同类型的文件的进行处理
- Loader 的执行顺序是固定从后往前
css-loader
- 安装
# 支持css文件的loade
npm install css-loader -D
style-loader
- 单靠 css-loader 是没有办法将样式加载到页面上。这个时候,我们需要再安装一个 style-loader 来完成这个功能
- 安装
# 支持css文件的loade
npm install style-loader -D
1.引入 css
import './main.css'
postcss-loader
- 使用 postcss-loader,自动添加 CSS3 部分属性的浏览器前缀
- 安装
npm install postcss-loader postcss -D
less-loader
- 安装
# 支持less文件的loade
npm install less-loader -D
sass-loader
-Sass 不光需要安装 sass-loader 还得搭配一个 node-sass,这里 node-sass 建议用淘宝镜像来安装
- 安装
npm install sass-loader -D
# 淘宝镜像
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
file-loader 处理图片的 loader
- 识别图片的 loader
- url-loader: 解依赖 file-loader,当图片小于 limit 值的时候,会将图片转为 base64 编码,大于 limit 值的时候依然是使用 file-loader 进行拷贝
- img-loader: 压缩图片
- 安装
npm install file-loader -D
npm install url-loader -D
配置 loader
// webpack.config.js
// loader配置
module.exports = {
mode: 'development', // 模式
entry: './src/main.css', // 打包入口地址
output: {
filename: 'bundle.css', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
},
module: {
rules: [
// 转换规则
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
},
{
test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
use: [
{
loader: 'file-loader',
options: {
name: '[name][hash:8].[ext]',
},
},
],
// use:[
// {
// loader: 'url-loader',
// options: {
// name: '[name][hash:8].[ext]',
// // 文件小于 50k 会转换为 base64,大于则拷贝文件
// limit: 50 * 1024
// }
// }
// ]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 匹配字体文件
use: [
{
loader: 'url-loader',
options: {
name: 'fonts/[name][hash:8].[ext]', // 体积大于 10KB 打包到 fonts 目录下
limit: 10 * 1024,
},
},
],
},
],
},
}
webpack5 新增资源模块使用(无需资源文件相关 loader)
- asset/resource 将资源分割为单独的文件,并导出 url,类似之前的 file-loader 的功能.
- asset/inline 将资源导出为 dataUrl 的形式,类似之前的 url-loader 的小于 limit 参数时功能.
- asset/source 将资源导出为源码(source code). 类似的 raw-loader 功能.
- asset 会根据文件大小来选择使用哪种类型,当文件小于 8 KB(默认) 的时候会使用 asset/inline,否则会使用 asset/resource
- 配置
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.(jpe?g|png|gif)$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
// [ext] 自带 "." 这个与 url-loader 配置不同
filename: '[name][hash:8][ext]',
},
parser: {
dataUrlCondition: {
maxSize: 50 * 1024, //超过50kb不转 base64
},
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
filename: '[name][hash:8][ext]',
},
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 超过100kb不转 base64
},
},
},
],
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=', argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config
}
babel-loader
- 在开发中我们想使用最新的 Js 特性,但是有些新特性的浏览器支持并不是很好,所以 Js 也需要做兼容处理,常见的就是将 ES6 语法转化为 ES5。
- 安装
npm install babel-loader @babel/core @babel/preset-env -D
#babel-loader 使用 Babel 加载 ES2015+ 代码并将其转换为 ES5
#@babel/core Babel 编译的核心包
#@babel/preset-env Babel 编译的预设,可以理解为 Babel 插件的超集
#ECMA 规范中的新特性(装饰器)
npm install babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
- 配置
// webpack.config.js
// ...
const config = {
// ...
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
],
},
// ...
],
},
//...
}
// 或者单独配置
// .babelrc.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
// useBuiltIns: false 默认值,无视浏览器兼容配置,引入所有 polyfill
// useBuiltIns: entry 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill
// useBuiltIns: usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加
useBuiltIns: 'entry',
corejs: '3.9.1', // 是 core-js 版本号
targets: {
chrome: '58',
ie: '11',
},
},
],
],
// ECMA 规范中的新特性插件
// plugins: [
// ['@babel/plugin-proposal-decorators', { legacy: true }],
// ['@babel/plugin-proposal-class-properties', { loose: true }],
// ],
}
plugin(插件)
- 插件(Plugin)可以贯穿 Webpack 打包的生命周期,执行不同的任务
html-webpack-plugin
1.安装
# js 或者 css 文件可以自动引入到 Html 中,就需要使用插件 html-webpack-plugin来帮助你完成这个操作
npm install html-webpack-plugin -D
- 配置
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//...
plugins: [
// 配置插件
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
}
clean-webpack-plugin
- 自动清空上次打包目录
- 安装
npm install clean-webpack-plugin -D
- 配置
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
plugins: [
new CleanWebpackPlugin(), // 引入插件
],
}
mini-css-extract-plugin 分离样式文件
- 依赖 style-loader 将样式通过 style 标签的形式添加到页面上,更多时候,我们都希望可以通过 CSS 文件的形式引入到页面上
- 安装
npm install mini-css-extract-plugin -D
- 配置
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const config = {
// ...
// ...
plugins: [
// 配置插件
// ...
new MiniCssExtractPlugin({
// 添加插件
filename: '[name].[hash:8].css',
}),
// ...
],
}
环境配置 cross-env
- 安装
npm install cross-env -D
- 配置
// ./package.json
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack serve --mode development",
"test": "cross-env NODE_ENV=test webpack --mode production",
"build": "cross-env NODE_ENV=prod webpack --mode production"
},
- 打印环境变量
// webpack.config.js
console.log('process.env.NODE_ENV=', process.env.NODE_ENV) // 打印环境变量 dev test prod
const config = {
//...
}
module.exports = (env, argv) => {
console.log('argv.mode=', argv.mode) // 打印 mode(模式)值 development production production
// 这里可以通过不同的模式修改 config 配置
return config
}
本地服务 webpack-dev-server
- 安装
npm intall webpack-dev-server@3.11.2 -D
- ⚠️ 注意:本文使用的 webpack-dev-server 版本是 3.11.2,当版本 version >= 4.0.0 时,需要使用 devServer.static 进行配置,不再有 devServer.contentBase 配置项。
- 配置
// webpack.config.js
const config = {
// ...
devServer: {
contentBase: path.resolve(__dirname, 'public'), // 静态文件目录
compress: true, //是否启动压缩 gzip
port: 8080, // 端口号
// open:true // 是否自动打开浏览器
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=', argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config
}
- 配置 contentBase 直接到对应的静态目录下面去读取文件,而不需对文件做任何移动,节省了时间和性能开销。
SourceMap 配置 devtool (本地开发查错工具)
- SourceMap 是一种映射关系,当项目运行后,如果出现错误,我们可以利用 SourceMap 反向定位到源码位置
- 配置
// webpack.config.js
const config = {
// ...
devtool: 'source-map',
// ...
}
-
执行打包后,dist 目录下会生成以 .map 结尾的 SourceMap 文件
-
不同配置不同效果 devtool | build | rebuild | 显示代码 | SourceMap 文件 | 描述 (none) | 很快 | 很快 | 无 | 无 | 无法定位错误 eval| 快| 很快(cache)| 编译后| 无 | 定位到文件 source-map| 很慢| 很慢| 源代码有| 定位到行列 eval-source-map| 很慢| 一般(cache)| 编译后有(dataUrl)| 定位到行列 eval-cheap-source-map| 一般| 快(cache)| 编译后有(dataUrl)| 定位到行 eval-cheap-module-source-map| 慢| 快(cache)| 源代码有(dataUrl)| 定位到行 inline-source-map| 很慢| 很慢| 源代码有(dataUrl)| 定位到行列 hidden-source-map| 很慢| 很慢| 源代码有| 无法定位错误 nosource-source-map| 很慢| 很慢| 源代码无| 定位到文件
-
本地开发:
推荐:eval-cheap-module-source-map
理由: 本地开发首次打包慢点没关系,因为 eval 缓存的原因,rebuild 会很快 开发中,我们每行代码不会写的太长,只需要定位到行就行,所以加上 cheap 我们希望能够找到源代码的错误,而不是打包后的,所以需要加上 module
生产环境: 推荐:(none) 理由: 就是不想别人看到我的源代码
Webpack 进阶
优化构建速度
resolve.alias
- 用的创建 import 或 require 的别名,用来简化模块引用,项目中基本都需要进行配置。
const path = require('path')
//...
// 路径处理方法
function resolve(dir) {
return path.join(__dirname, dir)
}
const config = {
//...
resolve: {
// 配置别名
alias: {
'~': resolve('src'),
'@': resolve('src'),
components: resolve('src/components'),
},
},
}
resolve.extensions
- 引入模块时允许不带扩展名
const config = {
//...
resolve: {
extensions: ['.js', '.json', '.wasm'],
},
}
resolve.modules
- 告诉 webpack 优先 src 目录下查找需要解析的文件,会大大节省查找时间
const path = require('path')
// 路径处理方法
function resolve(dir) {
return path.join(__dirname, dir)
}
const config = {
//...
resolve: {
modules: [resolve('src'), 'node_modules'],
},
}
externals
- externals 配置选项提供了「从输出的 bundle 中排除依赖,剥离不需要改动的一些依赖,大大节省打包构建的时间
// 使用时import即可
const config = {
//...
externals: {
jquery: 'jQuery',
vue: 'Vue',
'element-ui': 'Element',
'vue-router': 'VueRouter',
axios: 'axios',
},
}
缩小 loader 解析范围
-
在配置 loader 的时候,我们需要更精确的去指定 loader 的作用目录或者需要排除的目录,通过使用 include 和 exclude 两个配置项,可以实现这个功能,常见的例如:
-
include:符合条件的模块进行解析
-
exclude:排除符合条件的模块,不解析
-
exclude 优先级更高
-
例如在配置 babel 的时候
const path = require('path')
// 路径处理方法
function resolve(dir) {
return path.join(__dirname, dir)
}
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: ['babel-loader'],
},
// ...
],
},
}
多进程配置
- thread-loader
- 安装
npm i -D thread-loader
- 配置
const path = require('path')
// 路径处理方法
function resolve(dir) {
return path.join(__dirname, dir)
}
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
{
loader: 'thread-loader', // 开启多进程打包
options: {
worker: 3,
},
},
'babel-loader',
],
},
// ...
],
},
}
- happypack (webpack5 已弃用)
babel-loader 开启缓存
- babel 在转译 js 过程中时间开销比价大,将 babel-loader 的执行结果缓存起来,重新打包的时候,直接读取缓存
- 配置
const config = {
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
// ...
{
loader: 'babel-loader',
options: {
cacheDirectory: true, // 启用缓存
},
},
],
},
// ...
],
},
}
cache-loader
- 其他 loader 缓存
- 安装
npm i -D cache-loader
- 配置 cache-loader
const config = {
module: {
// ...
rules: [
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
// 'style-loader',
MiniCssExtractPlugin.loader,
'cache-loader', // 获取前面 loader 转换的结果
'css-loader',
'postcss-loader',
'sass-loader',
],
},
// ...
],
},
}
cache 持久化缓存
- 通过配置 cache 缓存生成的 webpack 模块和 chunk,来改善构建速度。
const config = {
cache: {
type: 'filesystem',
},
}
优化构建结果
构建结果分析 webpack-bundle-analyzer
- webpack-bundle-analyzer 可以直观的看到打包结果中,文件的体积大小、各模块依赖关系、文件是够重复等问题,极大的方便我们在进行项目优化的时候,进行问题诊断。
- 安装
npm i -D webpack-bundle-analyzer
- 配置
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const config = {
// ...
plugins: [
// ...
// 配置插件
new BundleAnalyzerPlugin({
// analyzerMode: 'disabled', // 不启动展示打包报告的http服务器
// generateStatsFile: true, // 是否生成stats.json文件
}),
],
}
- 启动命令
"scripts": {
// ...
"analyzer": "cross-env NODE_ENV=prod webpack --progress --mode production"
},
- npm run analyzer
- http://127.0.0.1:8888 的 web 服务,访问地址就可以看到
压缩 CSS
- optimize-css-assets-webpack-plugin
- 安装
npm install -D optimize-css-assets-webpack-plugin
- 配置
// 压缩css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...
const config = {
// ...
optimization: {
minimize: true,
minimizer: [
// 添加 css 压缩配置
new OptimizeCssAssetsPlugin({}),
],
},
// ...
}
压缩 JS terser-webpack-plugin
- 因为 webpack5 内置了 terser-webpack-plugin 插件,所以我们不需重复安装,直接引用就可以了,具体配置如下
- 配置
const TerserPlugin = require('terser-webpack-plugin')
const config = {
// ...
optimization: {
minimize: true, // 开启最小化
minimizer: [
// ...
new TerserPlugin({}),
],
},
// ...
}
清除无用的 CSS
- purgecss-webpack-plugin 会单独提取 CSS 并清除用不到的 CSS
- 安装
npm i -D purgecss-webpack-plugin
- 添加配置
// ...
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin')
const glob = require('glob') // 文件匹配模式
// ...
function resolve(dir) {
return path.join(__dirname, dir)
}
const PATHS = {
src: resolve('src'),
}
const config = {
plugins: [
// 配置插件
// ...
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
}),
],
}
Tree-shaking
Tree-shaking 作用是剔除没有使用的代码,以降低包的体积
- webpack 默认支持,需要在 .bablerc 里面设置 model:false,即可在生产环境下默认开启
优化运行时体验
- 运行时优化的核心就是提升首屏的加载速度,主要的方式就是
降低首屏加载文件体积,首屏不需要的文件进行预加载或者按需加载
代码懒加载 prefetch 与 preload 上面我们使用异步加载的方式引入图片的描述,但是如果需要异步加载的文件比较大时,在点击的时候去加载也会影响到我们的体验,这个时候我们就可以考虑使用 prefetch 来进行预拉取 3.4.1 prefetch
prefetch (预获取):浏览器空闲的时候进行资源的拉取
复制代码 3.4.2 preload
preload (预加载):提前加载后面会用到的关键资源 ⚠️ 因为会提前拉取资源,如果不是特殊需要,谨慎使用
- 入口点分割
配置多个打包入口,多页打包