一、mixin
什么是mixin:創造一種類似多重繼承的效果。事實上,說它是組合更為貼切。
1.封裝mixin方法實例:
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;
}
const BigMixin = {
????fly:()=>{
????????console.log('I can fly');
????}
}
const Big = function(){
????console.log('new big');
}
const FlyBig = mixin(Big,BigMixin); // new big
const flyBig = new FlyBig(); // I can fly?
對于廣義的mixin方法,就是用賦值的方式將mixin對象里的方法都掛載到原對象上,來實現對象的混入。
2.在React中使用mixin
React在使用createClass構建組件時提供了mixin屬性。(ES6 classes形式構建組件時,不支持mixin)
實例:
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin'; //官方封裝的mixin對象
React.creatClass({
? ? mixins:[PureRenderMixin],? ? reder(){
? ? ? ? return <div>foo</div>;
????}? ??
});
注:mixins屬性可以指定多個mixin。但,如果兩個mixin(也就是兩個對象)中有名稱相同的方法,會報命名沖突錯誤。
使用createClass實現的mixin可以為組件做兩件事:
(1)定義工具方法。用mixin混入寫好的工具方法。在需要用到工具方法的組件中設置mixin,即可使用相應工具方法。
(2)生命周期繼承,props、state的合并。如果多個mixin對象中,都定義了同一個生命周期,react會智能地將它們合并起來執行。
3.ES6 Classes 與 decorator
es6 classes語法,用decorator實現mixin。
注:decorator與Java中pre-defined annotation的區別是,decorator是運用在運行時的方法。
4.mixin存在的問題
(1)破壞了原有組件的封裝。
? ? mixin中的方法會帶來新的state和props,及其他未知操作,在使用組件中不可知,無法有目的地控制。
(2)命名沖突。
? ? 多個mixin中,或mixin與當前組件,可能存在相同命名的方法,從而命名沖突。
(3)增加復雜性。
? ? 當添加了越來越多的mixin,就會引入越來越多的方法,從而造成代碼邏輯復雜,不易維護。
二、高階組件
1.高階函數:
概念:接受函數作為輸入,或是輸出一個函數,的函數。
如常用的map、reduce、sort等,都是高階函數。
2.高階組件
概念:類似于高階函數。接受React組件作為輸入,輸出一個新的React組件。
實現方法:
(1)屬性代理:高階組件通過被包裹的React組件來操作props。
定義高階組件:
import React,{Component} from 'React';
const MyContainer = (WrappedComponent) =>
? ? class extends Component {
? ? ? ? render() {
? ? ? ? ? ? return <WrappedComponent {...this.props} />;
????????}
????}
高階組件:MyContainer
被包裹組件:WrappedComponent
{...this.props}是WrappedComponent的props對象。除了原封不動傳遞WrappedComponent的props,在高階組件中,可以設置其他props,并傳遞給WrappedComponent。例如:
import React,{Component} from 'React';
const MyContainer = (WrappedComponent) =>? ?
????class extends Component {? ? ? ?
????????render() {?
? ? ? ? ? ? ?const newProps = {
? ? ? ? ? ? ? ? ?text:newText,? ? ? ?
? ? ? ? ? ? ?};? ? ? ? ?
????????????return ?<WrappedComponent {...this.props} {...newProps} />;? ??
?//注:this.props讀取的是,調用WrappedComponent時傳入的props。注意{...this.props}和{...newProps}書寫的先后順序。如果this.props和newProps中有相同的prop,后面的會覆蓋前面的。
?????}???
?}
對于WrappedComponent來說,只要套用這個高階組件,我們的新組件中就會多一個text的prop。
使用高階組件:
import React,{Component} from 'React';
class MyComponent extends Component{
? ? //......
}
export default MyContainer(MyComponent);
或
import React,{Component} from 'React';
@MyContainer
class MyComponent extends Component{? ?
? ? render(){ }
}
export default?MyComponent;
生命周期執行過程(類似于堆棧調用):
didmount -> HOC didmount -> (HOCs didmount) ->?
(HOCs will unmount) -> HOC will unmount -> unmount
(2)反向繼承:高階組件繼承于被包裹的React組件。
定義高階組件:
const MyContainer = (WrappedComponent) =>
? ? class extends WrappedComponent {
? ? ? ? render(){
? ? ? ? ? ? return super.render();
????????}
? ? }
HOC調用順序(類似于隊列):
didmount -> HOC didmount => (HOCs didmount) ->?
will unmount -> HOC will unmount -> (HOCs will unmount)
渲染劫持示例:
NO1:條件渲染
const MyContainer = (WrappedComponent) =>
????class extends WrappedComponent {
? ? ? ? render(){
? ? ? ? ? ? if(this.props.loggedIn){
? ? ? ? ? ? ? ? return super.render();
????????????}else{
? ? ? ? ? ? ? ? return null;
????????????}
????????}
????}
NO2:修改render輸出結果
const MyContainer = (WrappedComponent) =>
? ? class extends WrappedComponent {
? ? ? ? render(){
? ? ? ? ? ? const elementsTree = super.render();
? ? ? ? ? ? let newProps = {};
? ? ? ? ? ? if(elementsTree && elementsTree.type === 'input'){
? ? ? ? ? ? ? ? newProps = {value:'may the force be with you'};
????????????}
? ? ? ? ? ? const props = Object.assign({},elementsTree.props,newProps);
? ? ? ? ? ? const newElementsTree = ????????????????React.cloneElement(elementsTree,props,elementsTree.props.children);
? ? ? ? ? ? return newElementsTree;
????????}
????}