跳到主要内容

webpack入门(webpak4)

webpack 基础知识

需要用到的包介绍

  • webpack - webpack 的核心包
  • webpack-cli - webpack 的命令行工具
  • webpack-dev-server - webpack 的开发服务器 (热更新)

webpack-cil 命令参考

https://webpack.js.org/api/cli/

webpack 的打包过程

webpack 初体验

  • webpack 将 ES6 的模块化编译成浏览器能够识别的模块化
  • webpack 默认只能打包 js 和 json 文件
  • 生产环境和开发环境的区别 ,开发环境是经过压缩的代码

使用命令行设置打包入口出口和 mode

webpack --entry  ./src/index.js -o ./dist --mode=development #开发模式

webpack --entry ./src/index.js -o ./dist --mode=production #生产模式

webpack.config.js

指示 webpack 来干那些活 当运行 webpack 指令时 会加载里面的配置

打包样式资源

下载插件

npm i css-loader style-loader less-loader less -D

修改配置

普通 css 文件
// webpack.config.js
const { resolve } = require("path");
module.exports = {
//入口
entry: "./src/index.js",
//出口
output: {
filename: "bundle.js",
path: resolve(__dirname, "dist"),
},
//模式 development 、 production
mode: "development",
// loader
module: {
rules: [
{
test: /\.css$/,
// 执行顺序由右到左
use: ["style-loader", "css-loader"],
},
],
},
// plugins 插件 (数组)
plugins: [],
};
//index.js 中引入样式 webpack 会通过import加载css 再通过 css-loader ->style-loader 转换为style标签放在head中
import "./index.css";
引入 less 或 sass 样式处理文件
// rules 中 添加 less  或 sass 的处理规则
{
test:/\.less$/,
use:[
//转化为style
"style-loader",
// 处理css
"css-loader",
// 转化为css
"less-loader"
]
}

注意事项

less 和 sass 不光要下对应的 loader 还需要 less 或 sass 包

ps:node-sass 安装起来比较麻烦 可以参照 https://www.cnblogs.com/zhishaofei/p/12712937.html

打包 HTML 资源

下载插件

npm install --save-dev html-webpack-plugin

tip: html-webpack-plugin 和 html-loader 的不同

  • html-loader 是用来解析入口文件中关联的 html 中的 image 图片的引入
  • html-webpack-plugin 是用来自动生成最后 dist 目录下的 index.html (并自动导入打包好的 js)

html-webpack-plugin 插件的更多配置见官网 :https://github.com/jantimon/html-webpack-plugin

修改配置

// webpack.config.js
const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: resolve(__dirname, "dist"),
},
mode: "development",
module: {
rules: [],
},
plugins: [
// HtmlWebpackPlugin 会自动在output目录下生成index.html
// 并自动引入打包好的 bundle.js
new HtmlWebpackPlugin({
// 以./src/index.html 为模板 生成最后打包好的html
template: "./src/index.html",
}),
],
};

打包图片资源

下载插件

npm install --save-dev html-loader url-loader file-loader

修改配置

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
entry: "./src/index.js",
output: {
filename: "bunle.js",
path: resolve(__dirname, "dist"),
publicPath: "", // webpack5 中html中导入image需要设置publicPath (升级html-webpack-plugin 到 5.xx可以不用)
},
mode: "development",
module: {
rules: [
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
// 解析图标 将小图标直接转base64 url-loader 依赖 file-loader 需要一起下载
{
test: /\.(jpg|png|gif)$/,
use: [
{
loader: "url-loader",
options: {
// 小于8kb的以base64位插入 data:image/jpeg;base64,/9j
// 大于8kb的以改为hash值为名称的原文件插入
limit: 8 * 1024,
esModule: false,
},
},
],
},
{
// 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
test: /\.html$/,
use: "html-loader",
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
}),
],
};

打包其他资源(字体等)

修改配置

  {
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'font'
}
}

devServer

下载插件

npm i webpack-dev-server -D

修改配置

webpack.config.js 中添加

// 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
// 特点:只会在内存中编译打包,不会有任何输出
devServer: {
// 项目构建后路径
contentBase: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true
}

webpack 5
  devServer: {
// 告诉服务器从哪个目录中提供内容。只有在你想要提供静态文件时才需要。。
static: {
directory: path.join(__dirname, "public"),
},
// 压缩
compress: false,
port: 9000,
hot: true,
devMiddleware: {
// 虚拟路径 devServer.publicPath 将用于确定应该从哪里提供 bundle,并且此选项优先
publicPath: "/public/",
},
},

调用服务使用命令

如全局安装 wabpack 直接: webpack serve

没有全局安装 webpack :npx webpack serve

启动服务后不会生成固定文件 devserver 会在内存中进行编译

使用 express 作为开发服务器配置

https://webpack.docschina.org/guides/development/#using-webpack-dev-middleware

提取 css 文件为单独资源

用上面的方法打包出来的 css 都在 js 文件中 感觉加载时会闪一下 所以单独提炼出 css (使用 link 标签)

下载插件

npm install --save-dev mini-css-extract-plugin

修改配置

 module: {
rules: [
{
test: /\.css$/,
use: [
// 这个loader取代style-loader。作用:提取js中的css成单独文件
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
// 对输出的css文件进行重命名
new MiniCssExtractPlugin({
filename: "css/index.css"
}),
]

css 兼容性处理

基于:postcss

postcss 相当于一个启动器,里面可以装各种 插件 如 postcss-preset-env(预设环境) 和 autoprefixer(自动前缀)

下载插件

npm install --save-dev postcss-loader postcss-preset-env
  • postcss-loader :

在所有 css | sass | less loader 前使用 作用: 使用 postcss 来解析 css

  • postcss-preset-env :

帮你将最新的 CSS 语法转换成大多数浏览器都能理解的语法,并根据你的目标浏览器或运行时环境来确定你需要的 polyfillss (postcss-preset-env 为 postcss 的 预设环境)

修改配置

rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
/*
css兼容性处理:postcss --> postcss-loader -postcsspreset-env

帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式

"browserslist": {
// 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境:默认是看生产环境
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
*/
// 使用loader的默认配置
// 'postcss-loader',
// 修改loader的配置
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// Options
},
],
// 或者 require("postcss-preset-env")()
],
},
},
},
],
},
];

browserslist

具体配置说明 https://github.com/browserslist/browserslist

在 package.json 中配置 browserslist 或添加 .browserslistrc 文件

具体的环境是根据 process.env.NODE_ENV 确定, 所以需要设置 process.env.NODE_ENV ,只设置 mode: 'production' 不好使

"browserslist": {
"development": [
"last 1 chrome version",//兼容最近的谷歌浏览器
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.01%",//兼容>0.01%
"not dead",//不用管弃用的浏览器
"not op_mini all"//不用op
]
}

压缩 css

webpack v4 使用 optimize-css-assets-webpack-plugin

对于 webpack v5 或更高版本,请改用 css-minimizer-webpack-plugin。

下载插件

npm install --save-dev css-minimizer-webpack-plugin

修改配置

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module :...,
plugins:...,
optimization: {
minimize: true,
minimizer: [
// For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line
// `...`,
new CssMinimizerPlugin(),
],
},

js 语法检查

下载插件

npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
  • eslint-loader 已经弃用 请使用 eslint-webpack-plugin

  • eslint-config-airbnb-base :airbnb 标准的所以规则 需要eslinteslint-plugin-import

npm install --save-dev eslint-webpack-plugin

修改配置

eslint-loader (已经弃用)
...
module: {
rules: [
/*
语法检查: eslint-loader eslint
注意:只检查自己写的源代码,第三方的库是不用检查的
设置检查规则:
package.json中eslintConfig中设置~
"eslintConfig": {
"extends": "airbnb-base"
}
airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复eslint的错误
fix: true
}
}
]
},
eslint-webpack-plugin
const ESLintPlugin = require("eslint-webpack-plugin");

module.exports = {
// ...
plugins: [new ESLintPlugin(options)],
// ...
};
eslint 的配置

在 package.json 中的"eslintConfig" 或者 .eslintrc.js 中修改 eslint 的配置

  extends: "airbnb-base",
env: {
browser: true
},
rules: {
"no-console": process.env.NODE_ENV === "development" ? "off" : "warn"
}

eslint 全配置:https://eslint.bootcss.com/docs/user-guide/configuring#extending-configuration-files

js 兼容性处理

下载插件

npm install --save-dev  babel babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
  • @babel/preset-env babel 预设环境

  • @babel/polyfill 所有的转换包集合 Babel 7.4.0 开始,不赞成使用此软件包

  • core-js 是 babel-polyfill 的底层依赖 可以配合 useBuiltIns 选项实现按需加载

修改配置

有两种方式配置 babel

  • 在 webpack.config.js 中配置
  • 在 babel.config.js 中配置
在 webpack.config.js 中配置
...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
rootMode: "upward",
},
options: {
// 预设:指示babel做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定core-js版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
]
},
在 babel.config.js 中配置

或者将配置放到项目根目录的 babel.config.js

  module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
rootMode: "upward", // 需要配置 rootMode: "upward" 意思是向上查找 找到 babel.config.[json | js]
},
}
]
},

babel.config.js

module.exports = {
// 预设:指示 babel 做怎么样的兼容性处理
presets: [
[
"@babel/preset-env",
{
// 按需加载
useBuiltIns: "usage",
// 指定core-js版本
corejs: {
version: 3,
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17",
},
},
],
],
};

js 压缩和 html 压缩

修改配置

...
// 生产环境下会自动压缩js代码 和 html代码
mode: 'production'

也可以自定义压缩配置

plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
// 当webpack的默认mode值为'production' 自动设置 minify: true
// 全部配置查看:https://www.npmjs.com/package/html-webpack-plugin#minification
// 压缩html代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true,
},
}),
];

webpack 性能优化

  • 开发环境性能优化
  • 生产环境性能优化

开发环境性能优化

  • 优化打包构建速度
    • HMR
  • 优化代码调试
    • source-map

生产环境性能优化

  • 优化打包构建速度
    • oneOf
    • babel 缓存
    • 多进程打包
    • externals
    • dll
  • 优化代码运行的性能
    • 缓存(hash-chunkhash-contenthash)
    • tree shaking
    • code split
    • 懒加载/预加载
    • pwa

HMR

tip

webpack 5 需要使用 npm i webpack-dev-server@4.0.0-beta.0

但是目前 4.0.0-beta.0 还不完善 文档没有更新 热模块重载不好使

定义

hot module replacement 热模块替换 / 模块热替换

作用

一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 极大提升构建速度

  • 样式文件:可以使用 HMR 功能:因为 style-loader 内部实现了~

    • js 文件:默认不能使用 HMR 功能 --> 需要修改 js 代码,添加支持 HMR 功能的代码 注意:HMR 功能对 js 的处理,只能处理非入口 js 文件的其他文件。
    • html 文件: 默认不能使用 HMR 功能.同时会导致问题:html 文件不能热更新了~ (不用做 HMR 功能) 解决:修改 entry 入口,将 html 文件引入

各大框架的 loader 内部支持了 HMR

  • React Hot Loader: 实时调整 react 组件。
  • Vue Loader: 此 loader 支持 vue 组件的 HMR,提供开箱即用体验。
  • Elm Hot webpack Loader: 支持 Elm 编程语言的 HMR。
  • Angular HMR: 没有必要使用 loader!直接修改 NgModule 主文件就够了

修改配置

// 4.0 开启热更新
devServer: {
contentBase: resolve(__dirname, 'dist'),
// 开启HMR功能
// 当修改了webpack配置,新配置要想生效,必须重新webpack服务
hot: true,
compress: true,
port: 3000,
open: true,
}

source-map

source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map:外部
错误代码准确信息 和 源代码的错误位置
inline-source-map:内联
只生成一个内联source-map
错误代码准确信息 和 源代码的错误位置
hidden-source-map:外部
错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的source-map,都在eval
错误代码准确信息 和 源代码的错误位置
nosources-source-map:外部
错误代码准确信息, 但是没有任何源代码信息
cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确的行
cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入

内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快

开发环境:速度快,调试更友好
速度快(eval>inline>cheap>...)
eval-cheap-souce-map
eval-source-map
调试更友好
souce-map
cheap-module-souce-map
cheap-souce-map

--> eval-source-map / eval-cheap-module-souce-map

生产环境:源代码要不要隐藏? 调试要不要更友好
内联会让代码体积变大,所以在生产环境不用内联
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

--> source-map / cheap-module-souce-map

修改配置

// 开发环境
devtool: "eval-source-map";
// 生产环境
devtool: "source-map"; // 根据需求进行选择

oneOf

oneOf:让一个文件只有一个 loader 处理 ,找打对应的 loader 就不往下遍历了。

修改配置

  module: {
rules: [
{
// 需要每个类型的文件都处理的loader 单独在oneOf外面
},
{
// oneOf 内部 一个 rule 只处理一个类型 找到对应类型的 rule 就不再继续往下遍历
oneOf: [
{
test: /\.css$/,
use: styleCommonLodar
},
...
]
}
]
},

优化效果

#优化前
webpack 5.22.0 compiled with 1 warning in 12820 ms
#优化后
webpack 5.22.0 compiled with 1 warning in 9615 ms

babel 缓存

缓存文件会放在 node_modules\.cache\babel-loader中,如果编译的不对删除这个文件再试

修改配置

  {
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
// 缓存文件会放在 node_modules\.cache\babel-loader中 如果编译的不对删除这个文件再试
cacheDirectory: true,
rootMode: "upward" // 需要配置 rootMode: "upward" 意思是向上查找 找到 babel.config.[json | js]
}
},

优化效果

#优化前
webpack 5.22.0 compiled with 1 warning in 11641 ms
#优化后 再次打包
webpack 5.22.0 compiled with 1 warning in 8476 ms

tree shaking 树摇

tree shaking:去除无用代码

前提

  1. 必须使用 ES6 模块化

  2. 开启 production 环境

作用

减少代码体积

修改配置

//webpack.config.js
mode: "production";
sideEffects

在 package.json 中配置 sideEffects :副作用 (导入了例如 css less 等样式 ,代码上是没用到的 ,但是需要保留的 )

// package.json
"sideEffects": false
//所有代码都没有副作用(都可以进行tree shaking) 问题:可能会把css / @babel/polyfill (副作用)文件干掉
"sideEffects": ["*.css", "*.less","*iconfont.js" ] //当需要保留的文件名放到数组中

官方结论

因此,我们学到为了利用 tree shaking 的优势, 你必须...

  • 使用 ES2015 模块语法(即 importexport)。
  • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
  • 在项目的 package.json 文件中,添加 "sideEffects" 属性。
  • 使用 mode"production" 的配置项以启用更多优化项,包括压缩代码与 tree shaking。

你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码) 和 library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

code split 代码分割

修改配置

  //webpack.config.js
output: {
// 这里使用 chunk name + contenthash 进行命名
filename: 'js/[name].[contenthash:10].js',
//..
},
optimization: {
//...
// 代码块 切割
// 可以将node_modules中代码单独打包一个chunk最终输出 如果想分割node_modules中的代码需要配合dll
// 项目的中业务代码通过 import() 语法进行分割
splitChunks: {
chunks: 'all',
},
},

修改业务代码

// 需要进行切割的js需要使用 import() 异步引入使用
// /* webpackChunkName: 'test01' */ test01 代表分割后 ChunkName, 比如以上配置会生成 test01.f20318151f.js
import(/* webpackChunkName: 'test01' */ "./test").then(({ mul, count }) => {
console.log("Bowen: mul", mul);
});

PWA

下载插件

# 开启 service worker
npm install --save-dev workbox-webpack-plugin
# 生成清单文件 manifest.json
npm install --save-dev webpack-pwa-manifest

修改配置

// pwa
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
module.exports = {
plugins: [
// ...
// 建议放到最下面 放到MiniCssExtractPlugin 会报错 Error: Cannot find module 'optimize-css-assets-webpack-plugin
new WorkboxWebpackPlugin.GenerateSW({
/*
1. 帮助serviceworker快速启动
2. 删除旧的 serviceworker

生成一个 serviceworker 配置文件~
*/
clientsClaim: true,
skipWaiting: true
})
// 生成清单文件 manifest.json 的目的是将Web应用程序安装到设备的主屏幕 谷歌上会提示下载图标
// https://developer.mozilla.org/zh-CN/docs/Web/Manifest
new WebpackPwaManifest({
name: 'My Progressive Web App',
short_name: 'MyPWA',
description: 'My awesome Progressive Web App!',
background_color: '#ffffff',
crossorigin: 'use-credentials', //can be null, use-credentials or anonymous
icons: [
{
src: resolve('src/assets/icon.png'),
sizes: [96, 128, 192, 256, 384, 512], // multiple sizes
},
{
src: resolve('src/assets/icon.png'),
size: '1024x1024', // you can also use the specifications pattern
},
{
src: resolve('src/assets/icon.png'),
size: '1024x1024',
purpose: 'maskable',
},
],
}),
]
};

业务代码修改

// index.js
/*
1. eslint不认识 window、navigator全局变量
解决:需要修改package.json中eslintConfig配置
"env": {
"browser": true // 支持浏览器端全局变量
}
2. sw代码必须运行在服务器上
--> nodejs
-->
npm i serve -g
serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
*/
// 注册serviceWorker
// 处理兼容性问题
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/service-worker.js")
.then(() => {
console.log("sw注册成功了~");
})
.catch(() => {
console.log("sw注册失败了~");
});
});
}

多进程打包

官方文档:https://webpack.docschina.org/loaders/thread-loader/#root

下载依赖

npm install --save-dev thread-loader

修改配置

module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
/*
开启多进程打包
进程启动大概为600ms,进程通信也有开销。
只有工作消耗时间比较长,才需要多进程打包
*/
use: [
"thread-loader",
// 耗时的 loader (例如 babel-loader)
],
},
],
},
};

externals 外部扩展 (排除无需打包的外部依赖)

官方文档:https://webpack.docschina.org/configuration/externals/ 防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。 例如,从 CDN 引入 react,而不是把它打包

修改 html 模板

<!-- index.html -->
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.production.min.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"
></script>

修改配置

module.exports = {
//...
externals: {
// 拒绝react被打包进来
// key -> import react from "react" 中的 "react"
// value -> react 的命名空间 namespace React
react: "React",
"react-dom": "ReactDOM",
},
};

DllPlugin (打包优化)

官方文档:https://webpack.docschina.org/plugins/dll-plugin/ dll 主要用户用于将第三方依赖 或者公共依赖 进行打包, 目的与 externals 差不多都是为了加快构建速度

添加 webpack.dll.js

webpack.dll.js 专门用于打包第三方依赖 或者公共依赖

/**
* webpack.dll.js
* 使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
* 当你运行 webpack 时,默认查找 webpack.config.js 配置文件
* 需求:需要运行 webpack.dll.js 文件
* --> webpack --config webpack.dll.js
*
* @format
*/
// 清理上次的打包文件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { resolve } = require("path");
const webpack = require("webpack");

module.exports = {
entry: {
// 最终打包生成的 vendor --> MyDll.[name].js name=vendor
// ['react',''react-dom'] --> 要打包的库是react 跟包名一致react
vendor: ["react", "react-dom"],
},
output: {
// 生成文件名
filename: "[name].dll.js",
path: resolve(__dirname, "dll"),
// 打包的库里面向外暴露出去的内容叫什么名字(全局变量名) 需要和 webpack.DllPlugin 中的 name 抱持一致
library: "[name]_[fullhash]_library",
},
plugins: [
new CleanWebpackPlugin(),
// 打包生成一个 manifest.json --> 提供和react映射 manifest.json中映射library名称
new webpack.DllPlugin({
name: "[name]_[fullhash]_library", // 映射库的暴露的内容名称 不用管叫什么 打包后的代码会使用 咱们构建前的代码还是该怎么使就怎么使
path: resolve(__dirname, "dll/manifest.json"), // 生成的清单文件 包含了react 和 react-dom
}),
],
mode: "production",
};

执行 npx webpack --config webpack.dll.js 会在当前目录下生成 dll 目录

dll
├── manifest.json // 清单
├── vendor.dll.js // 第三方包的集合
└── vendor.dll.js.LICENSE.txt

manifest.json

{
"name": "vendor_311590c415821fbaedf4_library",
"content": {
"../node_modules/react/index.js": {
"id": 378,
"buildMeta": {
"exportsType": "dynamic",
"defaultObject": "redirect"
},
"exports": [
"Children",
"Component",
"Fragment",
"Profiler",
"PureComponent",
"StrictMode",
"Suspense",
"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
"cloneElement",
"createContext",
"createElement",
"createFactory",
"createRef",
"forwardRef",
"isValidElement",
"lazy",
"memo",
"useCallback",
"useContext",
"useDebugValue",
"useEffect",
"useImperativeHandle",
"useLayoutEffect",
"useMemo",
"useReducer",
"useRef",
"useState",
"version"
]
},
"../node_modules/react-dom/index.js": {
"id": 542,
"buildMeta": {
"exportsType": "dynamic",
"defaultObject": "redirect"
},
"exports": [
"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
"createPortal",
"findDOMNode",
"flushSync",
"hydrate",
"render",
"unmountComponentAtNode",
"unstable_batchedUpdates",
"unstable_createPortal",
"unstable_renderSubtreeIntoContainer",
"version"
]
}
}
}

修改配置

webpack.config.js

const webpack = require("webpack");
// 自动在html中引入资源插件 (不用插件 手动在模板html中添加也可以)
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin");
module.exports = {
plugins: [
// ...
// 告诉webpack哪些库不参与打包
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, "dll/manifest.json"),
}),
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, "dll/vendor.dll.js"),
}),
],
};

打包生成的结构

dist
├── assets
├── css
├── index.html // html中引入 vendor.dll.js <script defer="defer" src="./vendor.dll.js"></script>
├── js
├── vendor.dll.js // 将dll下的 vendor.dll.js 放到当前打包后的目录下
└── vendor.dll.js.LICENSE.txt

DLL 和 externals 、code split 的区别

DLL

  • 用于将第三方依赖单独打包构建,跟项目一起放到同一个服务器下进行加载
  • 优点:优化项目本地的构建速度

externals

  • 不打包第三方依赖,第三方依赖使用 CDN
  • 优点:优化项目本地的构建速度,因为使用 CDN 优化第三方依赖加载速度

code split

  • 将项目中的进行模块代码分割成多个 js 文件
  • 优点:优化项目本身加载速度,依靠代码分割可以实现模块的懒加载和预加载

官方文档

生产环境配置官方文档

https://webpack.docschina.org/guides/production/

css 压缩、提取

https://webpack.docschina.org/plugins/mini-css-extract-plugin/#minimizing-for-production

每次打包清除打包文件

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
...
plugins:[
new CleanWebpackPlugin(),
]

报错

Error: Automatic publicPath is not supported in this browser

设置 output 中 publicPath: '' 为空字符串

tip :项目中的基础知识

node 模块的搜索流程

node 在使用模块名来引入模块时,会首先在当前目录的 node_modules 中寻找是否有该模块 如果有则直接使用,如果没有则会一直向上一级目录的 node_modules 中寻找,直到磁盘的根目录

.browserslistrc

对应 package.json 中的 browserslist

postcss-preset-env 会找到 browserslist 作为兼容的基础

browserslist 配置 https://github.com/browserslist/browserslist

.eslintrc.js

对应 package.json 中的 eslintConfig

eslint 配置 https://eslint.org/docs/developer-guide/nodejs-api#%E2%97%86-new-eslint-options

babel.config.js

bable 的配置文件 对应 webpack.config.js 中 babel-loader 的 options

babel.config.js 文件,可以使用不同的扩展名(.js.cjs.mjs)

bable 的配置文件分为两种:

  • babel.config.js 整个项目都用这个 babel 配置(项目范围的配置)
  • .babelrc.js 配置文件是否仅适用于项目的某个部分 (相对文件配置)

具体配置:https://babeljs.io/docs/en/config-files

.prettierrc.js

vscode 中的 prettier 插件 会优先已这个文件作为格式化的配置

配置同 prettier 插件 https://prettier.io/docs/en/options.html

核心模块

postcss

css 兼容性插件

配合 webpack 使用 :postcss-loader https://www.npmjs.com/package/postcss-loader

eslint

语法检查校验插件

配合 webpack 使用 :eslint-webpack-plugin https://www.npmjs.com/package/eslint-webpack-plugin

babel

js 兼容性插件

配合 webpack 使用 : babel-loader https://www.npmjs.com/package/babel-loader