前言
之前的文章中,已经完成了html,css,js的基本配置,接下来针对图片等资源文件的加载添加其他的配置。
图片资源加载
小tips:使用“../src/images/xxx.png”方式引入图片的时候,在img标签中可以直接使用,但是采用变量赋值的方式,这种写法在webpack中被认为是一个字符串,而不是一个图片路径,解决办法是,使用import引入图片,或者在赋值的时候使用require。
首先,我们先创建一个images文件夹,里面有两张图片,我选择的一张大小是4KB,一张是240KB,使用如下方式引入:
import zm from "../images/zmqny.png";
import Insourcing from "../images/Insourcing-business@2x.png";
const app = document.querySelector("#app");
app.innerHTML = "Hello World!!";
const img1 = new Image();
img1.src = zm;
app.appendChild(img1);
const img2 = new Image();
img2.src = Insourcing;
app.appendChild(img2);
在执行编译命令时,出现如下报错:
在执行打包命令时,出现如下报错:
根据报错提示,我们需要一个loader来处理图片。
loader加载
在 webpack5
之前加载资源文件一般会是用 file-loader
、url-loader
、raw-loader
等,但是在webpack5
之后可以直接使用资源模块类型(asset module type
)来替代上述 loader
。
下面简单介绍一下常用的file-loader和url-loader
file-loader
file-loader的作用是帮助我们处理import/require()
方式引入的文件资源并放到我们输出的文件夹中,上述错误,我们使用了import和require()的方式引入了图片,此时,我们下载file-loader,在webpack.config.js中进行相关的配置:
// webpack.config.js
...
module: {
rules: [
...
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader: "file-loader"
}
]
}
]
}
此时编译后,页面上能够正常展示图片了,并且执行打包命令,在dist文件夹下,也能看到打包后的图片。
打包后的图片名称是一串随机数字(这里的字符串是使用MD4
的散列函数处理生成的一个hash
值),接下来,我们来修改一下这个名称。
我们可以使用PlaceHolders
来修改打包后的图片命名格式:
一些常用的 PlaceHolders
ext
:处理文件扩展名name
:处理文件名称hash
:使用MD4
的散列函数处理生成的一个hash
值(默认情况下,它是哈希的十六进制摘要)contentHash
:指定生成文件内容哈希值的哈希方法。(默认 md4)path
:相对于 webpack/configcontext
的资源路径。hash:<length>
:截图hash的长度,默认32个字符 配置方式
hash的简单介绍
- [hash]:整个项目共用同一个 hash 值,只要项目里有文件更改,整个项目构建的 hash 值都会更改。
- [chunkhash]:同一个模块共用一个 hash 值,就算将 JS 和 CSS 分离,其 hash 值也是相同的,修改一处,JS 和 CSS 的 hash 值都会变。
- [contenthash]:单个文件单独的 hash 值,只要文件内容不一样,产生的 hash 值就不一样。
webpack.config.js的配置修改:
// webpack.config.js
...
module: {
rules: [
...
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader: "file-loader",
options: {
name: "test_[name]_[hash:6].[ext]"
}
}
]
}
]
}
修改后的名称显示:
然后,我们还可以指定打包输出的文件夹:
...
{
loader: "file-loader",
options: {
name: "images/test_[name]_[hash:6].[ext]"
// outputPath: "images" 这样也可以
}
}
url-loader
url-loader的用法和file-loader是一样的,其作用是将文件转换成base64的url。但是吧,使用这个loader有个弊端,那就是,如果图片资源特多,或者很大的时候,整个页面的加载速度会很慢,不过,也不用担心,这个loader提供了一个limit属性,设定超过某个阈值就不转换成base64的格式就好了。
使用配置如下:
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 15 * 1024,
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
fallback: {
loader: "file-loader",
options: {
name: "images/[name]_[hash:6].[ext]"
}
}
}
}
],
type: "javascript/auto"
}
这里有个坑,file-loader和url-loader不能采用上下结构来写,如果要同时使用,则需要使用上述写法,具体为什么,这个确实找不到答案,有大佬知道的话请解惑一下,万分感谢。
此时,可以发现,小于limit设置的值的图片会变成base64的url,而大于limit值的图片还是和原来一样。
webpack5的加载方式:asset module type
简单介绍一下:
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现。 配置 同样的添加rule
asset/resource
asset/resource起到两个作用:
- 一个是用来解析文件的 URL,
- 另外一个是将目标文件输出到打包目录。
webpack.config.js配置修改如下:
...
{
test: /\.(jpe?g|png|svg|gif)/i,
type: "asset/resource",
generator: {
filename: "images/[name]_[hash:8].[ext]"
}
}
asset/inline
如果想要实现url-loader的效果,可以使用这个。然,这里和loader一样,使用的时候要注释掉asset/resource,防止冲突。webpack.config.js的配置修改如下:
...
{
test: /\.(jpe?g|png|svg|gif)/i,
type: "asset/inline"
}
不过,这里出现了一个问题,没办法限制limit,这就意味着,很大的图也会被转换成base64,这体积,搞不好比原来的还要大得多。那么,这就需要使用下面这种方式了。
asset
asset资源类型可以根据指定的图片大小来判断是否需要将图片转化为base64,如果图片大于或等于限制,则使用asset/resource 处理,如果图片小于限制asset/inline 处理。webpack.config.js配置修改如下
...
{
test: /\.(jpe?g|png|svg|gif)/i,
type: "asset",
generator: {
filename: "images/[name]_[hash:8].[ext]" // 局部指定输出位置
},
parser: {
dataUrlCondition: {
maxSize: 15 * 1024 // 限制于 8kb
}
}
}
以上,就完成了webpack5的稳健资源加载的配置啦。
额外补充内容---别名设置
resolve: {
alias: {
"@": path.resolve(__dirname, "../src")
// 下面可以继续新增别名
}
},
完整代码
package.js
{
"name": "webpack-project",
"version": "1.0.0",
"description": "前端项目搭建练习",
"main": "index.js",
"scripts": {
"test": "npm run test",
"build:prod": "webpack --config build/webpack.config.prod.js",
"dev": "webpack server --config build/webpack.config.dev.js --open"
},
"author": "leo",
"license": "ISC",
"dependencies": {
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.73.0"
},
"devDependencies": {
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"file-loader": "^6.2.0",
"mini-css-extract-plugin": "^2.6.1",
"sass": "^1.54.4",
"sass-loader": "^13.0.2",
"style-loader": "^3.3.1",
"url-loader": "^4.1.1",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3",
"webpack-merge": "^5.8.0"
}
}
webpack.config.js
// webpack.config.js
const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: path.resolve(__dirname, "../src/main.js"), // path.resolve 方法用于生成绝对路径
resolve: {
alias: {
"@": path.resolve(__dirname, "../src"),
// 下面可以继续新增别名
images: path.resolve(__dirname, "../images")
}
},
plugins: [
new webpack.LoaderOptionsPlugin({
// test: /\.xxx$/, // may apply this only for some modules
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"), // 指定要编译的文件,不指定的话会按照默认的模板创建一个html
title: "webpack练习",
filename: "index.html" // 编译完成输出的文件名
}),
new MiniCssExtractPlugin({
filename: "css/[name].[fullhash].css"
})
],
module: {
rules: [
// {
// test: /\.css$/,
// // use: ["style-loader", "css-loader"]
// use: [
// { loader: "style-loader" },
// {
// loader: "css-loader",
// options: {
// modules: true // 启用/禁用 CSS 模块及其配置
// }
// }
// ]
// }
// loader,源码预处理器
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
},
// {
// test: /\.(png|jpg|jpeg|gif|svg)$/,
// use: [
// {
// loader: "file-loader",
// options: {
// name: "images/[name]_[hash:6].[ext]"
// }
// }
// ]
// },
// {
// test: /\.(png|jpg|jpeg|gif|svg)$/,
// use: [
// {
// loader: "url-loader",
// options: {
// limit: 15 * 1024,
// // 解决:关闭url-loader的es6模块化,使用commonjs解析
// esModule: false,
// fallback: {
// loader: "file-loader",
// options: {
// name: "images/[name]_[hash:6].[ext]"
// }
// }
// }
// }
// ],
// type: "javascript/auto"
// }
// asset/resource
// {
// test: /\.(jpe?g|png|svg|gif)/i,
// type: "asset/resource",
// generator: {
// filename: "images/[name]_[hash:8].[ext]"
// }
// }
// asset/inline
// {
// test: /\.(jpe?g|png|svg|gif)/i,
// type: "asset/inline"
// }
// asset
{
test: /\.(jpe?g|png|svg|gif)/i,
type: "asset",
generator: {
filename: "images/[name]_[hash:8].[ext]" // 局部指定输出位置
},
parser: {
dataUrlCondition: {
maxSize: 15 * 1024 // 限制于 8kb
}
}
}
]
}
};
main.js
import "./index.js";
import "./css/index.css";
import "./scss/index.scss";
console.log(process.env.NODE_ENV);
index.js
import zm from "images/zmqny.png";
import Insourcing from "images/Insourcing-business@2x.png";
const app = document.querySelector("#app");
app.innerHTML = "Hello World!!";
const img1 = new Image();
img1.src = zm;
app.appendChild(img1);
const img2 = new Image();
img2.src = Insourcing;
app.appendChild(img2);