一. Webpack
1. 模块打包工具的由来
- ES Module 存在环境兼容问题
- 模块文件过多,网络请求频繁
- 所有的前端资源都需要模块化
2. 模块打包工具概要
- 主流的模块打包工具
- Webpack
- Parcel
- Rollup
- 打包工具解决的是前端整体的模块化,并不单指 JavaScript 模块化
3. Webpack 快速上手
- 初始化 package.json 文件,yarn init
- 安装 webpack,webpack-cli,yarn add webpack webpack-cli –dev
- 查看 webpack 版本,yarn webpack –version
- 使用 webpack 打包代码,yarn webpack
4. 配置文件
-
webpack 默认以 src 目录下的 index.js 作为打包入口,最终打包结果会存放到 dist 目录下的 main.js
-
项目根目录新建 webpack.config.js 文件
const path = require('path') module.exports = { // 入口文件 entry: './src/main.js', // 输出文件 output: { // 输出文件名 filename: 'bundle.js', // 输出目录,注意:path 必须是一个绝对路径 path: path.join(__dirname, 'output') } } 复制代码
5. 工作模式
-
production 生产模式
-
development 开始模式
-
none 无模式
-
可以通过命令指定参数 –mode development 的方式指定工作模式
-
可以通过在配置文件中配置 type 属性指定工作模式
const path = require('path') module.exports = { // 工作模式 mode: 'development', // 入口文件 entry: './src/main.js', // 输出文件 output: { // 输出文件名 filename: 'bundle.js', // 输出目录,注意:path 必须是一个绝对路径 path: path.join(__dirname, 'dist') } } 复制代码
6. 资源模块加载
-
webpack 内部只会处理 js 文件
-
通过 loader 处理资源文件
-
安装 css-loader,yarn add css-loader –dev,用来加载 css 资源
-
安装 style-loader,yarn add style-loader –dev,用来将转换的 css 资源通过 style 标签的方式放到页面中
const path = require('path') module.exports = { // 工作模式 mode: 'development', // 入口文件 entry: './src/main.css', // 输出文件 output: { // 输出文件名 filename: 'bundle.js', // 输出目录,注意:path 必须是一个绝对路径 path: path.join(__dirname, 'dist') }, module:{ // 针对于其他的资源模块加载规则的配置 rules:[ { // 用于匹配打包过程中所遇到的文件路径 test: /\.css$/, // 指定匹配到的文件需要使用的 loader, 如果配置多个 loader ,执行顺序是从后向前 use: [ 'style-loader', 'css-loader' ] } ] } } 复制代码
7. 导入资源模块
- 将所需的资源文件导入到对应的 js 文件中,在 js 代码中使用资源
- 优势
- 逻辑合理,JS 确实需要这些资源文件
- 确保上线资源不缺失,都是必要的
8. 文件资源加载器
-
安装 file-loader,yarn add file-loader –dev
-
安装 url-loader,yarn add url-loader –dev
const path = require('path') module.exports = { // 工作模式 mode: 'development', // 入口文件 entry: './src/main.js', // 输出文件 output: { // 输出文件名 filename: 'bundle.js', // 输出目录,注意:path 必须是一个绝对路径 path: path.join(__dirname, 'dist'), // 网站的根目录 publicPath: './dist/' }, module: { // 针对于其他的资源模块加载规则的配置 rules: [ { // 用于匹配打包过程中所遇到的文件路径 test: /\.css$/, // 指定匹配到的文件需要使用的 loader, 如果配置多个 loader ,执行顺序是从后向前 use: [ 'style-loader', 'css-loader' ] }, { test: /\.png$/, use: { loader: 'url-loader', // 配置选项 options: { limit: 10 * 1024 // 以字节为单位 } } } ] } } 复制代码
9. 常用加载器分类
- 编译转换类,将模块转换为 js 代码,例如:css-loader
- 文件操作类,将模块拷贝至输出目录,将模块导出,例如:file-loader
- 代码检查类,检查模块代码,例如:esline-loader
10. webpack 与 ES 2015
- webpack 不能转换 ES 2015 特性,只能够转换 import 和 export
- 安装 babel-loader,yarn add babel-loader @babel/core @babel/preset-env –dev
11. webpack 加载资源的方式
-
遵循 ES Module 标准的 import 声明
-
遵循 CommonJS 标准的 require 函数
-
通过 require 函数导入 ES Module 默认导出,需要 以 .default 的方式
-
遵循 AMD 标准的 define 函数和 require 函数
-
样例
-
安装 html-loader,yarn add html-loader –dev
const path = require('path') module.exports = { // 工作模式 mode: 'development', // 入口文件 entry: './src/main.js', // 输出文件 output: { // 输出文件名 filename: 'bundle.js', // 输出目录,注意:path 必须是一个绝对路径 path: path.join(__dirname, 'dist'), // 网站的根目录 publicPath: './dist/' }, module: { // 针对于其他的资源模块加载规则的配置 rules: [ // 加载 css 资源 { // 用于匹配打包过程中所遇到的文件路径 test: /\.css$/, // 指定匹配到的文件需要使用的 loader, 如果配置多个 loader ,执行顺序是从后向前 use: [ 'style-loader', 'css-loader' ] }, // 加载 png 资源 { test: /\.png$/, use: { loader: 'url-loader', // 配置选项 options: { limit: 10 * 1024 // 以字节为单位 } } }, // 转译 es6 资源 { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, // 加载 html 资源 { test: /\.html$/, use: { // html-loader 默认只会处理 src 属性,如果其他属性也要参与打包,则需要相应配置 loader: 'html-loader', options: { attributes: { list: [ { tag: 'img', attribute: 'src', type: 'src', }, { tag: 'a', attribute: 'href', type: 'src', } ] } } } } ] } } 复制代码
-
12. webpack 核心工作原理
- loader 机制是 webpack 的核心
13. 开发一个 loader
-
初始化 package.json,yarn init
-
安装 webpack,webpack-cli,yarn add webpack webpack-cli –dev
-
项目根目录新建 loader 模块,例如:markdown-loader.js
// 每个 webpack 的 loader 都需要导出一个函数 // 需要一个参数 source 所加载到资源文件的内容 // 通过返回值,输出加工过后的结果,返回值必须是一段 JavaScript 代码 const marked = require('marked') module.exports = source => { const html = marked(source) // 使用 module.exports 导出 // return `module.exports = ${JSON.stringify(html)}` // 使用 export default 导出 // return `export default ${JSON.stringify(html)}` // 返回 html 字符串,交给下一个 loader (html-loader) 处理 return html } 复制代码
-
配置 webpack.config.js
module.exports = { entry: './src/main.js', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.md$/, use: { loader: ['html-loader', './markdown-loader.js'] } } ] } } 复制代码
14. 插件机制
-
目的:增强 webpack 自动化能力
-
拷贝静态文件至输出目录
-
安装 copy-webpack-plugin,yarn add copy-webpack-plugin –dev
-
配置 webpack.config.js
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin(), // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }), // 传入一个数组参数,指定拷贝的文件路径 new CopyWebpackPlugin({ // from 可以是目录,也可以是文件的相对路径 patterns: [ { from: "./public", to: "." } ] }) ] } 复制代码
-
-
压缩输出的代码
-
自动生成 html
-
安装 html-webpack-plugin,yarn add html-webpack-plugin –dev
-
在 src 目录新建 index.html 模板文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Webpack</title> </head> <body> <div class="container"> <!-- 通过 htmlWebpackPlugin.options 中拿到配置的参数 --> <h1><%= htmlWebpackPlugin.options.title%></h1> </div> </body> </html> 复制代码
-
配置 webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin(), // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename:'about.html' }) ] } 复制代码
-
-
清除 dist 目录
-
安装 clean-webpack-plugin,yarn add clean-webpack-plugin –dev
-
配置 webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin') module.exports = { entry: './src/main.js', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin() ] } 复制代码
-
-
15. 开发一个插件
-
实现机制:钩子机制
-
webpack 要求插件必须是一个函数或者是一个包含 apply 方法的对象
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // MyPlugin class MyPlugin { // 在 webpack 启动时自动被调用 // 有一个 compiler 参数,这个参数可以接受到 webpack 实例 apply(compiler) { console.log('MyPlugin 启动') // 通过 compiler.hooks.emit.tap() 注册一个函数 // tap() 接收两个参数,第一个是插件的名称,第二个是一个接收一个参数为 compilation 的函数 // compilation 为此次打包的上下文 compiler.hooks.emit.tap('MyPlugin', compilation => { for (const name in compilation.assets) { // console.log(name) // 通过 source() 方法或者内容 // console.log(compilation.assets[name].source()) if (name.endsWith('.js')) { const content = compilation.assets[name].source() const withoutContent = content.replace(/\/\*\*+\*\//g, '') // 将替换的结果覆盖到原有的内容中 compilation.assets[name] = { source: () => withoutContent, size: () => withoutContent.length } } } }) } } module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin(), // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }), // 传入一个数组参数,指定拷贝的文件路径 new CopyWebpackPlugin({ // from 可以是目录,也可以是文件的相对路径 patterns: [ { from: "./public", to: "." } ] }), new MyPlugin() ] } 复制代码
16. 自动编译
- 采用 webpack 中的 watch 工作模式,监听文件变化,自动重新打包
- 方式一
- 在启动 webpack 命令时添加一个 –watch 的参数
- yarn webpack –watch
17. 自动刷新浏览器
- 安装 browser-sync,yarn add browser-sync –dev
- 启动 browser-sync 并监听 dist 目录文件变化,yarn browser-sync dist –files “**/*”
18. webpack dev server
-
webpack 官方提供的开发工具
-
提供用于开发的 http server
-
集成了自动编译和自动刷新浏览器等功能
-
安装 webpack-dev-server,yarn add webpack-dev-server –dev
-
运行 webpack-dev-server,yarn webpack-dev-server
-
webpack-dev-server 并没有将打包结果存储于磁盘中,而是内存中
-
可以加 –open 的参数,用于自动唤起浏览器
-
静态资源访问
-
使用 contentBase 指定目录
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // MyPlugin class MyPlugin { // 在 webpack 启动时自动被调用 // 有一个 compiler 参数,这个参数可以接受到 webpack 实例 apply(compiler) { console.log('MyPlugin 启动') // 通过 compiler.hooks.emit.tap() 注册一个函数 // tap() 接收两个参数,第一个是插件的名称,第二个是一个接收一个参数为 compilation 的函数 // compilation 为此次打包的上下文 compiler.hooks.emit.tap('MyPlugin', compilation => { for (const name in compilation.assets) { // console.log(name) // 通过 source() 方法或者内容 // console.log(compilation.assets[name].source()) if (name.endsWith('.js')) { const content = compilation.assets[name].source() const withoutContent = content.replace(/\/\*\*+\*\//g, '') // 将替换的结果覆盖到原有的内容中 compilation.assets[name] = { source: () => withoutContent, size: () => withoutContent.length } } } }) } } module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, // 配置 webpack-dev-server devServer:{ // 指定额外的静态资源路径 // 可以是一个字符串,可以是一个数组 contentBase: './public' }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin(), // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }), // 传入一个数组参数,指定拷贝的文件路径 // 开发过程中一般不会使用 // new CopyWebpackPlugin({ // // from 可以是目录,也可以是文件的相对路径 // patterns: [ // { from: "./public", to: "." } // ] // }), new MyPlugin() ] } 复制代码
-
-
代理 API
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // MyPlugin class MyPlugin { // 在 webpack 启动时自动被调用 // 有一个 compiler 参数,这个参数可以接受到 webpack 实例 apply(compiler) { console.log('MyPlugin 启动') // 通过 compiler.hooks.emit.tap() 注册一个函数 // tap() 接收两个参数,第一个是插件的名称,第二个是一个接收一个参数为 compilation 的函数 // compilation 为此次打包的上下文 compiler.hooks.emit.tap('MyPlugin', compilation => { for (const name in compilation.assets) { // console.log(name) // 通过 source() 方法或者内容 // console.log(compilation.assets[name].source()) if (name.endsWith('.js')) { const content = compilation.assets[name].source() const withoutContent = content.replace(/\/\*\*+\*\//g, '') // 将替换的结果覆盖到原有的内容中 compilation.assets[name] = { source: () => withoutContent, size: () => withoutContent.length } } } }) } } module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, // 配置 webpack-dev-server devServer: { // 指定额外的静态资源路径 // 可以是一个字符串,可以是一个数组 contentBase: './public', // 添加代理服务配置 // key 为请求路径前缀 // value 为前缀所匹配到的代理规则配置 proxy: { '/api': { // 代理目标 // http://localhost:8080/api/users === https://api.github.com/api/users target: 'https://api.github.com', // 重写代理路径 // key 重写的目标 // value 重写的值 // http://localhost:8080/api/users === https://api.github.com/users pathRewrite: { '^/api': '' }, // 不能使用 localhost:8080 作为请求 Github 的主机名 changeOrigin: true } } }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin(), // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }), // 传入一个数组参数,指定拷贝的文件路径 // 开发过程中一般不会使用 // new CopyWebpackPlugin({ // // from 可以是目录,也可以是文件的相对路径 // patterns: [ // { from: "./public", to: "." } // ] // }), new MyPlugin() ] } 复制代码
-
Source Map
-
作用:解决源代码与运行代码不一致所产生的调试问题
-
通过在文件的最后一行添加 //# sourceMappingURL = <文件路径> 注释的形式引入 sourceMap 文件
-
webpack 配置 sourceMap
-
在 webpack.config.js 中设置 devtool
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // MyPlugin class MyPlugin { // 在 webpack 启动时自动被调用 // 有一个 compiler 参数,这个参数可以接受到 webpack 实例 apply(compiler) { console.log('MyPlugin 启动') // 通过 compiler.hooks.emit.tap() 注册一个函数 // tap() 接收两个参数,第一个是插件的名称,第二个是一个接收一个参数为 compilation 的函数 // compilation 为此次打包的上下文 compiler.hooks.emit.tap('MyPlugin', compilation => { for (const name in compilation.assets) { // console.log(name) // 通过 source() 方法或者内容 // console.log(compilation.assets[name].source()) if (name.endsWith('.js')) { const content = compilation.assets[name].source() const withoutContent = content.replace(/\/\*\*+\*\//g, '') // 将替换的结果覆盖到原有的内容中 compilation.assets[name] = { source: () => withoutContent, size: () => withoutContent.length } } } }) } } module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, // 配置 webpack-dev-server devServer: { // 指定额外的静态资源路径 // 可以是一个字符串,可以是一个数组 contentBase: './public', // 添加代理服务配置 // key 为请求路径前缀 // value 为前缀所匹配到的代理规则配置 proxy: { '/api': { // 代理目标 // http://localhost:8080/api/users === https://api.github.com/api/users target: 'https://api.github.com', // 重写代理路径 // key 重写的目标 // value 重写的值 // http://localhost:8080/api/users === https://api.github.com/users pathRewrite: { '^/api': '' }, // 不能使用 localhost:8080 作为请求 Github 的主机名 changeOrigin: true } } }, // 配置调试 devtool: 'source-map', module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ new CleanWebpackPlugin(), // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }), // 传入一个数组参数,指定拷贝的文件路径 // 开发过程中一般不会使用 // new CopyWebpackPlugin({ // // from 可以是目录,也可以是文件的相对路径 // patterns: [ // { from: "./public", to: "." } // ] // }), new MyPlugin() ] } 复制代码
-
webpack 中 sourceMap 风格
- 每种风格的效率和效果各不相同
- eval 模式
- 不会生成 sourceMap 文件
- 只能定位具体的源代码文件名称,不能定位具体的行列信息
- 构建速度最快
- cheap-eval-source-map 模式
- 可以定位到源代码文件名称,具体的行信息
- 定位的代码是 ES6 转换之后的结果
- cheap-module-eval-source-map 模式
- 可以定位到源代码文件名称,具体的行信息
- 定位的代码和源代码一模一样
- nosources-source-map 模式
- 定位的信息点进去是没有源代码的
- 可以定位具体的行列信息
- 规律
- eval – 是否使用 eval 执行模块代码
- cheap – Source Map 是否包含行信息
- module – 是否得到 loader 处理之前的代码
-
-
19. HMR 模块热替换
-
全称:hotModuleReplacement
-
应用程序运行的过程中实时替换某个模块,应用的运行状态不会受影响
-
使用方式
-
方式一
- webpack-dev-server –hot
-
方式二
-
webpack.config.js 配置文件中添加对应的配置
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', entry: './src/main.js', output: { filename: 'js/bundle.js' }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin() ] } 复制代码
-
-
-
webpack 中的 HMR 并不可以开箱即用
-
webpack 中的 HMR 需要手动处理模块热替换逻辑
-
HMR API
import createEditor from './editor' import background from './better.png' import './global.css' const editor = createEditor() document.body.appendChild(editor) const img = new Image() img.src = background document.body.appendChild(img) // module.hot 中的 hot 属性是 HMR API 的核心对象 // 提供了 accpet 用于注册某一个模块更新过后的处理函数 // accept 函数接收两个参数,第一个参数:依赖模块的路径,第二个参数:依赖模块的处理函数 // 处理 js 模块的热替换 if (module.hot) { let lastEditor = editor module.hot.accept('./editor.js', () => { // console.log('editor been updated') const value = lastEditor.innerHTML document.body.removeChild(lastEditor) const newEditor = createEditor() newEditor.innerHTML = value document.body.appendChild(newEditor) lastEditor = newEditor }) module.hot.acceps('./better.png', () => { img.src = background }) } 复制代码
20. 生产环境优化
-
不同环境下的配置
-
配置文件根据环境不同导出不同的配置
-
适用于中小型项目
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // MyPlugin class MyPlugin { // 在 webpack 启动时自动被调用 // 有一个 compiler 参数,这个参数可以接受到 webpack 实例 apply(compiler) { console.log('MyPlugin 启动') // 通过 compiler.hooks.emit.tap() 注册一个函数 // tap() 接收两个参数,第一个是插件的名称,第二个是一个接收一个参数为 compilation 的函数 // compilation 为此次打包的上下文 compiler.hooks.emit.tap('MyPlugin', compilation => { for (const name in compilation.assets) { // console.log(name) // 通过 source() 方法或者内容 // console.log(compilation.assets[name].source()) if (name.endsWith('.js')) { const content = compilation.assets[name].source() const withoutContent = content.replace(/\/\*\*+\*\//g, '') // 将替换的结果覆盖到原有的内容中 compilation.assets[name] = { source: () => withoutContent, size: () => withoutContent.length } } } }) } } // webpack 还可以导出一个函数,这个函数接收两个参数,第一个参数:环境参数,第二个参数:运行命令时传递的所有参数 module.exports = (env, argv) => { let config = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, // 配置 webpack-dev-server devServer: { // 指定额外的静态资源路径 // 可以是一个字符串,可以是一个数组 contentBase: './public', // 添加代理服务配置 // key 为请求路径前缀 // value 为前缀所匹配到的代理规则配置 proxy: { '/api': { // 代理目标 // http://localhost:8080/api/users === https://api.github.com/api/users target: 'https://api.github.com', // 重写代理路径 // key 重写的目标 // value 重写的值 // http://localhost:8080/api/users === https://api.github.com/users pathRewrite: { '^/api': '' }, // 不能使用 localhost:8080 作为请求 Github 的主机名 changeOrigin: true } } }, // 配置调试 devtool: 'source-map', module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }), new MyPlugin() ] } if(env === 'production'){ config.mode = 'production' config.devtool = false config.plugins = [ ...config.plugins, new CleanWebpackPlugin(), new CopyWebpackPlugin({ patterns: [ { from: "./public", to: "." } ] }), ] } } 复制代码
-
一个环境对应一个配置文件
-
项目根目录创建 webpack.common.js webpack.dev.js webpack.prod.js
-
webpack.common.js
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, // 配置 webpack-dev-server devServer: { // 指定额外的静态资源路径 // 可以是一个字符串,可以是一个数组 contentBase: './public', // 添加代理服务配置 // key 为请求路径前缀 // value 为前缀所匹配到的代理规则配置 proxy: { '/api': { // 代理目标 // http://localhost:8080/api/users === https://api.github.com/api/users target: 'https://api.github.com', // 重写代理路径 // key 重写的目标 // value 重写的值 // http://localhost:8080/api/users === https://api.github.com/users pathRewrite: { '^/api': '' }, // 不能使用 localhost:8080 作为请求 Github 的主机名 changeOrigin: true } } }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 } } } ] }, plugins: [ // 指定一个对象参数 new HtmlWebpackPlugin({ // html 页面标题 title: 'Webpack Plugin Sample', // html 中 meta meta: { viewport: 'width=device-width' }, // 指定模板文件 template: './src/index.html' }), // 创建额外的 html 文件 new HtmlWebpackPlugin({ filename: 'about.html' }) ] } 复制代码
-
webpack.dev.js
const common = require('./webpack.common.js') // 用于合并公共配置 const merge = require('webpack-merge') module.exports = merge(common, { mode: 'none', devtool: 'cheap-module-eval-source-map' }) 复制代码
-
webpack.prod.js
const common = require('./webpack.common.js') // 用于合并公共配置 const merge = require('webpack-merge') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = merge(common, { mode: 'production', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin({ patterns: [ { from: "./public", to: "." } ] }) ] }) 复制代码
-
运行打包
- yarn webpack –config 配置文件
-
-
21. webpack DefinePlugin
-
为代码注入全局成员
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', entry: './src/main.js', output: { filename: 'js/bundle.js' }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }) ] } 复制代码
22. Tree Shaking
-
摇掉代码中未引用的部分
-
在生产模式下自动执行
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', entry: './src/main.js', output: { filename: 'js/bundle.js' }, // 集中配置 webpack 的优化功能 optimization:{ // 只导出被使用的成员 usedExports: true, // 压缩代码,并且去除无引用的代码 minimize: true, // 合并模块 // 作用:尽可能将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积 concatenateModules: true }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }) ] } 复制代码
23. webpack SideEffects
-
副作用:模块执行时除了导出成员之外所作的事
-
一般用于 npm 包标记是否有副作用
-
生产模式下会自动开启
-
会找到 package.json 中是否有 sideEffects 属性,通过配置值来确定项目中是否有副作用
-
package.json
{ "name": "my-webpack5", "version": "1.0.0", "description": "my-webpack5", "main": "index.js", "author": "康小源", "license": "MIT", "devDependencies": { "css-loader": "^5.0.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^4.5.0", "style-loader": "^2.0.0", "webpack": "4.44.2", "webpack-cli": "3.3.12", "webpack-dev-server": "^3.11.0" }, // 标识项目中所有的代码没有副作用 // 没有用到的模块没有副作用会被移除 "sideEffects": false } 复制代码
-
webpack.config.js
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', entry: './src/main.js', output: { filename: 'js/bundle.js' }, // 集中配置 webpack 的优化功能 optimization:{ // 开启副作用 sideEffects: true, // 只导出被使用的成员 usedExports: true, // 压缩代码,并且去除无引用的代码 minimize: true, // 合并模块 // 作用:尽可能将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积 concatenateModules: true }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }) ] } 复制代码
24. 代码分割
-
多入口打包
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', // 配置一个打包入口 // entry: './src/main.js', // 配置多个打包入口 entry:{ album: './src/album.js', index: './src/index.js' }, output: { filename: 'js/[name].bundle.js' }, // 集中配置 webpack 的优化功能 optimization:{ // 开启副作用 sideEffects: true, // 只导出被使用的成员 usedExports: true, // 压缩代码,并且去除无引用的代码 minimize: true, // 合并模块 // 作用:尽可能将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积 concatenateModules: true, // 提取公共模块 splitChunks: { chunks: 'all' } }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html', // 指定自己的 bundle.js chunk: ['index'] }), new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/album.html', chunk: ['album'] }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }) ] } 复制代码
-
动态导入
import(/* webpackChunkName:'posts' */./posts/poss').then(module => { console.log(module) }) 复制代码
25. 提取 css 到单个文件
-
安装 mini-css-extract-plugin,yarn add mini-css-extract-plugin –dev
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { mode: 'development', // 配置一个打包入口 // entry: './src/main.js', // 配置多个打包入口 entry:{ album: './src/album.js', index: './src/index.js' }, output: { filename: 'js/[name].bundle.js' }, // 集中配置 webpack 的优化功能 optimization:{ // 开启副作用 sideEffects: true, // 只导出被使用的成员 usedExports: true, // 压缩代码,并且去除无引用的代码 minimize: true, // 合并模块 // 作用:尽可能将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积 concatenateModules: true }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ // 'style-loader', MiniCssExtractPlugin.loader, // 通过 link 标签的方式注入 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html', // 指定自己的 bundle.js chunk: ['index'] }), new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/album.html', chunk: ['album'] }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }), new MiniCssExtractPlugin() ] } 复制代码
-
压缩提取的 css 文件
-
安装 optimize-css-Assets-webpack-plugin,yarn add optimize-css-assets-webpack-plugin –dev
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { mode: 'development', // 配置一个打包入口 // entry: './src/main.js', // 配置多个打包入口 entry:{ album: './src/album.js', index: './src/index.js' }, output: { filename: 'js/[name].bundle.js' }, // 集中配置 webpack 的优化功能 optimization:{ // 开启副作用 sideEffects: true, // 只导出被使用的成员 usedExports: true, // 压缩代码,并且去除无引用的代码 minimize: true, minimizer: [ // 压缩 css 文件 new OptimizeCssAssetsWebpackPlugin(), // 压缩 js 文件 new TerserWebpackPlugin() ], // 合并模块 // 作用:尽可能将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积 concatenateModules: true }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ // 'style-loader', MiniCssExtractPlugin.loader, // 通过 link 标签的方式注入 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html', // 指定自己的 bundle.js chunk: ['index'] }), new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/album.html', chunk: ['album'] }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }), new MiniCssExtractPlugin() ] } 复制代码
-
26. 文件名 Hash
-
生产模式下,文件名需要使用 Hash
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { mode: 'development', // 配置一个打包入口 // entry: './src/main.js', // 配置多个打包入口 entry:{ album: './src/album.js', index: './src/index.js' }, output: { // 项目级别的 hash,一旦项目中有改动,hash 值改变 // filename: 'js/[name]-[hash].bundle.js' // chunk 级别的 hash,一旦属于同一路 chunk 有改变,hash 值改变 // filename: 'js/[name]-[chunkhash].bundle.js' // 文件级别的 hash,一旦文件改变,hash 值改变 // :8 控制 hash 值的长度 filename: 'js/[name]-[contenthash:8].bundle.js' }, // 集中配置 webpack 的优化功能 optimization:{ // 开启副作用 sideEffects: true, // 只导出被使用的成员 usedExports: true, // 压缩代码,并且去除无引用的代码 minimize: true, minimizer: [ // 压缩 css 文件 new OptimizeCssAssetsWebpackPlugin(), // 压缩 js 文件 new TerserWebpackPlugin() ], // 合并模块 // 作用:尽可能将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积 concatenateModules: true }, devServer:{ // 开启热替换,如果热替换没有执行,会自动唤起页面刷新 // hot: true, // 开启热替换,如果热替换没有执行,不会自动唤起页面刷新 hotOnly: true }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.css$/, use: [ // 'style-loader', MiniCssExtractPlugin.loader, // 通过 link 标签的方式注入 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: 'file-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html', // 指定自己的 bundle.js chunk: ['index'] }), new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/album.html', chunk: ['album'] }), // 利用 webpack 的内置插件 new webpack.HotModuleReplacementPlugin(), // 定义全局变量 // 参数为一个对象 new webpack.DefinePlugin({ API_BASE_API: JSON.stringify('https://api.example.com') }), new MiniCssExtractPlugin({ filename: '[name]-[contenthash:8].bundle.css' }) ] } 复制代码