最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 50行代码统计组件使用次数

    正文概述 掘金(Allan91)   2021-04-08   478

    前言

    想知道项目中使用的三方组件库的数据吗?来吧,本文带你了解如何统计项目中组件使用情况。

    作用:统计项目中组件被使用的数据,对高频组件进行统计(方便后期有针对性迭代)

    思路:Webpack loader(AST)。

    最终效果

    比如一个使用了 Ant Design 的项目启动后,可以得出组件使用情况统计:

    50行代码统计组件使用次数

    如何实现

    我们知道 loadersource 是文件的静态字符串如下图:

    50行代码统计组件使用次数

    最快的方案通过字符串统计用正则的方式一把梭,但是这样会有问题就是如果注释部分有的话也会被统计进去就不准确,所以想到了可以通过 AST(抽象语法树) 的方式来实现,关于 AST 的概念有很多大佬都讲过了我就不啰嗦了。

    分析 AST

    我这边是通过 @babel/parser 来分析的,我们先看下面这段代码在网站上的构成。

    import { Box } from '@material-ui/core';
    import Autocomplete from '@material-ui/lab/Autocomplete';
    

    50行代码统计组件使用次数

    我们可以看出路径 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 标识符这个字段里面有两个字段包含组件名:importedlocal

    50行代码统计组件使用次数

    • 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);
                    }
                },
            },
        };
    };
    
    

    一个简单的统计功能就完成了,当然可能还有其他更好的方式我只是提供这个想法,抛砖引玉,欢迎大家前来过来讨论。

    最后

    通过该插件,我们可以对自己的组件库上线以后进行数据统计,并且以某个时间维度来统计。用数据来告诉你,当前组件被使用频次,以便确定将来的优化方向。


    起源地下载网 » 50行代码统计组件使用次数

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元