参考链接
概述
Webpack 是一种前端资源构建工具,静态模块打包器 ( module bundler )。在 Webpack 看来,前端的所有资源文件 ( js / json / css / img / less / … ) 都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源 ( module )。
基础使用
安装
首先使用 npm init 初始化项目,然后安装 webpack 以及 webpack-cli 。
// 全局安装 npm i webpack webpack-cli -g // 本地安装(推荐) npm i webpack webpack-cli -D
配置文件
在文件根目录下新建 webpack.config.js 配置文件
// webpack.config.js module.exports = { entry: './assets/js/main.js', output: { filename: 'app.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ ] }, plugins: [ ], mode: 'development' }
打包命令
使用本地环境进行打包输出
npx webpack
核心概念
Entry 入口
指示 Webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
Output 输出
指示 Webpack 打包后的资源 bundles 输出到哪里,以及如何命名。
module.exports = { ... output: { // 输出文件名称 filename: 'app.js', // 输出文件路径 path: path.resolve(__dirname, 'dist'), // 删除不需要的旧文件 clean: true } }
Loader 解析器
让 Webpack 能够去处理那些非 JavaScript 文件 ( Webpack 自身只理解 JavaScript )。
Plugins 插件
可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
Mode 模式
指示 Webpack 使用相应模式的配置。
- development 开发模式:会将 process.env.NODE_ENV 的值设为 development。启用 NameChunksPlugin 和 NameModulesPlugin。特点是能让代码本地调试运行的环境。
- production 生产模式:会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin。特点是能让代码优化上线运行的环境。
- none
devServer
在开发环境中,用于自动编译并自动刷新页面,方便开发过程中的调试。注:该功能只会在内存中编译打包,不会有任何文件输出,如需更新到生产环境中,还需重新打包代码。
下载
npm i webpack-dev-server -D
配置
在 webpack.config.js 文件中进行配置
module.exports = { ... devServer: { // 环境目录 static: './dist', // 设置 gzip 压缩,提高传输效率 compress: true, // 设置服务器主机 host: '0.0.0.0', // 设置端口号 port: 3000, // 设置路由 historyApiFallback: true, // 自动打开页面 open: true, // 更改后自动更新 watchFiles: { paths: [ './*' ], options: { usePolling: false } }, // 启用热加载功能 liveReload: true, // 启用热模块功能 hot: true }
启动
npx webpack-dev-server
资源模块 Asset Modules
官方说明:webpack.docschina.org/guides/asse…
该方法需将资源在 JS 中通过 import 进行导入或css中进行导入
// js 文件导入 import 命名 from '资源路径' // css 文件引用 .box { background-image: url('资源路径'); }
资源模块类型
- asset/resource:发送一个单独的文件并导出 URL
- asset/inline:导出一个资源的 Data URI ( 64位图 )
- asset/source:导出资源的源代码
- asset:在导出一个资源的 Data URI 和发送一个单独的文件之间自动进行选择
resource
module.exports = { ... module: { rules: [ { // 监听资源文件 test: /.png$/i, // 设置资源类型 type: 'asset/resource', generator: { // 生成资源名称 filename: 'assets/images/[name][ext]' } } ] } }
- 资源名称可以使用 [contenthash][ext] 将资源名称生成为 hash 值命名
inline
module.exports = { ... module: { rules: [ { // 监听资源文件 test: /.svg$/i, // 设置资源类型 type: 'asset/inline' } ] } }
source
module.exports = { ... module: { rules: [ { // 监听资源文件 test: /.txt$/i, // 设置资源类型 type: 'source' } ] } }
asset
module.exports = { ... module: { rules: [ { // 监听资源文件 test: /.jpg$/i, // 设置资源类型 type: 'asset', // 小于设置的大小则转为 64 位图,否则转 URL parser: { dataUrlCondition: { maxSize: 4 * 1024 // 4kb } }, generator: { // 生成资源名称 filename: 'assets/images/[name][ext]' } } ] } }
资源处理
HTML 资源
打包 HTML
1、下载 html-webpack-plugin 插件
npm i html-webpack-plugin - D
2、在 webpack.config.js 文件中引入插件并调用
// 引用插件 const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { ... plugins: [ // 打包 HTML 文件 new HtmlWebpackPlugin({ // 指定 HTML 模版文件 template: './index.html', // 指定 Script 标签位置 inject: 'body' // 输出文件名 filename: '我是filename哦.html', }) ] }
- Webpack 会在输出目录中新创建一个 HTML 文件,在原始的 HTML 文件中无需引入 JS 文件,通过 Webpack 编译后的 HTML 文件会自动引入。
官方说明:webpack.docschina.org/plugins/htm…
样式资源
打包 CSS 资源
下载样式处理解析器 css-loader 与 style-loader
npm i css-loader style-loader -D
在配置文件中添加解析器
module.exports = { ... module: { rules: [ { test: /.css$/i, use: [ // 在 head 中创建 style 标签 'style-loader', // 将 css 文件整合到 js 文件中 'css-loader' ] } ] } }
在 JS 文件中导入 CSS 文件
import './css/main.css'
打包 SCSS 资源
下载样式处理解析器
npm i sass-loader sass -D
在配置文件中添加解析器
module.exports = { ... module: { rules: [ { test: /.s[ac]ss$/i, use: [ // 在 head 中创建 style 标签 'style-loader', // 将 css 文件整合到 js 文件中 'css-loader', // 编译 sass 文件为 css 文件 'sass-loader' ] } ] } }
在 JS 文件中导入 SCSS 文件
import './css/main.scss'
抽离 CSS 代码为独立文件
\
下载插件 mini-css-extract-plugin
npm i mini-css-extract-plugin -D
引用插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { ... module: { rules: [ { test: /.s[ac]ss$/i, use: [ // 抽离 css 为独立文件 MiniCssExtractPlugin.loader, // 将 css 文件整合到 js 文件中 'css-loader', // 编译 sass 文件为 css 文件 'sass-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ // 对输出结果重命名 filename: 'assets/css/[name].css' }) ] }
如果是生成模式,将自动压缩css文件,无需额外配置。
官方文档:webpack.docschina.org/plugins/min…
视频教程:www.bilibili.com/video/BV1YU…
CSS 代码压缩(生产模式)
安装插件 css-minimizer-webpack-plugin
npm i css-minimizer-webpack-plugin -D
在配置文件中进行配置
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") module.exports = { ... optimization: { minimizer: [ // 使用插件优化 css 代码 new CssMinimizerPlugin() ], }, // 模式 mode: 'production' }
- 压缩 CSS 代码,仅在生产模式下有效
官方文档:webpack.docschina.org/plugins/css…
CSS 兼容性处理
下载 postcss-loader, postcss, postcss-preset-env 模块
npm i postcss-loader postcss postcss-preset-env -D
在根目录下创建 postcss.config.js 文件并进行配置
module.exports = { plugins: [ [ 'postcss-preset-env', { // 其他选项 }, ], ], };
引用模块
const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { ... module: { rules: [ { test: /.s[ac]ss$/i, use: [ // 抽离 css 为独立文件 MiniCssExtractPlugin.loader, // 将 css 文件整合到 js 文件中 'css-loader', // css 兼容处理 'postcss-loader', // 编译 sass 文件为 css 文件 'sass-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ // 对输出结果重命名 filename: 'assets/css/[name].css' }) ] }
postcss-preset-env 帮助 postcss 找到 package.json 中 browserslist 里的配置,通过配置加载指定的 css 兼容性
// 在 package.json 中添加浏览器列表 { ... "browserslist": { "development": [ "last 1 chrome version", "last 1 firfoxe version", "last 1 safari version" ], "production": [ ">0.2%", "not dead", "not op_mini all" ] } }
视频教程:www.bilibili.com/video/BV1YU…
视频教程:www.bilibili.com/video/BV1e7…
\
图片资源 *
下载图片处理解析器
npm i url-loader file-loader html-loader -D
...
字体资源
通过 CSS 引入字体资源
@font-face { font-family: 'PujiSansExpandedHeavy'; src: url('../fonts/PujiSans-ExpandedHeavy.eot'); /* IE9 Compat Modes */ src: url('../fonts/PujiSans-ExpandedHeavy.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('../fonts/PujiSans-ExpandedHeavy.woff2') format('woff2'), /* Modern Browsers */ url('../fonts/PujiSans-ExpandedHeavy.woff') format('woff'), /* Modern Browsers */ url('../fonts/PujiSans-ExpandedHeavy.ttf') format('truetype'); /* Safari, Android, iOS */ font-style: normal; font-weight: normal; text-rendering: optimizeLegibility; }
在 webpack.config.js 文件中进行配置
module.exports = { ... module: { rules: [ { // 监听资源文件 test: /.(woff|woff2|eot|ttf|otf)$/i, // 设置资源类型 type: 'asset/resource', generator: { // 生成资源名称 filename: 'assets/fonts/[name][ext]' }, } ] } }
数据资源
如需导入 CSV, TSV, XML 等数据格式文件,需使用相关的数据 loader 进行加载
视频教程:www.bilibili.com/video/BV1YU…
自定义 JSON 资源
视频教程:www.bilibili.com/video/BV1YU…
JS 资源
JS 语法检查
使用 eslint 扫描我们所写的代码是否符合规范,严格意义上来说,eslint 配置跟 webpack 无关,但在工程化开发环境中,他往往是不可或缺的。
安装
npm i eslint -D
创建配置文件,根据提示选择需要的类型。配置完成后,将在 node_modules 文件夹中生成一个 .eslintrc.json 文件,将文件复制到根目录下。
npx eslint --init
HOW would you Like to use ESLint ?: style hat type of modules does your project use ?. Which framework does your project use ? NDne ies your project use TypeScript ?. No / Yes here does your code run ?. browser bw would you Like to define a style for your project ? guide lhich style guide do you want to follow ?. airbnb Ihat format do you want your config file to be in ?.JS0N
在 VSCode 中安装扩展 Eslint ,重启软件后将自动生效。
JS 兼容处理
将 ES6 代码转换为低版本 ES 代码
安装模块
- babel-loader: 在 webpack 里应用 babel 解析 ES6 的桥梁
- @babel/core: babel 核心模块
- @babel/preset-env: babel 预设,一组 babel 插件的集合
npm i babel-loader @babel/core @babel/preset-env -D
在 package.json 中配置
module.exports = { ... module: { rules: [ { test: /.m?js$/, // 排除 node_modules 中安装的库 exclude: /(node_modules|bower_components)/, use: { // 加载 loader loader: 'babel-loader', options: { // 配置预设 presets: ['@babel/preset-env'] } } } ] } }
视频教程:www.bilibili.com/video/BV1YU…
regeneratorRuntime
regeneratorRuntime 是 webpack 打包生成的全局辅助函数,由 babel 生成,用于兼容 async/await 的语法。
npm i @babel/runtime @babel/plugin-transform-runtime -D
module.exports = { ... module: { rules: [ { test: /.m?js$/, // 排除 node_modules 中安装的库 exclude: /(node_modules|bower_components)/, use: { // 加载 loader loader: 'babel-loader', options: { // 配置预设 presets: ['@babel/preset-env'] plugins: [ [ '@babel/plugin-transform-runtime' ] ] } } } ] } }
JS 压缩
安装插件 terser-webpack-plugin
npm i terser-webpack-plugin -D
配置
const TerserWebpackPlugin = require("terser-webpack-plugin") module.exports = { ... optimization: { minimizer: [ // 使用插件压缩 js 代码 (生产模式) new TerserWebpackPlugin() ] } }
优化
公共路径 publicPath
publicPath 配置公共路径,所有文件的引用将自动添加公共路径的绝对地址。
module.exports = { ... output: { ... publicPath: 'https://localhost:3000/' } }
环境变量 Environment variable
环境变量可以消除 webpack.config.js 在开发环境和生产环境之间的差异
module.exports = ( env ) => { return { ... mode: env.production ? 'production' : 'development' } }
打包命令时如果使用生产模式,则在命令后增加:
npx webpack --env production
配置文件优化
分别对 development 和 production 两种模式优化。完整配置文件可查看本页下方 “完整配置”。
1、首先新建 webpack-config 文件夹,在文件夹中添加三个文件,分别为通用的配置文件、开发模式的配置文件以及生产模式的配置文件。
2、使用 webpack-merge 将文件进行合并。安装 webpack-merge
npm i webpack-merge -D
3、添加一个合并文件 webpack.config.js
const { merge } = require('webpack-merge') const commonConfig = require('./webpack.config.common') const developmentConfig = require('./webpack.config.dev') const productionConfig = require('./webpack.config.prod') module.exports = (env) => { switch(true) { case env.development: return merge(commonConfig, developmentConfig) case env.production: return merge(commonConfig, productionConfig) default: return new Error('No matching configuration was found.') } }
4、修改 package.json 文件
// 将自定义的命令分别指向相应的文件以及添加 env 环境变量的参数 { "scripts": { "start": "webpack serve -c ./webpack-config/webpack.config.js --env development", "build": "webpack -c ./webpack-config/webpack.config.js --env production" }, }
5、使用命令运行
npm run start npm run build
HMR ( 开发环境 )
Hot module replacement 热模块替换,可使一个模块发生变化,只重新打包这一个模块,而非全部重新打包,可以更快速的构建代码打包速度。
module.exports = { ... devServer: { ... // 开启 HMR 功能 hot: true } }
视频教程:www.bilibili.com/video/BV1YU…
视频教程:www.bilibili.com/video/BV1e7…
Source Map
一种提供源代码到构建后代码映射的技术,如果构建后代码出错了,通过映射关系可以追踪源代码的错误。在 webpack.config.js 文件中配置
module.exports = { ... devtool: 'source-map' }
常用的几种 source-map 类型
- source-map:生成外部文件,错误代码的准确信息和源代码的错误位置
- inline-source-map:内联,错误代码的准确信息和源代码的错误位置。在代码底部生成,构建速度比外部文件更快
- hidden-source-map:生成外部文件,错误代码的原因,没有错误位置,无法追踪源代码错误。
- eval-source-map:内联,错误代码的准确信息和源代码的错误位置。每一个文件都生成对应的 source-map
- nosources-source-map:生成外部文件,
- cheap-source-map:生成外部文件,错误代码的准确信息和源代码的错误位置。只精确到行
- cheap-module-source-map:同 cheap-source-map,会将 loader 的 source map 加入
开发环境建议
- eval-source-map
- eval-cheap-module-source-map
生产环境建议
- source-map
- nosources-source-map
- hidden-source-map
视频教程:www.bilibili.com/video/BV1YU…
Oneof ( 生产模式 )
每个loader只会匹配一个,不能有两个配置处理一个类型的文件
module.exports = { module: { rules: [ { oneOf:[ { // 处理 css 资源 test: /.css$/i, use: [...CommonCssLoader] }, { // 处理 scss 资源 test: /.s[ac]ss$/i, use: [...CommonCssLoader, 'sass-loader'] }, { // 处理图片资源 test: /.(jpg|jpeg|png|gif)$/i, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:12].[ext]', esModule: false, outputPath: 'images' } }, { // 处理 html 中的图片资源 test: /.html$/i, loader: 'html-loader' } ] } ] } }
Tree shaking ( 生产模式 )
去除应用程序中没有使用的代码,可更大程度的优化代码。必须使用 ES6 模块化,并开启 production 模式。
import { module } from './filename.js'
如果不需要某些文件被 webpack 清除,可以在 package.json 中配置 sideEffects 属性
{ "sideEffects": ["*.css" ,"*.scss", "*.global.js"...] }
Code split ( 生产模式 )
代码分离是 webpack 中最引人瞩目的特性之一,可将代码分离到不同的文件中,然后将这些文件按需加载或并行加载,同时还可以获取代码加载的优先级。
方法1: 入口起点( 不推荐 )
使用 entry 配置手动分离代码,如果多个入口共享的文件,会分别在每个包里重复打包。
module.exports = { entry: { main: './assets/js/main.js', other: './assets/js/add.js' }, output: { filename: 'js/[name].bundle.js', path: path.resolve(__dirname, 'dist'), }, }
方法2: 防止重复
使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离代码
module.exports = { entry: { main: { import: './assets/js/main.js', dependOn: 'shared' }, other: { import: './assets/js/add.js', dependOn: 'shared' }, shared: 'jQuery' }, output: { filename: 'js/[name].bundle.js', path: path.resolve(__dirname, 'dist'), }, }
sliptChunks 插件
另外,还可使用 sliptChunks 插件来实现
module.exports = { ... entry: { main: './assets/js/main.js', other: './assets/js/add.js' }, output: { filename: 'js/[name].bundle.js', path: path.resolve(__dirname, 'dist'), }, optimization: { sliptChunks: { chunks: 'all' } } }
可以通过 import 方法对文件名进行自定义
import(/* webpackChunkName: '自定义文件名' */'文件路径')
方法3: 动态导入
function getComponent() { import('lodash') .then(({default: _}) => { const element = document.createElement('div') element.innerHTML = _.join(['Hello', 'webpack', '']) return element }) } getComponent().then((element) => { document.body.appendChild(element) })
视频教程:www.bilibili.com/video/BV1YU…
懒加载
指的是 JS 文件的懒加载,当事件触发或条件满足后才进行加载。是很好的优化网页或应用的方法。这种方法实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用一些新的代码块。这样加快了应用的初始加载速度,减轻总体体积,因为某些代码块可能永远不会被加载。
document.querySelector('button').addEventListener('click', () => { import(/* webpackChunkName: 'filename' */'./filename').then(({ module }) => { ... }) })
视频教程:www.bilibili.com/video/BV1YU…
视频教程:www.bilibili.com/video/BV1e7…
预加载
等其他资源加载完毕后再进行加载,当事件触发或条件满足后,才会执行。兼容性较差,只能在pc端高版本浏览器中使用,手机端浏览器兼容较差。
document.querySelector('button').addEventListener('click', () => { import(/* webpackChunkName: 'filename', webpackPrefetch: true */'./filename').then(({ module }) => { ... }) })