共计 13753 个字符,预计需要花费 35 分钟才能阅读完成。
react diff 算法
咱们晓得 React 会保护两个虚构 DOM,那么是如何来比拟,如何来判断,做出最优的解呢?这就用到了 diff 算法
diff 算法的作用
计算出 Virtual DOM 中真正变动的局部,并只针对该局部进行原生 DOM 操作,而非从新渲染整个页面。
传统 diff 算法
通过循环递归对节点进行顺次比照,算法复杂度达到
O(n^3)
,n 是树的节点数,这个有多可怕呢?——如果要展现 1000 个节点,得执行上亿次比拟。。即使是 CPU 快能执行 30 亿条命令,也很难在一秒内计算出差别。
React 的 diff 算法
- 什么是和谐?
将 Virtual DOM 树转换成 actual DOM 树的起码操作的过程 称为 和谐。
- 什么是 React diff 算法?
diff
算法是和谐的具体实现。
diff 策略
React 用 三大策略 将
O(n^3)
杂度 转化为O(n)
复杂度
策略一(tree diff):
- Web UI 中 DOM 节点跨层级的挪动操作特地少,能够忽略不计
- 同级比拟, 既然 DOM 节点跨层级的挪动操作少到能够忽略不计,那么 React 通过 updateDepth 对 Virtual DOM 树进行层级管制,也就是同一层,在比照的过程中,如果发现节点不在了,会齐全删除不会对其余中央进行比拟,这样只须要对树遍历一次就 OK 了
策略二(component diff):
- 领有雷同类的两个组件 生成类似的树形构造,
- 领有不同类的两个组件 生成不同的树形构造。
策略三(element diff):
对于同一层级的一组子节点,通过惟一 id 辨别。
tree diff
- React 通过 updateDepth 对 Virtual DOM 树进行层级管制。
- 对树分层比拟,两棵树 只对同一档次节点 进行比拟。如果该节点不存在时,则该节点及其子节点会被齐全删除,不会再进一步比拟。
- 只需遍历一次,就能实现整棵 DOM 树的比拟。
那么问题来了,如果 DOM 节点呈现了跨层级操作,diff 会咋办呢?
答:diff 只简略思考同层级的节点地位变换,如果是跨层级的话,只有创立节点和删除节点的操作。
如上图所示,以 A 为根节点的整棵树会被从新创立,而不是挪动,因而 官网倡议不要进行 DOM 节点跨层级操作,能够通过 CSS 暗藏、显示节点,而不是真正地移除、增加 DOM 节点
component diff
React 对不同的组件间的比拟,有三种策略
- 同一类型的两个组件,按原策略(层级比拟)持续比拟 Virtual DOM 树即可。
- 同一类型的两个组件,组件 A 变动为组件 B 时,可能 Virtual DOM 没有任何变动,如果晓得这点(变换的过程中,Virtual DOM 没有扭转),可节俭大量计算工夫,所以 用户 能够通过
shouldComponentUpdate()
来判断是否须要 判断计算。 - 不同类型的组件,将一个(将被扭转的)组件判断为
dirty component
(脏组件),从而替换 整个组件的所有节点。
留神:如果组件 D 和组件 G 的构造类似,然而 React 判断是 不同类型的组件,则不会比拟其构造,而是删除 组件 D 及其子节点,创立组件 G 及其子节点。
element diff
当节点处于同一层级时,diff 提供三种节点操作:删除、插入、挪动。
- 插入:组件 C 不在汇合(A,B)中,须要插入
-
删除:
- 组件 D 在汇合(A,B,D)中,但 D 的节点曾经更改,不能复用和更新,所以须要删除 旧的 D,再创立新的。
- 组件 D 之前在 汇合(A,B,D)中,但汇合变成新的汇合(A,B)了,D 就须要被删除。
- 挪动:组件 D 曾经在汇合(A,B,C,D)里了,且汇合更新时,D 没有产生更新,只是地位扭转,如新汇合(A,D,B,C),D 在第二个,毋庸像传统 diff,让旧汇合的第二个 B 和新汇合的第二个 D 比拟,并且删除第二个地位的 B,再在第二个地位插入 D,而是(对同一层级的同组子节点)增加惟一 key 进行辨别,挪动即可。
diff 的有余与待优化的中央
尽量减少相似将最初一个节点挪动到列表首部的操作,当节点数量过大或更新操作过于频繁时,会影响 React 的渲染性能
react-router4 的外围
- 路由变成了组件
- 扩散到各个页面,不须要配置 比方
<link> <route></route>
什么是 React Fiber?
Fiber 是 React 16 中新的协调引擎或从新实现外围算法。它的次要指标是反对虚构 DOM 的增量渲染。React Fiber 的指标是进步其在动画、布局、手势、暂停、停止或重用等方面的适用性,并为不同类型的更新调配优先级,以及新的并发原语。
React Fiber 的指标是加强其在动画、布局和手势等畛域的适用性。它的次要个性是增量渲染: 可能将渲染工作宰割成块,并将其扩散到多个帧中。
如何 React.createElement?
const element = <h1 className="greeting">Hello, world!</h1>;
上述代码如何应用 React.createElement
来实现:
const element = React.createElement("h1", { className: "greeting"}, "Hello, world!");
如何创立 refs
Refs 是应用 React.createRef()
创立的,并通过 ref
属性附加到 React 元素。在结构组件时,通常将 Refs
调配给实例属性,以便能够在整个组件中援用它们。
class MyComponent extends React.Component {constructor(props) {super(props);
this.myRef = React.createRef();}
render() {return <div ref={this.myRef} />;
}
}
或者这样用:
class UserForm extends Component {handleSubmit = () => {console.log("Input Value is:", this.input.value);
};
render() {
return (<form onSubmit={this.handleSubmit}>
<input type="text" ref={(input) => (this.input = input)} /> // Access DOM input in handle submit
<button type="submit">Submit</button>
</form>
);
}
}
这三个点 (…) 在 React 干嘛用的?
...
在 React(应用 JSX)代码中做什么?它叫什么?
<Modal {...this.props} title='Modal heading' animation={false}/>
这个叫扩大操作符号或者开展操作符,例如,如果 this.props
蕴含 a:1
和b:2
,则
<Modal {...this.props} title='Modal heading' animation={false}>
等价于上面内容:
<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>
扩大符号不仅实用于该用例,而且对于创立具备现有对象的大多数(或全副)属性的新对象十分不便,在更新state
咱们就常常这么做:
this.setState((prevState) => {return { foo: { ...prevState.foo, a: "updated"} };
});
参考 前端进阶面试题具体解答
如何配置 React-Router 实现路由切换
(1)应用<Route>
组件
路由匹配是通过比拟 <Route>
的 path 属性和以后地址的 pathname 来实现的。当一个 <Route>
匹配胜利时,它将渲染其内容,当它不匹配时就会渲染 null。没有门路的 <Route>
将始终被匹配。
// when location = {pathname: '/about'}
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>
(2)联合应用 <Switch>
组件和 <Route>
组件
<Switch>
用于将 <Route>
分组。
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
<Switch>
不是分组 <Route>
所必须的,但他通常很有用。一个 <Switch>
会遍历其所有的子 <Route>
元素,并仅渲染与以后地址匹配的第一个元素。
(3)应用 <Link>、<NavLink>、<Redirect>
组件
<Link>
组件来在你的应用程序中创立链接。无论你在何处渲染一个<Link>
,都会在应用程序的 HTML 中渲染锚(<a>
)。
<Link to="/">Home</Link>
// <a href='/'>Home</a>
是一种非凡类型的 当它的 to 属性与以后地址匹配时,能够将其定义为 ” 沉闷的 ”。
// location = {pathname: '/react'}
<NavLink to="/react" activeClassName="hurray">
React
</NavLink>
// <a href='/react' className='hurray'>React</a>
当咱们想强制导航时,能够渲染一个 <Redirect>
,当一个<Redirect>
渲染时,它将应用它的 to 属性进行定向。
useEffect 与 useLayoutEffect 的区别
(1)共同点
- 使用成果: useEffect 与 useLayoutEffect 两者都是用于解决副作用,这些副作用包含扭转 DOM、设置订阅、操作定时器等。在函数组件外部操作副作用是不被容许的,所以须要应用这两个函数去解决。
- 应用形式: useEffect 与 useLayoutEffect 两者底层的函数签名是完全一致的,都是调用的 mountEffectImpl 办法,在应用上也没什么差别,根本能够间接替换。
(2)不同点
- 应用场景: useEffect 在 React 的渲染过程中是被异步调用的,用于绝大多数场景;而 useLayoutEffect 会在所有的 DOM 变更之后同步调用,次要用于解决 DOM 操作、调整款式、防止页面闪动等问题。也正因为是同步解决,所以须要防止在 useLayoutEffect 做计算量较大的耗时工作从而造成阻塞。
- 应用成果: useEffect 是依照程序执行代码的,扭转屏幕像素之后执行(先渲染,后扭转 DOM),当扭转屏幕内容时可能会产生闪动;useLayoutEffect 是扭转屏幕像素之前就执行了(会推延页面显示的事件,先扭转 DOM 后渲染),不会产生闪动。useLayoutEffect 总是比 useEffect 先执行。
在将来的趋势上,两个 API 是会长期共存的,临时没有删减合并的打算,须要开发者依据场景去自行抉择。React 团队的倡议十分实用,如果切实分不清,先用 useEffect,个别问题不大;如果页面有异样,再间接替换为 useLayoutEffect 即可。
react 最新版本解决了什么问题,减少了哪些货色
React 16.x 的三大新个性 Time Slicing、Suspense、hooks
- Time Slicing(解决 CPU 速度问题)使得在执行工作的期间能够随时暂停,跑去干别的事件,这个个性使得 react 能在性能极其差的机器跑时,依然放弃有良好的性能
- Suspense(解决网络 IO 问题) 和 lazy 配合,实现异步加载组件。能暂停以后组件的渲染,当实现某件事当前再持续渲染,解决从 react 出世到当初都存在的「异步副作用」的问题,而且解决得非的优雅,应用的是 T 异步然而同步的写法,这是最好的解决异步问题的形式
- 提供了一个 内置函数 componentDidCatch,当有谬误产生时,能够敌对地展现 fallback 组件; 能够捕捉到它的子元素(包含嵌套子元素)抛出的异样; 能够复用谬误组件。
(1)React16.8 退出 hooks,让 React 函数式组件更加灵便,hooks 之前,React 存在很多问题:
- 在组件间复用状态逻辑很难
- 简单组件变得难以了解,高阶组件和函数组件的嵌套过深。
- class 组件的 this 指向问题
- 难以记忆的生命周期
hooks 很好的解决了上述问题,hooks 提供了很多办法
- useState 返回有状态值,以及更新这个状态值的函数
- useEffect 承受蕴含命令式,可能有副作用代码的函数。
- useContext 承受上下文对象(从 React.createContext 返回的值)并返回以后上下文值,
- useReducer useState 的代替计划。承受类型为(state,action)=> newState 的 reducer,并返回与 dispatch 办法配对的以后状态。
- useCalLback 返回一个回顾的 memoized 版本,该版本仅在其中一个输出产生更改时才会更改。纯函数的输入输出确定性 o useMemo 纯的一个记忆函数 o useRef 返回一个可变的 ref 对象,其 Current 属性被初始化为传递的参数,返回的 ref 对象在组件的整个生命周期内放弃不变。
- useImperativeMethods 自定义应用 ref 时公开给父组件的实例值
- useMutationEffect 更新兄弟组件之前,它在 React 执行其 DOM 扭转的同一阶段同步触发
- useLayoutEffect DOM 扭转后同步触发。应用它来从 DOM 读取布局并同步从新渲染
(2)React16.9
- 重命名 Unsafe 的生命周期办法。新的 UNSAFE_前缀将有助于在代码 review 和 debug 期间,使这些有问题的字样更突出
- 废除 javascrip: 模式的 URL。以 javascript: 结尾的 URL 非常容易蒙受攻打,造成安全漏洞。
- 废除 ”Factory” 组件。工厂组件会导致 React 变大且变慢。
- act()也反对异步函数,并且你能够在调用它时应用 await。
- 应用 <React.ProfiLer> 进行性能评估。在较大的利用中追踪性能回归可能会很不便
(3)React16.13.0
- 反对在渲染期间调用 setState,但仅实用于同一组件
- 可检测抵触的款式规定并记录正告
- 废除 unstable_createPortal,应用 CreatePortal
- 将组件堆栈增加到其开发正告中,使开发人员可能隔离 bug 并调试其程序,这能够分明地阐明问题所在,并更快地定位和修复谬误。
React.forwardRef 有什么用
forwardRef
- 应用
forwardRef
(forward
在这里是「传递」的意思)后,就能跨组件传递ref
。 - 在例子中,咱们将
inputRef
从Form
跨组件传递到MyInput
中,并与input
产生关联
const MyInput = forwardRef((props, ref) => {return <input {...props} ref={ref} />;
});
function Form() {const inputRef = useRef(null);
function handleClick() {inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
useImperativeHandle
除了「限度跨组件传递
ref
」外,还有一种「避免ref
失控的措施」,那就是useImperativeHandle
,他的逻辑是这样的:既然「ref 失控」
是因为「应用了不该被应用的 DOM 办法」(比方appendChild
),那我能够限度「ref
中只存在能够被应用的办法」。用useImperativeHandle
批改咱们的 MyInput 组件:
const MyInput = forwardRef((props, ref) => {const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({focus() {realInputRef.current.focus();
},
}));
return <input {...props} ref={realInputRef} />;
});
当初,Form
组件中通过 inputRef.current
只能取到如下数据结构:
{focus() {realInputRef.current.focus();
},
}
就杜绝了
「开发者通过 ref 取到 DOM 后,执行不该被应用的 API,呈现 ref 失控」
的状况
- 为了避免错用 / 滥用导致
ref
失控,React 限度「默认状况下,不能跨组件传递 ref」
- 为了破除这种限度,能够应用
forwardRef
。 - 为了缩小
ref
对DOM
的滥用,能够应用useImperativeHandle
限度ref
传递的数据结构。
React 高阶组件、Render props、hooks 有什么区别,为什么要一直迭代
这三者是目前 react 解决代码复用的次要形式:
- 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
- render props 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术,更具体的说,render prop 是一个用于告知组件须要渲染什么内容的函数 prop。
- 通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个应用场景更加简略。这两种模式仍有用武之地,(例如,一个虚构滚动条组件或者会有一个 renderltem 属性,或是一个可见的容器组件或者会有它本人的 DOM 构造)。但在大部分场景下,Hook 足够了,并且可能帮忙缩小嵌套。
(1)HOC 官网解释∶
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。
简言之,HOC 是一种组件的设计模式,HOC 承受一个组件和额定的参数(如果须要),返回一个新的组件。HOC 是纯函数,没有副作用。
// hoc 的定义
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {constructor(props) {super(props);
this.state = {data: selectData(DataSource, props)
};
}
// 一些通用的逻辑解决
render() {
// ... 并应用新数据渲染被包装的组件!
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
// 应用
const BlogPostWithSubscription = withSubscription(BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id));
HOC 的优缺点∶
- 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
- 毛病∶ hoc 传递给被包裹组件的 props 容易和被包裹后的组件重名,进而被笼罩
(2)Render props 官网解释∶
“render prop” 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术
具备 render prop 的组件承受一个返回 React 元素的函数,将 render 的渲染逻辑注入到组件外部。在这里,”render” 的命名能够是任何其余无效的标识符。
// DataProvider 组件外部的渲染逻辑如下
class DataProvider extends React.Components {
state = {name: 'Tom'}
render() {
return (
<div>
<p> 共享数据组件本人外部的渲染逻辑 </p>
{this.props.render(this.state) } </div>
);
}
}
// 调用形式
<DataProvider render={data => (<h1>Hello {data.name}</h1>
)}/>
由此能够看到,render props 的优缺点也很显著∶
- 长处:数据共享、代码复用,将组件内的 state 作为 props 传递给调用者,将渲染逻辑交给调用者。
- 毛病:无奈在 return 语句外拜访数据、嵌套写法不够优雅
(3)Hooks 官网解释∶
Hook 是 React 16.8 的新增个性。它能够让你在不编写 class 的状况下应用 state 以及其余的 React 个性。通过自定义 hook,能够复用代码逻辑。
// 自定义一个获取订阅数据的 hook
function useSubscription() {const data = DataSource.getComments();
return [data];
}
//
function CommentList(props) {const {data} = props;
const [subData] = useSubscription();
...
}
// 应用
<CommentList data='hello' />
以上能够看出,hook 解决了 hoc 的 prop 笼罩的问题,同时应用的形式解决了 render props 的嵌套天堂的问题。hook 的长处如下∶
- 应用直观;
- 解决 hoc 的 prop 重名问题;
- 解决 render props 因共享数据 而呈现嵌套天堂的问题;
- 能在 return 之外应用数据的问题。
须要留神的是:hook 只能在组件顶层应用,不可在分支语句中应用。、
为什么 React 元素有一个 $$typeof 属性
目标是为了避免 XSS 攻打。因为 Synbol 无奈被序列化,所以 React 能够通过有没有 $$typeof 属性来断出以后的 element 对象是从数据库来的还是本人生成的。
- 如果没有 $$typeof 这个属性,react 会回绝解决该元素。
- 在 React 的古老版本中,上面的写法会呈现 XSS 攻打:
// 服务端容许用户存储 JSON
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {__html: '/* 把你想的搁着 */'},
},
// ...
};
let message = {text: expectedTextButGotJSON};
// React 0.13 中有危险
<p>
{message.text}
</p>
react router
import React from 'react'
import {render} from 'react-dom'
import {browserHistory, Router, Route, IndexRoute} from 'react-router'
import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'
render(<Router history={browserHistory}> // history 路由
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route path='features' component={Features} />
</Route>
</Router>,
document.getElementById('app')
)
render(<Router history={browserHistory} routes={routes} />,
document.getElementById('app')
)
React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件能够拦挡正在产生的跳转,或在来到 route 前提醒用户。routerWillLeave 返回值有以下两种:
return
false
勾销此次跳转 return
返回提示信息,在来到 route 前提醒用户进行确认。
高阶组件
高阶函数:如果一个函数 承受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数。
高阶组件:如果一个函数 承受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件。
react 中的高阶组件
React 中的高阶组件次要有两种模式:属性代理 和反向继承。
属性代理 Proxy
- 操作
props
- 抽离
state
- 通过
ref
拜访到组件实例 - 用其余元素包裹传入的组件
WrappedComponent
反向继承
会发现其属性代理和反向继承的实现有些相似的中央,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component
,反向继承中继承的是传入的组件 WrappedComponent
。
反向继承能够用来做什么:
1. 操作 state
高阶组件中能够读取、编辑和删除 WrappedComponent
组件实例中的 state
。甚至能够减少更多的state
项,然而 十分不倡议这么做 因为这可能会导致 state
难以保护及治理。
function withLogging(WrappedComponent) {
return class extends WrappedComponent {render() {
return (
<div>;
<h2>;Debugger Component Logging...<h2>;
<p>;state:<p>;
<pre>;{JSON.stringify(this.state, null, 4)}<pre>;
<p>props:<p>;
<pre>{JSON.stringify(this.props, null, 4)}<pre>;
{super.render()}
<div>;
);
}
};
}
2. 渲染劫持(Render Highjacking)
条件渲染通过 props.isLoading 这个条件来判断渲染哪个组件。
批改由 render() 输入的 React 元素树
为什么应用 jsx 的组件中没有看到应用 react 却须要引入 react?
实质上来说 JSX 是 React.createElement(component, props, ...children)
办法的语法糖。在 React 17 之前,如果应用了 JSX,其实就是在应用 React,babel
会把组件转换为 CreateElement
模式。在 React 17 之后,就不再须要引入,因为 babel
曾经能够帮咱们主动引入 react。
useEffect(fn, []) 和 componentDidMount 有什么差别
useEffect
会捕捉props
和 state。所以即使在回调函数里,你拿到的还是初始的 props 和 state。如果想得到“最新”的值,能够应用 ref。
react 中这两个生命周期会触发死循环
componentWillUpdate
生命周期在shouldComponentUpdate
返回 true 后被触发。在这两个生命周期只有视图更新就会触发,因而不能再这两个生命周期中应用 setState。否则会导致死循环
在 React 中如何防止不必要的 render?
React 基于虚构 DOM 和高效 Diff 算法的完满配合,实现了对 DOM 最小粒度的更新。大多数状况下,React 对 DOM 的渲染效率足以业务日常。但在个别简单业务场景下,性能问题仍然会困扰咱们。此时须要采取一些措施来晋升运行性能,其很重要的一个方向,就是防止不必要的渲染(Render)。这里提下优化的点:
- shouldComponentUpdate 和 PureComponent
在 React 类组件中,能够利用 shouldComponentUpdate 或者 PureComponent 来缩小因父组件更新而触发子组件的 render,从而达到目标。shouldComponentUpdate 来决定是否组件是否从新渲染,如果不心愿组件从新渲染,返回 false 即可。
- 利用高阶组件
在函数组件中,并没有 shouldComponentUpdate 这个生命周期,能够利用高阶组件,封装一个相似 PureComponet 的性能
- 应用 React.memo
React.memo 是 React 16.6 新的一个 API,用来缓存组件的渲染,防止不必要的更新,其实也是一个高阶组件,与 PureComponent 非常相似,但不同的是,React.memo 只能用于函数组件。
对 React-Intl 的了解,它的工作原理?
React-intl 是雅虎的语言国际化开源我的项目 FormatJS 的一部分,通过其提供的组件和 API 能够与 ReactJS 绑定。
React-intl 提供了两种应用办法,一种是援用 React 组件,另一种是间接调取 API,官网更加举荐在 React 我的项目中应用前者,只有在无奈应用 React 组件的中央,才应该调用框架提供的 API。它提供了一系列的 React 组件,包含数字格式化、字符串格式化、日期格式化等。
在 React-intl 中,能够配置不同的语言包,他的工作原理就是依据须要,在语言包之间进行切换。
新版生命周期
在新版本中,React 官网对生命周期有了新的 变动倡议:
- 应用
getDerivedStateFromProps
替换componentWillMount;
- 应用
getSnapshotBeforeUpdate
替换componentWillUpdate;
- 防止应用
componentWillReceiveProps
;
其实该变动的起因,正是因为上述提到的
Fiber
。首先,从下面咱们晓得 React 能够分成reconciliation
与commit
两个阶段,对应的生命周期如下:
reconciliation
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
commit
componentDidMount
componentDidUpdate
componentWillUnmount
在
Fiber
中,reconciliation
阶段进行了工作宰割,波及到 暂停 和 重启,因而可能会导致reconciliation
中的生命周期函数在一次更新渲染循环中被 屡次调用 的状况,产生一些意外谬误
新版的倡议生命周期如下:
class Component extends React.Component {
// 替换 `componentWillReceiveProps`,// 初始化和 update 时被调用
// 动态函数,无奈应用 this
static getDerivedStateFromProps(nextProps, prevState) {}
// 判断是否须要更新组件
// 能够用于组件性能优化
shouldComponentUpdate(nextProps, nextState) {}
// 组件被挂载后触发
componentDidMount() {}
// 替换 componentWillUpdate
// 能够在更新之前获取最新 dom 数据
getSnapshotBeforeUpdate() {}
// 组件更新后调用
componentDidUpdate() {}
// 组件行将销毁
componentWillUnmount() {}
// 组件已销毁
componentDidUnMount() {}
}
应用倡议:
- 在
constructor
初始化state
; - 在
componentDidMount
中进行事件监听,并在componentWillUnmount
中解绑事件; - 在
componentDidMount
中进行数据的申请,而不是在componentWillMount
; -
须要依据
props
更新state
时,应用getDerivedStateFromProps(nextProps, prevState)
;- 旧 props 须要本人存储,以便比拟;
public static getDerivedStateFromProps(nextProps, prevState) {
// 当新 props 中的 data 发生变化时,同步更新到 state 上
if (nextProps.data !== prevState.data) {
return {data: nextProps.data}
} else {return null1}
}
能够在 componentDidUpdate 监听 props 或者 state 的变动,例如:
componentDidUpdate(prevProps) {
// 当 id 发生变化时,从新获取数据
if (this.props.id !== prevProps.id) {this.fetchData(this.props.id);
}
}
- 在 componentDidUpdate 应用 setState 时,必须加条件,否则将进入死循环;
- getSnapshotBeforeUpdate(prevProps, prevState)能够在更新之前获取最新的渲染数据,它的调用是在 render 之后,update 之前;
- shouldComponentUpdate: 默认每次调用 setState,肯定会最终走到 diff 阶段,但能够通过 shouldComponentUpdate 的生命钩子返回 false 来间接阻止前面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。