React 事件机制
<div onClick={this.handleClick.bind(this)}> 点我 </div>
React 并不是将 click 事件绑定到了 div 的实在 DOM 上,而是在 document 处监听了所有的事件,当事件产生并且冒泡到 document 处的时候,React 将事件内容封装并交由真正的处理函数运行。这样的形式不仅仅缩小了内存的耗费,还能在组件挂在销毁时对立订阅和移除事件。
除此之外,冒泡到 document 上的事件也不是原生的浏览器事件,而是由 react 本人实现的合成事件(SyntheticEvent)。因而如果不想要是事件冒泡的话应该调用 event.preventDefault()办法,而不是调用 event.stopProppagation()办法。JSX 上写的事件并没有绑定在对应的实在 DOM 上,而是通过事件代理的形式,将所有的事件都对立绑定在了 document
上。这样的形式不仅缩小了内存耗费,还能在组件挂载销毁时对立订阅和移除事件。
另外冒泡到 document
上的事件也不是原生浏览器事件,而是 React 本人实现的合成事件(SyntheticEvent)。因而咱们如果不想要事件冒泡的话,调用 event.stopPropagation
是有效的,而应该调用 event.preventDefault
。
实现合成事件的目标如下:
- 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
- 对于原生浏览器事件来说,浏览器会给监听器创立一个事件对象。如果你有很多的事件监听,那么就须要调配很多的事件对象,造成高额的内存调配问题。然而对于合成事件来说,有一个事件池专门来治理它们的创立和销毁,当事件须要被应用时,就会从池子中复用对象,事件回调完结后,就会销毁事件对象上的属性,从而便于下次复用事件对象。
connect 原理
- 首先
connect
之所以会胜利,是因为Provider
组件: - 在原利用组件上包裹一层,使原来整个利用成为
Provider
的子组件 接管Redux
的store
作为props
,通过context
对象传递给子孙组件上的connect
connect
做了些什么。它真正连贯Redux
和React
,它包在咱们的容器组件的外一层,它接管下面Provider
提供的store
外面的state
和dispatch
,传给一个构造函数,返回一个对象,以属性模式传给咱们的容器组件
connect
是一个高阶函数,首先传入mapStateToProps
、mapDispatchToProps
,而后返回一个生产Component
的函数 (wrapWithConnect
),而后再将真正的Component
作为参数传入wrapWithConnect
,这样就生产出一个通过包裹的Connect
组件,
该组件具备如下特点
- 通过
props.store
获取先人Component
的store props
包含stateProps
、dispatchProps
、parentProps
, 合并在一起失去nextState
,作为props
传给真正的Component componentDidMount
时,增加事件this.store.subscribe(this.handleChange)
,实现页面交互 shouldComponentUpdate
时判断是否有防止进行渲染,晋升页面性能,并失去nextState
componentWillUnmount
时移除注册的事件this.handleChange
因为
connect
的源码过长,咱们只看次要逻辑
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {constructor(props, context) {
// 从先人 Component 处取得 store
this.store = props.store || context.store
this.stateProps = computeStateProps(this.store, props)
this.dispatchProps = computeDispatchProps(this.store, props)
this.state = {storeState: null}
// 对 stateProps、dispatchProps、parentProps 进行合并
this.updateState()}
shouldComponentUpdate(nextProps, nextState) {
// 进行判断,当数据产生扭转时,Component 从新渲染
if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {this.updateState(nextProps)
return true
}
}
componentDidMount() {
// 扭转 Component 的 state
this.store.subscribe(() = {
this.setState({storeState: this.store.getState()
})
})
}
render() {
// 生成包裹组件 Connect
return (<WrappedComponent {...this.nextState} />
)
}
}
Connect.contextTypes = {store: storeShape}
return Connect;
}
}
在哪个生命周期中你会收回 Ajax 申请?为什么?
Ajax 申请应该写在组件创立期的第五个阶段,即 componentDidMount 生命周期办法中。起因如下。
在创立期的其余阶段,组件尚未渲染实现。而在存在期的 5 个阶段,又不能确保生命周期办法肯定会执行(如通过 shouldComponentUpdate 办法优化更新等)。在销毀期,组件行将被销毁,申请数据变得无意义。因而在这些阶段发岀 Ajax 申请显然不是最好的抉择。
在组件尚未挂载之前,Ajax 申请将无奈执行结束,如果此时发出请求,将意味着在组件挂载之前更新状态(如执行 setState),这通常是不起作用的。
在 componentDidMount 办法中,执行 Ajax 即可保障组件曾经挂载,并且可能失常更新组件。
调用 setState 之后产生了什么
在代码中调用 setState 函数之后,React 会将传入的参数与之前的状态进行合并,而后触发所谓的和谐过程(Reconciliation)。通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面。在 React 失去元素树之后,React 会计算出新的树和老的树之间的差别,而后依据差别对界面进行最小化从新渲染。通过 diff 算法,React 可能准确制导哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。
- 在 setState 的时候,React 会为以后节点创立一个 updateQueue 的更新列队。
- 而后会触发 reconciliation 过程,在这个过程中,会应用名为 Fiber 的调度算法,开始生成新的 Fiber 树,Fiber 算法的最大特点是能够做到异步可中断的执行。
- 而后 React Scheduler 会依据优先级高下,先执行优先级高的节点,具体是执行 doWork 办法。
- 在 doWork 办法中,React 会执行一遍 updateQueue 中的办法,以取得新的节点。而后比照新旧节点,为老节点打上 更新、插入、替换 等 Tag。
- 以后节点 doWork 实现后,会执行 performUnitOfWork 办法取得新节点,而后再反复下面的过程。
- 当所有节点都 doWork 实现后,会触发 commitRoot 办法,React 进入 commit 阶段。
- 在 commit 阶段中,React 会依据后面为各个节点打的 Tag,一次性更新整个 dom 元素
讲讲什么是 JSX?
当 Facebook 第一次公布 React 时,他们还引入了一种新的 JS 方言 JSX
,将原始 HTML 模板嵌入到 JS 代码中。JSX 代码自身不能被浏览器读取,必须应用 Babel
和webpack
等工具将其转换为传统的 JS。很多开发人员就能有意识应用 JSX,因为它曾经与 React 联合在始终了。
class MyComponent extends React.Component {render() {
let props = this.props;
return (
<div className="my-component">
<a href={props.url}>{props.name}</a>
</div>
);
}
}
React 怎么做数据的检查和变动
Model
扭转之后(可能是调用了setState
),触发了virtual dom
的更新,再用diff
算法来把 virtual DOM
比拟real DOM
,看看是哪个dom
节点更新了,再渲染real dom
参考 前端进阶面试题具体解答
在 ReactNative 中,如何解决 adb devices 找不到连贯设施的问题?
在应用 Genymotion 时,首先须要在 SDK 的 platform-tools 中退出环境变量,而后在 Genymotion 中单击 Setting,抉择 ADB 选项卡,单击 Use custom Android SDK tools,浏览本地 SDK 的地位,单击 OK 按钮就能够了。启动虛拟机后,在 cmd 中输出 adb devices 能够查看设施。
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 Router 时,如何获取以后页面的路由或浏览器中地址栏中的地址?
在以后组件的 props 中,蕴含 location 属性对象,蕴含以后页面路由地址信息,在 match 中存储以后路由的参数等数据信息。能够间接通过 this .props 应用它们。
diff 算法?
- 把树形构造依照层级合成,只比拟同级元素。
- 给列表构造的每个单元增加惟一的
key
属性,不便比拟。 React
只会匹配雷同class
的component
(这外面的class
指的是组件的名字)- 合并操作,调用
component
的setState
办法的时候,React
将其标记为 –dirty
. 到每一个事件循环完结,React
查看所有标记dirty
的component
从新绘制. - 抉择性子树渲染。开发人员能够重写
shouldComponentUpdate
进步diff
的性能
setState 是同步的还是异步的
有时体现出同步,有时体现出异步
- setState 只有在 React 本身的合成事件和钩子函数中是异步的,在原生事件和 setTimeout 中都是同步的
- setState 的异步并不是说外部由异步代码实现,其实自身执行的过程和代码都是同步的,只是合成事件和钩子函数中没法立马拿到更新后的值,造成了所谓的异步。当然能够通过 setState 的第二个参数中的 callback 拿到更新后的后果
- setState 的批量更新优化也是建设在异步(合成事件、钩子函数)之上的,在原生事件和 setTimeout 中不会批量更新,在异步中如果对同一个值进行屡次 setState,setState 的批量更新策略会对其进行笼罩,去最初一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新
- 合成事件中是异步
- 钩子函数中的是异步
- 原生事件中是同步
- setTimeout 中是同步
React 中 keys 的作用是什么?
Keys
是React
用于追踪哪些列表中元素被批改、被增加或者被移除的辅助标识
- 在开发过程中,咱们须要保障某个元素的
key
在其同级元素中具备唯一性。在React Diff
算法中React
会借助元素的Key
值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染。此外,React 还须要借助Key
值来判断元素与本地状态的关联关系,因而咱们绝不可漠视转换函数中Key
的重要性
setState 到底是异步还是同步?
先给出答案: 有时体现出异步, 有时体现出同步
setState
只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout
中都是同步的setState
的“异步”并不是说外部由异步代码实现,其实自身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用程序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,造成了所谓的“异步”,当然能够通过第二个参数setState(partialState, callback)
中的callback
拿到更新后的后果setState
的批量更新优化也是建设在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout
中不会批量更新,在“异步”中如果对同一个值进行屡次setState
,setState
的批量更新策略会对其进行笼罩,取最初一次的执行,如果是同时setState
多个不同的值,在更新时会对其进行合并批量更新
react 性能优化计划
- 重写
shouldComponentUpdate
来防止不必要的 dom 操作 - 应用
production
版本的react.js
- 应用
key
来帮忙React
辨认列表中所有子组件的最小变动
diff 算法?
- 把树形构造依照层级合成,只比拟同级元素
- 给列表构造的每个单元增加惟一的 key 属性,不便比拟
- React 只会匹配雷同 class 的 component(这外面的 class 指的是组件的名字)
- 合并操作,调用 component 的 setState 办法的时候, React 将其标记为 dirty. 到每一个 事件循环完结, React 查看所有标记 dirty 的 component 从新绘制.
- 抉择性子树渲染。开发人员能够重写 shouldComponentUpdate 进步 diff 的性能。
react 性能优化是哪个周期函数
shouldComponentUpdate
这个办法用来判断是否须要调用 render 办法从新描述 dom。因为 dom 的描述十分耗费性能,如果咱们能在shouldComponentUpdate 方
法中可能写出更优化的dom diff
算法,能够极大的进步性能
Diff 的瓶颈以及 React 的应答
因为 diff 操作自身会带来性能上的损耗,在 React 文档中提到过,即便最先进的算法中,将前后两棵树齐全比对的算法复杂度为O(n3)
,其中 n 为树中元素的数量。
如果 React 应用了该算法,那么仅仅一千个元素的页面所须要执行的计算量就是十亿的量级,这无疑是无奈承受的。
为了升高算法的复杂度,React 的 diff 会预设三个限度:
- 只对同级元素进行 diff 比对。如果一个元素节点在前后两次更新中逾越了层级,那么 React 不会尝试复用它
- 两个不同类型的元素会产生出不同的树。如果元素由 div 变成 p,React 会销毁 div 及其子孙节点,并新建 p 及其子孙节点
- 开发者能够通过 key 来暗示哪些子元素在不同的渲染下能保持稳定
React 的生命周期有哪些?
React 通常将组件生命周期分为三个阶段:
- 装载阶段(Mount),组件第一次在 DOM 树中被渲染的过程;
- 更新过程(Update),组件状态发生变化,从新更新渲染的过程;
- 卸载过程(Unmount),组件从 DOM 树中被移除的过程;
1)组件挂载阶段
挂载阶段组件被创立,而后组件实例插入到 DOM 中,实现组件的第一次渲染,该过程只会产生一次,在此阶段会顺次调用以下这些办法:
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
(1)constructor
组件的构造函数,第一个被执行,若没有显式定义它,会有一个默认的构造函数,然而若显式定义了构造函数,咱们必须在构造函数中执行 super(props)
,否则无奈在构造函数中拿到 this。
如果不初始化 state 或不进行办法绑定,则不须要为 React 组件实现构造函数Constructor。
constructor 中通常只做两件事:
- 初始化组件的 state
- 给事件处理办法绑定 this
constructor(props) {super(props);
// 不要在构造函数中调用 setState,能够间接给 state 设置初始值
this.state = {counter: 0}
this.handleClick = this.handleClick.bind(this)
}
(2)getDerivedStateFromProps
static getDerivedStateFromProps(props, state)
这是个静态方法,所以不能在这个函数里应用 this
,有两个参数 props
和 state
,别离指接管到的新参数和以后组件的 state
对象,这个函数会返回一个对象用来更新以后的 state
对象,如果不须要更新能够返回 null
。
该函数会在装载时,接管到新的 props
或者调用了 setState
和 forceUpdate
时被调用。如当接管到新的属性想批改 state
,就能够应用。
// 当 props.counter 变动时,赋值给 state
class App extends React.Component {constructor(props) {super(props)
this.state = {counter: 0}
}
static getDerivedStateFromProps(props, state) {if (props.counter !== state.counter) {
return {counter: props.counter}
}
return null
}
handleClick = () => {
this.setState({counter: this.state.counter + 1})
}
render() {
return (
<div>
<h1 onClick={this.handleClick}>Hello, world!{this.state.counter}</h1>
</div>
)
}
}
当初能够显式传入 counter
,然而这里有个问题,如果想要通过点击实现 state.counter
的减少,但这时会发现值不会产生任何变动,始终放弃 props
传进来的值。这是因为在 React 16.4^ 的版本中 setState
和 forceUpdate
也会触发这个生命周期,所以当组件外部 state
变动后,就会从新走这个办法,同时会把 state
值赋值为 props
的值。因而须要多加一个字段来记录之前的 props
值,这样就会解决上述问题。具体如下:
// 这里只列出须要变动的中央
class App extends React.Component {constructor(props) {super(props)
this.state = {
// 减少一个 preCounter 来记录之前的 props 传来的值
preCounter: 0,
counter: 0
}
}
static getDerivedStateFromProps(props, state) {
// 跟 state.preCounter 进行比拟
if (props.counter !== state.preCounter) {
return {
counter: props.counter,
preCounter: props.counter
}
}
return null
}
handleClick = () => {
this.setState({counter: this.state.counter + 1})
}
render() {
return (
<div>
<h1 onClick={this.handleClick}>Hello, world!{this.state.counter}</h1>
</div>
)
}
}
(3)render
render 是 React 中最外围的办法,一个组件中必须要有这个办法,它会依据状态 state
和属性 props
渲染组件。这个函数只做一件事,就是返回须要渲染的内容,所以不要在这个函数内做其余业务逻辑,通常调用该办法会返回以下类型中一个:
- React 元素:这里包含原生的 DOM 以及 React 组件;
- 数组和 Fragment(片段):能够返回多个元素;
- Portals(插槽):能够将子元素渲染到不同的 DOM 子树种;
- 字符串和数字:被渲染成 DOM 中的 text 节点;
- 布尔值或 null:不渲染任何内容。
(4)componentDidMount()
componentDidMount()会在组件挂载后(插入 DOM 树中)立刻调。该阶段通常进行以下操作:
- 执行依赖于 DOM 的操作;
- 发送网络申请;(官网倡议)
- 增加订阅音讯(会在 componentWillUnmount 勾销订阅);
如果在 componentDidMount
中调用 setState
,就会触发一次额定的渲染,多调用了一次 render
函数,因为它是在浏览器刷新屏幕前执行的,所以用户对此是没有感知的,然而我该当防止这样应用,这样会带来肯定的性能问题,尽量是在 constructor
中初始化 state
对象。
在组件装载之后,将计数数字变为 1:
class App extends React.Component {constructor(props) {super(props)
this.state = {counter: 0}
}
componentDidMount () {
this.setState({counter: 1})
}
render () {
return (
<div className="counter">
counter 值: {this.state.counter} </div>
)
}
}
2)组件更新阶段
当组件的 props
扭转了,或组件外部调用了 setState/forceUpdate
,会触发更新从新渲染,这个过程可能会产生屡次。这个阶段会顺次调用上面这些办法:
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
(1)shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)
在说这个生命周期函数之前,来看两个问题:
- setState 函数在任何状况下都会导致组件从新渲染吗?例如上面这种状况:
this.setState({number: this.state.number})
- 如果没有调用 setState,props 值也没有变动,是不是组件就不会从新渲染?
第一个问题答案是 会,第二个问题如果是父组件从新渲染时,不论传入的 props 有没有变动,都会引起子组件的从新渲染。
那么有没有什么办法解决在这两个场景下不让组件从新渲染进而晋升性能呢?这个时候 shouldComponentUpdate
退场了,这个生命周期函数是用来晋升速度的,它是在从新渲染组件开始前触发的,默认返回 true
,能够比拟 this.props
和 nextProps
,this.state
和 nextState
值是否变动,来确认返回 true 或者 false
。当返回 false
时,组件的更新过程进行,后续的 render
、componentDidUpdate
也不会被调用。
留神: 增加 shouldComponentUpdate
办法时,不倡议应用深度相等查看(如应用 JSON.stringify()
),因为深比拟效率很低,可能会比从新渲染组件效率还低。而且该办法保护比拟艰难,倡议应用该办法会产生显著的性能晋升时应用。
(2)getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState)
这个办法在 render
之后,componentDidUpdate
之前调用,有两个参数 prevProps
和 prevState
,示意更新之前的 props
和 state
,这个函数必须要和 componentDidUpdate
一起应用,并且要有一个返回值,默认是 null
,这个返回值作为第三个参数传给 componentDidUpdate
。
(3)componentDidUpdate
componentDidUpdate() 会在更新后会被立刻调用,首次渲染不会执行此办法。该阶段通常进行以下操作:
- 当组件更新后,对 DOM 进行操作;
- 如果你对更新前后的 props 进行了比拟,也能够抉择在此处进行网络申请;(例如,当 props 未发生变化时,则不会执行网络申请)。
componentDidUpdate(prevProps, prevState, snapshot){}
该办法有三个参数:
- prevProps: 更新前的 props
- prevState: 更新前的 state
- snapshot: getSnapshotBeforeUpdate()生命周期的返回值
3)组件卸载阶段
卸载阶段只有一个生命周期函数,componentWillUnmount() 会在组件卸载及销毁之前间接调用。在此办法中执行必要的清理操作:
- 革除 timer,勾销网络申请或革除
- 勾销在 componentDidMount() 中创立的订阅等;
这个生命周期在一个组件被卸载和销毁之前被调用,因而你不应该再这个办法中应用 setState
,因为组件一旦被卸载,就不会再装载,也就不会从新渲染。
4)错误处理阶段
componentDidCatch(error, info),此生命周期在后辈组件抛出谬误后被调用。它接管两个参数∶
- error:抛出的谬误。
- info:带有 componentStack key 的对象,其中蕴含无关组件引发谬误的栈信息
React 常见的生命周期如下:React 常见生命周期的过程大抵如下:
- 挂载阶段,首先执行 constructor 构造方法,来创立组件
- 创立实现之后,就会执行 render 办法,该办法会返回须要渲染的内容
- 随后,React 会将须要渲染的内容挂载到 DOM 树上
- 挂载实现之后就会执行 componentDidMount 生命周期函数
- 如果咱们给组件创立一个 props(用于组件通信)、调用 setState(更改 state 中的数据)、调用 forceUpdate(强制更新组件)时,都会从新调用 render 函数
- render 函数从新执行之后,就会从新进行 DOM 树的挂载
- 挂载实现之后就会执行 componentDidUpdate 生命周期函数
- 当移除组件时,就会执行 componentWillUnmount 生命周期函数
React 次要生命周期总结:
- getDefaultProps:这个函数会在组件创立之前被调用一次(有且仅有一次),它被用来初始化组件的 Props;
- getInitialState:用于初始化组件的 state 值;
- componentWillMount:在组件创立后、render 之前,会走到 componentWillMount 阶段。这个阶段我集体始终没用过、十分鸡肋。起初 React 官网曾经不举荐大家在 componentWillMount 里做任何事件、到当初 React16 间接废除了这个生命周期,足见其鸡肋水平了;
- render:这是所有生命周期中惟一一个你必须要实现的办法。一般来说须要返回一个 jsx 元素,这时 React 会依据 props 和 state 来把组件渲染到界面上;不过有时,你可能不想渲染任何货色,这种状况下让它返回 null 或者 false 即可;
- componentDidMount:会在组件挂载后(插入 DOM 树中后)立刻调用,标记着组件挂载实现。一些操作如果依赖获取到 DOM 节点信息,咱们就会放在这个阶段来做。此外,这还是 React 官网举荐的发动 ajax 申请的机会。该办法和 componentWillMount 一样,有且仅有一次调用。
React-Router 的实现原理是什么?
客户端路由实现的思维:
-
基于 hash 的路由:通过监听
hashchange
事件,感知 hash 的变动- 扭转 hash 能够间接通过 location.hash=xxx
-
基于 H5 history 路由:
- 扭转 url 能够通过 history.pushState 和 resplaceState 等,会将 URL 压入堆栈,同时可能利用
history.go()
等 API - 监听 url 的变动能够通过自定义事件触发实现
- 扭转 url 能够通过 history.pushState 和 resplaceState 等,会将 URL 压入堆栈,同时可能利用
react-router 实现的思维:
- 基于
history
库来实现上述不同的客户端路由实现思维,并且可能保留历史记录等,磨平浏览器差别,下层无感知 - 通过保护的列表,在每次 URL 发生变化的回收,通过配置的 路由门路,匹配到对应的 Component,并且 render
React 的事件和一般的 HTML 事件有什么不同?
区别:
- 对于事件名称命名形式,原生事件为全小写,react 事件采纳小驼峰;
- 对于事件函数解决语法,原生事件为字符串,react 事件为函数;
- react 事件不能采纳 return false 的形式来阻止浏览器的默认行为,而必须要地明确地调用
preventDefault()
来阻止默认行为。
合成事件是 react 模仿原生 DOM 事件所有能力的一个事件对象,其长处如下:
- 兼容所有浏览器,更好的跨平台;
- 将事件对立寄存在一个数组,防止频繁的新增与删除(垃圾回收)。
- 不便 react 对立治理和事务机制。
事件的执行程序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为须要冒泡到 document 上合成事件才会执行。