1、plugin
1.1 compiler
Compiler 模块是 webpack 的支柱引擎,它通过 CLI 或 Node API 传递的所有选项,创建出一个 compilation 实例
相关钩子:
以下生命周期钩子函数,是由 compiler 暴露,可以通过如下方式访问:
compiler.hooks.someHook.tap(...)
取决于不同的钩子类型,也可以在某些钩子上访问 tapAsync 和 tapPromise。
1.2 compilation
Compilation 模块会被 Compiler 用来创建新的编译(或新的构建)。compilation 实例能够访问所有的模块和它们的依赖(大部分是循环依赖)。它会对应用程序的依赖图中所有模块进行字面上的编译(literal compilation)。在编译阶段,模块会被加载(loaded)、封存(sealed)、优化(optimized)、分块(chunked)、哈希(hashed)和重新创建(restored)。
Compilation 类扩展(extend)自 Tapable,并提供了以下生命周期钩子。可以按照 compiler 钩子的相同方式,调用 tap:
compilation.hooks.someHook.tap(...)
和 compiler 用法相同,取决于不同的钩子类型,也可以在某些钩子上访问 tapAsync 和 tapPromise。
compiler.hooks.emit.tapAsync(pluginName, (compilation, callback) => {
...一系列疯狂操作之后
callback();
}
compiler.hooks.emit.tapPromise(pluginName, (compilation) => {
...一系列疯狂操作之后
return new Promise(resolve => {
// 执行完你的任务后
resolve();
});
}
1.3 Compiler 和 Compilation
在插件开发中最重要的两个资源就是 compiler 和 compilation 对象。理解它们的角色是扩展 webpack 引擎重要的第一步。
compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
1.4 手写plugin例子
const replacePathVariables = (path, data, assetInfo) => {
...一系列疯狂操作之后
return path;
};
const plugin = "TemplatedPathPlugin";
class TemplatedPathPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(plugin, compilation => {
compilation.hooks.assetPath.tap(plugin, replacePathVariables);
});
}
}
module.exports = TemplatedPathPlugin;
2、loader
2.1使用loader-utils,schema-utils
loader-utils, schema-utils是webpack的loader工具库,有很多便捷的方法可以调用。
const { getOptions,stringifyRequest, parseQuery } = require("loader-utils");
const validateOptions = require("schema-utils");
const schema = {
type: "object",
properties: {
test: {
type: "string"
}
}
};
module.exports = function(source) {
//getOptions 用于在loader里获取传入的options,返回的是对象值。
const options = getOptions(this);
// stringifyRequest转换路径,避免require()或impot时使用的绝对路径
stringifyRequest(this, "./test.js"); // Result => "\"./test.js\""
//parseQuery获取query参数的,这个很简单就不说啦
parseQuery('?name=kev&age=14') // Result => {name: 'kev', age: '14'}
//验证参数的类型是否正确。
validateOptions(schema, options, "loader");
};
2.2编写自己的loader
设计稿在pc端的尺寸是750px,部分文件要转化成移动端也可以适配。
定义一个移动端适配的样式文件,如:**.mobile
//webapck.config.js
const path = require("path");
module.exports = {
entry: "./src",
output: {
path: path.resolve(__dirname, "dist"),
filename: "package.js"
},
mode: "production",
module: {
rules: [{
test: /\.mobile$/,
use: [
'style-loader',
'css-loader',
{
loader: './mobile-css-loader',
options: {
width: 750,
}
}
]
}]
}
};
第一步先验证options是否符合类型,
第二步获取参数,然后替换传入的资源文件字符串。
//loader.js
const { getOptions } = require("loader-utils");
const validateOptions = require("schema-utils");
const schema = {
type: "object",
properties: {
width: {
type: 'number',
}
}
};
module.exports = function(source) {
const options = getOptions(this);
validateOptions(schema, options, 'loader');
validate(schema, options, {
name: 'px2vw Loader',
baseDataPath: 'options',
});
const px2vw = px => px / options.width * 100 + 'vw';
return source.replace(/(\d+)px/g, (_, px) => px2vw(px));
};