文章目录
- 一、默认入口和默认出口
- 二、资源配置
- 三、输出文件
- 3.1 多文件入口
- 3.2 HtmlWebpackPlugin插件
- 四、环境
- 4.1 环境变量
- 4.2 热更新
- 五、代码分离
- 5.1 公共模块
- 5.2 懒加载
- 5.3 预获取/预加载模块
- 六、缓存
- 七、Tree Shaking
- 八、公共路径
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 -Dnpm 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中指定的keypath: 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-mapentry: {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-mapentry: {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-mapentry: {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-mapentry: {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-mapentry: {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-mapentry: {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自带的插件进行处理},],},};
};