参考:
注意:当前 node 版本要尽量用最新版本,目前 16.13.0 是没有问题的
创建项目文件夹 webpack-study
npm init -y
npm i -D webpack webpack-cli
新建一个文件夹 src ,然后新建一个文件 main.js,写一点代码测试一下
console.log('Hello World')
配置 package.json 命令
"scripts": {
"build": "webpack src/main.js"
}
执行 npm run build
此时如果生成了一个 dist 文件夹,并且内部含有 main.js 说明已经打包成功了
上面一个简单的例子只是 webpack 自己默认的配置,下面我们要实现更加丰富的自定义配置 新建一个 build 文件夹,里面新建一个 webpack.config.js
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development", // 开发模式
entry: path.resolve(__dirname, "../src/main.js"), // 入口文件
output: {
filename: "output.js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
};
更改我们的打包命令
"scripts": {
"build": "webpack --config build/webpack.config.js"
}
执行 npm run build 会发现生成了以下目录 其中 dist 文件夹中的 main.js 就是我们需要在浏览器中实际运行的文件 当然实际运用中不会仅仅如此,下面让我们通过实际案例带你快速入手 webpack
js 文件打包好了,但是我们不可能每次在 html 文件中手动引入打包好的 js
这里可能有的朋友会认为我们打包 js 文件名称不是一直是固定的嘛(output.js)?这样每次就不用改动引入文件名称了呀?实际上我们日常开发中往往会这样配置:
module.exports = {
// 省略其他配置
output: {
filename: "[name].[hash:8].js", // 打包后的文件名称, hash已被启用,改fullhash,
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
};
这时候生成的 dist 目录文件如下
main.8626b326.js
为了缓存,你会发现打包好的 js 文件的名称每次都不一样。webpack 打包出来的 js 文件我们需要引入到 html 中,但是每次我们都手动修改 js 文件名显得很麻烦,因此我们需要一个插件来帮我们完成这件事情
npm i -D html-webpack-plugin
新建一个 build 同级的文件夹 public,里面新建一个 index.html 具体配置文件如下
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // 开发模式
entry: path.resolve(__dirname, "../src/main.js"), // 入口文件
output: {
filename: "[name].[fullhash:8].js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
],
};
可以发现打包生成的 js 文件已经被自动引入 html 文件中
<script defer src="main.df19b0e4.js"></script>思考?为什么多了个 defer,查看博客文章https://blog.csdn.net/qq_43238599/article/details/115273893
查看官方插件配置选项https://github.com/jantimon/html-webpack-plugin#options
重新调整插件配置,再打包
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
scriptLoading: "blocking",
}),
];
打包结果的 html 内容:
<script src="main.df19b0e4.js"></script> js 打包进入到了 body,如果不配置 scriptLoading 却在 head,配置了却在 body,再加个配置项plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
inject: "head",
scriptLoading: "blocking",
}),
];
再打包就 ok
生成多个 html-webpack-plugin 实例来解决这个问题
public 目录下新建 header.html,新建 webpack.config.multi.js
// webpack.config.multi.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // 开发模式
entry: {
main: path.resolve(__dirname, "../src/main.js"),
header: path.resolve(__dirname, "../src/header.js"),
},
output: {
filename: "[name].[fullhash:8].js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
filename: "index.html",
inject: "head",
scriptLoading: "blocking",
chunks: ["main"], // 与入口文件对应的模块名
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/header.html"),
filename: "header.html",
inject: "head",
scriptLoading: "blocking",
chunks: ["header"], // 与入口文件对应的模块名
}),
],
};
package.json 调整
"scripts": {
"build": "webpack --config build/webpack.config.js",
"buildmul": "webpack --config build/webpack.config.multi.js"
},
执行 npm run buildmul
每次执行 npm run build 会发现 dist 文件夹里会残留上次打包的文件,这里我们推荐一个 plugin 来帮我们在打包输出前清空文件夹 clean-webpack-plugin
npm i -D clean-webpack-plugin
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
// ...省略其他配置
plugins: [new CleanWebpackPlugin()],
};
loader 用于对模块的源代码进行转换
loader 都在 module 下的 rules 中配置
loader 配置项包括:
tip:use 链式调用,都是从右向左解析,需注意调用 loader 的顺序。loader 要记住,面试经常被问到有哪些 loader 以及其作用
我们的入口文件是 js,所以我们在入口 js 中引入我们的 css 文件
/* index.css */
* {
margin: 0;
padding: 0;
}
/* index.less */
.red {
color: red;
}
/* index.scss */
.green {
color: green;
}
import "./assets/index.css";
import "./assets/index.less";
import "./assets/index.scss";
console.log("Hello World");
同时我们也需要一些 loader 来解析我们的 css 文件
npm i -D style-loader css-loader
如果我们使用 less 来构建样式,则需要多安装两个
npm i -D less less-loader
如果我们使用 scss 来构建样式,则需要安装
npm i -D sass-loader
配置文件如下
// webpack.config.js
module.exports = {
// ...省略其他配置
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"], // 从右向左解析原则
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"], // 从右向左解析原则
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"], // 从右向左解析原则
},
],
},
};
执行编译的时候会报 scss 相关错误,还需安装 node-sass,但没有看到相关使用 node-sass 的字眼!!! npm i -D node-sass sass-loader
全局安装 live-server, npm i -g live-server
cmd cd dist
cmd live-server
打开浏览器,观察 html 内容 为了直观可以修改 public/index.html 的内容
<body>
<div class="red">Hello World</div>
<div class="green">Hello World</div>
</body>
可以看到 css 都已经注入
npm i -D postcss-loader autoprefixer
给 index.css 添加内容
.noselect {
user-select: none;
}
模板 html
<body>
<div class="red">Hello World</div>
<div class="green noselect">Hello World</div>
</body>
调整 webpack.config.js 的 rules 配置如下:
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"], // 从右向左解析原则
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "postcss-loader", "less-loader"], // 从右向左解析原则
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"], // 从右向左解析原则
},
];
接下来,我们还需要引入 autoprefixer 使其生效,这里有两种方式 1,在项目根目录下创建一个 postcss.config.js 文件,配置如下:
module.exports = {
plugins: [require("autoprefixer")], // 引用该插件即可了
};
在 index.css 修改为
.noselect {
user-select: none;
transform: rotate(3deg);
display: flex;
}
重新 npm run build,浏览器发现 transfrom 只有-webkit-前缀,如果兼容性更强有两种方法:
"browserslist": [
"defaults",
"not ie <= 8",
"last 2 versions",
"> 1%",
"Safari >= 6",
"Firefox > 3",
"iOS >= 7",
"Android >= 4.0"
]
或者
ie > 8
last 2 versions
> 1%
Safari >= 6
Firefox > 3
iOS >= 7
Android >= 4.0
cmd npx browserslist 可以查看浏览器厂商前缀兼容的范围
2,直接在 webpack.config.js 里配置,由于高版本的 webpack 不兼容了,这里暂不研究
这时候我们发现 css 通过 style 标签的方式添加到了 html 文件中,但是如果样式文件很多,全部添加到 html 中,难免显得混乱。这时候我们想用把 css 拆分出来用外链的形式引入 css 文件怎么做呢?这时候我们就需要借助插件来帮助我们
npm i -D mini-css-extract-plugin
webpack 4.0 以前,我们通过 extract-text-webpack-plugin 插件,把 css 样式从 js 文件中提取到单独的 css 文件中。webpack4.0 以后,官方推荐使用 mini-css-extract-plugin 插件来打包 css 文件
配置文件如下 webpack.config.jminicss.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
//...省略其他配置
module: {
rules: [
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].[hash].css",
chunkFilename: "[id].css",
}),
],
};
执行 npm run buildminicss
刚开始的的时候 rules 中使用了 style-loader,发现和 MiniCssExtractPlugin.loader 一起使用会出现 warning
style-loader 的作用是头部用<style></style>嵌入式引入, MiniCssExtractPlugin 插件的作用是提取 JS 中的 CSS 样式,用 link 外部引入,减少 JS 文件的大小,简称 CSS 样式分离
如果使用了 MiniCssExtractPlugin.loader 又要使用 webpack5 的内置资源打包静态资源 需加入
// webpack.config.minicss.js
rules: [
{
test: /\.(jpe?g|png|gif)$/i, // 图片文件
type: "asset",
// 解析
parser: {
// 超过10kb将转成base64,优点:减少请求 缺点:文件体积变大
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
generator: {
// 与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
filename: "images/[name].[hash:6][ext]",
// 打包后对资源的引入
// publicPath: '../'
},
},
];
一定不要使用 publicPath !!!不兼容,会报错
这里需要说的细一点,上面我们所用到的 mini-css-extract-plugin 会将所有的 css 样式合并为一个 css 文件。如果你想拆分为一一对应的多个 css 文件,我们需要使用到 extract-text-webpack-plugin,而目前 mini-css-extract-plugin 还不支持此功能。我们需要安装@next 版本的 extract-text-webpack-plugin
npm i -D extract-text-webpack-plugin
// webpack.config.js
const path = require("path");
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
let indexLess = new ExtractTextWebpackPlugin("index.less");
let indexCss = new ExtractTextWebpackPlugin("index.css");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: indexCss.extract({
use: ["css-loader"],
}),
},
{
test: /\.less$/,
use: indexLess.extract({
use: ["css-loader", "less-loader"],
}),
},
],
},
plugins: [indexLess, indexCss],
};
运行不成功,暂不研究 webpack4 以上版本不支持该插件,废弃 https://github.com/webpack-contrib/extract-text-webpack-plugin Since webpack v4 the extract-text-webpack-plugin should not be used for css. Use mini-css-extract-plugin instead.
file-loader 就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件 url),并将文件移动到输出的目录中 url-loader 一般与 file-loader 搭配使用,功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件移动到输出的目录中
// webpack.config.js
module.exports = {
// 省略其它配置 ...
module: {
rules: [
// ...
{
test: /\.(jpe?g|png|gif)$/i, //图片文件
use: [
{
loader: "url-loader",
options: {
limit: 10240,
fallback: {
loader: "file-loader",
options: {
name: "img/[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
use: [
{
loader: "url-loader",
options: {
limit: 10240,
fallback: {
loader: "file-loader",
options: {
name: "media/[name].[hash:8].[ext]",
},
},
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: "url-loader",
options: {
limit: 10240,
fallback: {
loader: "file-loader",
options: {
name: "fonts/[name].[hash:8].[ext]",
},
},
},
},
],
},
],
},
};
webpack5 内置资源模块 asset module 来替换 raw-loader、url-loader、file-loader, 上述配置仅提供参考
博客文章: https://www.jianshu.com/p/36e972b19b28 https://blog.csdn.net/QQ1443003435/article/details/121108377
官方文档: https://webpack.docschina.org/guides/asset-modules/
一定要使用 url-loader 和 file-loader 须使用如下,这里也仅验证图片资源
rules: [
// ...其他配置
{
test: /\.(jpe?g|png|gif)$/i,
loader: "url-loader",
type: "javascript/auto",
options: {
limit: 10 * 1024,
esModule: false,
fallback: {
loader: "file-loader",
options: {
esModule: false,
name: "[name].[hash:8].[ext]",
},
},
},
},
];
这里 type 和 esModule 很重要!!!
在 src/assets/images 下存放 三张图片资源,其中一个小于 10kb
index.css 添加
#box1 {
width: 200px;
height: 100px;
background-image: url("./images/logo.jpg");
background-repeat: no-repeat;
background-size: 100% 100%;
}
#box2 {
width: 200px;
height: 100px;
background-image: url("./images/web.jpeg");
background-repeat: no-repeat;
background-size: 100% 100%;
}
#box3 {
width: 400px;
height: 600px;
background-image: url("./images/iu.jpg");
background-repeat: no-repeat;
background-size: 100% 100%;
}
在 index.html 中代码调整修改
<body>
<div class="red">Hello World</div>
<div class="green noselect">Hello World</div>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
</body>
npm run build 在 dist 目录下旧可以看到只有两张图片资源,其中有个小于 10kb 的图片被转成了 base64,校验此配置选项成功
由于 webpack5 已经使用 asset 来打包资源,现在使用如下最新配置选项
rules: [
// ...其他配置
{
test: /\.(jpe?g|png|gif)$/i, // 图片文件
type: "asset",
// 解析
parser: {
// 超过10kb将转成base64,优点:减少请求 缺点:文件体积变大
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
generator: {
// 与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
filename: "images/[name].[hash:6][ext]",
// 打包后对资源的引入
publicPath: "./",
},
},
];
这里 filename 多生成了一个 images 目录
npm i html-loader -D
在 index.html 的内容调整
<body>
<div class="red">Hello World</div>
<div class="green noselect">Hello World</div>
<img src="../src/assets/images/logo.jpg" />
<img src="../src/assets/images/iu.jpg" />
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
</body>
rules 的配置
rules: [
{
test: /\.html$/,
loader: "html-loader",
},
];
npm run build 查看 html 的内容,其中一张是转成了 base64 一张是文件路径形式
免费视频素材网站
下载个视频放到 media 目录下 video.mp4
index.html 内容调整
<body>
<div class="red">Hello World</div>
<div class="green noselect">Hello World</div>
<video width="320" height="240" controls>
<source src="../src/assets/media/video.mp4" type="video/mp4" />
<object
data="../src/assets/media/video.mp4"
width="320"
height="240"
></object>
</video>
<img src="../src/assets/images/logo.jpg" />
<img src="../src/assets/images/iu.jpg" />
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
</body>
// webpack.config.js
rules: [
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, // 媒体文件
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
generator: {
filename: "media/[name].[hash:6][ext]",
publicPath: "./",
},
},
];
cmd npm run build
查看 html 引入的视频路径和 dist/media 下的视频
添加字体 src/assets/fonts/Poppins-Regular.woff
// index.css追加如下
.font {
font-family: "Poppins-Regular";
}
@font-face {
font-family: "Poppins-Regular";
src: url("./fonts/Poppins-Regular.woff") format("woff");
}
// webpack.config.js
rules: [
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
type: "asset/inline", // inline 的时候不需要指定文件名
},
];
inde.html 调整添加 font
<div class="red font">Hello World</div>
npm run build 查看 index.html,动态去除 font 类能看到字体有变化,或者通过浏览器也能看到字体的加载说明成功
为了使我们的 js 代码兼容更多的环境我们需要安装依赖
npm i -D babel-loader @babel/preset-env @babel/core
注意 babel-loader 与 babel-core 的版本对应关系
最新安装的都是高版本
配置如下
// webpack.config.js
rules = [
// ... 其他配置
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
exclude: /node_modules/,
},
];
在 main.js 中添加如下 ES6 代码
let arr = [1, 2, , 3, 4, 5, 6, 7, 8, 9];
arr.map((item) => {
console.log(item);
});
class Animal {
constructor() {
this.type = "animal";
}
says(say) {
console.log(this.type + " says " + say);
}
}
let animal = new Animal();
animal.says("hello");
npm run build 以后观察打包出来的 js 代码,可以看到相关转化成 ES5 的代码,不够明显,可以把转换的配置规则去掉,再打包可以看到打包出来的 js 是没有转化的 ES6 代码
如果 options 去掉 在项目的根目录下新建.babelrc 内容如下:
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime"]
}
// webpack.config.js
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
},
exclude: /node_modules/,
},
];
还需安装一个
npm i @babel/plugin-transform-runtime -D
打包观察 js 的是否转换成 es5
Babel 其实是几个模块化的包:
上面的 babel-loader 只会将 ES6/7/8 语法转换为 ES5 语法,但是对新 api 并不会转换 例如(promise、Generator、Set、Maps、Proxy 等) 此时我们需要借助 babel-polyfill 来帮助我们转换 如下未实验,仅供参考
npm i @babel/polyfill
// webpack.config.js
const path = require("path");
module.exports = {
entry: ["@babel/polyfill", path.resolve(__dirname, "../src/index.js")], // 入口文件
};
进度条插件有两种,取其中一个使用就行
- npm i progress-bar-webpack-plugin -D
const chalk = require("chalk");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
// ...
plugins: [
// 进度条
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(":percent")} (:elapsed s)`,
}),
];
- npm i webpackbar
const WebpackBar = require("webpackbar");
// ...
plugin: [new WebpackBar()];
保留前面的 webpack.config.js 配置到 webpack.config.bak.js
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const chalk = require("chalk");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const WebpackBar = require("webpackbar");
module.exports = {
mode: "development", // 开发模式
entry: path.resolve(__dirname, "../src/main.js"), // 入口文件
output: {
filename: "[name].[fullhash:8].js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
// 进度条
new WebpackBar(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
inject: "head",
scriptLoading: "blocking",
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"], // 从右向左解析原则
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "postcss-loader", "less-loader"], // 从右向左解析原则
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"], // 从右向左解析原则
},
{
test: /\.(jpe?g|png|gif)$/i, // 图片文件
type: "asset",
// 解析
parser: {
// 超过10kb将转成base64,优点:减少请求 缺点:文件体积变大
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
generator: {
// 与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
filename: "images/[name].[hash:6][ext]",
// 打包后对资源的引入
publicPath: "./",
},
},
{
test: /\.html$/,
loader: "html-loader",
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, // 媒体文件
type: "asset/resource",
generator: {
filename: "media/[name].[hash:6][ext]",
publicPath: "./",
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
type: "asset/inline", // inline 的时候不需要指定文件名
},
{
test: /\.js$/,
use: {
loader: "babel-loader",
/* options: {
presets: ["@babel/preset-env"]
} */
},
exclude: /node_modules/,
},
],
},
};
npm i webpack-dev-server -D
将 webpack.config.js 复制一份,改名称 webpack.common.js 添加选项:
devServer: {
hot: true, // 热更新
open: false, // 编译完自动打开浏览器
compress: true,// 开启gzip压缩
port: 8088, // 开启端口号
client: { //在浏览器端打印编译进度
progress: true,
},
}
在 package.json 添加命令
"dev": "webpack serve --config build/webpack.common.js"
npm run dev
浏览器输入 http://localhost:8088/
使用了本地服务将不会打包到 dist 目录
npm i webpack-merge -D
build 下新建 webpack.dev.js,webpack.prod.js
webpack.dev.js 内容如下:
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "development",
devServer: {
hot: true, // 热更新
open: false, // 编译完自动打开浏览器
compress: true, // 开启gzip压缩
port: 8088, // 开启端口号
client: {
//在浏览器端打印编译进度
progress: true,
},
},
});
webpack.prod.js 内容如下:
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "production",
});
webpack.common.js 内容如下:
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const chalk = require("chalk");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const WebpackBar = require("webpackbar");
module.exports = {
entry: path.resolve(__dirname, "../src/main.js"), // 入口文件
output: {
filename: "[name].[fullhash:8].js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
new WebpackBar(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
inject: "head",
scriptLoading: "blocking",
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"], // 从右向左解析原则
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "postcss-loader", "less-loader"], // 从右向左解析原则
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"], // 从右向左解析原则
},
{
test: /\.(jpe?g|png|gif)$/i, // 图片文件
type: "asset",
// 解析
parser: {
// 超过10kb将转成base64,优点:减少请求 缺点:文件体积变大
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
generator: {
// 与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
filename: "images/[name].[hash:6][ext]",
// 打包后对资源的引入
publicPath: "./",
},
},
{
test: /\.html$/,
loader: "html-loader",
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, // 媒体文件
type: "asset/resource",
generator: {
filename: "media/[name].[hash:6][ext]",
publicPath: "./",
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
type: "asset/inline", // inline 的时候不需要指定文件名
},
{
test: /\.js$/,
use: {
loader: "babel-loader",
/* options: {
presets: ["@babel/preset-env"]
} */
},
exclude: /node_modules/,
},
],
},
};
package.json 命令
{
"scripts": {
"dev": "webpack serve --config build/webpack.dev.js",
"prod": "webpack --config build/webpack.prod.js"
}
}
npm run prod 和 npm run dev 可以正常执行
在 index.html 添加内容
<img src="~@/assets/images/iu.jpg" />
注意:~@
在 webpack.common.js 中添加配置选项 resolve 和 entry 同级
resolve: {
alias: { // 配置别名
"@": path.resolve(__dirname, "../src"),
},
},
npm run dev 编译通过,并且浏览器正常访问,如果把配置去掉,编译不通过会报错!
npm i webpack-bundle-analyzer -D
只有打包才会分析所以在 webpack.prod.js 添加如下配置:
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
// ... 其他配置
plugins: [
new BundleAnalyzerPlugin({
// 可以是`server`,`static`或`disabled`。
// 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
// 在“静态”模式下,会生成带有报告的单个HTML文件。
// 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
analyzerMode: "static",
// 将在“服务器”模式下使用的主机启动HTTP服务器。
analyzerHost: "127.0.0.1",
// 将在“服务器”模式下使用的端口启动HTTP服务器。
analyzerPort: 8888,
// 路径捆绑,将在`static`模式下生成的报告文件。
// 相对于捆绑输出目录。
reportFilename: "report.html",
// 模块大小默认显示在报告中。
// 应该是`stat`,`parsed`或者`gzip`中的一个。
// 有关更多信息,请参见“定义”一节。
defaultSizes: "parsed",
// 在默认浏览器中自动打开报告
openAnalyzer: false,
// 如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
generateStatsFile: false,
// 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
// 相对于捆绑输出目录。
statsFilename: "stats.json",
// stats.toJson()方法的选项。
// 例如,您可以使用`source:false`选项排除统计文件中模块的来源。
// 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null,
logLevel: "info", // 日志级别。可以是'信息','警告','错误'或'沉默'。
}),
];
npm run prod
webpack.prod.js 也只有打包才需要分离
//webpack.prod.js
//与plugins同级
optimization: {
splitChunks: {
chunks: "all",
name: "vendor",
cacheGroups: {
"echarts.vendor": {
name: "echarts.vendor",
priority: 40,
test: /[\\/]node_modules[\\/](echarts|zrender)[\\/]/,
chunks: "all",
},
lodash: {
name: "lodash",
chunks: "async",
test: /[\\/]node_modules[\\/]lodash[\\/]/,
priority: 40,
},
"async-common": {
chunks: "async",
minChunks: 2,
name: "async-commons",
priority: 30,
},
commons: {
name: "commons",
chunks: "all",
minChunks: 2,
priority: 20,
},
},
},
},
安装 echarts
npm i echarts -S
新建 src/echart.js
import * as echarts from "echarts";
var myChart = echarts.init(document.getElementById("main"));
var option = {
title: {
text: "ECharts 入门示例",
},
tooltip: {},
legend: {
data: ["销量"],
},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
},
yAxis: {},
series: [
{
name: "销量",
type: "bar",
data: [5, 20, 36, 10, 10, 20],
},
],
};
myChart.setOption(option);
在 main.js 中引入
import './echart'
修改 public/index.html
<div id="main" style="width: 600px; height: 400px"></div>
这个时候还需调整 HtmlWebpackPlugin 配置的 nject: 'body',思考为什么?
运行 npm run prod,在 dist 下会发现新增了 echarts.vendor.xxxx.js 这就是通过 splitChunks 分离出来 echarts 包
按需下载资源,如路由懒加载。可以提升首屏加载速度
npm i lodash -S
通过 import()语法实现动态导入
//在main.js添加
function getComponent() {
// Lodash, now imported by this script
return import("lodash")
.then(({ default: _ }) => {
const element = document.createElement("div");
element.innerHTML = _.join(["Hello", "webpack"], " ");
return element;
})
.catch((error) => "An error occurred while loading the component");
}
const button = document.createElement("button");
button.innerHTML = "Click me ";
button.onclick = () => {
getComponent().then((component) => {
document.body.appendChild(component);
});
};
document.body.appendChild(button);
在 webpack.prod.js 中 cacheGroups 下添加(在上面 splitChunks 中已经加过了)
lodash: {
name: "lodash",
chunks: "async",
test: /[\\/]node_modules[\\/]lodash[\\/]/,
priority: 40,
},
npm run prod 可以看到有生成 lodash.xxx.js
cd dist
cmd live-server 启动服务,点击底部的 Click me 按钮,可以看到动态加载的 js
npm i cross-env -D
package.json 添加两个命令
"scripts": {
"cdev": "cross-env NODE_ENV=development webpack serve --config build/webpack.dev.js",
"cbuild": "cross-env NODE_ENV=production webpack --config build/webpack.prod.js",
}
在 webpack.common.js 添加输出
console.log('process.env.NODE_ENV',process.env.NODE_ENV)
cmd npm run cdev 或者 npm run cbuild 可以看到輸出的变量
通过 DefinePlugin 实现
根目录下新建 config/dev.env.js
module.exports = {
NODE_ENV: '"development"',
APP_API: '"wwww.testapi.com"',
};
// webpck.dev.js
const env = require("../config/dev.env")
const webpack =require("webpack")
module.exports = merge(common,{
plugins: [
new webpack.DefinePlugin({
"process.env": env,
}),
],
})
// main.js try { console.log(process.env) } catch (error) {
}
防止将外部资源包打包到自己的 bundle 中
示例:从 cdn 引入 jQuery,而不是把它打包
必须是</ body>前,如果放</ body>后就不行,思考?
import $ from "jquery";
console.log($("#main"));
npm i jquery -S 这里主要满足 dev 环境使用, html 引入了 jq,main.js 中又引入,会出现引入两次的情况,后面再考虑如何 dev 只引入一次
webpack.prod.js 添加配置
externals: {
jquery: 'jQuery',
,
npm run dev 和 npm run prod 查看结果
思考后续是否可以打包组件相关的样式,只打包样式文件
新建 src\assets\main.css
* {
margin: 0;
padding: 0;
}
.red {
color: red;
}
.green {
color: green;
}
新建 webpack.config.css.js
const path = require('path')
module.exports = {
mode: 'development', // 模式
entry: path.resolve(**dirname, '../src/assets/main.css'), // 打包入口地址
output: {
filename: 'bundle.css', // 输出文件名
path: path.join(**dirname, '../dist') // 输出文件目录
}
}
package.json 添加命令
"buildcss": "webpack --config build/webpack.config.css.js"
npm run buildcss 编译错误
处理 css 文件需要使用 css-loader 调整配置
// webpack.config.css.js
const path = require("path");
module.exports = {
mode: "development", // 模式
entry: path.resolve(__dirname, "../src/assets/main.css"), // 打包入口地址
output: {
filename: "bundle.css", // 输出文件名
path: path.join(__dirname, "../dist"), // 输出文件目录
},
module: {
rules: [
// 转换规则
{
test: /\.css$/, //匹配所有的 css 文件
use: "css-loader", // use: 对应的 Loader 名称
},
],
},
};
npm run buildcss
常用的 loader:
module.exports = {
module: {
noParse: /jquery/,
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
include: [path.resolve(__dirname, "src")],
exclude: /node_modules/,
},
{
test: '/\\.(jep?g|png|gif)$/,
use: {
loader: 'url-loader',
include: [path.resolve(__dirname, 'src/assets/icons)],
exclude: /node_modules/
}
}
],
},
resolve: {
alias: {
'vue$', 'vue/dist/vue.runtime.esm.js',
'@': path.resolve(__dirname, '../src'),
'assets': resolve('src/assets'),
'components': resolve('src/components')
},
extensions: ['*', '.js', '.json', '.vue']
}
}
适用生产环境
npm i webpack-parallel-uglify-plugin -D
// webpack.prod.js
const ParallelUglifyPlugin = require("webpack-parallel-uglify-plugin");
plugins: [
new ParallelUglifyPlugin({
// 压缩js的一些配置
uglifyJS: {
output: {
beautify: false, // 不需要格式化,以最紧凑的方式输出
comments: false, // 删除注释
},
warnings: false, // 删除未使用一些代码时引起的警告
compress: {
drop_console: true, // 删除所有console.log
// 是否内嵌虽定义,但只使用了一次的变量
// 比如var x = 2, y = 10, z = x + y 变成 z = 12
collapse_vars: true,
// 提出多次出现但没定义的变量,将其变成静态值;
// 比如x = 'xx', y = 'xx' 变成 var a = 'xx', x = a, y = a
reduce_vars: true,
},
},
}),
];
npm run prod 后查看打包出来的 js 已经没有相关 console.log 等
因为 js 是单线程的,如果引用的模块很多,且模块间引用的层级很深,那么 webpack 在递归解析依赖时,速度就会很慢。而使用 happyPack 可以开启多进程打包,会提高构建速度 它在开发或者生产环境都可以使用,不过对于小项目,使用这个优化空间不大,且开启进程可能消耗性能会更多;在大项目时,才会有较多的优化空间
npm i happypack -D
// wepback.common.js 或webpack.prod.js
const HappyPack = require('happypack')
// 将原来babel的配置改下,改为使用happypack多进程打包
module: {
rules: [
{
test: /\.js$/,
// use: ['babel-loader?cacheDirectory'],
// 改为使用 happypack打包
use: ['happypack/loader?id=babel'], // 这个id是自定义命名的,要跟插件中id对应
// 排除 node_modules 目录下的文件
exclude: /node_modules/
}
]
},
plugins: [
new HappyPack({
id: 'babel', // 唯一标识符
// 使用的loader配置改写到happypack的配置项中
use: ['babel-loader']
})
]
npm run prod
HappyPack 添加多个 loader,如下参考, 未实验:
// ...
// 引入 happypack
const HappyPack = require("happypack");
// 创建 happypack 共享进程池,其中包含 6 个子进程
const happyThreadPool = HappyPack.ThreadPool({ size: 6 });
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
// use: ['babel-loader?cacheDirectory'] 之前是使用这种方式直接使用 loader
// 现在用下面的方式替换成 happypack/loader,并使用 id 指定创建的 HappyPack 插件
use: ["happypack/loader?id=babel"],
include: path.resolve(__dirname, "src"),
},
{
test: /\\.(css|less)$/,
// 之前是使用这种方式直接使用 loader
// use: ['style-loader',
// {
// loader: 'css-loader',
// options: {
// sourceMap: true
// }
// },
// {
// loader: 'postcss-loader',
// options: {
// plugins: () => [autoprefixer()]
// }
// },
// {
// loader: 'less-loader',
// options: {
// javascriptEnabled: true,
// }
// }]
// 现在用下面的方式替换成 happypack/loader,并使用 id 指定创建的 HappyPack 插件
use: ["happypack/loader?id=styles"],
include: path.resolve(__dirname, "src"),
},
],
},
plugins: [
// ...
new HappyPack({
/*
* 必须配置项
*/
// id 标识符,要和 rules 中指定的 id 对应起来
id: "babel",
// 需要使用的 loader,用法和 rules 中 Loader 配置一样
// 可以直接是字符串,也可以是对象形式
loaders: ["babel-loader?cacheDirectory"],
// 使用共享进程池中的进程处理任务
threadPool: happyThreadPool,
}),
new HappyPack({
/*
* 必须配置
*/
// id 标识符,要和 rules 中指定的 id 对应起来
id: "styles",
// 需要使用的 loader,用法和 rules 中 Loader 配置一样
// 可以直接是字符串,也可以是对象形式
loaders: [
"style-loader",
{
loader: "css-loader",
options: {
sourceMap: true,
},
},
{
loader: "postcss-loader",
options: {
plugins: () => [autoprefixer()],
},
},
{
loader: "less-loader",
options: {
javascriptEnabled: true,
},
},
],
// 使用共享进程池中的进程处理任务
threadPool: happyThreadPool,
}),
],
};
CSS 压缩之前会使用 optimize-css-assets-webpack-plugin 这个插件,在 webpack v5 之后推荐使用 css-minimizer-webpack-plugin 这个插件。
npm i css-minimizer-webpack-plugin -D 修改 webpack.config.minicss.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimizer: [new CssMinimizerPlugin()],
},
};
忘了 mode: 'development', // 开发模式 改为 productions,相关 hash 改为 fullhash
npm run buildminicss 观察添加配置前后的 css 变化
npm i -D copy-webpack-plugin
webpack.common.js 添加配置选项
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: "./",
globOptions: {
ignore: ["**/*.html"],
},
},
],
}),
],
};
public 下面必须需要有可拷贝的资源(除了 html),这里放了个 logo,否则会有警告信息
npm run dev / npm run prod
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。