首先须要去理解纯函数的概念:相同的输入,拥有有相同输出,没有任何反作用。同理: 咱们的函数式组件也不会去根据组件的状态(state)的不一样输入,不会拥有不一样的表现形式。(与state无关,取决于props与context)。javascript
// 引入package等省略,这是一个全局公共的Loading。
const Loading = ({ size = '', loading = false, inner = false }) => {
if (loading) {
return inner? (
// 没有size属性,默认用自定义的大小
<div className={classNames(
styles['inner-loading-container'],
{[styles['custom-size']]: !size}
)}>
<Spin size={size || 'small'} className={styles.loading} /> </div>
): (
createPortal(
<div className={styles['loading-container']}> <Spin className={styles.loading} /> </div>, document.querySelector('body'), ) ); } return null; }; 复制代码
正常状况下,父组件每次有state或者props改变,子组件都会从新渲染。可是若是咱们在shouldComponentUpdate阶段判断新旧属性和状态是否相等,是否须要从新渲染子组件。减小render()方法的触发,节省了在虚拟DOM生成与对比的过程,性能获得提高。前端
这个比较的过程是一个浅比较,没法判断复杂数据类型(引用类型)的变化。java
即便是浅比较,一样是须要消耗性能。react
若是不是immutale数据的话你可能会出现问题(同一引用类型改变致使不更新渲染),一旦深层次的结构出现问题,你定位问题可能须要好久。git
这不足以成为一个正常应用的性能瓶颈,当你发现已经到不优化不可的地步,那确定不是PureComponent致使的,优化的优点也就不那么明显了。github
// 浅比较shallowEqual的源码
const hasOwn = Object.prototype.hasOwnProperty
// 这个函数其实是Object.is()的polyfill
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}
export default function shallowEqual(objA, objB) {
// 首先对基本数据类型的比较
if (is(objA, objB)) return true
// 因为Obejct.is()能够对基本数据类型作一个精确的比较, 因此若是不等
// 只有一种状况是误判的,那就是object,因此在判断两个对象都不是object以后,就能够返回false了
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false
}
// 过滤掉基本数据类型以后,就是对对象的比较了
// 首先拿出key值,对key的长度进行对比
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
// 长度不等直接返回false
if (keysA.length !== keysB.length) return false
for (let i = 0; i < keysA.length; i++) {
// key值相等的时候
// 借用原型链上真正的 hasOwnProperty 方法,判断ObjB里面是否有A的key的key值
// 属性的顺序不影响结果也就是{name:'daisy', age:'24'} 跟{age:'24',name:'daisy' }是同样的
// 最后,对对象的value进行一个基本数据类型的比较,返回结果
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
复制代码
高阶组件的概念其实是来源于咱们的高阶函数:接收函数做为输入,或者输出另外一个函数的一类函数,被称做高阶函数。高阶组件: 接受React组件做为输入,输出一个新的React组件的组件。高阶组件和装饰器是一个模式,高阶组件能够看成装饰器使用。redux
react-redux中的connect(mapStateToProps, mapDispatchToProps, mergeProps,options): 此方法会将react组件链接到redux的store。connect经过函数参数mapStateToProps
,从全局store中取出当前组件须要的state,并把state转化成当前组件的props;同时经过函数参数mapDispatchToProps
,把当前组件用到的Redux的action creator,以props的方式传递给当前组件。connect
并不会修改传递进去的组件的定义,而是它会返回一个新的组件。bash
react-router中的withRouter: 经过withRouter
包装的组件,咱们能够在props中访问到location, router
等对象,这正是withRouter
经过高阶组件的方式传递过来的。react-router
UI框架中的受控组件:react-hoc-example框架
不要在组件的render方法中使用高阶组件,尽可能也不要在组件的其余生命周期方法中使用高阶组件。由于高阶组件每次都会返回一个新的组件,在render中使用会致使每次渲染出来的组件都不相等(===
),因而每次render,组件都会卸载(unmount),而后从新挂载(mount),既影响了效率,又丢失了组件及其子组件的状态。高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响了。
若是须要使用被包装组件的静态方法,那么必须手动拷贝这些静态方法。由于高阶组件返回的新组件,是不包含被包装组件的静态方法。hoist-non-react-statics能够帮助咱们方便的拷贝组件全部的自定义静态方法。
Refs不会被传递给被包装组件。尽管在定义高阶组件时,咱们会把全部的属性都传递给被包装组件,可是ref
并不会传递给被包装组件,由于ref
根本不属于React组件的属性。若是你在高阶组件的返回组件中定义了ref
,那么它指向的是这个返回的新组件,而不是内部被包装的组件。若是你但愿获取被包装组件的引用,你能够把ref
的回调函数定义成一个普通属性(给它一个ref之外的名字)。
function FocusInput({ inputRef, ...rest }) {
return <input ref={inputRef} {...rest} />;
}
//enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的render方法中...
return (<EnhanceInput
inputRef={(input) => {
this.input = input
}
}>)
// 让FocusInput自动获取焦点
this.input.focus();
复制代码
类组件使用PureComponent或者shouldComponentUpdate可以优化props值不变时候的渲染性能(默认是shallowEqual)。如今, 你能够经过使用React.memo对function组件进行一样的优化。固然你也能够在方法的第二个参数自定义compare方法。实际意义是:函数式组件也有“shouldComponentUpdate”生命周期了
const MyComponent = React.memo(function MyComponent(props) {
/* 只在props更改的时候才会从新渲染 */
});
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
function MyComponent(props) {
/* render using props */
}
export default React.memo(MyComponent, areEqual);
复制代码
类声明和类表达式的主体以 严格模式 执行,主要包括构造函数、静态方法和原型方法。Getter 和 setter 函数也在严格模式下执行。不是React的缘由,这是JavaScript中原本就有的。若是你传递一个函数名给一个变量,而后经过在变量后加括号()来调用这个方法,此时方法内部的this的指向就会丢失.
箭头函数没有 this,因此须要经过查找做用域链来肯定 this 的值。这就意味着若是箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this。this 是有词法约束力的。这意味它可使用封闭的函数上下文或者全局上下文做为 this 的值。