React 高级指引
1、代码分隔
大多数的React应用都会使用Webpack或Browserify这类的构建工具来打包文件。打包是一个将文件引入并合并
到一个单独文件的过程。接着在页面上引入该捆绑,整个应该即可一次性加载!
代码分隔是由诸如Webpack(代码分隔)和诸如因字束缚(factor-bundle)这类打包器支持的一项技术,能够创建
多个包并在运行时动态加载;
1.1)import-- 你在应用的引入中分隔代码的最佳方式是通过动态的import()语法;
例如:import OtherComponent from './OtherComponent';
注意:动态import()语法目前只是一个ECMAScript提案,而不是正式的语法标准;
1.2) React.lazy ----能够让你想渲染常规组件一样处理动态引入;
例如:const OtherComponent = React.lazy(()=>import('./OtherComponent'));
注意:React.lazy接受一个函数,这个函数需要动态调用import().它必须返回一个Promise,该Promise需要解决一个
default export的React组件;
如果MyComponent渲染完成后,所有OtherComponent的模块还没有被加载完成,我们可以使用加载指示器为此组件做
优雅降级。使用Suspense组件来解决;(你可以将Suspense
组件置于懒加载组件之上的任何位置。你甚至可以用一个Suspense
组件包裹多个懒加载组件。)
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
2、上下文
上下文提供了一个无需为每层组件手动添加道具,就能在组件树间进行数据传递的方法;
在一个典型的React应用中,数据是通过props属性自上而下(由父及子)进行传递的,但是这种做法对于
某些类型的属性而言是极其繁琐的(例如:地区偏好,UI主题),这些属性是应用中许多组件都需要的。Context
提供了一种在组件之间共享此类值的方式,而不必显示的通过组件树逐层传递道具;
使用Context之前到考虑:Context 主要应用场景在于很多不同层级的组件需要访问同样的一些数据。请谨慎使用,因为会使得组件的复用性变差;
如果你只是想避免层层传递一些属性,组件组合有时候是一个比context更好的解决方案;
2.2) 上下文的API
2.2.1) React.createContext
const MyContext = React.createContext(defaultValue);----创建一个Context对象。当React 渲染一个这个Context对象的组件,这个组件
会从组件树中离自身最近的那个匹配Provider中读取到当前的context值;如果没有,defaultValue参数才会生效;
(注意:将undefined传递给Provider时,组件的defaultValue不会生效)
2.2.2)Context.Provider
<MyContext.Provider value={/*某个值*/}>-</MyContext.Provider>----每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context变化;
Provider接受一个value属性,传递给消费组件。一个Provider可以和多个消费组件有对应的关系。多个Provider也可以嵌套使用,
里层会覆盖外层的数据;
(当Provider的value值发生变化时,他内部所有消费组件都会重新渲染。Provider以及内部consumer组件都不受制于shouldComponentUpdate函数
因此当consumer组件在其祖先组件退出更新的情况下也能更新;)
2.2.3)Class.contextType
挂载在class上的contextType属性会被重赋值为一个由React.createContext()创建的Context对象;这能让你
使用this.context来消费最近Context上的值。你可以在任何生命周期中访问到它,包含render函数中;
(注意:你只通过该API订阅单一context.如果想要订阅多个,阅读使用多个Context篇章。如果你正在使用实验性的public class fields语法,可以
使用static这个类属性来初始化你的contextType)
2.2.4)Context.Consumer
<MyContext.Consumer>{value=>/*基于context值进行渲染*/}</MyContext.Consumer>
React组件也可以订阅到context变更,这能让你在函数式组件中完成订阅context;
(注意:这需要函数作为子元素,这个函数接收当前的context值,返回一个React节点。传递给函数的value值等同于
网上组件树离这个context最近的Provider提供的value值。如果没有对应Provider,value参数等同于传递给createContext()的defaultValue)
3、错误边界
错误边界产生背景:过去,组件内的JavaScript错误会导致React的内部状态被破坏,并且在下一次渲染时产生可能无法追踪的错误。这些错误基本上由较早的其他代码(非React组件)
错误引起。但是React并没有提供一种在组件中优雅处理这些错误的方式;
为了解决上述问题,React 16引入了一个新的概念-----错误边界;
错误边界是一种React组件,这种组件可以捕获并打印发生在其子组件树任何位置的JavaScript错误,并且会渲染出备用UI,
而不是渲染那些崩溃了的子组件。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误;
(注意:错误边界无法捕获一下场景中产生的错误:
1、事件处理
2、异步代码(例如:setTimeout 或 requestAnimationFrame回调函数)
3、服务端渲染
4、它自身抛出来的错误(并非他的子组件)
)
注意:错误边界无法捕获事件处理器内部的错误。
React不需要错误边界来从事件处理程序的错误中恢复。与Render方法和生命周期方法不同,事件处理器不会再渲染期间触发。因此,
抛出异常,React仍然能够知道需要在屏幕上显示什么。如果你需要在事件处理器内部捕获错误,使用普通的JavaScript try/catch
4、Refs转发
ref转发是一项将ref自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必须的。但是对某些组件,
尤其是可重用的组件库是很有用的。
4.1)转发refs到DOM组件
上述实例发生情况的逐步解释:
1、我们通过调用React.createRef创建一个React ref并将其赋值给ref变量;
2、我们通过指定ref为JSX属性,将其向下传递给<FancyButton ref={ref}>;
3、React传递ref给forwardRef内函数(props,ref)=>....作为其第二个参数;
4、我们向下转发该ref参数到<button ref={ref}>,将其指定为JSX属性;
5、当ref挂载完成,ref.current将指向<button>DOM节点;
(注意:第二个参数ref只能使用React.forwardRef定义组件时存在。常规函数或class组件不接收ref参数,props中也不存在ref.
Ref转发不仅限于DOM组件,也可以转发refs到class组件实例中)
5、Fragments
React中一个常见模式是一个组件返回多个元素。Fragments允许你将子列表分组,而无需向DOM添加额外节点;
render(){
return (
<React.Fragment>
<ChildA/>
<ChildB/>
<ChildC/>
</React.Fragment>
)
}
或者可以将上述的<React.Fragment>转换为<>(目前很多工具尚不支持),如果是一个集合映射到一个Fragments数组,则
<React.Fragment key = {item.id} >
6、高阶组件
高阶组件(HOC)是React中用于重用组件逻辑的高级技术。HOC本身不是React API一部分。它们是React组成性质的一种模式。
高阶组件是一个获取组件并返回新组建的函数;
const EnhanceComponent = higherOrderComponent(WrapperComponent);
组件将props转换为UI,而高阶组件将组件转换为另一个组件;