@H_403_1@在React组件的构建过程中,常常有这样的场景,有一类功能需要被不同的组件公用。此时就涉及到了抽象的话题。
@H_403_1@下面我们重点讨论:mixin和高阶组件。
1、工具方法:这是mixin的基本功能,如果你希望共享一些工具类的方法,就可以直接定义它们然后在组件中使用。
2、生命周期继承,props和state合并。mixin能够合并生命周期方法。如果有很多mixin来定义componentDidMount这个周期,那么React会很机智的将它们都合并起来执行。同样,mixin也可以作state和props的合并。 @H_403_1@ES6 Classes和decorator @H_403_1@然而,当我们使用ES6 classes的形式构建组件的时候,却并不支持mixin。为了使用这个强大的功能,我们还需要采取其他方法,来达到模块重用的目的。 @H_403_1@可以使用ES7的语法糖decorator来实现class上的mixin。 @H_403_1@core-decorators库为开发者提供了一些实用的decorator,其中也正好实现了我们想要的@mixin。 @H_403_1@注:decorator的知识将在下一篇博客中给出。 @H_403_1@使用@mixin的代码:
1、属性代理:高阶组件通过被包裹的React组件来操作props。
2、反向继承:高阶组件继承于被包裹的React组件。 @H_403_1@属性代理 @H_403_1@示例代码:
@H_403_1@高阶组件符合函数式编程思想。对于原组件来说,并不会感知到高阶组件的存在。只需要把功能套在它之上就可以了。避免了mixin的副作用。 @H_403_1@反向继承
原文链接:https://www.f2er.com/react/303702.htmlmixin
@H_403_1@mixin 的目的,就是为了创造一种类似多重继承的效果,或者说,组合。 @H_403_1@实际上,包括C++等一些年龄较大的OOP语言,都有一个强大但是危险的多重继承特性。现代语言权衡利弊,大都舍弃了它,只采用单继承。但是单继承在实现抽象的时候有很多不便,为了弥补缺失,Java引入接口(interface),其他一些语言则引入了mixin的技巧。 @H_403_1@封装mixin方法 @H_403_1@方法:const mixin = function(obj,mixins) {
const newObj = obj;
newObj.prototype = Object.create(obj.prototype);
for (let prop in mixins) {
if (mixins.hasOwnProperty(prop)) {
newObj.prototype[prop] = mixins[prop];
}
}
return newObj;
}
@H_403_1@应用:
const BigMixin = {
fly: () => {
console.log('I can fly');
}
};
const Big = function() {
console.log('new big');
};
const FlyBig = mixin(Big,BigMixin);
const flyBig = new FlyBig(); // => 'new big'
flyBig.fly(); // => 'I can fly'
@H_403_1@上面这段代码实现对象混入的方法是:用赋值的方式将mixin对象里的方法都挂载到原对象上。
@H_403_1@在React中使用mixin
@H_403_1@React在使用createClass构建组件时提供了mixin属性,比如官方封装的:PureRenderMixin.
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
React.createClass({
mixins: [PureRenderMixin],render() {
return <div>foo</div>;
}
});
@H_403_1@在createClass对象参数中传入数组mixins,里面封装了我们需要的模块。mixins数组也可以添加多个mixin。同时,在React中不允许出现重名普通方法的mixin。而如果是生命周期方法,则React将会将各个模块的生命周期方法叠加在一起然后顺序执行。
@H_403_1@使用createClass实现的mixin为组件做了两件事: 1、工具方法:这是mixin的基本功能,如果你希望共享一些工具类的方法,就可以直接定义它们然后在组件中使用。
2、生命周期继承,props和state合并。mixin能够合并生命周期方法。如果有很多mixin来定义componentDidMount这个周期,那么React会很机智的将它们都合并起来执行。同样,mixin也可以作state和props的合并。 @H_403_1@ES6 Classes和decorator @H_403_1@然而,当我们使用ES6 classes的形式构建组件的时候,却并不支持mixin。为了使用这个强大的功能,我们还需要采取其他方法,来达到模块重用的目的。 @H_403_1@可以使用ES7的语法糖decorator来实现class上的mixin。 @H_403_1@core-decorators库为开发者提供了一些实用的decorator,其中也正好实现了我们想要的@mixin。 @H_403_1@注:decorator的知识将在下一篇博客中给出。 @H_403_1@使用@mixin的代码:
import React,{ Component } from 'React';
import { mixin } from 'core-decorators';
const PureRender = {
shouldComponentUpdate() {}
};
const Theme = {
setTheme() {}
};
@mixin(PureRender,Theme)
class MyComponent extends Component {
render() {}
}
@H_403_1@mixin的问题
@H_403_1@1、破坏了原有组件的封装:mixin会混入方法,给原有的组件带来新特性。但同时它也可能带来新的state和props,这意味着组件有一些“不可见”的状态需要我们去维护。另外,mixin也有可能去依赖其他的mixin,这样会建立一个mixin的依赖链,当我们改动一个mixin的状态,很有可能也会影响其他的mixin。
@H_403_1@2、命名冲突
@H_403_1@3、增加复杂性
@H_403_1@针对这些困扰,React提出的新的方式来取代mixin,那就是高阶组件。
高阶组件
@H_403_1@如果已经理解高阶函数,那么理解高阶组件也很容易的。 @H_403_1@高阶函数:就是一种这样的函数,它接受函数作为参数输入,或者将一个函数作为返回值。例如我们常见的方法map,reduce,sort等都是高阶函数。 @H_403_1@高阶组件和和高阶函数很类似,高阶组件就是接受一个React组件作为参数输入,输出一个新的React组件。 @H_403_1@高阶组件让我们的代码更具有复用性、逻辑性与抽象性,它可以对render方法作劫持,也可以控制props和state。 @H_403_1@实现高阶组件的方法有如下两种:1、属性代理:高阶组件通过被包裹的React组件来操作props。
2、反向继承:高阶组件继承于被包裹的React组件。 @H_403_1@属性代理 @H_403_1@示例代码:
import React,{ Component } from 'React';
const MyContainer = (WrappedComponent) =>
class extends Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
@H_403_1@在代码中我们可以看到,render方法返回了传入的WrappedComponent组件。这样,我们就可以通过高阶组件来传递props。这种方式就是属性代理。
@H_403_1@如何使用上面这个高阶组件:
import React,{ Component } from 'React';
class MyComponent extends Component {
// ...
}
export default MyContainer(MyComponent);
@H_403_1@这样组件就可以一层层的作为参数被调用,原始组件久具备了高阶组件对它的修饰。这样,保持单个组件封装的同时也保留了易用行。
@H_403_1@从功能上, 高阶组件一样可以做到像mixin对组件的控制:
@H_403_1@1、控制props
@H_403_1@我们可以读取、增加、编辑或是移除从WrappedComponent传进来的props。
@H_403_1@例如:新增props
import React,{ Component } from 'React';
const MyContainer = (WrappedComponent) =>
class extends Component {
render() {
const newProps = { text: newText,};
return <WrappedComponent {...this.props} {...newProps} />;
}
}
@H_403_1@注意:
<WrappedComponent {...this.props}/>
// is equivalent to
React.createElement(WrappedComponent,this.props,null)
@H_403_1@这样,当调用高阶组件的时候,就可以使用text这个新的props了。
@H_403_1@2、通过refs使用引用
@H_403_1@3、抽象state
@H_403_1@高阶组件可以讲原组件抽象为展示型组件,分离内部状态。
const MyContainer = (WrappedComponent) =>
class extends Component {
constructor(props) {
super(props);
this.state = { name: '',4 };
this.onNameChange = this.onNameChange.bind(this);
}
onNameChange(event) {
this.setState({
name: event.target.value,})
}
render() {
const newProps = {
name: {
value: this.state.name,onChange: this.onNameChange,},}
return <WrappedComponent {...this.props} {...newProps} />;
}
}
@H_403_1@在这个栗子中,我们把组件中对name prop 的onChange 方法提取到高阶组件中,这样就有效的抽象了同样的state操作。
@H_403_1@使用方式:
@MyContainer
class MyComponent extends Component {
render() {
return <input name="name" {...this.props.name} />;
}
}
@H_403_1@高阶组件和mixin的不同: @H_403_1@高阶组件符合函数式编程思想。对于原组件来说,并不会感知到高阶组件的存在。只需要把功能套在它之上就可以了。避免了mixin的副作用。 @H_403_1@反向继承
const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
return super.render();
}
}
@H_403_1@高阶组件返回的组件继承于WrappedComponent。
@H_403_1@(待续)