1. {
  2. test: /\.css$/,
  3. use: [
  4. 'style-loader',
  5. 'css-loader'
  6. ]
  7. }

配置项

  1. {
  2. loader: 'style-loader',
  3. options: {
  4. injectTag: 'styleTag'
  5. }
  6. }

injectTag

  1. styleTag:自动往 header 添加 style 标签,默认是多个
  2. singletonStyleTag:往 header 添加 style 标签,只添加一个 style
  3. autoStyleTag:默认行为是 styleTag,当 IE6 - 9 时,表现为 singletonStyleTag
  4. lazyStyleTag:懒加载的 styleTag,只有当调用 styles.use 才会加入 style
  5. lazySingletonStyleTag:懒加载的 singletonStyleTag,调用 use 加入,unuse 取消
  6. lazyAutoStyleTag:懒加载的 autoStyleTag
  7. linkTag:需要搭配 file-loader 使用

    singletonStyleTag

    懒加载的样式结构: ```javascript import styles from ‘css-loader’

styles 结构: { locals: { container: ‘_2Zi7gYtNVL0Ohubm—3hA2’, btn: ‘Z-kqLN1uX0jQAwM63yfq6’ }, unuse: f (), use: f () }

调用 styles.use() 加载 css,unuse 卸载 css

  1. <a name="JMRTt"></a>
  2. #### linkTag 配合 file-loader 使用
  3. ```javascript
  4. {
  5. test: /\.css$/,
  6. use: [
  7. {
  8. loader: 'style-loader',
  9. options: {
  10. injectType: 'linkTag'
  11. }
  12. },
  13. 'file-loader'
  14. ]
  15. }

attributes

  1. {
  2. loader: 'style-loader',
  3. options: {
  4. attributes: { id: 'id' }
  5. }
  6. }

insert

  1. 选择器
  2. 方法 ```javascript // 选择器
  3. ‘head’ 默认
  4. ‘body’
  5. ‘#app’ 等

// 方法 insert: element => { const parent = document.querySeletor(‘head’); const lastElementInsertedByStyleLoader = window.lastElementInsertedByStyleLoader; if (!lastElementInsertedByStyleLoader) { parent.insertBefore(element, parent.firstChild); } else if (lastElementInsertedByStyleLoader.nextSibling) { parent.insertBefore(element, lastElementInsertedByStyleLoader.nextSibling);
} else { parent.appendChild(element); } window.
lastElementInsertedByStyleLoader = element; }

  1. <a name="4xHqK"></a>
  2. ### styleTagTransform
  3. styleTagTransform 是在浏览器执行的,有两个参数 css 和 style,css 是解析得到的 css 数据,style 是创建的 style 标签
  4. ```javascript
  5. styleTagTransform: (css, style) => {
  6. style.innerHTML = `${css}\n.modify{ color: #ff0000 }`;
  7. document.head.appendChild(style);
  8. }

style-loader自研思路

  1. style-loader 的核心是:拿到其他 loader 处理完成的 css 内容,把它插入页面
    1. 如何拿到其他 loader 处理完成的 css
    2. 如何把内容插入页面
    3. webpack 热更新时如何替换 css
  2. insert,injectTag,attributes 如何实现?

    如何拿到其他 loader 处理完成的 css?

    一点思路也没有的话,可以先看 Loader 预备知识。
    loader 有两种:1. 前置 loader; 2. 普通 loader
    webpack loader 是洋葱模型,先从左往右执行各个 loader 的 pitch,然后从右往左执行 loader。一般会使用 css-loader 做 import,url,模块化相关工作,执行完成之后 source 格式如下:
    1. // Imports
    2. import ___CSS_LOADER_API_IMPORT___ from "../node_modules/css-loader/dist/runtime/api.js";
    3. var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
    4. // Module
    5. ___CSS_LOADER_EXPORT___.push([module.id, "html {\n height: 100%;\n background: #ff0;\n /* background: aquamarine; */\n}", ""]);
    6. // Exports
    7. export default ___CSS_LOADER_EXPORT___;
    可以看到 css 被编译成了 js,调用 CSS_LOADER_EXPORT.toString() 可以获得 css 内容
    这样的内容暂时还没有想到办法可以引入,所以普通 loader 就行不通了

    前置 loader pitch 拦截

    使用 pitch 拦截后续 loader 执行,然后使用 require 行内 loader 的方式来调用后续 loader 获得结果,这也是 webpack 所提倡的方法
    1. module.exports.pitch = function (remainingRequest, precedingRequest, data) {
    2. return `module.exports = retuire('-!${remainingRequest}')`
    3. }
    在 pitch 中 return,就拦截了后续 loader 的执行,调用 require remainingRequest 可以以模块化的方式获得后续 loader 执行结果

    require 内联 loader

    webpack 地址看这里:https://www.webpackjs.com/api/loaders/
    其实 webpack 文档中提供了除了使用 webpack 配置文件配置 loader 加载资源之外的另一种方法:
    1. require('style-loader!css-loader?modules!./index.css?name=Alex')
    在入口文件中直接这么调用,就不需要配置 webpack 配置文件了,但是 css 文件很多的话,每个都这么写就很麻烦

    内联 loader 研究

    内联 loader 有个问题 ```javascript 比如代码中 import ‘style-loader!css-loader!./index.css’ 然后 webpack.config.js 中又配置了 css 的 loader 检测 { test: /.css/, use: [ ‘style-loader’, ‘css-loader’, ] }

那么 index.css 文件会被加载两次。

  1. 以我目前理解是,webpack 配置文件配置的属于 preLoader,内联 import 属于 loader,所以导致执行了两次,所以在内联 import 的时候,最好是都加上 -! 以跳过 **普通和前置 loader**
  2. 1. ! 跳过普通 loaders
  3. 1. -! 跳过前置和普通 loaders
  4. 1. !! 禁用所有的 loaders
  5. 到这里,拿到其他 loader 处理完成的 css 就解决了
  6. <a name="gDPfd"></a>
  7. ## 如何把内容插入页面?
  8. 上面方法 require 之后可以调用 toString 拿到编译之后的 css,创建 style 标签,放入 css 即可
  9. ```javascript
  10. module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  11. return `
  12. import cssModule from '${remainingRequest}'
  13. cosnt style = document.createElement('style');
  14. style.innerHTML = cssModule;
  15. document.head.appendChild(style);
  16. `
  17. }

webpack 热更新时如何替换 css?

要替换 css,那么就需要找到当前 css 内容对应哪个 style,操作 style 就完事了

window.__styleInjectedMappedByRemainingRequest = {}
window.__styleInjectedMappedByRemainingRequest[remainingRequest] = style;

remainingRequest 的结构如下 👇:
/Users/wholee/Desktop/webpack-css-system/node_modules/css-loader/dist/cjs.js!/Users/wholee/Desktop/webpack-css-system/my-style-loader/index2.css
可以看到使用 loader 的绝对路径以及源文件绝对路径都很清楚,所以可以作为查找 style 的键

insert,injectTag,attributes 如何实现?

这些就不细聊了,只是字符串替换的问题。具体插件源码参考如下:
StyleLoader.js