update: Mixin 只适用于 ES5。如果你的项目里面用的是 ES6 ,可以采用高阶组件来实现 Mixin 的功能。
我使用 React.js
构建大型项目已经有一段时间了,我遇到过很多在不同的组件中都要用到相同功能的情况。因此,我花了一个小时左右的时间去了解mixin
的用法,然后分享我所学习到的一些东西。
为什么使用 Mixin ?
React
回避子类组件,但是我们知道,到处重复地编写同样的代码是不好的。所以为了将同样的功能添加到多个组件当中,你需要将这些通用的功能包装成一个mixin
,然后导入到你的模块中。 可以说,相比继承而已,React
更喜欢这种组合的方式。嗯,我也喜欢这样。
写一个简单的 Mixin
现在假设我们在写一个app,我们知道在某些情况下我们需要在好几个组件当中设置默认的name
属性。
现在,我们不再是像以前一样在每个组件中写多个同样的getDefaultProps
方法,我们可以像下面一样定义一个mixin
:
var DefaultNameMixin = { getDefaultProps: function () { return {name: "Skippy"}; } };
它没什么特殊的,就是一个简单的对象而已。
加入到 React 组件中
为了使用mixin
,我们只需要简单得在组件中加入mixins
属性,然后把刚才我们写的mixin
包裹成一个数组,将它作为该属性的值即可:
var ComponentOne = React.createClass({ mixins: [DefaultNameMixin],render: function() { return <h2>Hello {this.props.name}</h2>; } }); React.renderComponent(<ComponentOne />,document.body);
重复使用
就像你想象的那样,我们可以在任何其他组件中包含我们的mixin
:
var ComponentTwo = React.createClass({ mixins: [DefaultNameMixin],render: function () { return ( <div> <h4>{this.props.name}</h4> <p>Favorite food: {this.props.food}</p> </div> ); } });
生命周期方法会被重复调用!
如何你的mixin
当中包含生命周期方法,不要焦急,你仍然可以在你的组件中使用这些方法,而且它们都会被调用:
JSFiddle 示例:展示了两个 default props 都会被设置
两个getDefaultProps
方法都将被调用,所以我们可以得到默认为Skippy
的name
属性和默认为Pancakes
的food
属性。任何一个生命周期方法或属性都会被顺利地重复调用,但是下面的情况除外:
render
:包含多个render
方法是不行的。React 会跑出异常:
Uncaught Error: Invariant Violation: ReactCompositeComponentInterface: You are attempting to define `render` on your component more than once. This conflict may be due to a mixin.
displayName
:你多次的对它进行设置是没有问题的,但是,最终的结果只以最后一次设置为准。
需要指出的是,mixin
是可以包含在其他的mixin
中的:
var UselessMixin = { componentDidMount: function () { console.log("asdas"); } }; var LolMixin = { mixins: [UselessMixin] }; var PantsOpinion = React.createClass({ mixins: [LolMixin],render: function () { return (<p>I dislike pants</p>); } }); React.renderComponent(<PantsOpinion />,document.body);
程序会在控制台打印出asdas
。
包含多个 Mixins
我们的mixins
要包裹在数组当中,提醒了我们可以在组件中包含多个mixins
:
var DefaultNameMixin = { getDefaultProps: function () { return {name: "Lizie"}; } }; var DefaultFoodMixin = { getDefaultProps: function () { return {food: "Pancakes"}; } }; var ComponentTwo = React.createClass({ mixins: [DefaultNameMixin,DefaultFoodMixin],render: function () { return ( <div> <h4>{this.props.name}</h4> <p>Favorite food: {this.props.food}</p> </div> ); } });
注意事项
这里有几件事需要引起我们的注意,当我们使用mixins
的时候。 幸运地是,这些看起来并不是什么大问题,下面是我们在实践当中发现的一些问题:
设置相同的 Prop 和 State
如果你尝试在不同的地方定义相同的属性时会出现下面的异常:
Uncaught Error: Invariant Violation: mergeObjectsWithNoDuplicateKeys(): Tried to merge two objects with the same key: name
设置相同的方法
在不同的mixin
中定义相同的方法,或者mixin
和组件中包含了相同的方法时,会抛出异常:
var logonMountMixin = { componentDidMount: function () { console.log("mixin mount method"); this.logBlah() },// add a logBlah method here... logBlah: function () { console.log("blah"); } }; var MorelogonMountMixin = { componentDidMount: function () { console.log("another mixin mount method"); },// ... and again here. logBlah: function () { console.log("something other than blah"); } };
异常信息同多次定义rander
方法时抛出的异常一样:
Uncaught Error: Invariant Violation: ReactCompositeComponentInterface: You are attempting to define `logBlah` on your component more than once. This conflict may be due to a mixin.
多个生命周期方法的调用顺序
如果我们的组件和mixin
中都包含了相同的生命周期方法的话会怎样呢?
那当我们的组件中包含多个mixin
,而这些mixin
中又包含相同的生命周期方法时,调用顺序又是如何?
它们会根据
mixins
中的顺序从左到右的进行调用。
实例代码:
var logonMountMixin = { componentDidMount: function () { console.log("mixin mount method"); } }; var MorelogonMountMixin = { componentDidMount: function () { console.log("another mixin mount method"); } }; var ComponentOne = React.createClass({ mixins: [MorelogonMountMixin,logonMountMixin],componentDidMount: function () { console.log("component one mount method"); },... var ComponentTwo = React.createClass({ mixins: [logonMountMixin,MorelogonMountMixin],componentDidMount: function () { console.log("component two mount method"); },...
控制台将输出:
another mixin mount method mixin mount method component one mount method mixin mount method another mixin mount method component two mount method
总结
Mixin 使你React程序变得更为可重用,It's a Good Thing.
我希望这篇文章能够对你有所帮助,如果有任何反馈,很高兴你能够在twitter上@veddermatic
翻译水平有限,文中带有个人理解,如有不恰当的地方,请在评论中指出,非常感谢!
原文链接:https://www.f2er.com/react/307854.html