Skip to main content

🍆 拓展

React.lazy

React.lazy 函数能让你像渲染常规组件一样处理动态引入(的组件)。

使用之前:

import OtherComponent from './OtherComponent';

使用之后:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 default export 的 React 组件。

然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。

fallback 属性接受任何在组件加载过程中你想展示的 React 元素。你可以将 Suspense 组件置于懒加载组件之上的任何位置。你甚至可以用一个 Suspense 组件包裹多个懒加载组件。

import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}

如果模块加载失败(如网络问题),它会触发一个错误。你可以通过异常捕获边界(Error boundaries)技术来处理这些情况。

import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

const MyComponent = () => (
<div>
<MyErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</MyErrorBoundary>
</div>
);

错误边界

部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界。

错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。

如果一个 class 组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。

当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}

render() {
if (this.state.hasError) {
// 如果有错误
return <h1>Something went wrong.</h1>;
} else {
// 如果没有错误,显示其子组件
return this.props.children;
}
}
}

然后你可以将它作为一个常规组件去使用,它的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。

<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
注意

错误边界无法捕获以下场景中产生的错误:

  • 事件处理(如果你需要在事件处理器内部捕获错误,使用普通的 JavaScript try / catch 语句)
  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调函数)
  • 服务端渲染
  • 它自身抛出来的错误(并非它的子组件)

Fragments

它可以用来解决组件中过多的DOM节点,key 是唯一可以传递给 Fragment 的属性。未来 React 可能会添加对其他属性的支持,例如事件。

function Hello(){
return (
<React.Fragment key={1}>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}

还有一种新的短语法可用于声明它们。

function Hello(){
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}

Context

用于自顶向下传数据。下面这个例子中,A包裹BB包裹C

const MyContext = React.createContext();

function A(){
const [name, setName] = useState();

return (
<MyContext.Provider value={name}>
<B/>{/*在这里的所有组件及其子组件都能获取value*/}
</MyContext.Provider>
)
}
  • 类组件获取context的方式
class C extends Component {
// 声明接受context
static contextType = MyContext;

render(){
this.context //就能得到之前Provider的value值了。
}
}
  • 函数组件获取context的方式(类组件也能使用)
function C(){
return (
<MyContext.Consumer>
{value => return <span>获取到的context{value}</span>}
</MyContext.Consumer>
)
}

其实使用hooks更加方便

function C(){
const value = useContext(MyContext);
.....
}

Render Props

术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。

具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。

<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
class DataProvider extends Component {
render(
return (<>
{/* 这里可以预留非常多的参数会传到那个回调里 */}
{/* 通过回调的返回值渲染组件出来 */}
{this.props.render(a, b, c, d)}
</>);
);
}

PureComponent

React.PureComponentReact.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 propstate 的方式来实现了该函数。

如果赋予 React 组件相同的 propsstaterender() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

注意

React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 propsstate 较为简单时,才使用 React.PureComponent,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。你也可以考虑使用 immutable 对象加速嵌套数据的比较。

此外,React.PureComponent 中的 shouldComponentUpdate() 将跳过所有子组件树的 prop 更新。因此,请确保所有子组件也都是“纯”的组件。

Portals

它可以将需要渲染的组件,渲染在另一个组件下。(可以不是父组件)

第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。

render() {
// React 并没有创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
// `domNode` 是一个可以在任何位置的有效 DOM 节点。
return ReactDOM.createPortal(
this.props.children, // 这是需要渲染的虚拟DOM
domNode // 这个domNode是真实的dom,通过可以getElementById获取
);
}

Profiler

一个测量渲染性能的API。