开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
1.6 loader
1.6.1 什么是loader
- webpack 默认支持处理 JS 与 JSON 文件,其他类型都处理不了。
- Loader 就是将 Webpack 不认识的内容转化为认识的内容
1.6.2 loader-CSS
css-loader
-
- 安装:
npm i css-loader -D
- 文档:
https://github.com/webpack-contrib/css-loader
- 安装:
style-loader
-
- 安装:
npm i style-loader -D
- 文档:
https://github.com/webpack-contrib/style-loader
- 安装:
module: {
rules: [
//...
{
test: /.css$/, //匹配所有的 css 文件
use: ["style-loader", "css-loader"],
},
],
},
⚠️ Loader 的执行顺序是固定从后往前,即按 css-loader --> style-loader 的顺序执行
less-loader``sass-loader
-
npm install less less-loader --save-dev
npm install sass-loader sass --save-dev
module: {
rules: [
// ...
{
test: /.(css|sass)$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
1.6.3 抽离、压缩CSS
抽离CSS
- 安装插件:
npm i mini-css-extract-plugin -D
(webpack5) - 修改
webpack.config.js
配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
// ...
plugins: [
// ...
new MiniCssExtractPlugin()
],
module: {
rules: [
// ...
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
- 配置输出的css路径和文件名
plugins: [
// ...
new MiniCssExtractPlugin({
filename: "styles/[contenthash].css",
}),
],
压缩CSS
- 安装插件:
npm i css-minimizer-webpack-plugin -D
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
mode: "production", // 模式
// ...
optimization: {
minimizer: [new CssMinimizerWebpackPlugin()], // 压缩CSS
},
};
1.6.4 CSS加载images
tag {
background-image: url('../asset/logo.png')
}
1.6.5 加载fonts字体
module: {
rules: [
// ...
{
test: /.(woff|woff2|eot|ttf|otf)$/,
type: "asset/resoure",
},
],
},
@font-face {
font-family: 'iconfont';
src: url('../asset/iconfont.ttf') format('truetype');
}
.icon {
font-family: 'iconfont';
font-size: 30px;
}
1.6.6 加载其他数据
CSV``TSV``XML
- 安装loader :
npm i csv-loader xml-loader -D
module: {
rules: [
// ...
{
test: /.(csv|tsv)/,
use: "csv-loader",
},
{
test: /.xml$/,
use: "xml-loader",
},
],
},
import Data from './asset/data.xml'
import Notes from './asset/data.csv'
console.log(Data)
console.log(Notes)
1.6.7 parser
npm i toml yaml json5 -D
const toml = require("toml");
const yaml = require("yaml");
const json5 = Frequire("json5");
//...
module: {
rules: [
// ...
{
test: /.toml$/,
type: "json",
parser: {
parse: toml.parse,
},
},
{
test: /.yaml$/,
type: "json",
parser: {
parse: yaml.parse,
},
},
{
test: /.json5$/,
type: "json",
parser: {
parse: json5.parse,
},
}
],
},
import toml from './asset/data.toml'
import yaml from './asset/data.yaml'
import json5 from './asset/data.json5'
console.log(toml)
console.log(yaml)
console.log(json5)
1.7 babel-loader
1.7.1 为什么要babel-loader
ES6
--->ES5
1.7.2 使用 babel-loader
- 安装
npm i babel-loader @babel/core @babel/preset-env -D
-
babel-loader
webpack里应用babel解析ES6的桥梁@babel/core
babel 的核心代码@babel/preset-env
babel预设,babel插件的集合
配置
rules: [
// ...
{
test: /.js$/,
exclude: "/node_modules/", //排除
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
- 再打包,js代码就转换成了ES5
1.7.3 regeneratorRuntime
1.8
1.8.1 代码分离
入口起点
:使用entry配置手动的分离代码防止重复
:使用Entry dependencies
或者SplitChunksplugin
去重和代码分离动态导入
:通过模块的内联函数调用来分离代码
1.8.2 入口起点
module.exports = {
mode: "development", // 模式
entry: {
index: "./src/index.js",
other: "./src/other.js",
}, // 打包入口地址
output: {
filename: "[name].bundle.js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
},
//...
};
- 将
entry
改为一个对象,output
的filename
改为动态 - 这种方式,会将
entry
中的js
文件引入的其他公共文件重复打包
1.8.3 防止重复
Entry dependencies
module.exports = {
mode: "development", // 模式
entry: {
index: {
import: "./src/index.js",
dependOn: "shared",
},
other: {
import: "./src/other.js",
dependOn: "shared",
},
shared: "lodash",
},
output: {
filename: "[name].bundle.js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
},
//...
};
- 将
lodash
库抽离出来
SplitChunksplugin
module.exports = {
mode: "development", // 模式
entry: {
index: "./src/index.js",
other: "./src/other.js",
}, // 打包入口地址
output: {
filename: "[name].bundle.js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
},
//...
optimization: {
//...
splitChunks: {
chunks: "all",
},
},
};
1.8.4 动态导入
function getComponent() {
return import("lodash").then(({ default: _ }) => {
const element = document.createElement("div");
element.innerHTML = _.join(["1", "2", "3"], "-");
return element;
});
}
getComponent().then((element) => {
document.body.appendChild(element);
});
module.exports = {
mode: "development", // 模式
entry: {
index: "./src/index.js",
other: "./src/other.js",
}, // 打包入口地址
output: {
filename: "[name].bundle.js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
},
//...
optimization: {
//...
splitChunks: {
chunks: "all",
},
},
};
1.8.5 懒加载
即按需加载
const button = document.createElement("button");
button.textContent = "add";
button.addEventListener("click", () => {
import(/* webpackChunkName: 'math' */ "./math.js").then(({ add }) => {
console.log(add(1, 2));
});
});
document.body.appendChild(button);
- 只有点击按钮的时候才会加载
math.js
的资源 /* webpackChunkName: 'math' */
自定义打包文件的名字
1.8.6 预加载
const button = document.createElement("button");
button.textContent = "add";
button.addEventListener("click", () => {
import(/* webpackChunkName: 'math', webpackPrefetch: true */ "./math.js").then(({ add }) => {
console.log(add(1, 2));
});
});
document.body.appendChild(button);
webpackPrefetch
:在其他资源加载完成后,网络空闲时再加载webpackPreload
: 类似webpackPrefetch
preload和prefetch在用法上相差不大,效果上的差别如下(引自官方文档):
preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
1.9 缓存
1.9.1 输出文件的文件名
output: {
filename: "[name].[contenthash].js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
assetModuleFilename: "images/[contenthash][ext]", // 配置名字和扩展名
},
- 发布新版本时,如果文件名没有改,浏览器会认为文件没有改动,继续从缓存里读取资源。
[name].[contenthash]
生成动态文件名
1.9.2 第三方库
- 第三方库一般不会更新,尽量使用浏览器缓存的资源。
optimization: {
//...
splitChunks: {
// chunks: "all",
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: "vendors",
chunks: "all",
},
},
},
},
- 第三方库都打包进
vendors.*******.js
文件里
1.9.3 将js文件放到一个文件夹中
js
文件统一放到script
文件夹中
output: {
filename: "script/[name].[contenthash].js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
assetModuleFilename: "images/[contenthash][ext]", // 配置名字和扩展名
},
1.10 环境拆分
1.10.1 公共路径
publicPath
output: {
filename: "script/[name].[contenthash].js", // 输出文件名
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
assetModuleFilename: "images/[contenthash][ext]", // 配置名字和扩展名
publicPath: 'http://localhost:8080'
},
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link href="http://localhost:8080/styles/4ab8b30553ab33e8fb28.css" rel="stylesheet"></head>
<body><script defer src="http://localhost:8080/script/vendors.d53cf84e4b1532ebba33.js"></script><script defer src="http://localhost:8080/script/index.71743e14d7f58f5748dc.js"></script><script defer src="http://localhost:8080/script/other.53cde6b793d0fb0883d0.js"></script></body>
</html>
1.10.2 环境变量
module.exports = (env) => {
return {
// console.log(env.goal)
mode: env.production ? 'production' : 'development', // 模式
// ...
};
};
// 生产环境
npx webpack --env production
// 开发环境
npx webpack --env development
// 携带参数
npx webpack --env production --env goal=local
js代码压缩terser-webpack-plugin
- 生产环境压缩,开发环境不压缩
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = (env) => {
return {
mode: env.production ? 'production' : 'development', // 模式Ï
//...
optimization: {
minimizer: [
new CssMinimizerWebpackPlugin(), // 压缩CSS
new TerserPlugin(),
],
},
};
};
1.10.3 拆分配置文件
拆分配置文件,一个做生产环境配置,一个做开发环境配置
根目录新建两个js
文件
webpack.config.dev.js
-
npx webpack -c webpack.config.dev.js
npx webpack serve -c webpack.config.dev.js
webpack.config.prod.js
-
npx webpack -c webpack.config.prod.js
npx webpack serve -c webpack.config.prod.js
1.10.4 npm脚本
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"b": "npx webpack",
"bp": "npx webpack --env production",
"bd": "npx webpack --env development",
"bdev": "npx webpack -c webpack.config.dev.js",
"bprod": "npx webpack -c webpack.config.prod.js",
"s": "npx webpack-dev-server --open"
},
1.10.5 提取公共配置
根目录新建webpack.config.common.js
- 里面写生产环境和开发环境通用的配置代码
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const toml = require("toml");
const yaml = require("yaml");
const json5 = require("json5");
module.exports = {
entry: {
index: "./src/index.js",
other: "./src/other.js",
}, // 打包入口地址
output: {
path: path.join(__dirname, "dist"), // 输出文件目录
clean: true,
assetModuleFilename: "images/[contenthash][ext]", // 配置名字和扩展名
},
performance: {
hints: "warning",
//入口起点的最大体积
maxEntrypointSize: 50000000,
//生成文件的最大体积
maxAssetSize: 30000000,
//只给出 js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith(".js");
},
},
plugins: [
new HtmlWebpackPlugin({
title: "管理输出", // 输出html的title, 指定template时不生效
template: "./index.html", // 基于哪个文件生成html
filename: "app.html", // 生成的html文件名
inject: "body", // script标签生成到哪里
}),
new MiniCssExtractPlugin({
filename: "styles/[contenthash].css", // 配置输出的css路径和文件名
}),
],
module: {
rules: [
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/inline",
parser: {
dataUrlCondition: {
maxSize: 1024 * 10 * 10,
},
},
},
{
test: /.(css|sass)$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /.(woff|woff2|eot|ttf|otf)$/,
type: "asset/resoure",
},
{
test: /.(csv|tsv)/,
use: "csv-loader",
},
{
test: /.xml$/,
use: "xml-loader",
},
{
test: /.toml$/,
type: "json",
parser: {
parse: toml.parse,
},
},
{
test: /.yaml$/,
type: "json",
parser: {
parse: yaml.parse,
},
},
{
test: /.json5$/,
type: "json",
parser: {
parse: json5.parse,
},
},
{
test: /.js$/,
exclude: "/node_modules/", //排除
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
optimization: {
splitChunks: {
// chunks: "all",
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: "vendors",
chunks: "all",
},
},
},
},
};
module.exports = {
mode: "development", // 模式
output: {
filename: "script/[name].[contenthash].js", // 输出文件名
},
devtool: "inline-source-map",
devServer: {
static: "./dist",
},
};
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
mode: "production", // 模式
output: {
filename: "script/[name].[contenthash].js", // 输出文件名
publicPath: "http://localhost:8080",
},
optimization: {
minimizer: [
new CssMinimizerWebpackPlugin(), // 压缩CSS
new TerserPlugin(),
],
},
performance: {
hints: false, //不显示警告
},
};
1.10.6 合并配置文件
webpack-merge
:npm i webpack-merge -D
const { merge } = require("webpack-merge");
const commonConfig = require("./webpack.config.common");
const productionConfig = require("./webpack.config.prod");
const developmentConfig = require("./webpack.config.dev");
module.exports = (env) => {
switch (true) {
case env.development:
return merge(commonConfig, developmentConfig);
case env.production:
return merge(commonConfig, productionConfig);
default:
return new Error("No matching configuration was found");
}
};
运行
npx webpack -c webpack.config.js --env development
npx webpack serve -c webpack.config.js --env development
npx webpack -c webpack.config.js --env production
npx webpack serve -c webpack.config.js --env production