update: Mixin 只適用於 ES5。若是你的項目裏面用的是 ES6 ,能夠採用高階組件來實現 Mixin 的功能。javascript
我使用 React.js
構建大型項目已經有一段時間了,我遇到過不少在不一樣的組件中都要用到相同功能的狀況。所以,我花了一個小時左右的時間去了解mixin
的用法,而後分享我所學習到的一些東西。html
React
迴避子類組件,可是咱們知道,處處重複地編寫一樣的代碼是很差的。因此爲了將一樣的功能添加到多個組件當中,你須要將這些通用的功能包裝成一個mixin
,而後導入到你的模塊中。 能夠說,相比繼承而已,React
更喜歡這種組合的方式。嗯,我也喜歡這樣。java
如今假設咱們在寫一個app,咱們知道在某些狀況下咱們須要在好幾個組件當中設置默認的name
屬性。
如今,咱們再也不是像之前同樣在每一個組件中寫多個一樣的getDefaultProps
方法,咱們能夠像下面同樣定義一個mixin
:react
var DefaultNameMixin = { getDefaultProps: function () { return {name: "Skippy"}; } };
它沒什麼特殊的,就是一個簡單的對象而已。ios
爲了使用mixin
,咱們只須要簡單得在組件中加入mixins
屬性,而後把剛纔咱們寫的mixin
包裹成一個數組,將它做爲該屬性的值便可:git
var ComponentOne = React.createClass({ mixins: [DefaultNameMixin], render: function() { return <h2>Hello {this.props.name}</h2>; } }); React.renderComponent(<ComponentOne />, document.body);
JSFiddle 示例:一個簡單的 mixin 例子github
就像你想象的那樣,咱們能夠在任何其餘組件中包含咱們的mixin
:數組
var ComponentTwo = React.createClass({ mixins: [DefaultNameMixin], render: function () { return ( <div> <h4>{this.props.name}</h4> <p>Favorite food: {this.props.food}</p> </div> ); } });
JSFiddle 示例:在多個組件中使用同一個 mixinapp
如何你的mixin
當中包含生命週期方法,不要焦急,你仍然能夠在你的組件中使用這些方法,並且它們都會被調用:less
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
:
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
的時候。 幸運地是,這些看起來並非什麼大問題,下面是咱們在實踐當中發現的一些問題:
若是你嘗試在不一樣的地方定義相同的屬性時會出現下面的異常:
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
,而這些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
翻譯水平有限,文中帶有我的理解,若有不恰當的地方,請在評論中指出,很是感謝!