真相大白了,原来style-loader是这么用的

前言

webpack不仅有打包功能,还能对项目中各种文件按照我们的需求进行处理,这就用到了loader,所谓loader就是集成到webpack的文件处理方法,这些loader在webpack打包过程中,可以对指定类型的文件进行相应的处理,比如把less语法转换成浏览器可以识别的css语法,引入特定类型的文件(html)等等。下面将介绍一下一系列常用的loader,比如less-loadercss-loaderstyle-loader,随便上一段常用配置:

const lessLoader = {
  test: /\.less$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        modules: true,
      },
    },
    'less-loader',
  ],
}

以上文件就是告诉webpack,在遇到文件名以.less结尾的文件,先用less-loader编译,再用css-loader编译,最后用style-loader,曾经我是用这样一段js伪代码来概述的:

// 伪代码
const res = styleLoader(cssLoader(lessLoader('xxx.less')));

其实这个伪代码,一点都不严谨.

都是loader,但作用却大相径庭

style-loader 其实与 css-loaderless-loader的作用是有区别的,后者其实承担的是模块化与语法转译这一块;而style-loader这一类(还有常用的mini-css-extract-plugin)承担的是粘结剂功能,就是将js中的css加载到html中,从而使样式生效;

style-loader的用法

随意严谨一点的伪代码应该这样写:

const cssContent = cssLoader(lessLoader('xxx.less'));
// 通过styleTag插入css 字符串到head中
styleLoader(cssContent,  document.head);

好像文章写到这里就应该结束了,但实际上好戏才开始。

曾经我以为,style-loader是在构建时,就将样式插入到了html中,但实际上我错了,而且这一错就是五年,正确的答案是css被构建到了js文件中,然后在js文件加载时,通过style-loader提供的方法将其加载到html中。

简单来讲,曾经我以为这个loader是个构建时,但他其实是一个运行时。

怎么判断呢,很简单,就像如何判断一个网站是否是服务端渲染一样,看网站首页请求获取的html文件是否含样式(也可以直接通过source面板查看html文件, 不是elements)。

之所以会产生这个误判,是我直接平移了mini-css-extract-plugin的构建思想,这个plugin就剥离了JS中的样式,形成了一个新的文件,然后再把样式地址插入到html头中,这是个完完全全的构建时。

而最近为什么会突然关注到这个点,是因为我们在调一个微组件的方案,在组件成功加载时,发现样式丢失,看看下图的惨不忍睹:

style-loader的用法

经过debug,发现样式确实被打包进了文件,只是没有被加载到style标签中,第一直觉就是缺少loader,而后查看构建工具,发现样式编译配置只到了css-loader这一级,缺少style-loader

从构建结果看样式加载过程

如果到这文章就结束了,似乎就太不像我了,毕竟刨根问底的我才是真的我。接下来,通过一段实例代码,来看webpack构建,是怎样实现样式加载的。

示例代码:

// style.less代码
@font-size: 28px;

.Demo {
  box-sizing: border-box;
  :global {
    .demo-title {
      font-size: @font-size;
    }
  }
}
import React from 'react';
// 引入样式
import style from './style.less';

export default function Demo() {
  return (
    <div className={style.Demo}>
      <h3 className="emo-title">this is a demo</h3>
    </div>
  );
}

然后通过webpack,采用style-loader的方式构建,为了方便阅读,没有做代码压缩和丑化。

然后从入口可以看见样式引入是这样的:

// 注释无用代码有删减
__webpack_require__.d(__webpack_exports__, "default", function() { return Demo; });
var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
// 引入样式
var _style_less__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/demo/style.less");
var _style_less__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_style_less__WEBPACK_IMPORTED_MODULE_1__);


function Demo() {
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
    className: _style_less__WEBPACK_IMPORTED_MODULE_1___default.a.Demo
  }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h3", {
    className: "emo-title"
  }, "this is a demo"));
}

可以看见,在入口文件,有一个从./src/demo/style.less模块的样式引入,并赋值给了_style_less__WEBPACK_IMPORTED_MODULE_1___default变量;

然后顺藤摸瓜,继续看一下style.less模块长什么样:

// "./src/demo/style.less" 模块
function(module, exports, __webpack_require__) {
  var api = __webpack_require__("../../../..[email protected][email protected]@style-loader/dist/runtime/injectStylesIntoStyleTag.js");
  var content = __webpack_require__("../../../..[email protected][email protected]@css-loader/index.js?!../../../..[email protected][email protected]@postcss-loader/src/index.js?!../../../..[email protected][email protected]@less-loader/dist/cjs.js?!./src/demo/style.less");
  content = content.__esModule ? content.default : content;

  if (typeof content === 'string') {
    content = [[module.i, content, '']];
  }

  var options = {};
  options.insert = "head";
  options.singleton = false;

  var update = api(content, options);
  module.exports = content.locals || {};
}

这一块的代码就可以看出,这一个模块是一个承上(css引入)启下(插入html);

承上通过从less-loader/dist/cjs.js?!./src/demo/style.less这个模块引入, 而插入html则是通过从injectStylesIntoStyleTag导入了一个方法,从方法名就可以知道他的作用就是通过style标签插入css样式.

这里面还有一个点,就是最后的导出,模块最后将content.locals导出,继续根据线索去看看content到底是什么:

function(module, exports, __webpack_require__) {
  exports = module.exports = __webpack_require__("../../../..[email protected][email protected]@css-loader/lib/css-base.js")(false);

  exports.push([module.i, "._1rfoVjSya7b-iuQB2_qKPh {\n  -webkit-box-sizing: border-box;\n          box-sizing: border-box;\n}\n._1rfoVjSya7b-iuQB2_qKPh .demo-title {\n  font-size: 28px;\n}\n", ""]);

  exports.locals = {
    "Demo": "_1rfoVjSya7b-iuQB2_qKPh"
  };
}

通过上面的代码,就可以看出exports.locals的作用就是css的模块化。

至此,真相大白….

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有资源及文章均来源于网络及用户分享或为本站原创,仅限用于学习和研究,任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » 真相大白了,原来style-loader是这么用的

发表评论

IT互联网行业相关广告投放 更专业 更精准

立即查看 联系我们