Skip to content

利用 webpack 拆分 css

要拆分 css,就必须把 css 当成像 js 那样的模块;要把 css 当成模块,就必须有一个构建工具(webpack),它具备合并代码的能力

而 webpack 本身只能读取 css 文件的内容、将其当作 JS 代码进行分析,因此,会导致错误(原因是 AST 抽象语法树无法解析 css)

于是,就必须有一个 loader,能够将 css 代码转换为 js 代码,通过 AST 抽象语法树。

该问题解决了上个章节中提出的最后一问---如何将 css 进行细分。

css-loader

css-loader 的作用,就是将 css 代码转换为 js 代码

sh
pnpm i css-loader -D

它的处理原理极其简单:将 css 代码作为字符串导出

例如:

css
.red {
  color: "#f40";
}

经过 css-loader 转换后变成 js 代码:

js
module.exports = `.red{
    color:"#f40";
}`;

上面的 js 代码是经过我简化后的,不代表真实的 css-loader 的转换后代码,css-loader 转换后的代码会有些复杂,同时会导出更多的信息,但核心思想不变

再例如:

css
.red {
  color: "#f40";
  background: url("./bg.png")
    //无法解析,因为图片通不过AST,需要file-loader或者url-loader
;
}

经过 css-loader 转换后变成 js 代码:

js
var import1 = require("./bg.png"); //file-loader会返回解析后的路径
module.exports = `.red{
    color:"#f40";
    background:url("${import1}")
}`;

这样一来,经过 webpack 的后续处理,会把依赖./bg.png添加到模块列表,然后再将代码转换为

js
var import1 = __webpack_require__("./src/bg.png");
module.exports = `.red{
    color:"#f40";
    background:url("${import1}")
}`;

再例如:

css
@import "./reset.css"; //css-loader会将该模块解析为字符串,并拼接到最终结果去
.red {
  color: "#f40";
  background: url("./bg.png");
}

会转换为:

js
var import1 = require("./reset.css");
var import2 = require("./bg.png");
module.exports = `${import1}
.red{
    color:"#f40";
    background:url("${import2}")
}`;

总结,css-loader 干了什么:

  1. 将 css 文件的内容作为字符串导出
  2. 将 css 中的其他依赖作为 require 导入,以便 webpack 分析依赖
  3. 就算多次导入引用,也只会执行一次,因为 webpack 分析依赖的时候会对照资源记录表

style-loader

由于 css-loader 仅提供了将 css 转换为字符串导出的能力,剩余的事情要交给其他 loader 或 plugin 来处理

style-loader 可以将 css-loader 转换后的代码进一步处理,将 css-loader 导出的字符串加入到页面的 style 元素中

sh
pnpm i style-loader -D

例如:

css
.red {
  color: "#f40";
}

经过 css-loader 转换后变成 js 代码:

js
module.exports = `.red{
    color:"#f40";
}`;

经过 style-loader 转换后变成:

js
module.exports = `.red{
    color:"#f40";
}`;
var style = module.exports;
var styleElem = document.createElement("style");
styleElem.innerHTML = style;
document.head.appendChild(styleElem);
module.exports = {};

以上代码均为简化后的代码,并不代表真实的代码 style-loader 有能力避免同一个样式的重复导入

实践

src/index.js

js
const bannerCss = require("./assets/banner.css"); //解析不了,我们要使用css-loader来解析css
console.log(bannerCss.toString());

const pagerCss = require("./assets/pager.css"); //解析不了,我们需要file-loader或者url-loader来解析里面引用的文件

// //css-loader只是将css文件解析为js字符串,并没有自动的创建style插入到页面,这个工作需要style-loader来完成
const style = document.createElement("style");
style.innerHTML = pagerCss.toString();
document.head.appendChild(style);

src/assets/banner.css

css
.banner {
  color: "#f40";
}

src/assets/pager.css

css
@import "./banner.css";

.pager {
  width: 1000px;
  height: 1000px;
  color: "#f40";
  /* 需要file-loader或者url-loader */
  background: url("./bg.png");
}

webpack.config.js

js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "development",
  devtool: "source-map",
  watch: true,
  module: {
    rules: [
      //loader执行顺序是从后向前的,这里是要先css-loader然后才style-loader
      { test: /\.css$/, use: ["style-loader", "css-loader"] },
      { test: /\.png$/, use: "file-loader" },
    ],
  },
  //
  plugins: [new CleanWebpackPlugin()],
};

总结

我们只需要记住一点,webpack 想要解析 css 的话,就需要 loader 来进行。

我们可以使用css-loaderstyle-loader来解析,有了这两个 loader,我们就解决了 css 模块化的问题。。

以后还会介绍将 css 以文件的形式生成的插件 plugins。

MIT License