文章目录
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具,在许多项目中都有应用,但是往往平台搭建以后很少去修改配置,熟悉基础配置可以更快的修复配置问题。
一、默认入口和默认出口
默认目录结构
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js // 可以先不配置,最后一步再加
|- /dist
|- index.html
|- /src
|- index.js
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>起步</title>
</head>
<body>
// 默认出口就是dist/main.js
// 这里是直接指定,包括使用插件默认也是引入dist/main.js
<script src="main.js"></script>
</body>
</html>
index.js
// 引入的包可以被识别的打包
import _ from "lodash";
function component() {
const element = document.createElement("div");
// lodash 现在使用 import 引入
element.innerHTML = _.join(["Hello", "webpack"], " ");
return element;
}
document.body.appendChild(component());
检查npm
// 检查npm registry
npm config get registry
// 改成淘宝源的新地址,下载包更快,不容易出错
npm config set registry https://registry.npmmirror.com
// npm 开发依赖;只会在开发环境需要,不会打包到dist中
// 也就是package.json中的devDependencies
// --save-dev 简写 -D
npm i package --save-dev // 示意,不用安装
// npm 生产依赖;会打包到dist中,影响包的大小和打包速度
// 也就是package.json中的dependencies
// --save 简写 -S
npm i package --save // 示意,不用安装
安装包
npm init -y // y表示yes,跳过一步
npm i webpack webpack-cli -D
npm i lodash -S
- 运行npx webpack 打包;
- vscode安装插件Live Server,右键index.html启动Open with Live Server
- 可以看到已经启动了页面
- 增加webpack.config.js
- path.resolve相关用法可以查看另一篇 path.resolve相关用法
- 在package.json中script对象中加入一行 “build”: “webpack”(注意最后一行没有,号)
- npm run build进行打包;发现打包后正常访问;
- 之前没有webpack.config.js,也可以访问,也就是说这里的配置是webpack的
默认打包配置
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js", // 入口
output: { // 出口 // 输出文件
filename: "main.js",
// 注意这个dist是相对于文件夹根目录,也就是创建一个dist目录并且把输出文件放在里面
path: path.resolve(__dirname, "dist"),
},
};
二、资源配置
- src下增加style.css, 引入index.js中,给dom添加上类名
// style.css
.red {
color: red;
font-size: 20px;
}
// index.js
import _ from "lodash";
import "./style.css";
function component() {
const element = document.createElement("div");
// lodash 现在使用 import 引入
element.innerHTML = _.join(["Hello", "webpack"], " ");
element.classList.add("red");
return element;
}
document.body.appendChild(component());
- npm i style-loader css-loader -D 安装loader
- webpack.config.js配置加上处理style的loader
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
// 正则,以.css文件结尾,$表示结尾,\表示转义
test: /\.css$/,
// 从右边的loader开始加载处理,返回结果给左边的继续处理
use: ["style-loader", "css-loader"],
},
],
},
};
图片资源
// .style.css
.red {
color: red;
font-size: 20px;
height: 300px;
background: url('./logo.png'); // 增加图片的使用
}
// webpack.config.js中module.rules添加一条
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: 'asset/resource', // 这个type应该是webpack自带的插件进行处理
},
其他资源就不一一列举了,要么有默认处理插件,要么需要安装对应的插件再配置rules;比如csv表格、json文件、字体文件、ts文件
三、输出文件
3.1 多文件入口
增加/print.js,修改index.html、index.js、webpack.config.js
// src/print.js
export default function print(msg = "Hello World") {
console.log(msg);
}
// index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>起步</title>
// index.html就在dist目录下,dist目录新增了两个出口文件print.js、index.js
<script src="./print.js"></script>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
// index.js
import _ from "lodash";
import "./style.css";
import print from "./print";
function component() {
const element = document.createElement("div");
// lodash 现在使用 import 引入
element.innerHTML = _.join(["Hello", "webpack"], " ");
element.classList.add("red");
const btn = document.createElement("button");
btn.innerHTML = "click me";
btn.onclick = print;
element.appendChild(btn);
return element;
}
document.body.appendChild(component());
// webpack.config.js
const path = require("path");
module.exports = {
// 配置多个入口,那么就会产生多个出口文件; 多个入口文件都会在index.html中单独引入
entry: {
index: "./src/index.js",
print: "./src/print.js",
},
output: {
filename: "[name].js", // name表示动态文件名,entry中指定的key
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
};
重新打包,可以正常访问和点击按钮
3.2 HtmlWebpackPlugin插件
- 帮助自动生成index.html文件(前面是手动引入出口文件),安装HtmlWebpackPlugin插件
- npm i html-webpack-plugin -D
- 修改webpack.config.js
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
index: "./src/index.js",
print: "./src/print.js", // 注意这个顺序,index.html中的引入顺序也是如此
},
output: {
// 增加contenthash值,是一个随机字符串
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
// 可能改造过配置文件,导致dist目录有之前构建的很多不需要文件,构建前清理一下dist目录
clean: true,
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
],
};
四、环境
4.1 环境变量
- 修改package.json;webpack.config.js;print.js
- 通过命令行传入参数;通过mode选择打包模式;通过process.env.NODE_ENV获取当前打包环境
- webpack.config.js中module.exports改造成匿名函数,以便获取参数
- 增加source-map源代码映射
// package.json
// 这一条改成这个 增加打包的参数;在env对象里增加goal: local, production: true
// --progress 是增加进步条;--color增加颜色;打包时间长的时候出现;
"build": "webpack --env goal=local --env production --progress --color"
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 改造成匿名函数,这样可以获取参数
module.exports = (env, argv) => {
// env中增加goal和production属性; env是argv中的一个属性
console.log("env, argv", env, argv);
return {
// mode是指定模式,可以通过env参数来决定是什么模式
mode: "development", // 开发模式 development // 默认是 production 生产模式
// devtool 打包后的文件是压缩的不是源代码,报错了找不到错误位置,需要源代码映射
devtool: "inline-source-map", // 开发模式是 inline-source-map // 生产模式是 source-map
entry: {
index: "./src/index.js",
print: "./src/print.js",
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
clean: true,
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
],
};
};
// print.js
export default function print(msg = "Hello World") {
// 注意在一般文件中访问process或者process.env会报错,node只暴漏了process.env.NODE_ENV变量出来
// process.env.NODE_ENV就是webpack.config.js中的mode属性
console.log("process", process.env.NODE_ENV);
}
4.2 热更新
- 之前手动build打包,还要在页面上刷新一下才能看到最新内容
- 现在安装插件,npm i webpack-dev-server -D
// package.json scripts增加
// webpack-dev-server --open 和 webpack serve --open 都是可以的;出现错误可以都试一下;
"start-dev": "webpack-dev-server --open --env production=false",
"start": "webpack serve --open --env production=false",
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
console.log("env, argv", env, argv);
return {
mode: "development", // 开发模式 development // 默认是 production 生产模式
// devtool 打包后的文件是压缩的不是源代码,报错了找不到错误位置,需要源代码映射
devtool: "inline-source-map", // 开发模式是 inline-source-map // 生产模式是 source-map
entry: {
print: "./src/print.js",
index: "./src/index.js",
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
clean: true,
publicPath: "/", // 解决静态资源加载路径问题
},
optimization: {
// runtimeChunk是模块关系文件,single表示这里打包成一个runtime名称的文件
runtimeChunk: "single",
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
],
// 开发模式下开启热更新
devServer: {
static: "./dist", // 插件寻找启动文件
hot: true, // 开启热更新
open: true, // 自动打开浏览器
port: 3000, // 端口号
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
};
};
五、代码分离
5.1 公共模块
- index.js\print.js中都引入了lodash
- 添加splitChunks让lodash单独打包成一个文件
// print.js
import _ from "lodash";
export default function print(msg = "Hello World") {
console.log("process", process.env.NODE_ENV, 1111);
console.log(_.join([1, 2, 3]));
}
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
console.log("env, argv", env, argv);
return {
mode: "development", // 开发模式 development // 默认是 production 生产模式
// devtool 打包后的文件是压缩的不是源代码,报错了找不到错误位置,需要源代码映射
devtool: "inline-source-map", // 开发模式是 inline-source-map // 生产模式是 source-map
entry: {
print: "./src/print.js",
index: "./src/index.js",
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
clean: true,
publicPath: "/", // 解决静态资源加载路径问题
},
optimization: {
runtimeChunk: "single",
// all 表示公共的依赖模块提取到各自的chunk中;
// 比如两个文件里引入了lodash,那么lodash会被打包lodash的chunk中
splitChunks: {
chunks: "all",
},
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
],
// 开发模式下开启热更新
devServer: {
static: "./dist", // 插件寻找启动文件
hot: true, // 开启热更新
open: true, // 自动打开浏览器
port: 3000, // 端口号
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
};
};
5.2 懒加载
- index.js改成动态引入print.js
- import导入时加上/* webpackChunkName: “print” /,可以让这个文件单独打包,不会加载,等需要加载的时候才会请求资源;注意/ 之间的空格
index.js
import _ from "lodash";
import "./style.css";
// import print from "./print"; // 注释掉,使用动态导入 否则也不会单独打包
function component() {
const element = document.createElement("div");
// lodash 现在使用 import 引入
element.innerHTML = _.join(["Hello", "webpack"], " ");
element.classList.add("red");
const btn = document.createElement("button");
btn.innerHTML = "click me";
btn.onclick = (e) =>
//
import(/* webpackChunkName: "print" */ "./print").then((module) =>
module.default()
);
element.appendChild(btn);
return element;
}
document.body.appendChild(component());
// webpack.config.js
// 用5.1配置就可以了
5.3 预获取/预加载模块
预获取,空闲时进行;加载将来需要的资源
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
预加载,父 chunk 加载时以并行方式开始加载;加载当前路由下可能需要的资源
import(/* webpackPreload: true */ './path/to/LoginModal.js');
六、缓存
- contenthash是文件名唯一标识,这样文件更新了,客户端就能进行资源重新请求和更新
- 公共依赖包不会经常更新,所以对应的打包文件也不需要更新,把这些不需要经常更新的包进行缓存
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
console.log("env, argv", env, argv);
return {
mode: "development", // 开发模式 development // 默认是 production 生产模式
// devtool 打包后的文件是压缩的不是源代码,报错了找不到错误位置,需要源代码映射
devtool: "inline-source-map", // 开发模式是 inline-source-map // 生产模式是 source-map
entry: {
index: "./src/index.js",
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
clean: true,
publicPath: "/", // 解决静态资源加载路径问题
},
optimization: {
// moduleIds 模块标识符,使模块更容易被缓存和重复使用;
// 项目中新增和删除文件,导致解析顺序发生变化,会导致vendor包变化; 创建唯一模块标识符
moduleIds: "deterministic",
runtimeChunk: "single",
// all 表示公共的依赖模块提取到各自的chunk中;
// 比如两个文件里引入了lodash,那么lodash会被打包lodash的chunk中
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
// node_modules下的包都打包到vendors这个chunk中
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
},
},
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
],
// 开发模式下开启热更新
devServer: {
static: "./dist", // 插件寻找启动文件
hot: true, // 开启热更新
open: true, // 自动打开浏览器
port: 3000, // 端口号
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
};
};
七、Tree Shaking
- optimization.usedExports = true;移除未使用的代码
- 如果引入了资源(函数funTest),但是觉得可以不用打包他,他不会对结果有影响,可以设置标识;/#PURE/ funTest();
- sideEffects表示该文件引入,但是未使用,需要确定这个文件有没有必要引入;在package.json中增加 “sideEffects”: [“./index.js”],表示如果你在index.js中引入了另外一个文件,但是该文件内容没有使用,可以放心移除这个文件不必打包进index.js,index.js被标记为无副作用
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
console.log("env, argv", env, argv);
return {
mode: "development", // 开发模式 development // 默认是 production 生产模式
// devtool 打包后的文件是压缩的不是源代码,报错了找不到错误位置,需要源代码映射
devtool: "inline-source-map", // 开发模式是 inline-source-map // 生产模式是 source-map
entry: {
index: "./src/index.js",
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
clean: true,
publicPath: "/", // 解决静态资源加载路径问题
},
optimization: {
// moduleIds 模块标识符,使模块更容易被缓存和重复使用;
// 项目中新增和删除文件,导致解析顺序发生变化,会导致vendor包变化; 创建唯一模块标识符
moduleIds: "deterministic",
runtimeChunk: "single",
// all 表示公共的依赖模块提取到各自的chunk中;
// 比如两个文件里引入了lodash,那么lodash会被打包lodash的chunk中
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
// node_modules下的包都打包到vendors这个chunk中
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
},
},
// tree shaking
// // 标记未使用的导出(代码级别),production模式下不会被打包进输出文件(代码体积会减少)
usedExports: true,
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
],
// 开发模式下开启热更新
devServer: {
static: "./dist", // 插件寻找启动文件
hot: true, // 开启热更新
open: true, // 自动打开浏览器
port: 3000, // 端口号
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
};
};
八、公共路径
- 默认公共路径是从output.publicPath去引用
- 可以通过webpack.DefinePlugin自己定义环境变量,在页面可以使用;没有定义就使用否则会报错(process not defined)
- webpack_public_path = window.test_public_path,这一行单独放在一个文件里,在入口文件最上面引入,可以在运行时规定公共路径;注意在服务器上挂载环境变量配置,给变量赋值window.test_public_path
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 尝试使用环境变量,否则使用根路径; process.env.ASSET_PATH 默认值是undefined
const ASSET_PATH = process.env.ASSET_PATH || "/"; //
console.log("ASSET_PATH", process.env.ASSET_PATH, ASSET_PATH);
module.exports = (env, argv) => {
console.log("env, argv", env, argv);
return {
mode: "development", // 开发模式 development // 默认是 production 生产模式
// devtool 打包后的文件是压缩的不是源代码,报错了找不到错误位置,需要源代码映射
devtool: "inline-source-map", // 开发模式是 inline-source-map // 生产模式是 source-map
entry: {
index: "./src/index.js",
},
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist"),
clean: true,
publicPath: "/", // 解决静态资源加载路径问题
},
optimization: {
// moduleIds 模块标识符,使模块更容易被缓存和重复使用;
// 项目中新增和删除文件,导致解析顺序发生变化,会导致vendor包变化; 创建唯一模块标识符
moduleIds: "deterministic",
runtimeChunk: "single",
// all 表示公共的依赖模块提取到各自的chunk中;
// 比如两个文件里引入了lodash,那么lodash会被打包lodash的chunk中
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
// node_modules下的包都打包到vendors这个chunk中
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
},
},
// tree shaking
// 标记未使用的导出(代码级别),production模式下不会被打包进输出文件(代码体积会减少)
usedExports: true,
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Test",
}),
// 这可以帮助我们在代码中安全地使用环境变量
// 让你可以自己定义一些变量,在代码中使用
new webpack.DefinePlugin({
"process.env.ASSET_PATH": JSON.stringify("ASSET_PATH"),
}),
],
// 开发模式下开启热更新
devServer: {
static: "./dist", // 插件寻找启动文件
hot: true, // 开启热更新
open: true, // 自动打开浏览器
port: 3000, // 端口号
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i, // i表示不区分大小写
type: "asset/resource", // 这个type应该是webpack自带的插件进行处理
},
],
},
};
};