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

antd图标库按需加载的插件实现的更多相关文章
- [转] 组件库按需加载 借助babel-plugin-import实现
前段时间一直在基于webpack进行前端资源包的瘦身.在项目中基于路由进行代码分离,http://www.cnblogs.com/legu/p/7251562.html.对于公司内部的组件库,所有内容 ...
- 组件库按需加载 借助babel-plugin-import实现
前段时间一直在基于webpack进行前端资源包的瘦身.在项目中基于路由进行代码分离,http://www.cnblogs.com/legu/p/7251562.html.对于公司内部的组件库,所有内容 ...
- AngularJS中的按需加载ocLazyLoad插件应用;
一.前言 ocLoayLoad是AngularJS的模块按需加载器.一般在小型项目里,首次加载页面就下载好所有的资源没有什么大问题.但是当我们的网站渐渐庞大起来,这样子的加载策略让网速初始化速度变得越 ...
- vue3.0使用ant-design-vue进行按需加载原来这么简单
下载 ui库 yarn add ant-design-vue 默认是全局引入,打包后体积很大, 非常影响首屏加载速度, 按需加载 下载按需加载的插件;推荐使用cnpm cnpm install bab ...
- 按需加载.js .css文件
首先,理解按需加载当你需要用到某个js里面的函数什么鬼,或者某个css里的样式的时候你才开始加载这个文件. 然后是怎样实现的,简单来说就是在js中动态的createElem<script> ...
- react-antd 按需加载报错
基于create-react-app 搭建的 react 项目 引入 antd UI 配置按需加载 但是报一下错误 .翻译过了一下 是内嵌JavaScript选项没有开启什么的 大白话就是 les ...
- vue项目引用 iView 组件——全局安装与按需加载
框架的热度,出现了不少基于Vue的UI组件库,这次项目用到了 iView 这个组件库.使用方法官网很详细. 官网:https://www.iviewui.com/ 这篇文章主要是记录一下npm 全局安 ...
- 优雅的写好Vue项目代码 — 路由拆分、Vuex模块拆分、element按需加载
目录 路由的拆分 VUEX模块拆分 Element UI库按需加载的优雅写法 路由的拆分 项目较大路由较多时,路由拆分是一个不错的代码优化方案,按不同业务分为多个模块,结构清晰便于统一管理. requ ...
- 在webpack自定义配置antd的按需加载和修改主题色
最近使用antd来做react项目的UI.从antd官网上,在使用create-react-app脚手架搭建项目时步骤如下: (1)添加模块 react-app-rewired, babel-plug ...
随机推荐
- PAT 跟奥巴马一起编程
美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统.2014 年底,为庆祝“计算机科学教育周”正式启动,奥巴马编写了很简单的计算机代码:在屏幕上画一个 ...
- cacti 流量断图
问题描述 Cacti监控系统新增了一台设备,后来查询流量的时候发现流量不太对,客户跑的流量远不止8M, 下边就是记录一下问题解决的过程了. 解决过程 看到 rrdtool info 2331.rr ...
- 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件
写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...
- CSS3弹性布局内容对齐(justify-content)属性使用详解
内容对齐(justify-content)属性应用在弹性容器上,把弹性项沿着弹性容器的主轴线(main axis)对齐. 该操作发生在弹性长度以及自动边距被确定后. 它用来在存在剩余空间时如何加以分配 ...
- Winner Winner【模拟、位运算】
Winner Winner 题目链接(点击) 题目描述 The FZU Code Carnival is a programming competetion hosted by the ACM-ICP ...
- 列表、元组、字典和简单if语句【python实验1】
第一次实验报告: 学生姓名 总成绩 tom 90 jack 89 john 96 kate 86 peter 100 实验内容3-1 建立两个列表分别对学生的姓名和总成绩信息进行存储 name=['t ...
- 使用Java代码配置MyBatis Generator
使用MyBatis Generator生成器时,有时候没办法使用xml型的配置文件,比如将Maven项目设置成pom打包方式(<packaging>pom</packaging> ...
- dB是乘以10还是乘以20
dB即分贝(decibel),经常用来表示信号的大小.然而,今天在学习计算机网络的时候发现分贝的公式有两种:10lg(X) 或者 20lg(X) 很迷惑所以查找资料.得到的结果是dB在表示功率的时候用 ...
- Centos7.X 搭建Grafana+Jmeter+Influxdb 性能实时监控平台(不使用docker)
工具介绍 [centos7安装influxDB] Influxdata官网下载路径:https://portal.influxdata.com/downloads/ 1.直接执行以下命令安装 2.安装 ...
- android 中使用自定义权限
1.如果在一个进程中启动另外一个进程的activity <?xml version="1.0" encoding="utf-8"?> <man ...