这周,产品提了个需求,其中包含一个多选的级联选择框,还给我截了个图,(信心满满)说:“我们项目都是用AntD组件,直接用这个组件就成,还不用额外开发。省事儿”。
然而,她不知道的是,我们用的AntD3,截图的是AntD4,而AntD3版本是有级联但是没多选的。
此时,我计上心来,npm
使用别名同时安装两个版本,直接用不就成事儿了吗!
AntD3正常安装:
npm install [email protected]
AntD4别名安装:
npm install antd4@npm:[email protected]
业务代码中使用:
import { Input, Select } from 'antd'
import { Cascader } from 'antd4'
同时安装两个版本后,遇到了第一个问题,Webpack打包出错了,AntD3和AntD4用了相同的依赖,但大版本不同,而npm平铺式的依赖安装,让这同名不同版本的依赖更加混乱了。那么有没有一种从天而降的掌法,可以让二级依赖互相隔离呢?
当然是有的,这个掌法就是_pnpm_
此处贴一个pnpm官网的pnpm与npm对比:pnpm.io/zh/pnpm-vs-…
于是,项目使用pnpm代替了npm,解决二级依赖混乱的问题。
果然,开发从来没有一帆风顺。
此时,遇到了第二个问题,AntD4组件样式没有引入,而全局引antd4/dist/antd.css
的话显然不合适。
很不凑巧的,我们项目本地开发用的Vite,线上打包用的Webpack。
又是一套组合拳:
plugins: [
// 引入AntD3样式
[
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
},
],
// 引入AntD4样式
[
'import',
{
libraryName: 'antd4',
libraryDirectory: 'es',
style: true,
},
'import-antd4', // 用了两次 babel-plugin-import,加个key,防止babel报错
]
]
vite-plugin-style-import
额外引入antd4组件样式 import { createStyleImportPlugin, AntdResolve } from 'vite-plugin-style-import'
{
plugins: [
createStyleImportPlugin({
// 引入AntD3样式
resolves: [AntdResolve()],
libs: [
// 引入AntD4样式
{
libraryName: 'antd4',
esModule: true,
resolveStyle: (name) => {
// vite开发模式下,此处路径需要额外加node_modules,很奇怪,暂时没有深究原因
return `node_modules/antd4/es/${name}/style/index`
},
},
],
})
]
}
配置完成,解决了样式引入问题。
搓了搓手,又开始磨刀霍霍了。
很不幸,第三个问题来了,点击Cascader
下拉框不弹出。。。
开控制台检查一下Elements,发现是AntD3和AntD4的样式重名,3把4的样式覆盖了,而Cascader
组件下拉框写法改了,导致不弹出。
从下图可以看到,同一类名的样式有两份。
接下来,开始思考怎么实现样式隔离。。。
查一下官方文档,ConfigProvider
的prefixCls
属性正合我意。通过修改类名通用前缀实现样式隔离。
Property | Description | Type | Default |
---|---|---|---|
prefixCls | Set prefix className (cooperated with @ant-prefix) | string | ant |
import { ConfigProvider as AntD3ConfigProvider } from 'antd'
import { ConfigProvider as AntD4ConfigProvider } from 'antd4'
// ...
lessOption: {
modifyVars: {
'ant-prefix': 'ant4'
},
javascriptEnabled: true,
sourceMap: false,
}
此处的lessOption传入给Webpack的less-loader
的options
或Vite的css.preprocessorOptions.less
改完一跑,结果发现HTML tag上的类名成功改成了ant4-
开头,这很好。但灾难发生了,AntD3 / AntD4所有的CSS内容都变成了ant4-
开头,结果就是整个页面使用的AntD3组件完全没有样式。
初始状态:
版本 | HTML class | CSS class |
---|---|---|
AntD3 | ant-xxx | ant-xxx |
AntD4 | ant-xxx | ant-xxx |
我想要的:
版本 | HTML class | CSS class |
---|---|---|
AntD3 | ant-xxx | ant-xxx |
AntD4 | ant4-xxx | ant4-xxx |
实际结果:
版本 | HTML class | CSS class |
---|---|---|
AntD3 | ant-xxx | ant4-xxx |
AntD4 | ant4-xxx | ant4-xxx |
less的modifyVars
配置不分文件,把所有@ant-prefix
值全改了。显然,这个配置失败了。
想用个组件真是太难了。
接下来,就是实现less按需的modifyVars
功能了。
先在less官网一顿找,发现less支持plugins lesscss.org/tools/#plug… ,顿时眼前一亮,内心毫无波澜,写个插件不就结了?
接着,在官网逛了一圈没发现写plugin的文档,文档真差。
不气馁,又跑GitHub搜一下less的文档。
先看看plugins.md
没有任何发现,再看看api.md会不会有宝藏。
Coming soon 这是什么个情况!!!
看来要拿出最后的大招,撸源码了,官方给出了一些已有插件的github地址,一个一个点开看,突然发现 less-plugin-sass2less代码中国呢写到的pluginManager.addPreProcessor
有点像我要的。接着又到less源码中搜 addPreProcessor
的用法。
less 源码部分:
/**
* Adds a pre processor object
* @param {object} preProcessor
* @param {number} priority - guidelines 1 = before import, 1000 = import, 2000 = after import
*/
PluginManager.prototype.addPreProcessor = function(preProcessor, priority) {
var indexToInsertAt;
for (indexToInsertAt = 0; indexToInsertAt < this.preProcessors.length; indexToInsertAt++) {
if (this.preProcessors[indexToInsertAt].priority >= priority) {
break;
}
}
this.preProcessors.splice(indexToInsertAt, 0, {preProcessor: preProcessor, priority: priority});
};
addPreProcessor
支持传入 preProcessor
预处理器 和 priority
优先级两个参数。
找到addPreProcessor
后又去找preProcessors
在哪里调用。 全局搜preProcessors
关键字
// ....
if (context.pluginManager) {
var preProcessors = context.pluginManager.getPreProcessors();
for (var i = 0; i < preProcessors.length; i++) {
str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo });
}
}
// ...
每一个preProcessor
接受less源码字符串str
,和对应的文件信息{ context: context, imports: imports, fileInfo: fileInfo }
,返回修改后的str
。
一阵感叹,苦心人天不负啊!!!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VAYk1uPV-1652945548529)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b1bdf5dbc88f4d1c929df5233dddd1a0~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
接下来可以撸一个less插件了。过程不表,直接上源码。此插件支持通过不同pathReg
来限定对应modifyVars
的生效范围。
npm地址:www.npmjs.com/package/les…
GitHub地址:less-plugin-scope-modifyvar
pnpm install -D less-plugin-scope-modifyvar
去掉之前的全局modifyVars
配置,改在less-plugin-scope-modifyvar
插件内配置。
const LessPluginScopeModifyVar = require('less-plugin-scope-modifyvar')
lessOption: {
javascriptEnabled: true,
sourceMap: false,
plugins: [
LessPluginScopeModifyVar([
{
// pnpm安装后的AntD4对应的全局变量文件路径,AntD的变量都在default|variable这俩文件里
// 嫌麻烦直接 pathReg: /antd@4.*/ 也行,就是处理耗时会增加
pathReg: /antd@4.*/antd/es/style/themes/(default|variable)/,
modifyVars: {
'ant-prefix': 'ant4'
}
},
]),
]
}
至此,项目就完成了AntD3与AntD4组件混用的升级。
pnpm
实现二级依赖隔离babel-plugin-import
(Webpack) 或vite-plugin-style-import
(Vite)实现组件样式按需引入ConfigProvider
组件的prefixCls
属性实现HTML tag 类名隔离less-plugin-scope-modifyvar
less插件实现CSS 类名隔离