loader
- webpack 做的事情,仅仅是分析出各种模块的依赖关系,然后形成资源列表,最终打包生成到指定的文件中;更多的功能需要借助 webpack loaders 和 webpack plugins 完成;
- loader 本质上是一个函数,它的作用是将某个源码字符串转换成另一个源码字符串返回;loader 函数将在模块解析的过程中被调用,从而得到最终的源码;
构建打包全流程
chunk 中解析模块的流程
处理 loaders 流程
解析 loader 的时候,从上到下匹配,命中匹配后将 loader 依次放入到一个数组中,最后再从后向前依次调用 loader
loader 配置
完整配置
module.exports = {
module: { // 针对模块的配置,目前版本只有两个配置,rules、noParse
rules: [ // 模块匹配规则,可以存在多个规则
{ // 每个规则是一个对象
test: /\.js$/, // 匹配的模块正则
use: [ // 匹配到后应用的规则模块
{ // 其中一个规则
loader: "模块路径", // loader模块的路径,该字符串会被放置到 require 中
options: { // 向对应 loader 传递的额外参数
}
}
]
}
]
}
}
简化配置
module.exports = {
module: { // 针对模块的配置,目前版本只有两个配置,rules、noParse
rules: [ // 模块匹配规则,可以存在多个规则
{ // 每个规则是一个对象
test: /\.js$/, // 匹配的模块正则
use: ["模块路径1", "模块路径2"] // loader 模块的路径,该字符串会被放置到 require 中
}
]
}
}
loader 练习
手写 style-loader
// index.js
var content = require("./assets/index.css");
console.log(content); // css 的源码字符串
/* assets/index.css */
body {
background: #333;
color : #fff;
}
// loaders/style-loader.js
module.exports = function (sourceCode) {
var code = `
var style = document.createElement("style");
style.innerHTML = \`${sourceCode}\`;
document.head.appendChild(style);
module.exports = \`${sourceCode}\`;
`;
return code;
}
// webpack.config.js
module.exports = {
mode: "development",
devtool: "source-map",
module: {
rules: [{
test: /\.css$/,
use: ["./loaders/style-loader"]
}]
}
}
手写 img-loader
// index.js
var src = require("./assets/webpack.png");
console.log(src);
var img = document.createElement("img");
img.src = src;
document.body.appendChild(img);
// loaders/img-loader.js
var loaderUtil = require("loader-utils"); // 用于获取 webpack 中配置的 options 中的参数
/***
* 将 buffer 数组转成base64 字符串
* @param {*} buffer
* @returns
*/
function getBase64(buffer) {
return "data:image/png;base64," + buffer.toString("base64");
}
/***
* 获取文件名字
* @param {*} buffer
* @param {*} name [contenthash].[ext]
* @returns
*/
function getFilePath(buffer, name) {
// 根据 [单文件 buffer] 生成 hash
var filename = loaderUtil.interpolateName(this, name, { content: buffer });
this.emitFile(filename, buffer); // 向最终的文件列表中增加文件
return filename;
}
function loader(buffer) {
// 获取原始数据 buffer
console.log("文件数据大小:(字节)", buffer.byteLength);
var { limit = 1000, filename = "[contenthash].[ext]" } = loaderUtil.getOptions(this);
if (buffer.byteLength >= limit) {
var content = getFilePath.call(this, buffer, filename);
} else {
var content = getBase64(buffer);
}
return `module.exports = \`${content}\``;
}
// 设置为 true 后,loader 返回的就是原始数据,而不是字符串了
loader.raw = true;
module.exports = loader;
// webpack.config.js
module.exports = {
mode: "development",
devtool: "source-map",
module: {
rules: [
{
test: /\.(png)|(jpg)|(gif)$/,
use: [
{
loader: "./loaders/img-loader.js",
options: {
limit: 3000, // 3000 字节以上使用图片,3000 字节以内使用 base64
filename: "img-[contenthash:5].[ext]"
}
}]
}
]
}
}
面试题
最后的输出结果
-
题目代码:
JavaScriptJavaScriptJavaScriptJavaScriptJavaScriptJavaScript// index.js require("./a.js"); // a.js 中内容为空
// loaders/loader1.js module.exports = function (sourceCode) { console.log("loader1"); return sourceCode; }
// loaders/loader2.js module.exports = function (sourceCode) { console.log("loader2"); return sourceCode; }
// loaders/loader3.js module.exports = function (sourceCode) { console.log("loader3"); return sourceCode; }
// loaders/loader4.js module.exports = function (sourceCode) { console.log("loader4"); return sourceCode; }
// webpack.config.js module.exports = { mode: "development", module: { rules: [ { test: /index\.js$/, //正则表达式,匹配模块的路径 use: ["./loaders/loader1", "./loaders/loader2"] //匹配到了之后,使用哪些加载器 }, //规则1 { test: /\.js$/, //正则表达式,匹配模块的路径 use: ["./loaders/loader3", "./loaders/loader4"] //匹配到了之后,使用哪些加载器 } //规则2 ], //模块的匹配规则 } }
-
输出:看 chunk 中解析模块的更详细流程
// index.js loader4 loader3 loader2 loader1 // a.js loader4 loader3
Module:模块化
上一篇