Skip to main content

处理静态资源

目前为止我们通过 webpack 编译 tsx 代码,通过 HtmlWebpackPlugin 生成 html 文件,还没有涉及到静态资源的打包,即图片、字体、音效、css、less。

目标

通过 webpack 打包资源,并生成对应 hash 名以作区分。

webpack 处理资源,是在配置 modules 里通过 loader 来处理,通过 rules 来区分哪些文件该如何处理(即使用哪些 loader)。于是配置就会长这样:

{
"entry": "./src/index.tsx",
"module": {
"rules": [
{
"test": /\.tsx?$/,
"loader": "ts-loader"
},
{
"test": /\.css$/,
"use": [
"style-loader",
"css-loader"
]
}
]
}
}

重点就是使用 webpack 的 rules,通过每个 rule 下的 test 正则来筛选要处理的文件,对应如何处理则一律交给 loader。

样式资源

这里使用 MiniCssExtractPlugin、css-loader 来处理,为啥需要这么多插件?

css-loader

css-loader 将 css 文件转换成 js 文件,并且把 css 文件中的 url() 替换成相应的资源路径。 所以主要做两件事:

  1. css 里有@import 和 url()写法,开发时用的本地相对路径,需要转换成发布后的真实路径。
  2. 将 css 的内容转换为 js 模块
  3. 所以 css-loader 并不负责把样式放到节点上,只做转译。
为啥要转换为 js 模块而不是 css 文件呢?

因为 css 样式默认直接添加在节点上的 style 属性,css-loader 并不做这个事情,它只是把 css 文件转移成供 js 使用的代码,由其他插件(如 style-loader)来负责使用。

安装:

npm i css-loader style-loader -D

在 webpack 的 modules 中设置针对 css 文件使用 css-loader 来处理:

scripts/webpack.config.js
...
plugins: ...
module: {
rules: [
{
test: /\.(css)$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
},
],
},
],
},
...

模块化引入资源文件

在 pages/index/index.tsx 同目录下创建 styles.module.css 文件,在里面写上样式,然后在 index.tsx 中引入这个文件,会发现有报错:

Cannot find module './styles.module.css' or its corresponding type declarations.ts(2307)

这是因为 ts 不认识.css 文件,所以我们需要添加一个 global.d.ts 文件,里面把各种在代码中引入的资源作为模块导出,这样就不会报错了。

src/global.d.ts
declare module '*.css' {
const styles: any;
export default styles;
}

declare module '*.scss' {
const styles: any;
export default styles;
}

declare module '*.less' {
const styles: any;
export default styles;
}

declare module '_.svg';
declare module '_.png';
declare module '_.jpg';
declare module '_.jpeg';
declare module '_.gif';
declare module '_.bmp';
declare module '_.tiff';
declare module '_.mp3';
declare module '*.webp';

解决完报错,在 index.tsx 中的 div 节点设置 className,编译后就可以看到 css 样式生效了。

为什么要命名成 styles.module.css?而不是 styles.css?

css-loader 默认只处理以 module.css 命名结尾的文件,将这个 css 文件内容进行 js 模块化处理。如果想支持所有扩展名为 css 的文件,需要在 css-loader 的 options 配置中添加一个参数 modules: true,或者设置一个空对象也行。

具体参考:modules 参数

小结
  1. css-loader 将 css 转译为 js 模块,替换里面的资源路径,并不负责使用。
  2. style-loader 负责在代码中使用 css-loader 转译后的 js 模块,把它插入到 DOM 节点中。

MiniCssExtractPlugin

因为 css-loader 生成对应 js 模块后是直接内嵌到 js 中的,这样会增加 js 文件的体积,一般我们期望 css 文件能单独导出,然后在 index.html 文件中通过 link 引入。MiniCssExtractPlugin 就是干这个事情的。有了它,style-loader 就不需要了。 安装:

npm i mini-css-extract-plugin -D

修改 webpack.config.js:

scripts/webpack.config.js
...
plugins: [...htmlEntries, new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.(css)$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
],
},

编译后,就会看到 index.html 中的 link 标签,把 css 文件单独导出了。

图片音乐字体等

这类资源的 rules 比较简单,只需要指定类型为 asset 就行。

{
test: /\.(svg|png|jpg|gif|woff|woff2|eot|ttf|otf|mp3|webp)$/,
type: 'asset',
},