We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
说说react的virtual dom及其diff的实现方式
当下react中的virtual dom主要使用Fiber架构实现,核心包括fiber数据结构以及调度器。
react
virtual dom
Fiber
fiber
fiber数据结构可以说是一种升级版virtual dom,采用的是链表的方式,其中child属性指向子fiber对象,return指向父fiber对象,sibling指向兄弟fiber对象,并且真实DOM结构中每一个节点都对应一个fiber对象。
child
return
sibling
DOM
所谓调度器,主要处理两件事情,根据任务的优先级计算好过期时间以及采用requestIdleCallback方法实现调度。其中过期时间的计算是当前时间 + 优先级,实现的requestIdleCallback则是使用requestAnimationFrame和setTimeout实现。原理就是,将整个更新任务拆分成一个个小的任务,并且可控制这些任务的执行,高优先级的会比低优先级的优先执行,而且优先级高的还可以中断优先级低的。
requestIdleCallback
当前时间 + 优先级
requestAnimationFrame
setTimeout
至于diff的实现,原理上和Vue实现的一致,可观看这篇文章 【Vue 源码分析 】如何在更新 Patch 中进行 Diff。
diff
Vue
说一说react的生命周期
在react 16版本之前,生命周期主要分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段。
react 16
在react 16版本开始,已经抛弃了componentWillReceiveProps、componentWillMount和componentWillUpdate,引入了getDerivedStateFromProps、getSnapshotBeforeUpdate、componentDidCatch。
componentWillReceiveProps
componentWillMount
componentWillUpdate
getDerivedStateFromProps
getSnapshotBeforeUpdate
componentDidCatch
getDerivedStateFromProps:静态方法,存在目的是让组件在props变化时更新state。与组件本身无相关,无法直接访问组件上任何数据。触发机制是组件本身任何情况下的更新都会触发该回调函数(官方不推荐使用)。官方替代方案主要有两种:让组件变成完全受控组件和让组件变成不完全受控组件结合key属性(key值变化时,React会重新创建组件而非直接更新)。返回null则说明不需要更新state。
props
state
key
getSnapshotBeforeUpdate:在组件update之前发生,返回一个值,作为componentDidUpdate第三个参数。
update
componentDidUpdate
若需要根据props更新后直接请求后段获取数据,改用componentDidUpdate,参数为prevProps、prevState、snapshot,可直接根据prevProps与现在props判断该次更新是否为props更新导致。
prevProps
prevState
snapshot
react中组件有哪几种类型?那么创建组件又有哪几种方式?
react中组件类型有三种,分别是无状态组件、普通组件、PureComponent。
PureComponent
创建组件方式有函数式定义无状态组件、ES5中使用React.createClass创建、ES6使用class extends React.Component创建。
ES5
React.createClass
ES6
class extends React.Component
在react和vue中,组件上写上key的作用是什么?
由于react和vue采用的都是diff策略,而key的作用则可以在新老节点对比时准确地判断该节点是否需要重新渲染,避免了不必要的渲染从而提升性能。常常应用于列表以及组件,一旦key值变更,将直接重新创建该元素或组件而不是直接更新。
vue
聊聊setState机制
首先,setState的处理采取的是队列机制完成更新。当调用setState时,会将需要更新的state浅合并后放入状态队列,而不会立即更新state,队列机制会采取批量更新的形式对state进行更新。(当然为了处理这种情况,可以使用function作为参数模拟数据合并)
setState
function
先看下setState入更新队列源码。
function enqueueUpdate(component) { // ... if (!batchingStrategy.isBatchingUpdates) { batchingStaretegy.batchedUpdates(enqueueUpdate, component) return } dirtyComponents.push(component) } // 先简单看看batchingStrategy对象 var batchingStrategy = { isBatchingUpdates: false, batchedUpdates: function(callback, a, b, c, d, e) { // ... batchingStrategy.isBatchingUpdates = true; transaction.perform(callback, null, a, b, c, d, e); } }
简单滴讲,在生命周期调用setState前,其实就已经进入了enqueueUpdate方法并且设置isBatchingUpdates为true。因此当直接调用setState时直接丢进了dirtyComponent中而不会立马更新处理。
enqueueUpdate
isBatchingUpdates
true
dirtyComponent
总结下来就是,如果是由React引发的事件处理(比如通过onClick引发的事件处理或生命周期),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
至于setState机制,简单总结便是,React会将任务全部放进一个队列中,根据isBatchingUpdates标记判断是否同步更新state状态,默认为false,表示setState会同步更新状态。实现上,只有React中引发的事件处理过程才会异步处理,其他处理都会同步处理。
React
false
详细可以看看这里https://github.com/sisterAn/blog/issues/26。
谈谈对react批量更新(batchUpdates)理解?
直接参考一下从源码全面剖析 React 组件更新机制。
react如何区别component和dom?
使用ReactDom的render方法可以确认。根据在render方法中传入的第一个参数来确定,当传入的是jsx时,就会使用React.createElement来创建相应的虚拟DOM,只有当传入的是函数式组件或class组件就会认为这就是一个组件。
ReactDom
render
jsx
React.createElement
class
说说react组件间的通信方式都有哪些
父向子间通信
使用props传递数据。
子向父间通信
使用props传递的callback。
callback
跨级间通信
使用context通信。
context
无任何嵌套关系间通信
使用EventEmitter自定义事件机制。
EventEmitter
使用Redux数据管理。
Redux
如何将一个子组件单独渲染出来和父组件平级关系?
使用unstable_renderSubtreeIntoContainer实现
unstable_renderSubtreeIntoContainer
export default class A extends Component { componentDidMount() { this.parentElement = ReactDOM.findDOMNode(this).parentElement; this.renderChild(); } componentDidUpdate() { this.renderChild(); } renderChild() { const renderACom = ( <div> {this.render()} {this.props.children} </div> ) ReactDOM.unstable_renderSubtreeIntoContainer( this, renderACom, this.parentElement ); } render() { return <div>a组件</div>; } }
使用createPortal实现
createPortal
export default class A extends Component { componentDidMount() { this.forceUpdate(); } render() { const renderACom = ( <div> <div>a组件</div> {this.props.children} </div> ) if (!ReactDOM.findDOMNode(this)) { return <div></div>; } return ReactDOM.createPortal( renderACom, ReactDOM.findDOMNode(this).parentElement ); } }
react组件都有哪些优化手段?
shouldComponentUpdate
说说react的事件机制
react基于 Virtual DOM 实现了一个 SyntheticEvent(合成事件)层,组件中定义的事件处理器会接收到一个 SyntheticEvent 对象的实例,与原生的浏览器事件一样拥有同样的接口,同样支持事件的冒泡机制。
它与原生事件区别主要在于驼峰规则、处理对象(合成事件处理的是函数,而原生事件处理的却是字符串)。
另外,合成事件的实现中采用的是事件代理机制。不会把处理函数直接绑定到真实的节点上,而是把所有事件绑定到结构的最外层,使用一个统一的事件监听器,该事件监听器维持了一个映射来保存所有组件内部的事件监听和处理函数。当事件发生时,首先被该统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用,当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象。
事件代理
有了解过Fiber吗?
Fiber架构可以说是react在虚拟DOM上的一个重构。主要包含的内容就是fiber数据结构和调度器。处理的目标是针对动画、布局和手势。
通过将渲染任务进行拆分小任务,并根据各自的优先级计算好过期时间,采用增量渲染的方式,每次只执行一小段渲染任务,然后把任务放回给主线程,这样就可以很好滴避免由于长时间渲染而导致主线程被阻塞。
有兴趣的童鞋可以看看这位童鞋的分享,我觉得真的讲的很好。这可能是最通俗的 React Fiber(时间分片) 打开方式。
谈谈高阶组件和基类
高阶组件是将组件作为参数,通过包裹的形式,为传入的组件增加功能并返回一个新的react组件。
基类是可为子类继承方法或属性的一个类。
在react中推崇的是组合而非继承,因此常常推荐使用的是高阶组件。其中高阶组件常用地方主要体现在属性代理组件、反向继承组件以及组合多个高阶组件。
谈谈React.Component、React.PureComponent和无状态组件间区别
React.Component作为一个常见组件写法,可拥有单独的状态state以及生命周期,但有一个缺陷就是,父组件更新即使传入的props数据不变,也会直接影响该组件重新渲染。
React.Component
React.PureComponent就是在React.Component上的一个升级,使用生命周期shouldComponentUpdate对传入数据props进行浅层比较,若无变化就不会重新渲染该组件,其他行为和React.Component一致。
React.PureComponent
无状态组件只接受数据props的传入,不会存在任何组件内部状态state以及生命周期,只负责UI的渲染,因此更多推荐的是使用无状态组件。
UI
react-router的实现以及原理
先说下实现,react-router根据环境的不同采取不同的实现,主要分为三种情况,分别是:
react-router
history
hash
createHashHistory
h5
createBrowserHistory
Node
memory
createMemoryHistory
接着说下hash和history之间区别。
URL
http
onhashchange
popState
react-router基本原理是:将URL对应Location对象,而UI则由React.Component决定,直接将Location与component之间的同步问题。
Location
component
redux和vuex的设计思想有什么异同?
共同点: 两者都是作为全局状态管理的工具库,思想都是差不多:
dispatch
action
redux
reducer
vuex
mutation
getter
区别:
commit
redux为什么要把reducer设计成纯函数?
首先针对的是引用类型,由于redux在源码的实现上对于引用类型只会进行浅比较,我们都知道深比较是很耗性能的。
所以若直接修改原数据返回一个同引用地址的对象(即只是更改它内容),在react会认为是一个没有人变化对象,就会导致无法触发重新渲染。
了解过redux-saga和dva吗?
redux-saga就是用于管理副作用如异步获取数据,访问浏览器缓存的一个中间件。其中reducer负责处理state更新,sagas负责协调异步操作。
redux-saga
sagas
dva是阿里体验技术开发的react应用框架,主要用于解决组件之间通信问题。在引入redux后,又由于redux没有异步操作,又需要引入redux-saga等插件,才有了dva的出现。
dva
dva = React-Router + Redux + Redux-saga
dva的最简结构:
import dva from 'dva'; const App = () => <div>Hello dva</div>; // 创建应用 const app = dva(); app.model(model) // 注册视图 app.router(() => <App />); // 启动应用 app.start('#root');
The text was updated successfully, but these errors were encountered:
No branches or pull requests
当下
react
中的virtual dom
主要使用Fiber
架构实现,核心包括fiber
数据结构以及调度器。fiber
数据结构可以说是一种升级版virtual dom
,采用的是链表的方式,其中child
属性指向子fiber
对象,return
指向父fiber
对象,sibling
指向兄弟fiber
对象,并且真实DOM
结构中每一个节点都对应一个fiber
对象。所谓调度器,主要处理两件事情,根据任务的优先级计算好过期时间以及采用
requestIdleCallback
方法实现调度。其中过期时间的计算是当前时间 + 优先级
,实现的requestIdleCallback
则是使用requestAnimationFrame
和setTimeout
实现。原理就是,将整个更新任务拆分成一个个小的任务,并且可控制这些任务的执行,高优先级的会比低优先级的优先执行,而且优先级高的还可以中断优先级低的。至于
diff
的实现,原理上和Vue
实现的一致,可观看这篇文章 【Vue 源码分析 】如何在更新 Patch 中进行 Diff。在
react 16
版本之前,生命周期主要分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段。在
react 16
版本开始,已经抛弃了componentWillReceiveProps
、componentWillMount
和componentWillUpdate
,引入了getDerivedStateFromProps
、getSnapshotBeforeUpdate
、componentDidCatch
。getDerivedStateFromProps
:静态方法,存在目的是让组件在props
变化时更新state
。与组件本身无相关,无法直接访问组件上任何数据。触发机制是组件本身任何情况下的更新都会触发该回调函数(官方不推荐使用)。官方替代方案主要有两种:让组件变成完全受控组件和让组件变成不完全受控组件结合key
属性(key值变化时,React会重新创建组件而非直接更新)。返回null则说明不需要更新state。getSnapshotBeforeUpdate
:在组件update
之前发生,返回一个值,作为componentDidUpdate
第三个参数。若需要根据
props
更新后直接请求后段获取数据,改用componentDidUpdate
,参数为prevProps
、prevState
、snapshot
,可直接根据prevProps
与现在props
判断该次更新是否为props
更新导致。react
中组件类型有三种,分别是无状态组件、普通组件、PureComponent
。创建组件方式有函数式定义无状态组件、
ES5
中使用React.createClass
创建、ES6
使用class extends React.Component
创建。由于
react
和vue
采用的都是diff
策略,而key
的作用则可以在新老节点对比时准确地判断该节点是否需要重新渲染,避免了不必要的渲染从而提升性能。常常应用于列表以及组件,一旦key
值变更,将直接重新创建该元素或组件而不是直接更新。首先,
setState
的处理采取的是队列机制完成更新。当调用setState
时,会将需要更新的state
浅合并后放入状态队列,而不会立即更新state
,队列机制会采取批量更新的形式对state
进行更新。(当然为了处理这种情况,可以使用function
作为参数模拟数据合并)先看下
setState
入更新队列源码。简单滴讲,在生命周期调用
setState
前,其实就已经进入了enqueueUpdate
方法并且设置isBatchingUpdates
为true
。因此当直接调用setState
时直接丢进了dirtyComponent
中而不会立马更新处理。总结下来就是,如果是由React引发的事件处理(比如通过onClick引发的事件处理或生命周期),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
至于
setState
机制,简单总结便是,React
会将任务全部放进一个队列中,根据isBatchingUpdates
标记判断是否同步更新state
状态,默认为false
,表示setState
会同步更新状态。实现上,只有React
中引发的事件处理过程才会异步处理,其他处理都会同步处理。详细可以看看这里https://github.com/sisterAn/blog/issues/26。
直接参考一下从源码全面剖析 React 组件更新机制。
使用
ReactDom
的render
方法可以确认。根据在render
方法中传入的第一个参数来确定,当传入的是jsx
时,就会使用React.createElement
来创建相应的虚拟DOM
,只有当传入的是函数式组件或class
组件就会认为这就是一个组件。父向子间通信
使用
props
传递数据。子向父间通信
使用
props
传递的callback
。跨级间通信
使用
context
通信。无任何嵌套关系间通信
使用
EventEmitter
自定义事件机制。使用
Redux
数据管理。使用
unstable_renderSubtreeIntoContainer
实现使用
createPortal
实现PureComponent
。shouldComponentUpdate
生命周期进行过滤。react
基于 Virtual DOM 实现了一个 SyntheticEvent(合成事件)层,组件中定义的事件处理器会接收到一个 SyntheticEvent 对象的实例,与原生的浏览器事件一样拥有同样的接口,同样支持事件的冒泡机制。它与原生事件区别主要在于驼峰规则、处理对象(合成事件处理的是函数,而原生事件处理的却是字符串)。
另外,合成事件的实现中采用的是
事件代理
机制。不会把处理函数直接绑定到真实的节点上,而是把所有事件绑定到结构的最外层,使用一个统一的事件监听器,该事件监听器维持了一个映射来保存所有组件内部的事件监听和处理函数。当事件发生时,首先被该统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用,当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象。Fiber
架构可以说是react
在虚拟DOM
上的一个重构。主要包含的内容就是fiber
数据结构和调度器。处理的目标是针对动画、布局和手势。通过将渲染任务进行拆分小任务,并根据各自的优先级计算好过期时间,采用增量渲染的方式,每次只执行一小段渲染任务,然后把任务放回给主线程,这样就可以很好滴避免由于长时间渲染而导致主线程被阻塞。
有兴趣的童鞋可以看看这位童鞋的分享,我觉得真的讲的很好。这可能是最通俗的 React Fiber(时间分片) 打开方式。
高阶组件是将组件作为参数,通过包裹的形式,为传入的组件增加功能并返回一个新的
react
组件。基类是可为子类继承方法或属性的一个类。
在
react
中推崇的是组合而非继承,因此常常推荐使用的是高阶组件。其中高阶组件常用地方主要体现在属性代理组件、反向继承组件以及组合多个高阶组件。React.Component
作为一个常见组件写法,可拥有单独的状态state
以及生命周期,但有一个缺陷就是,父组件更新即使传入的props
数据不变,也会直接影响该组件重新渲染。React.PureComponent
就是在React.Component
上的一个升级,使用生命周期shouldComponentUpdate
对传入数据props
进行浅层比较,若无变化就不会重新渲染该组件,其他行为和React.Component
一致。无状态组件只接受数据
props
的传入,不会存在任何组件内部状态state
以及生命周期,只负责UI
的渲染,因此更多推荐的是使用无状态组件。先说下实现,
react-router
根据环境的不同采取不同的实现,主要分为三种情况,分别是:history
实现。通过hash
实现,对应createHashHistory
方法。h5
里的history
实现,对应createBrowserHistory
方法。Node
环境下实现。通过存储在memory
里实现,对应createMemoryHistory
方法。接着说下
hash
和history
之间区别。hash
模式:hash
会被包含在URL
中,不会被包含在http
请求中,对后端完全没影响,即改变hash
不会重新加载页面。另外**hash
模式原理是onhashchange
事件**。history
模式:刚好相反,history
模式会去直接请求接口获取页面。history
模式则是通过popState
事件进行监听。react-router
基本原理是:将URL
对应Location
对象,而UI
则由React.Component
决定,直接将Location
与component
之间的同步问题。共同点: 两者都是作为全局状态管理的工具库,思想都是差不多:
state
存储状态。dispatch
分发action
触发数据的变更。redux
使用reducer
统一管理数据变化的操作,而vuex
中则是用mutation
管理。getter
形式获取状态。区别:
redux
采用中间件形式,而vuex
则使用在commit
前直接处理异步。redux
不会直接更改原数据,对于引用类型采取新建方式,而vuex
则是直接修改状态属性来触发更新操作。首先针对的是引用类型,由于
redux
在源码的实现上对于引用类型只会进行浅比较,我们都知道深比较是很耗性能的。所以若直接修改原数据返回一个同引用地址的对象(即只是更改它内容),在
react
会认为是一个没有人变化对象,就会导致无法触发重新渲染。redux-saga
就是用于管理副作用如异步获取数据,访问浏览器缓存的一个中间件。其中reducer
负责处理state
更新,sagas
负责协调异步操作。dva
是阿里体验技术开发的react
应用框架,主要用于解决组件之间通信问题。在引入redux
后,又由于redux
没有异步操作,又需要引入redux-saga
等插件,才有了dva
的出现。dva = React-Router + Redux + Redux-saga
dva
的最简结构:The text was updated successfully, but these errors were encountered: