前言
想知道项目中使用的三方组件库的数据吗?来吧,本文带你了解如何统计项目中组件使用情况。
作用:统计项目中组件被使用的数据,对高频组件进行统计(方便后期有针对性迭代)
思路:Webpack loader(AST)。
最终效果
比如一个使用了 Ant Design 的项目启动后,可以得出组件使用情况统计:
如何实现
我们知道 loader
的 source
是文件的静态字符串如下图:
最快的方案通过字符串统计用正则的方式一把梭,但是这样会有问题就是如果注释部分有的话也会被统计进去就不准确,所以想到了可以通过 AST
(抽象语法树) 的方式来实现,关于 AST
的概念有很多大佬都讲过了我就不啰嗦了。
分析 AST
我这边是通过 @babel/parser
来分析的,我们先看下面这段代码在网站上的构成。
import { Box } from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
我们可以看出路径 program => body
,然后声明类型是 type: ImportDeclaration,继续看如下构成
"source": {
"type": "StringLiteral",
"value": "@material-ui/core"
},
// 第二段
"source": {
type": "StringLiteral",
"value": "@material-ui/lab/Autocomplete"
},
我们发下这个字段里面的 value 有我们想要的包名所以第一段代码就是
const ast = parser.parse(source, {
sourceType: 'module',
plugins: ['jsx'],
});
const getImport = 'ImportDeclaration';
const getMaterialImport = packageName || '@material-ui';
const importAst = ast.program.body.filter(
// type 节点类型,这里我们去过滤 import 声明类型 同时去过滤
(i) => i.type === getImport && i.source.value.includes(getMaterialImport),
);
拿到相关的 ast 数组下一步就要去拿到组件名字了, 通过观察我们发现 specifiers 标识符这个字段里面有两个字段包含组件名:imported、local。
- imported 表示从导出模块导出的变量
- local 表示导入后当前模块的变量
这里我取的 local, 因为下面这种方式并不会出现 imported 这个字段。
import Autocomplete from '@material-ui/lab/Autocomplete';
这个时候包的名字也拿到了后面就简单了直奔主题贴完整代码了。
demo
const parser = require('@babel/parser');
const loaderUtils = require('loader-utils');
const total = {
len: 0,
components: {},
};
// 对象排序
const sortable = (obj) => Object.fromEntries(Object.entries(obj).sort(([, a], [, b]) => b - a));
module.exports = function(source) {
console.log(source, '--');
const options = loaderUtils.getOptions(this);
const { packageName = '' } = options;
const callback = this.async();
if (!packageName) return callback(null, source);
try {
// 解析成 ast
const ast = parser.parse(source, {
sourceType: 'module',
plugins: ['jsx'],
});
if (ast) {
setTimeout(() => {
const getImport = 'ImportDeclaration';
const getMaterialImport = packageName;
const importAst = ast.program.body.filter(
// type 节点类型,这里我们去过滤 import 声明类型 同时去过滤
(i) => i.type === getImport && i.source.value.includes(getMaterialImport),
);
total.len = total.len + importAst.length;
for (let i of importAst) {
const { specifiers = [] } = i;
for (let s of specifiers) {
if (s.local) {
const { name } = s.local;
total.components[name] = total.components[name] ? total.components[name] + 1 : 1;
}
}
}
total.components = sortable(total.components);
console.log(total, 'total');
callback(null, source);
}, 0);
} else callback(null, source);
} catch (error) {
callback(null, source);
}
};
调用 loader
{
test: /\.(jsx|)$/,
exclude: /node_modules/,
include: [appConfig.eslintEntry],
use: [
{
loader: path.resolve(__dirname, './loader/total.js'),
options: {
packageName: '@material-ui',
},
},
],
},
plugin 的方式
引入
[
vincePlugin,
{
libraryName: '@material-ui',
},
],
Plugin 代码
const chalk = require('chalk');
const total = {
len: 0,
components: {},
};
let cache = -1;
let task = setInterval(() => {
console.log(chalk.green('~~~~统计中~~~~'));
if (total.len !== cache) {
cache = total.len;
} else {
clearInterval(task);
console.log(total);
}
}, 1000);
const sortable = (obj) => Object.fromEntries(Object.entries(obj).sort(([, a], [, b]) => b - a));
module.exports = function(babel) {
var t = babel.types;
return {
visitor: {
ImportDeclaration(path, source) {
const {
opts: { libraryName = '@material-ui' },
} = source;
if (path.node.source.value.includes(libraryName)) {
const { specifiers = [] } = path.node;
total.len = total.len + 1;
for (let s of specifiers) {
if (s.local) {
const { name } = s.local;
total.components[name] = total.components[name] ? total.components[name] + 1 : 1;
}
}
total.components = sortable(total.components);
}
},
},
};
};
一个简单的统计功能就完成了,当然可能还有其他更好的方式我只是提供这个想法,抛砖引玉,欢迎大家前来过来讨论。
最后
通过该插件,我们可以对自己的组件库上线以后进行数据统计,并且以某个时间维度来统计。用数据来告诉你,当前组件被使用频次,以便确定将来的优化方向。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!