如果你的工具型面对的对象有很丰富的场景需求,或者不想再为频繁的增减需求而频繁迭代,是时候考虑为你的系统设计一款插件系统。
插件机制: Core-Plugin 架构的组成
Core-Plugin 模式的好处,总结几点:
和开箱即用的库 / 组件功能模块相比,可能比较明显的缺点就是,Plugin 的开发需要遵循规范,复杂一点的库(比如 Babel、webpack)还需要理解其运行原理,会相对有些门槛。
官方定义:Babel 是一个 JavaScript 编译器。
babel 大家都很熟悉,最重要的功能是将 ES6 版本的代码转换为 ES5 语法,使我们的代码能兼容不同的浏览器以及版本。随着 ES 语法的日渐丰富和扩展,对 babel 转换代码的规则也有更多的要求,babel 提供了一套插件机制支持开发者自定义插件来实现特殊的转换规则。在了解 babel 插件机制之前,需要掌握如下知识点:
babel 转换流程:
推荐一个网站: https://astexplorer.net/ ,在线 AST 解析器。
例子:
let tips = 'gun';
const func1 = (a) => {
console.log(a);
};
它的 AST 长这样:
bebel 插件开发 - es6 转换 es5
这里以转换箭头函数和 let/const 为例:
// 转化es6语法的babel插件
// babel-types:https://github.com/babel/babel/tree/master/packages/babel-types
// babel-types是babel的工具集之一,用于处理AST节点,包含了构造、验证以及变换AST节点的方法。
exportdefaultfunction({ types: babelTypes }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {},
// 转换箭头函数
ArrowFunctionExpression(path) {
const node = path.node;
if (!path.isArrowFunctionExpression()) return;
path.arrowFunctionToExpression({ //... });
},
// 转换let/const -> var
VariableDeclaration(path) {
const node = path.node;
if (node.kind === 'let' || node.kind === 'const') {
// let a ,这里的 a 就是 node.declarations
const varNode = type.variableDeclaration('var', node.declarations);
path.replaceWith(varNode);
}
},
}
};
}
// .bebelrc
{
plugins: ['xxx']
}
执行流程:获取到插件的 vistor 对象,遍历 AST 节点(图 (5) AST 对象中标蓝的字段),如果遍历的节点 type 在 vistor 对象能找到对应的 key,则执行 vistor [key] 对应的逻辑(transform),遍历结束生成对应语法。单个 babel 插件的执行逻辑很清晰。然而实际开发中我们会在.babelrc 里配置很多 plugins,babel 是如何组织管理插件呢?我们知道,babel 会深度递归遍历 AST,代价很高,最好的方式是把插件组织起来,在一次遍历中全部执行完成。 传送门
// bad case
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
// great case
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
babel 内部为了提高效率,正是采用 merge visitors 的方式:
// ...
// 插件合并
const visitor = traverse.visitors.merge(
visitors,
passes,
file.opts.wrapPluginVisitorMethod,
);
// 一次性执行插件 visitor 中定义的方法
traverse(file.ast, visitor, file.scope);
// ...
合并的原则是对于相同类型的节点,将处理方法组合成一个数组,当遇到该类型节点的时候,一次执行处理方法,合并的数据结构类似如下:
{
ArrowFunctionExpression: {
enter: [...]
},
BlockStatement: {
enter: [...],
exit: [...]
},
DoWhileStatement: {
enter: [...]
}
}
webpack 插件
webpack 插件的目的在于解决 loader 无法实现的其他事。除了自身提供的开箱即用的插件,还支持自定义插件。
// 自定义插件
class MyWebpackPlugin {
const webpacksEventHook = 'emit';
apply(compiler) {
// 监听'emit'事件,同步方式
compiler.hooks.emit.tap('MyWebpackPlugin', function(compilation) {
// ...
});
// 监听webpacksEventHook(emit)事件,异步方式
compiler.plugin(webpacksEventHook, function(compilation, callback) {
// ...
const compilationEvenetHook = 'xxx'
compilation.plugin(compilationEvenetHook, function() {
console.log(`${compilationEvenetHook} done.`);
});
// 回调,插件功能完成后调用
callback();
});
}
}
// 使用
module.exports = {
plugins: [
new MyWebpackPlugin(options)
]
};
编写插件几个关键点:
必须为插件实例提供 apply 方法。
// webpack
function webpack(options, callback) {
// ...
compiler = new Compiler();
// 在初始化插件的时候是通过执行apply方法,并传入compiler对象。
for (const plugin of options.plugins) {
plugin.apply(compiler);
}
// ...
}
插件通过 compiler.plugin 注册 webpack 事件钩子 (webpacksEventHook)。
compiler.plugin 的回调可以拿到 complication 对象。
前置知识
webpack 构建流程
这里我们目前只关注在 webpack 开始正式工作之前,会初始化生成 compiler 对象,之后可以理解为充当整个 webpack 的工作环境用于 plugins/loaders。
Tapable-webpack 中的事件流机制
webpack 的本质是处理事件流,在编译过程中会依据钩子执行不同的 plugin,如何将 plugin 与钩子对应起来正是 Tapable 要干的事,核心原理是发布订阅模式。Webpack 中的 Tapable 是独立的一个工具包,可以理解为 webpack 用来挂载插件的钩子(很形象了 (Ĭ ^ Ĭ)),暴露了不同的方法(异步 / 同步)来挂载:
const {
// 同步
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
// 异步
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
简单看看 tapable 是怎么关联 webpack 和它的插件的,以上面的自定义插件为栗子:
//
compiler.hooks.emit.tap('MyWebpackPlugin', function(compilation) {
// ...
});
// 实现
// 引入tapable
const { SyncHook } = require('tapable');
// Compiler类
Class Compiler {
constructor () {
this.hooks = {
emit: new SyncHook(['arg1', 'arg2']),
};
}
}
// webpack
const compiler = new Compiler();
// 绑定同步钩子 并传参
compiler.hooks.emit.tap("MyWebpackPlugin", (arg1, arg2) =>console.log(`emit hook params: ${arg1}-${arg2}`));
// webpack中执行MyWebpackPlugin插件挂载在emit钩子的函数 同步执行
myCar.hooks.emit.call('hello', 'noaherzhang');
SyncHook 为同步钩子,通过 tap/call 挂载和同步执行,Tapable 提供了同步和异步钩子,也会有对应的方法来进行挂载和执行:
同步 | 异步 | |
---|---|---|
绑定 | tap | tapAsync/tapPromise |
执行 | call | callAsync/promise |
另外,我们自定义插件时,用到的 compiler 对象和 complication 对象都是继承自 Tapable 类,通过 apply/plugin 进行广播 / 监听事件。
// 广播事件
compiler.apply('事件名', params);
compilation.apply('事件名', params);
// 监听事件
compiler.plugin('事件名', function(params){});
compilation.plugin('事件名', function(params){});
总结:
Tapable 就是 webpack 的一个工具库,在插件绑定对应的事件到对应的 webpack 暴露的钩子上,webapck 编译过程中触发事件,随后根据不同的 Tapable 方法执行绑定的函数。
Compiler 对象 & Complication 对象
从字面理解,compiler (v.) 表示运行时 (编译),complication (n.) 表示运行后产物 (bundles)。
我们更多的是关注 webpack 通过 compiler、complication 对象暴露的钩子 (hook) 列举一些重要的,详细的参考:
compiler.hooks
钩子 | 作用 | 类型 |
---|---|---|
after-plugins | 插件初始化之后 | sync |
after-resolvers | resolvers 初始化之后 | sync |
run | 在读取记录之前 | async |
compile | 【开始编译】在创建新 compilation 之前 | sync |
compilation | compilation 创建完成 | sync |
emit | 【编译完成】在生成资源并输出到目录之前 | async |
after-emit | 在生成资源并输出到目录之后 | async |
done | 编译完成 | sync |
关于 webpack 的构建机制,个人比较推荐的文章:
同样结合 plugin-core 三要素,总结一下 webpack 插件设计:
针对插件要素做个简要分类:
- END -
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/BwXviUanebU4zxBWcetpfQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。