您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
React与Preact差异之 -- setState
 
作者: SuperEVO
   次浏览      
 2021-3-9 
 
编辑推荐:
本文主要源码分析包含:React、Preact,接着说明了Preact和React相同之处和不同之处。
本文来自于博客园,由火龙果软件Anna编辑推荐。

Preact是React的轻量级实现,是React比较好的替代者之一,有着体积小的优点,当然与React之间一定会存在实现上的差异,本文介绍了在 setState 方面的差异之处。

源码分析

首先来分析下React以及Preact在setState部分的具体实现。

(太长不看想偷懒,可以直接下翻看结论)

React

关键代码:

setState 阶段:

// ReactUpdateQueue.js
enqueueSetState: function(publicInstance, partialState) {
...

var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState);

enqueueUpdate(internalInstance);
}

可以看到React在 setState 的时候不会做任何处理,会把变更直接放到一个专门处理 state 的队列里供组件更新时使用。

更新阶段:

// ReactCompositeComponent.js
updateComponent: function(
transaction,
prevParentElement,
nextParentElement,
prevUnmaskedContext,
nextUnmaskedContext,
) {
var inst = this._instance;
...

var willReceive = false;
var nextContext;

if (this._context === nextUnmaskedContext) {
nextContext = inst.context;
} else {
nextContext = this._processContext (nextUnmaskedContext);
willReceive = true;
}

var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;

if (prevParentElement !== nextParentElement) {
willReceive = true;
}

if (willReceive && inst.componentWillReceiveProps) {
...
inst.componentWillReceiveProps (nextProps, nextContext);
}

// 在此处才计算 nextState
var nextState = this._processPendingState (nextProps, nextContext); // 此处传入了 nextProps
var shouldUpdate = true;

if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
...
shouldUpdate = inst.shouldComponentUpdate(
nextProps,
nextState,
nextContext,
);
} else {
if (this._compositeType === CompositeTypes.PureClass) { // 敲黑板,知识点 —— 如果你的组件没实现shouldComponentUpdate,那么把React.Component 换成 React.PureComponent 可以获得基础版优化,提高性能。
shouldUpdate =
!shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState); // 浅比较,可以抄去自己改成属性黑/白名单版
}
}
}
...
}

// ReactCompositeComponent.js
_processPendingState: function(props, context) { // props: nextProps
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = false;
this._pendingStateQueue = null;

if (!queue) {
return inst.state;
}

if (replace && queue.length === 1) {
return queue[0];
}

var nextState = Object.assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
Object.assign(
nextState,
typeof partial === 'function'
? partial.call (inst, nextState, props, context) // nextProps
: partial,
);
}

return nextState;
}

 

通过上面组件更新的流程代码可以看到:

在 updateComponent 中,在 componentWillReceiveProps 之后才会计算 nextState,所以在 componentWillReceiveProps 中 setState 是可以在当次更新中生效的。

在 _processPendingState 会对队列里的 state 进行叠加,如果修改是函数方式,此处传入的state参数是 nextState,props 是 nextProps。

Preact

关键代码:

setState 阶段:

// component.js
setState(state, callback) {
let s = this.state;
if (!this.prevState) this.prevState = extend({}, s);
extend(s, typeof state==='function' ? state(s, this.props) : state);
if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
enqueueRender(this);
}

实现的简单粗暴,在 setState 的时候就进行了合并,会立即改写 this.state,在第一次 setState 时会保留 state 状态到 prevState。由于是立即合并state,如果入参state是函数,props 将只是当前 this.props。

更新阶段:

export function renderComponent (component, opts, mountAll, isChild) {
...
previousProps = component.prevProps || props,
previousState = component.prevState || state,
previousContext = component.prevContext || context,
...

// if updating
if (isUpdate) {
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (opts!==FORCE_RENDER
&& component.shouldComponentUpdate
&& component.shouldComponentUpdate (props, state, context) === false) {
skip = true;
}
else if (component.componentWillUpdate) {
component.componentWillUpdate (props, state, context);
}
component.props = props;
component.state = state;
component.context = context;
}
...
}

在更新流程前提取了旧 state,shouldComponentUpdate、componentWillUpdate 之后还原回新值,所以在 shouldComponentUpdate 生命周期中,this.props 将获取的是 prevProps,这里与 React 的逻辑并不一致。

划重点

相同点:

在 componentWillReceiveProps 中 setState 都会应用到 nextState。

在 shouldComponentUpdate 中 setState 都不会应用到 nextState,但是可以直接操作传入的 nextState。

不同点:

React下 setState 的值不会立即生效,会一直积累到 componentWillReceiveProps,在此之后会进行合并,并提供给后续生命周期。而Preact下 setState 会立即反映到 this.state,但是,在更新组件的生命周期到 render 前(eg: shouldComponentUpdate), this.state 将会是 prevState。

shouldComponentUpdate 阶段 setState 虽然不会影响到最终 state 的值,但是Preact下会影响 this.state 的值,比如之后 componentWillUpdate 中的 this.state, 总之此阶段不要 setState 反正也没用。

setState 如果使用函数修改,Preact下传入的 props 将会是 prevProps,而React中是 nextProps,在 componentWillReceiveProps 中 setState 时要注意。

总结

如果你写的工程需要同时兼容React及Preact的话:

不要利用React下 setState 在同一次组件更新执行前 state 不立即更新的特性,注意多个 setState 之间是否影响,必要时手动保存旧值。

在组件更新生命周期内,除 componentWillReceiveProps 之外不要使用 setState,提供了 nextState 的生命周期,可以直接修改 nextState。

尽量避免使用 setState 函数修改方式,在 componentWillReceiveProps 中使用时,使用生命周期中的 prevProps(this.props) 和 nextProps。

p.s: antd-mobile 2.0正式版已发布,同时兼容react、preact,轻量、快速、易用的移动端组件库,等你来用

 

 
   
次浏览       
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]
 
最新文章
如何设计高扩展的在线网页制作平台
electron入门心得
使用 Electron 构建桌面应用
VUE.JS组件化开发实践
深入理解JSCore
最新课程
HTML 5 + CSS3 原理与开发应用
Web前端高级工程师必备技能实战
Vue大型项目开发实战
React原理与实践
Vue.js进阶与案例实践
更多...   
成功案例
中交集团 构建Web自动化测试框架
某著名电信公司 Vue.js进阶与案例
国电通网络技术 HTML5+CSS3 +web前端框
移动通信 移动互联网应用开发原理
某电力行 android开发平台最佳
更多...