zoukankan      html  css  js  c++  java
  • React与Preact差异之 -- setState

    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,轻量、快速、易用的移动端组件库,等你来用~ 【传送门】

  • 相关阅读:
    开淘店记录
    广告轮播效果
    loading事件加载效果
    正则表达式摘要
    cookie存取数据分析
    js 空格与回车处理
    数据对象型转换为数组型
    变换闪烁效果
    eclipse配置新环境
    五小时轻松入门Python
  • 原文地址:https://www.cnblogs.com/zhang740/p/7714625.html
Copyright © 2011-2022 走看看