虽然组件的原则就是模块化,彼此之间相互独立,但是有时候不同的组件之间可能会共用一些功能,共享一部分代码。所以 React 提供了 mixins 这种方式来处理这种问题。
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = '';
},
setInterval: function(fn,time) {
this.intervals = setInterval(fn,time)
},
componentWillUnmount: function() {
clearTimeinterval(this.intervals)
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0,
};
},
componentDidMount: function() {
this.setInterval(this.tick.bind(this), 1000); // Call a method on the mixin
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
上述是用es5语法写的mixin,每个组件都可以引入这个mixin在每个生命周期中调用。
Mixin 的特性一直广泛存在于各种面向对象语言。尤其在脚本语言中大都有原生支持,比如 Perl、Ruby、Python,甚至连 Sass 也支持。先来看一个在 Ruby 中使用 Mixin 的简单例子(装个逼好吗。。)
module D
def initialize(name)
@name = name
end
def to_s
@name
end
end
module Debug
include D
def who_am_i?
"#{self.class.name} (\##{self.object_id}): #{self.to_s}"
end
end
class Phonograph
include Debug
# ...
end
class EightTrack
include Debug
# ...
end
ph = Phonograph.new("West End Blues")
et = EightTrack.new("Real Pillow")
puts ph.who_am_i? # Phonograph (#-72640448): West End Blues
puts et.who_am_i? # EightTrack (#-72640468): Real Pillow
一层套一层,类型方法的公用。
写过css的同学都知道,sass这种预编译的语言也可以写出mixins。
// triangle
@mixin triangle($direction, $size, $borderColor )
{ content:""; height: 0; width: 0;
@if $direction == top {
border-bottom:$size solid $borderColor;
border-left:$size dashed transparent;
border-right:$size dashed transparent; }
@else if $direction == right {
border-left:$size solid $borderColor;
border-top:$size dashed transparent;
border-bottom:$size dashed transparent;
} @else if $direction == bottom {
border-top:$size solid $borderColor;
border-left:$size dashed transparent;
border-right:$size dashed transparent;
} @else if $direction == left {
border-right:$size solid $borderColor;
border-top:$size dashed transparent;
border-bottom:$size dashed transparent; }
}
定义了一个实现三角形的mixins,参数依次为尖角朝向,大小,边缘颜色。
如何使用呢
.div{@include triangle}
封装一个mixins函数
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');
};
Big.prototype.say = function(){
console.log('hello')
}
const FlyBig = mixin(Big, BigMixin);
const flyBig = new FlyBig(); // 'new big'
flyBig.fly(); // 'I can fly'
flyBig.say() // 'hello'
如果对create这种写法不太了解的,可以点这里.
我么来再来写一个稍微复杂的mixin
var anotherMixin = {
componentWillUpdate:function(newPro,newState){
this.refs.input.value=''
this.refs.input.focus()
console.log(newState)
}
change:function(e){
if(e.keyCode == 13 && e.target.value != ''){
! localStorage.getITem('list') ? localStorage.setItem('list',[e.value]) :
localStorage.getItem('list').indexOf(e.value) == -1 ?
this.setState({todos:this.state.todos.push(e.target.value),
count:this.state.count++}) : ''
}
setInterval: function(fn,time) {
this.intervals = setInterval(fn,time)
},
componentWillUnmount: function() {
clearTimeinterval(this.intervals)
}
}
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {todos: [],count:0,step:1
};
},
componentDidMount: function() {
this.setInterval(this.tick.bind(this), 1000); // Call a method on the mixin
},
tick: function() {
if(thia.state.count >10){
this.setState({todos:[],count:1.step:this.state.step++})
}
},
render: function() {
return (
<p>当前处于第{this.state.step}阶段<p>
<input ref = 'input' onkeydown = this.change.bind(this)>
{this.state.todos.map (toto) => (
<div>'-----' todo '------'
) }
);
}
});
我们做了这样一件事情,有一个任务列表,我们可以往里面添加事情,每次到了10之后,step上升一步,同时会根据localstorage检测是否有重复事件的添加。
最后我们来看一下如何在python中使用mixin吧
class SimpleItemContainer(object):
def __init__(self, id, item_containers):
self.id = id
self.data = {}
for item in item_containers:
self.data[item.id] = item
class BetterSimpleItemContainer(object,SimpleItemContainer):
def __getitem__(self, id):
return self.data[id]
def __setitem__(self, id, value):
self.data[id] = value
def __delitem__(self, id):
del self.data[id]
def keys(self):
return self.data.keys()
其实就是一个类的继承和复用而已,用到了一些特殊方法。