• antd图标库按需加载的插件实现


    前景概要

    antd是阿里出品的一款基于antd的UI组件库,使用简单,功能丰富,被广泛应用在中台项目开发中,虽然也出现了彩蛋事故,但不能否认antd本身的优秀,而我们公司在实际工作中也大量使用antd进行开发,使用的版本主要集中在3.x这个大版本中,在实际使用过程中发现了一个比较明显的问题,那就是antd的图标输出打包体积过大,即便是使用了一个图标,也会将所有图标打包输出,没有按需加载(4.x版本已经实现了按需加载,但目前公司还没做整体的升级),在这种情况下,就亟需一款能按需加载的插件来减小图标输出的体积。

    解决思路

    定位了问题,接下来就是想办法解决了,前期在网上搜索到一种解决办法,那就是在webpack中的配置图标文件路径,具体如下:

    resolve: {
      alias: {
        '@ant-design/icons/lib/dist$': './youIcon.js',
      }
    }
    

    youIcon.js中导出使用到的图标,通过这种方式能极大减小静态资源输出的体积,但是这一过程是手动配置,维护和使用不是很方便,借助这种解决方式,加上动态生成本地youIcon.js文件就可以了,确定了解决思路,接下来就动手设计插件。

    插件设计

    1.运行流程

    图片描述

    2.功能设计

    如上图所示,我们需要的插件需要满足两个功能,第一,动态提取项目源代码和使用到的antd组件中的图标,第二,修改webpack的alias配置,接下来将分别讲述设计过程:

    1.动态提取图标

    提取图标,需要对目标文件进行代码分析,提取图标代码的相关特征,然后整理输出到本地的目标文件下,过程如下图:
    图片描述
    1.) 特征匹配
    利用babel将源代码编译输出,然后得到icon的生成代码,结合astexplorer分析节点属性,确定出匹配方案,这里还需要注意一点的是,有些组件在生成图标的过程中比较特殊,比如Button可以通过loading和icon属性设置,在匹配的时候需要特殊处理;
    2.) 属性提取
    在提取图标的过程中,得到了图标的属性后,需要和本地node_modules里面的@ant-design/icon/lib/dist的所有官方导出库匹配,找到目标图标,就能确定图标的有效性;
    3.) 字符串拼接写入
    将图标名称和寻址路径拼接起来,再、在写入antd-icon-reduce.js文件之前,判断是否已经存在相同的图标名称,如果存在则放弃写入,经过上述步骤就可以将项目中用到的所有图标全部收集到antd-icon-reduce.js文件中了。

    2.动态修改配置

    动态修改配置依赖于antd-icon-reduce-plugin插件来实现,其中的工作原理如下图:
    图片描述
    1.) 初始化
    在插件初始化的时候,插件主要干了两件事,第一,生成antd-icon-reduce.js文件,然后将这个文件路径传递给antd-icon-reduce-loader,第二,添加专门匹配node_modules/antd目录下的所有js文件,确保项目中使用到的组件都能被loader处理,经过初始化之后,项目就有了存放导出的图标文件,更重要的是适配了所有可能生成图标的源文件;
    2.) 配置文件
    当loader运行完成之后,我们就得到了一份完整的图标导出文件(antd-icon-reduce.js),这个时候就需要修改webpack的alias了,这里需要在每次loader运行完成后都重新生成一份文件(内容拷贝至antd-icon-reduce.js文件),文件名需要动态更新,确保每次webpack内存加载的配置文件都是最新的;
    3.) 文件删除
    在编译输出完成后,需要清除antd-icon-reduce.js和另一份配置文件,这里都是在webpack相应的hooks里面实现,具体可以参考插件源代码。

    3.插件实现

    上面也介绍到,此次涉及到两个插件,下面分别简单讲述一下具体的编码实现:

    1.antd-icon-reduce-loader

    loader的主要实现如下:

    module.exports = function(source) {
        parseOptions.call(this);
        ......
        var ast = parser.parse(source, { sourceType: "module", plugins: ['dynamicImport'] }); // 解析源代码,获取ast对象
        traverse(ast, {
            CallExpression: function(path) { // 匹配所有调用表达式
    		    ......
                    if (isCreateIcon(Identifier)) { // Icon组件
                            var iconProps = getIconProps(ObjectExpression.properties);
                           if (Object.keys(iconProps).length > 0) {
                                var type = iconProps.type;
                                var theme = iconProps.theme || 'outline';
                                if (isArray(type)) {  // 三元符情况下,type值不止一个
                                    type.forEach(function(item) {
                                        searchIconByName(item, theme);
                                    });
                                } else {
                                    searchIconByName(type, theme);
                                }
                            }
                        } else if (isButton(Identifier)) { // Button组件
                            var btnProps = getBtnProps(ObjectExpression.properties);
                            Object.keys(btnProps).forEach(function(k) {
                                searchIconByName(k === 'loading' ? k : btnProps[k]);
                            });
                    }
            },
        });
        return core.transformFromAstSync(ast).code;
    };
    
    
    
    2.antd-icon-reduce-plugin
    
    AntdIconReducePlugin.prototype.apply = function(compiler) {
        ......
        const rules = compiler.options.module.rules;
        rules.forEach(function(ruleItem) {
            ......
                    if (ruleItem.use[i] === 'ant-icon-reduce-loader') {
                        ruleItem.use[i] = {
                            loader: loaderName,
                            options: {
                                filePath: tempFilePath, // 给loader添加临时路径配置
                            },
                        };
                        ......
                    }
           ......
        });
        // 添加专门匹配antd依赖包的loader配置
        rules.push({
            test: (filePath) => {
                if (filePath.indexOf(antdModulePath) >= 0 && path.extname(filePath) === '.js') {
                    return true;
                }
                return false;
            },
            use: [{
                loader: "antd-icon-reduce-loader",
                options: {
                    filePath: tempFilePath, 
                },
            }]
        });
     ......
    };
    
    
    

    插件使用

    1.安装依赖项

    npm i antd-icon-reduce-loader antd-icon-reduce-plugin -D
    

    2.webpack配置

    1.添加antd-icon-reduce-loader

    ......
     module: {
        rules: [
            {
                test: /.js(x)?$/,
                exclude: /node_modules/,
                use: ["antd-icon-reduce-loader", "babel-loader"],
            }
        ],
    },
    ......
    

    2.添加antd-icon-reduce-plugin插件

    ......
    var AntdIconReducePlugin = require('antd-icon-reduce-plugin');
    ......
     plugins: [
        ......
        new AntdIconReducePlugin({
                icons: ['download', { type: 'up', theme: 'outline' }], // 自定义需要加入的图标,在插件不能解析源代码的情况下使用 
    		development: true, // 开发模式下运行插件,默认true
        }),
        ......
    ]
    
    

    插件效果展示

    图片描述
    从上图中可以看到使用了插件之后,main.js体积减小了差不多500kb左右,效果还是比较明显(这只是演示插件效果,没有做其他输出优化)。

    注意事项

    • 插件只能处理使用字符串字面量来定义Icon类型,使用变量或者其他赋值方式将会被忽略,只有如下两种方式可以被识别:

    1.字符串字面量直接定义

    <Icon type="down" />
    

    2.三元符

    const isUp = true;
    ......
    <Icon type={isUp ? 'up' : 'down'}
    
    
    • 在其他未识别的情况下,需要通过插件的icons属性手动传入图标。

    最后

    该插件是基于react+antd+webpack的基础上使用,其中antd适用于3.x大版本,webpack为4.x版本,如果在使用过程中有任何问题,欢迎联系我,
    github地址:https://github.com/fuluteam/antd-icon-reduce-plugin

  • 相关阅读:
    操作系统:进程同步
    操作系统:线程的概念
    操作系统:进程的概念与控制
    操作系统:操作系统概述
    CTF-WEB:攻防世界 ics-05(preg_replace() 函数 /e 漏洞)
    《剑指 Offer》学习记录:题 11:旋转数组的最小数字
    《剑指 Offer》学习记录:题 28:对称二叉树
    Linux为什么不是硬实时
    普通线程和内核线程
    linux内核栈和用户栈
  • 原文地址:https://www.cnblogs.com/fulu/p/13255538.html
Copyright © 2020-2023  润新知