首先打开style-loader的package.json,找到main,可以看到它的入口文件即为:dist/index.js,内容如下:`
var _path = _interopRequireDefault(require("path"));
var _loaderUtils = _interopRequireDefault(require("loader-utils"));
var _schemaUtils = _interopRequireDefault(require("schema-utils"));
var _options = _interopRequireDefault(require("./options.json"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
module.exports = () => {};
module.exports.pitch = function loader(request) {
// ...
}
`其中_interopRequireDefault的作用是:如果引入的是 es6 模块,直接返回,如果是 commonjs 模块,则将引入的内容放在一个对象的 default 属性上,然后返回这个对象。
我首先来看pitch函数,它的内容如下:`
// 获取webpack配置的options
const options = _loaderUtils.default.getOptions(this) || {};
// (0, func)(),运用逗号操作符,将func的this指向了windows,详情请查看:https://www.jianshu.com/p/cd188bda72df
// 调用_schemaUtils是为了校验options,知道其作用就行,这里就不讨论了
(0, _schemaUtils.default)(_options.default, options, {
name: 'Style Loader',
baseDataPath: 'options'
});
// 定义了两个变量,**insert**、**injectType**,不难看出insert的默认值为head,injectType默认值为styleTag
const insert = typeof options.insert === 'undefined' ? '"head"' : typeof options.insert === 'string' ? JSON.stringify(options.insert) : options.insert.toString();
const injectType = options.injectType || 'styleTag';
switch(injectType){
case 'linkTag':
{
// ...
}
case 'lazyStyleTag':
case 'lazySingletonStyleTag':
{
// ...
}
case 'styleTag':
case 'singletonStyleTag':
default:
{
// ...
}
}`
在这里,我们就看默认的就好了,即insert=head,injectType=styleTag`
const isSingleton = injectType === 'singletonStyleTag';
const hmrCode = this.hot ? `
// ...
` : '';
return `
// _loaderUtils.default.stringifyRequest这里就不叙述了,主要作用是将绝对路径转换为相对路径
var content = require(${_loaderUtils.default.stringifyRequest(this, `!!${request}`)});
if (typeof content === 'string') {
content = [[module.id, content, '']];
}
var options = ${JSON.stringify(options)}
options.insert = ${insert};
options.singleton = ${isSingleton};
var update = require(${_loaderUtils.default.stringifyRequest(this, `!${_path.default.join(__dirname, 'runtime/injectStylesIntoStyleTag.js')}`)})(content, options);
if (content.locals) {
module.exports = content.locals;
}
${hmrCode}
`;`
去掉多余的代码,可以清晰的看到pitch方法实际上最后返回了一个字符串,该字符串就是编译后在浏览器执行的代码,让我们来看看它在浏览器是如何操作的:
首先调用require方法获取css文件的内容,将其赋值给content,如果content是字符串,则将content赋值为数组,即:[[module.id], content, ''],接着我们覆盖了options的insert、singleton属性,由于我们暂时只看默认的,所以insert=head,singleton=false;再往下面看,我们又使用require方法引用了runtime/injectStyleIntoStyleTag.js,它返回一个函数,我们将content和options传递给该函数,并立即执行它:`
module.exports = function (list, options) {
options = options || {};
options.attributes = typeof options.attributes === 'object' ? options.attributes : {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of