爲何要寫這個主題,是由於首先就是由於週會立了一個flag要說分享,可是我又不想去分享一些什麼類庫了,由於一些別的類庫對於咱們業務來講,並無什麼特別好的幫助,其次以前跟同事聊天裏面,他問了我一個問題「大家是怎麼作到對新庫,新API保持這種持續學習的熱情的」,我是這麼回答他的「由於業務」,哈哈,你們會以爲我在扯淡,但其實裏面是有道理的,一些新的api,新的庫其實就是爲了解決一些歷史問題,當你在當前環境下沒法解決一些複雜問題而頭疼,用一些繞來繞去的方式解決的時候,發現來了一個新的模式去解決,你就會保持興奮和激動。那此次就帶着思考的角度去講React的編年史,也但願各位可以從中複習或者有些小夥伴從沒接觸過一些 react歷史問題,也一樣看看爲何新的React會產生這些新的API來幫助開發者們。react
早於0.14x 的時候,ES6尚未普及,因此你們建立一個React的類的時候,都是以函數調用的形式建立的,傳入對應的鍵值對函數執行相應的lifeCycle, state, 和 props。git
一個普通最基本,帶有props 以及 state的組件是以這樣的形式組織的github
var Counter = React.createClass({
getInitialState: function() {
return {
count: 0
}
},
getDefaultProps: function() {
return {
name: 'Mary'
};
},
componentDidMount: function() {
this.setState({
count: this.state.count + 1
})
},
handleClick: function() {
this.setState(function(preState) {
return {
count: preState.count + 1
}
})
},
render: function() {
return (
<div
onClick={this.handleClick}
>
{this.state.count}
</div>
)
}
})
複製代碼
Jsx和ES6版本是基本上沒有差別性的api
state的初始值是以initialState的函數調用返回值來建立的bash
不用ES6: 方法調用和ES6版本有一個最大的區別,對於ES6來講,咱們都知道它必須手動bind this。可是對於React.CreateClass來講,是不須要的,緣由實際上是在舊版本的reactClass這個對象裏面,在初始化的時候,會遍歷全部的傳入到createClass的key value,默認的內置lifCycle都會走默認的行爲,可是那些全部不是lifeCycle的,都通通會通過一個判斷循環,源碼不貼了,你們能夠本身翻, [github.com/facebook/re…]架構
if (this.__reactAutoBindMap) {
bindAutoBindMethods(this);
}
function bindAutoBindMethods(component) {
for (var autoBindKey in component.__reactAutoBindMap) {
if (component.__reactAutoBindMap.hasOwnProperty(autoBindKey)) {
var method = component.__reactAutoBindMap[autoBindKey];
component[autoBindKey] = bindAutoBindMethod(
component,
method
);
}
}
}
複製代碼
上面的this,就是整個ReactClass的上下文環境,至此,對於handler的autoBind就是這樣實現的。app
Es6: JS丟失this問題框架
var obj = {
a: 123,
test: function() {
console.log(this.a)
}
}
function render(func) {
func()
}
render(obj.test)
複製代碼
對於class OOP來講,最基本的就是符合里氏替換原則,里氏替換原則中說,任何基類能夠出現的地方,子類必定能夠出現。也就是,打個比方,有一個BaseHeader,其中一個子類集成了BaseHeader,叫作HeaderWithAvatar,那麼全部BaseHeader出現的地方都可以替換成HeaderWithAvatar,這就稱爲同一類型的組件。dom
首先能夠確定的是,用繼承來實現邏輯複用沒有問題,可是有侷限性ide
先看一下代碼
export class BaseHeaderInh extends React.Component {
state = {
classname: 'base'
}
componentDidMount() {
console.log('shit')
}
render() {
return (
<div className={this.state.classname}>HeaderInh base</div>
)
}
}
export class HeaderInh extends BaseHeaderInh {
componentDidMount() {
console.log('bull shit')
}
componentWillReceiveProps(nextProps) {
// code for base components/* */
console.log('base componentWillReceiveProps')
}
state = {
classname: `${this.state.classname} red`
}
}
export class HeaderReadAvatar extends HeaderInh {
componentWillReceiveProps(nextProps) {
super.componentWillReceiveProps()
console.log('i want change somethin in domain props')
}
render() {
return (
<div>
{super.render()}
icon
</div>
)
}
}
複製代碼
對於React的哲學思想來講,但願開發者對於每一個組件都承擔着對應本身的單一職責,進行解耦,比方說:HOC,render props 模式。
繼承耦合度高,在React組件模式下更難用好,這是一個顯而易見的問題。
組合各個組件是分離的,因此組合更加符合單一責任原則,而且組合的狀況可以更好地利用好children, state, props。
說到底,繼承是一種多態工具,而不是一種代碼複用工具,當使用組合來實現代碼複用的時候,是不會產生繼承關係的。過分使用繼承的話,若是修改了父類,會損壞全部的子類。這是由於子類和父類的緊耦合關係是在編譯期產生的。
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var TickTock = createReactClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 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>
);
}
});
複製代碼
mixins形成了隱式的依賴 假設,你有一個組件有一個狀態count = 1 ,而後有一個同事建立了一個mixins,是一個通用的mixins,去讀取了本地狀態裏面的count 處理一些複用邏輯,過了幾個月以後,你但願作一些狀態共享的業務,把count也傳遞給別人,那麼作了狀態提高,把count拉高到父組件,這時候這個mixins就會爆炸了。而且mixins之間能夠相互依賴,移除其中一個,有可能會形成另一個的爆炸。在這種狀況下,很難準確的描述mixins之間的依賴關係
mixins會有命名衝突的問題
相似於class 繼承的問題
因此綜合以上的種種問題,在React裏面,甚至是facebook這麼多優秀工程師的團隊,都會出現以上的問題,而且沒法很好的組織,重構,維護諸如此類的複雜問題。那明顯,mixins是一個bad design 。
因此,對於邏輯複用,組件複用,隨着時間的推移,經驗的推動,就出現了咱們最熟悉的higher order component
了。
完美的解決了以上的種種問題,對於邏輯複用,組件複用可以很好的處理,正是由於HOC是以組合的形式出現的。hoc就不須要過多的介紹了。可是hoc依然出現了一些問題:
Render props 和 hoc都是解決一樣的事情的,就是邏輯複用,全部的hoc都可以經過render props 重寫,render props 剔除了全部上面hoc的問題,寫法會相對優雅一點,可是這是須要分場景的,我我的以爲並無說全部的邏輯複用的hoc都用render props去重寫。有一些場景,的確不必去用render props,比方說一些權限問題,套一個render props是真的麻煩。由於render props終究是一個jsx,不能從外部解決問題,而是在render函數內解決問題。
function isAuth(Component) {
return class Auth extends React.Component {
state = {
auth: false
}
componentDidMount() {
setTimeout(() => {
this.setState({ auth: true})
}, 300)
}
render() {
return(
<div>
{this.state.auth ? <Component {...props} /> : '無權查看'}
</div>
)
}
}
}
@isAuth
class Demo extends React.Component {
}
// render props
class Auth extends React.Component {
state = {
auth: false
}
componentDidMount() {
setTimeout(() => {
this.setState({ auth: true})
}, 300)
}
renderError = () => {
return(
<div>Error component</div>
)
}
render() {
return (this.props.children({
auth: this.state.auth,
renderError: this.render
}))
}
}
class RenderProps extends React.Component{
render() {
return(
<div>
<Auth>
{({auth, renderError}) => {
return <div>
{auth ? Component : renderError}
</div>
}}
</Auth>
</div>
)
}
}
複製代碼
對於Hoc來講基本沒有對JSX的入侵性,只須要套一個decorator或者套一個函數返回組件 可是對於render props就必然對JSX 有一個入侵性,也就是說,不管如何,你都須要有一個合理的JSX結構來組織。
但其實,Render props依然還有一些問題,就是call backhell了,比方說,最底下的一個div須要用到多個createContext的props,那就須要寫成這樣。
class Demo extends React.Component {
render() {
return(
<Auth>
{({ auth, renderError }) => (
<Game>
{(props) => (
<Dude>
{(props) => (
<Shit>
{props => (
<div>213</div>
)}
</Shit>
)}
</Dude>
)}
</Game>
)}
</Auth>
)
}
}
複製代碼
Hooks api 除了基本上可以解決現階段全部的問題,還解決了一些額外的問題
階段總結一下:
Mixins(0.14x<): 邏輯複用初代目,雖然解決了邏輯複用,可是本質和Class inheritance有相似問題(工程協做會形成困擾)
Class Inheritance:是一種多態工具,而不是一種代碼複用工具,(須要很是完整的OOP能力,但也無法解決耦合問題)
Higher Order Component(0.14x-15.6):邏輯代碼複用以組合的形式出現,顆粒度適中,具有完整的state , props形態,符合React核心的單一職責原則(可是會形成冗餘嵌套組件的問題)
Render Props(16.x):優雅地處理hoc剩餘問題,但依然可能會出現(Call BackHell)
Hooks (16.7 alpha):基本是現階段邏輯複用的解決方案
0.14.x – 16.6(2016年-201七、8年) 這個裏面出現了不少的api更新,Reconciler的架構不斷地改進,以及拆包,例如像ReactDom , React.CSSTransitionGroup,React.CreateClass,React.PropTypes等,包括開始加入fiber架構在以後的代碼增進,ComponentDidCatch,getDerivedStateFromProps,getSnapShotBeforeUpdate等等等等
除了一些咱們所已經接觸到的還有如下兩個(這兩個細節其實均可以從官網和iceland dan的演講視頻中找到)
對於這麼多突飛猛進的框架和API,我本身我的來講,是以解決業務,解決問題的想法去保持熱情從而學習他們的。