React组件抽象(一)-Mixin
写业务组件的时候常常会有一些功能反复被不同的组件公用,这时候就需要取出相同部分功能,这个过程涉及组件抽象。抽象方式有许多种,如高阶组件(High-order components)
和mixin
。
一、MIxin是什么,为什么要使用Mixin
一些古老的面向对象的语言,如C++,有个很强大但是很危险的功能:多重继承
。
多重继承
举个例子:
有个程序员写了一本书,那我们可以称这位程序员为作家。
以下例子使用类似JavaScript的语法,因为C++我已经忘记了。
class Person{
name = "人类"
writing(){
// 用笔写字
}
}
class Writer extends Person{
name = "作家"
writing(){
// 写文章
}
}
class Coder extends Person{
name = "程序员"
writing(){
//写代码
}
}
张三恰好是程序员作家,于是继承了两个类
class ZhangSan extends Writer,Coder{
}
那么问题来了,当张三writing的时候写代码还是写文章?这就是弊端二义性
。如果类数量多了,那将会很难受。当然处理方法是有,主要是麻烦。Java等语言为了规避这么麻烦的事情又要使用继承,出了interface
接口。
继承接口
Java允许继承多个接口,如
public class ZhangSan extends Person implements WriterInterface,CoderInterface{
}
接口内部不会有实现,子类必须实现interface的方法除非是Abstract
。这样就防止了二义性的发生。
然而,其他一些语言引入了其他的方式:mixin
mixin
通过改变prototype的一些属性来给组件注入新的能力。
直接直接举个栗子吧
const mixin = (clazz,plugins) =>{
Object.entires(clazz).forEach(plugin =>{
const [key,value] = plugin;
clazz.prototype[key] = value;
})
return clazz;
}
class Zhangsan {
}
const mixins = {
writing(){
// 写作
}
}
const WriterZhangSan = mixin(Zhangsan,mixin);
// 是不是很像
//const WriterZhangSan = Object.assign(ZhangSan.prototype,mixin)
二、怎么在React中使用
React.createClass
官方预留接口
React.createClass({
mixins:[mixins],
render(){
return <div>Hello World</div>
}
})
注意:
- 多个mixin如果有相同name的实现会报错,并不会后面的覆盖前面的。
- 定义生命周期如
componentDidMount
的mixin,并不会覆盖组件方法,而是两个方法都会执行
import createClass from "create-react-class"
const MixinDemo = createClass({
mixins: [{
componentDidMount() {
console.log("mixin")
}
}],
componentDidMount(){
console.log("Demo")
},
render() {
return <div>123</div>
}
})
// minxin
// Demo
ES6 class & ES7 decorator :类语修饰器
const mixin = (...mixins) => {
return target =>{
mixins.forEach(mixin =>{
Object.assign(target.prototype,mixin)
})
return target;
}
}
const didMountSayHello = {
componentDidMount(){
console.log("Hello World")
}
}
@mixin(didMountSayHello)
class MixinDemo extends React.Component{
render(){
return <div>2333</div>
}
}
// Helloworld
举个贴近生产的例子:
每个组件需要国际化语言处理
const languageText = {
'en_US':user:{
userName:"User Name"
},
'zh_CN':user:{
userName:"用户名"
}
}
const injectLanguage = (nameSpace) =>{
return targetComponent =>{
targetComponent.prototype.getLanguage = function(){
return "en_US"
}
targetComponent.prototype.getText = function(){
const currentLanguage = this.getLanguage();
return languageText[currentLanguage]
}
return targetComponent;
}
}
@languageText("user")
class User extends React.Component{
render(){
return <div>{this.props.getText().userName}:<input/></div>
}
}
三、Mixin的缺点
墙倒众人推,破鼓万人捶。
社区开始不待见这个方式,导致成了“反模式”
Daniel Avramov 发现并提出问题:
- 破坏原组件的封装
mixin给组件注入新功能的时候,也可能导致加入一些隐藏的状态,维护组件的人难免会踩坑;
- 命名冲突
引用多个mixin的时候难免会有作者取名雷同,这时候就要花时间去解决这种冲突。
- 增加复杂性
引入多个mixin的时候尽管你能规避命名和隐藏的功能,但是几个月后或者其他人来看却是难以理解。一个组件内居然实现了这么多功能。
四、替代品
mixin被打入冷宫称为"反模式",必定会有"正模式"来替代它,没错,他就是官方推荐的高阶组件
。
我们下期再见!