业务页面开发过程中,难免会使用到第三方组件,如 antd 或者自己团队提供的第三方组件。那么业务开发同学遇到需要与第三方组件库不一致的页面元素时,应该如何做呢?
首先我们划分一下组件覆写的类型,遇到 UI 不一致的地方,会牵涉到样式覆写;遇到一些交互设计不同的地方,牵涉到源码的改动,如 DatePicker 不支持周选择,需要修改支持,这类覆写为交互复写。
样式覆写
以 antd 为例,样式覆写分为两种方式:一个是全局的样式 less 变量修改,一个是外部样式覆盖。
less 变量修改
通过 less.modifyVars 配置来进行 less 变量的覆写
// webpack.config.js
module.exports = {
rules: [{
test: /.less$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader', // translates CSS into CommonJS
}, {
loader: 'less-loader', // compiles Less to CSS
+ options: {
+ lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
+ modifyVars: {
+ 'primary-color': '#1DA57A',
+ 'link-color': '#1DA57A',
+ 'border-radius-base': '2px',
+ },
+ javascriptEnabled: true,
+ },
+ },
}],
// ...other rules
}],
// ...other config
}
这种方式修改样式比较直接快捷,但是也有其中自己的局限性,如存在某些组件样式属性没有被抽离成 less 变量,我们应该如何处理呢?我们可以用外部样式覆盖的方式书写
外部样式覆写
外部样式覆写一般又有两种方式:
- 项目封装组件
my-button/index.js
const MyButton = ({ className, ...props }) => {
return <Button className={c('my-button', className)} {...props} />
}
my-button/style.less
.my-button {
.ant-button {
// 样式覆盖在这
}
}
2. 直接全局覆写样式
common.less
.ant-button {
// 样式覆盖在这
}
个人更喜欢 项目封装组件 方式,这样可以更有利于组件独立,提高组件复用性,降低耦合。
总体来说,样式覆写还是比较简单直接的,不涉及到内部逻辑的修改,但是如果牵涉到交互覆写的话,很可能会需要修改内部逻辑
交互覆写
依旧以 DatePicker 为例,第三方 DatePicker 已经支持日,月,年选择器,但是不支持 周 选择。
这种情况,可能有同学倾向自己 copy 第三方源码,然后再自己单独修改内部逻辑,这样就很容易导致多人协同开发的项目中,其他人使用的是 第三方 DatePicker,而你用的是 MyDatePicker,不仅破坏 DRY 原则,加大理解成本,而且 Webpack 打包会重复打包两份 DatePicker,加大 Bunder Size
以我个人来说,我比较倾向以下方式,使用继承模式来覆写内部逻辑:
// 注意!这里继承的是 DatePicker
class ExtendDatePicker extends DatePicker {
static propTypes = {
...DatePicker.propTypes,
// 扩展 PropType
mode: PropTypes.oneOf(['day', 'week', 'month', 'year'])
}
someDatePickerMethod(...args) {
// 可以随意注入自己的扩展逻辑
super.someDatePickerMethod(...args)
}
}
const MyDatePicker = React.forwardRef(({ className, ...props }, ref) => {
return <ExtendDatePicker ref={ref} className={c('my-button', className)} {...props} />
})
优点:这种方式能比较快捷的修改组件内部逻辑,而且尽量少的打包重复代码
但是这种方式也有缺陷:
- 对于开发人员能力需要门槛
- 同时适合于比较稳定的第三方组件,第三方组件迭代频繁的话,很容易导致组件内部逻辑修改,导致这种方式失效
- 需要理解第三方组件的内部逻辑
- 不适合于 Function Component
而且对于绑定在实例上的方法,覆写的方式也需要注意,如下:
// 第三方的 DatePicker
class DatePicker extends Component {
// 该方法绑定在实例上
bindedMethod = () => {
}
// 该方法绑定在原型上
protoMethod() {}
}
class ExtendedDatePicker extends DatePicker {
// 这样写是不能覆写 第三方 DatePicker 方法的
bindedMethod() {}
constructor(props, context) {
super(props, context)
const originMethod = this.bindedMethod
this.bindedMethod = () => {
// 这样才可以覆写
originMethod()
}
}
}
以上便是本人经常使用的组件覆写方式,欢迎讨论
最后!自家团队找人啦,欢迎投递简历
儿葱:美团到店技术 招收 高级 前端/Android/IOS 开发工程师/负责人zhuanlan.zhihu.com