为何要写这个主题,是由于首先就是由于周会立了一个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,我本身我的来讲,是以解决业务,解决问题的想法去保持热情从而学习他们的。