为什么要实现一个babel插件:
- 有时间,有精力,写写代码,滑滑水,大家一起快乐地赚ZB主义的💰
- 面试官:自己有实现过babel的相关插件吗?我:额(⊙o⊙)…
需要具备哪些前置条件:
- babel的基本概念及使用方法
- 了解AST(抽象语法树)的基本结构
- 再具备一些能正确copy的能力
如何实现一个babel插件:
Talk is cheap, show me the code(直接上源码)
- 在项目根目录下新建comp-analysis-plugin.js
- 新增如下代码:
const chalk = require('chalk');
const fs = require('fs')
const analysisInfo = {
packageName: '',
sum: 0,
components: {},
}
// 按照引用的次数,对components进行排序(由大到小)
const sortObj = obj => Object.fromEntries(Object.entries(obj).sort(([ ,a], [ ,b]) => b - a))
module.exports = function({ types: t }) {
return {
visitor: {
ImportDeclaration: { // 通过import方式引入的
enter(path, source) {
// opts的相关配置在 .babelrc 中设置
const { opts: { packageName="" } } = source;
// 相关对象可以直接分析AST语法树的结构
if (path.node.source.value === packageName) {
console.log(chalk.green(`~~~~正在统计${packageName}的引用情况~~~~`));
analysisInfo.packageName = packageName
analysisInfo.sum += 1
// 相关组件可以在 specifiers 中找到
const { specifiers = [] } = path.node
for(let i of specifiers) {
// 此处需要取 local下 的相关字段
if(i.local) {
const { name } = i.local
analysisInfo.components[name] = analysisInfo.components[name] ? analysisInfo.components[name] + 1 : 1
}
}
// 对components进行排序
analysisInfo.components = sortObj(analysisInfo.components)
}
},
exit(path, source) {
const { opts: { packageName="" } } = source;
if (path.node.source.value === packageName) {
let str = `包名称: ${packageName}\n组件引用总次数: ${analysisInfo.sum}\n组件引用详情\n`
Object.keys(analysisInfo.components).map(function(name) {
str += `${name}: ${analysisInfo.components[name]}\n`
})
// 将 analysisInfo 相关信息写入 comp-analysis-plugin.md
const fileName = `comp-analysis-plugin.md`
// 异步写入相关文件
fs.writeFile(fileName, str, (err) => {
if(err) throw err;
})
}
}
}
},
}
}
如何在babel中引用上述插件
- 找到项目中的 .babelrc 文件(一般放在项目根目录下)
- 找到 plugins 对象,新增如下引用
此处存在一个冲突:如果你们的项目使用了ts-loader,建议用@babel/preset-typescript去代替它,因为该插件是运行在babel-loader中的,使用babel-loader和ts-loader去转化.ts、.tsx等文件,会存在问题
{
"presets": [
...
],
"plugins": [
...
["./comp-analysis-plugin", { "packageName": "antd" }] +++// 此处的引用路径根据comp-analysis-plugin.js的实际路径确定,我直接放在根目录下了;需要分析的packageName也需要根据实际需要配置
]
}
- 执行项目的打包命令
npm run build
或
yarn build
- 在项目根目录下,会新建 comp-analysis-plugin.md 文件,类似如下内容:
包名称: antd
组件引用总次数: 98
组件引用详情:
notification: 58
Row: 55
Button: 49
Modal: 49
Col: 48
Select: 44
Input: 42
Spin: 37
Icon: 27
Form: 27
Popover: 22
Radio: 21
Table: 18
Tag: 16
DatePicker: 14
InputNumber: 14
Tabs: 13
Checkbox: 9
Switch: 9
Pagination: 6
Empty: 6
Dropdown: 6
Menu: 6
TimePicker: 5
Alert: 4
Cascader: 4
Upload: 3
Divider: 3
Tooltip: 2
Layout: 2
Badge: 2
Popconfirm: 2
Card: 2
ConfigProvider: 1
Progress: 1
插件实现思路和相关源码参考自网络,在此基础上进行了二次完善,上述源码已亲测,后续会同步至Github。