export default function loader(content) { if (!this.emitFile) throw new Error('File Loader\n\nemitFile is required from module system'); const options = loaderUtils.getOptions(this) || {}; validateOptions(schema, options, 'File Loader'); }
以上代码中,emitFile 是由 loader 上下文提供的方法,用于输出一个文件,对应的函数签名如下:
emitFile(name: string, content: Buffer|string, sourceMap: {...})
在调用 file-loader 时,如果发现 this.emitFile 无效,则会抛出异常。接着 file-loader 会先调用 loaderUtils.getOptions() 方法,获取当前 loader 对应的配置对象,然后基于已定义的 Schema,验证配置对象的有效性。对应的 Schema 定义如下(不包含异常提示信息):
{ "type": "object", "properties": { "name": {}, "regExp": {}, "context": { "type": "string" }, "publicPath": {}, "outputPath": {}, "useRelativePath": { "type": "boolean" }, "emitFile": { "type": "boolean" } }, "additionalProperties": true }
获取 context 及生成文件名称
const context = options.context //自定义文件context // 从webpack 4开始,原先的this.options.context // 被改进为this.rootContext || this.rootContext || (this.options && this.options.context); const url = loaderUtils.interpolateName( this, options.name, // 默认为"[hash].[ext]" { context, content, regExp: options.regExp, });
loaderUtils 中的 interpolateName 方法,用于生成对应的文件名,该方法的签名如下:
interpolateName(loaderContext, name, options);
其中 loaderContext 为 loader 的上下文对象,name 为文件名称模板,options 为配置对象,支持 context,content 和 regExp 属性。该方法的使用示例如下:
示例一:
// loaderContext.resourcePath = "/app/js/javascript.js"; let interpolatedName = loaderUtils.interpolateName( loaderContext, "js/[hash].script.[ext]", { content: "console.log('loaderUtils')" }); // => js/e353f4da4c3e380646d2b4d75c8a13ab.script.js
以上示例核心的处理流程如下:
示例二:
// loaderContext.resourcePath = "/app/js/page-home.js" loaderUtils.interpolateName( loaderContext, "script-[1].[ext]", { regExp: "page-(.*)\\.js", content: "console.log('loaderUtils')" }); // => script-home.js
处理 outputPath
let outputPath = url; if (options.outputPath) { if (typeof options.outputPath === 'function') { outputPath = options.outputPath(url); } else { outputPath = path.posix.join(options.outputPath, url); } }
处理 publicPath
// __webpack_require__.p = ""; let publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`; if (options.publicPath) { if (typeof options.publicPath === 'function') { publicPath = options.publicPath(url); } else if (options.publicPath.endsWith('https://www.jb51.net/')) { publicPath = options.publicPath + url; } else { publicPath = `${options.publicPath}/${url}`; } publicPath = JSON.stringify(publicPath); }
处理 emitFile
if (options.emitFile === undefined || options.emitFile) { // 把文件输出到指定的outputPath路径 this.emitFile(outputPath, content); }
导出最终路径
return `module.exports = ${publicPath};`;
参考资源