前端打包工具之Webpack5
-
- 什么是打包工具
- 打包工具的作用
- 常见的打包工具
- 一、Webpack
-
- 1、什么是[webpack](https://webpack.docschina.org/concepts/)
- 2、webpack本身的功能是有限的
- 3、webpack基本使用:基于本身功能只解析JS资源
- 4、webpack配置
-
- 4.1、`entry`(入口):指示webpack从哪个文件开始打包
- 4.2、`output`(出口):指示webpack打包完的文件输出到哪里、如何命名
- [4.3、`loader` ([加载器](https://webpack.docschina.org/concepts/loaders)):webpack本身只能处理js、json资源,其他资源需要借助loader](#4.3、
loader
(加载器):webpack本身只能处理js、json资源,其他资源需要借助loader) - [4.4、`plugins` ([插件](https://webpack.docschina.org/api/plugins)): 扩展webpack功能](#4.4、
plugins
(插件): 扩展webpack功能) - [4.5、`mode` ([模式](https://webpack.docschina.org/configuration/mode)) :开发、生产模式](#4.5、
mode
(模式) :开发、生产模式) - [4.6、`browser compatibility` (浏览器兼容性)](#4.6、
browser compatibility
(浏览器兼容性)) - [4.7、`environment` (环境): Webpack 5 运行于 Node.js v10.13.0+ 的版本](#4.7、
environment
(环境): Webpack 5 运行于 Node.js v10.13.0+ 的版本)
- 5、webpack提升打包构建速度
- 6、webpack配置代码
什么是打包工具
将浏览器无法运行的高级语法代码(如:es6、scss)编译成浏览器能识别的JS、CSS等语法。
打包工具的作用
- 语法编辑
- 压缩代码
- 兼容性处理
- 代码性能优化
- ...
常见的打包工具
- Vite
- Webpack
- parcel
一、Webpack
1、什么是webpack
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具 。它会以一个或多个文件作为打包的入口,将整个项目所有文件编译组合成一个或者多个文件(
bundles
)输出。
webpack 开箱即用,可以无需使用任何配置文件。然而,webpack 会假定项目的入口起点为 src/index.js,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化
2、webpack本身的功能是有限的
webpack本身的功能是有限的
- 开发模式: 仅能编译JS中的ES Model语法
- 生产模式:编译JS中的ES Model语法,还能压缩JS代码
即是:webpack本身只能处理JS编译,其他编译和处理需要进一步的配置
3、webpack基本使用:基于本身功能只解析JS资源
- 初始化package.json
bash
$ npm init -y
- 下载依赖 cli
bash
$ npm i webpack webpack-cli -D
webpack从4.0版本开始,在安装时,就需要安装webpack和webpack-cli这两个东西。(1、webpack是打包代码时的核心内容;2、webpack-cli是一个用来在命令行中运行webpack的工具)
xxx-cli的主要功能就是:处理命令行参数(如果你想使用 npx 来运行 webpack,请确保你已经安装了 webpack-cli);npm run build -> 执行node_modules/webpack文件 -> 执行webpack-cli文件 -> 执行cli.run() -> 处理命令行参数。
- 执行webpack打包
bash
// 开发环境
$ npx webpack ./app.js --mode=development
// 生产环境
$ npx webpack ./app.js --mode=production
npx webpack: 运行本地安装的webpack包(npx: 用于快速执行本地或远程的npm包)
./app.js: 打包入口文件
--mode=XXX:打包模式
以上步骤默认打包的是JS代码,CSS等其他代码的处理,需要另行配置
4、webpack配置
npx webpack init: 根据项目需求快速生产webpack配置
4.1、entry
(入口):指示webpack从哪个文件开始打包
默认src/index.js
4.2、output
(出口):指示webpack打包完的文件输出到哪里、如何命名
4.3、loader
(加载器):webpack本身只能处理js、json资源,其他资源需要借助loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
loader 的两个属性:
test
属性,识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个 loader。
js
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
// 嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用) raw-loader 转换一下
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
};
4.4、plugins
(插件): 扩展webpack功能
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例
js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
4.5、mode
(模式) :开发、生产模式
4.6、browser compatibility
(浏览器兼容性)
Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import() 和 require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill。
4.7、environment
(环境): Webpack 5 运行于 Node.js v10.13.0+ 的版本
5、webpack提升打包构建速度
5.1、热更新:只针对开发模式
js
// 生产模式下不需要devServer
devServer: {
host: '0.0.0.0',
port: 3000,
compress: true,
// 默认为true: 提升开发模式的打包构建速度
// 默认只支持css热更新 js的需要加载相应loader
hot: true, // 开启HRM 热更新
},
5.2、oneOf配置项
js
// 加载器: 帮助webpack识别 本身不能识别的资源
module: {
// loader的配置
rules: {
// 避免找到了loader 还会继续遍历后面的loader
oneOf: [
// 处理css资源
{},
// 处理图片资源
{},
// 处理其他资源
{},
// 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
{
test: /\.js$/,
// 排除哪些不需要处理的文件
exclude: /(node_modules|bower_components)/,
// 只加载一个loader 可以直接使用loader替换use
loader: 'babel-loader',
// 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
// options: {
// presets: ['@babel/preset-env']
// }
}
]
}
},
5.3、exclude配置项或者include配置项
exclude、include不能同时使用
js
{
test: /\.js$/,
// 排除哪些不需要处理的文件
exclude: /(node_modules|bower_components)/,
// include: path.resolve(__dirname, "../src"),
// 只加载一个loader 可以直接使用loader替换use
loader: 'babel-loader',
// 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
// options: {
// presets: ['@babel/preset-env']
// }
}
5.4、ESLint、babel添加缓存
- babel添加缓存
js
// 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
{
test: /\.js$/,
// 排除哪些不需要处理的文件
exclude: /(node_modules|bower_components)/,
// 只加载一个loader 可以直接使用loader替换use
loader: 'babel-loader',
options: {
// 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
// presets: ['@babel/preset-env']
cacheDirectory: true, // 开启babel缓存
cacheCompression: false // 关闭缓存文件压缩
}
}
- ESLint 添加缓存
js
// 插件
plugins: [
// ESLint检验
new ESLintPlugin({
// eslint 只检测src下面的文件
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules', // 默认值: node_modules
cache: true, // 开启缓存
cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslintcache")
}),
],
5.5、启动多线程打包
- 获取cpu核数
js
const os = require('os')
// 获取cpu核数
const threads = os.cpus().length;
- babel操作开启多线程
bash
# 安装thread-loader
$ npm install --save-dev thread-loader
js
// 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
{
test: /\.js$/,
// 排除哪些不需要处理的文件
exclude: /(node_modules|bower_components)/,
use: [
{
loader: "thread-loader",
options: {
workers: threads, // babel: 进程数量
}
},
{
loader: 'babel-loader',
options: {
// 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
// presets: ['@babel/preset-env']
cacheDirectory: true, // 开启babel缓存
cacheCompression: false // 关闭缓存文件压缩
}
}
]
}
- ESLint开启多进程
js
plugins: [
// ESLint检验
new ESLintPlugin({
// eslint 只检测src下面的文件
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules', // 默认值: node_modules
cache: true, // 开启缓存
cacheLocation: path.resolve(__dirname, "../node_modules/.cache/eslintcache"),
threads // 开启多进程
}),
],
- 压缩js开启多进程
js
const TerserWebpackPlugin = require('terser-webpack-plugin')
optimization: {
// 压缩的操作
minimizer: [
// 压缩js
new TerserWebpackPlugin({
// 开启多进程
parallel: threads,
}),
],
},
5.6、减少代码体积
Tree Shaking: 移除JS中没有使用上的代码。 生产环境下已经默认开启了
5.7、减少文件中的babel辅助代码引入
js
{
test: /\.js$/,
// 排除哪些不需要处理的文件
exclude: /(node_modules|bower_components)/,
use: [
{
loader: "thread-loader",
options: {
workers: threads, // babel: 进程数量
}
},
{
loader: 'babel-loader',
options: {
// 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
// presets: ['@babel/preset-env']
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
plugins: ["@babel/plugin-transform-runtime"] // 减少代码体积:避免不必要的babel的辅助代码注入
}
}
]
}
5.8、压缩静态资源图片
6、webpack配置代码
js
const path = require('path')
const os = require('os')
const ESLintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
const nodeExternals = require('webpack-node-externals')
const TerserWebpackPlugin = require('terser-webpack-plugin')
// 获取cpu核数
const threads = os.cpus().length;
// 生产模式下:进行项目优化: 优化打包速度; 优化运行性能
module.exports = {
target: 'node', // ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // 排除Node.js核心模块
// 项目打包入口: 相对路径
entry: './src/app.js',
// 模式
mode: "production",
// 包含行和列的映射 打包速度慢
// 开发模式: 'cheap-module-source-map'
devtool: 'source-map', // 将编译后的代码映射回原始源代码
// 打包出口: 绝对路径
output: {
// __dirname: node变量: 当前文件的文件夹目录
path: path.resolve(__dirname, '../dist'), // 打包后所有文件的输出路径
filename: 'static/js/[name].js', // 入口文件打包后的输出路径
// 给打包输出的其他文件命名 比如import动态导入的文件
// 结合webpack 魔法命名 import(/* webpackChunkName: "app" */ "./src/app.js").then(()=>{})
chunkFilename: 'static/js/[name].chunk.js',
// 图片、字体等通过type: asset 处理资源命名方式 替换掉generator
assetModuleFilename: 'static/media/[hash:10][ext][query]',
clean: true, // 自动清空path目录内容,再进行打包
publicPath: '/assets/',
},
// 加载器: 帮助webpack识别 本身不能识别的资源
module: {
// loader的配置
rules: {
// 避免找到了loader 还会继续遍历后面的loader
oneOf: [
// 处理css资源
{
test: /\.s[ac]ss$/,
// use的加载顺序: 从右向左
use: [
// "style-loader", // 将js中的css通过创建style标签添加到html文件中
MiniCssExtractPlugin.loader, // 将css资源抽离成css文件, 通过link引入(避免了加载js的阻塞)
"css-loader", // 将css资源编译成commonjs模块到js中
// sass兼容处理
// 需要在package 添加需要兼容到哪些浏览器
// "browserslist": [
// "last 2 version", // 所有的浏览器取最新的两个版本
// "> 1%", // 覆盖99% 的浏览器
// "not dead"
// ]
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
},
},
},
"sass-loader" // 将sass编译成css
],
},
// 处理图片资源
{
// webpack5内置了处理图片的loader
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
// 将小于10kb的图片打包成base64
// 优点: 较少请求数量
// 缺点: 图片体积变大(所以一般都转的小体积的图片)
maxSize: 10 * 1024 // 10kb
}
},
// 图片打包后的目录
// generator: {
// // hash:10 取hash前10个字符
// filename: 'static/images/[hash:10][ext][query]'
// }
},
// 处理其他资源
{
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: 'asset/resource', // 原样打包到dist中
// generator: {
// // hash:10 取hash前10个字符
// filename: 'static/media/[hash:10][ext][query]'
// }
},
// 将高阶JS语法处理成IE等旧浏览器能识别的JS语法
// babel-loader: 不能处理async、promise等ES+的语法 需要借助core-js
// core-js 是专门做ES6以上API的polyfill(补丁)
// 在babel.config.js
// module.exports = {
// // 智能预设: 能够编译ES6语法
// presets: [
// '@babel/preset-env',
// {
// useBuiltIns: 'usage', // 按需自动加载core-js对ES6+的兼容性处理
// corejs: 3 // core-js的版本号
// }
// ]
// }
{
test: /\.js$/,
// 排除哪些不需要处理的文件
exclude: /(node_modules|bower_components)/,
// include: path.resolve(__dirname, "../src"),
// 只加载一个loader 可以直接使用loader替换use
use: [
{
loader: "thread-loader",
options: {
workers: threads, // babel: 进程数量
}
},
{
loader: 'babel-loader',
options: {
// 可以直接在项目中 新建babel配置文件(babel.config.js), 方便配置扩展
// presets: ['@babel/preset-env']
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
plugins: ["@babel/plugin-transform-runtime"] // 减少代码体积:避免不必要的babel的辅助代码注入
}
}
]
}
]
}
},
// 插件
plugins: [
// ESLint检验
new ESLintPlugin({
// eslint 只检测src下面的文件
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules',
threads
}),
// html文件自动引入依赖资源
new HtmlWebpackPlugin({
// 模板: 以public/index.html 文件作为模板,创建打包后新的html文件
// 新的html文件特点:1、结构和打包之前的一致; 2、自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
}),
// 单独打包出css文件
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].chunk.[contenthash:10].css'
}),
],
optimization: {
// 压缩的操作
minimizer: [
// css压缩
// 开启生产模式 html、js会自动压缩的
new CssMinimizerPlugin(),
// 压缩js
new TerserWebpackPlugin({
// 开启多进程
parallel: threads,
}),
],
// 代码分割配置
splitChunks: {
chunks: 'all'
},
// 保证引用文件不变
// 只变runtime和map里面的文件
runtimeChunk: {
name: (entrypoint) => `runtime-${entrypoint.name}.js`
}
},
// 开发服务器: 不会输出资源到dist中的, 是在内存中进行编译打包的
// src下的代码更改后,自动打包/自动编译代码
// 生产模式下不需要devServer
devServer: {
host: '0.0.0.0',
port: 3000,
compress: true,
// 默认为true: 提升开发模式的打包构建速度
// 默认只支持css热更新 js的需要加载相应loader
hot: true, // 开启HRM 热更新
},
}