美文网首页redux学习Web前端之路让前端飞
翻译|从提高性能角度优化react-redux store

翻译|从提高性能角度优化react-redux store

作者: smartphp | 来源:发表于2017-04-27 23:45 被阅读191次

翻译|从提高性能角度优化react-redux store
原文请见

译注:一旦在React的程序中引入Redux框架,application的逻辑关注点就转移到了Redux store中.所有的与application有关的逻辑都在store中集中处理.当你对一个事物有了一定了解的时候,你会发现原来世界上有如此之多的事物不管是虚拟的,还是实际存在的,运行原理竟然高度相似.程序开发中的数据库系统和实际生活中的商店(store,supermarket)的运作方式几乎完全一样.

  • 低档商店,天桥上卖针头线脑的老奶奶的货架,就是一个map,货物无序组织在一个平面上,由于货物少,存取的方法也很简单,直接手到擒来。
  • 中档的便利店,货物丰富,再用平面管理就不行了,于是引入了维度,就是分类系统. 食品,饮料,报刊杂志等等,每种货物都属于一个分类,需要找到的时候,可以先找类,再找具体的货物。便利店往往都会在收银台贴出一个分类的指示内容,这个就是索引. 比尔.盖茨先生的计算机启蒙其实就是从图书馆分类系统开始的.所以这个分类系统蕴含着计算机数据库的初步模型。
  • 更高层次的电商超市,就不在货架上存储实际的货物了,每个货架的位置这是仓库中的位置,就是完全的索引.

在程序设计中就应该要如何来组织数据. 从这个角度来看,Redux中的数据集中管理需要数据库的先进概念的支持是必须的.在React/React-native的设计中要在store的 state结构上倾注一定精力.

下面就看看这篇文章怎么来改进store从而提高React的渲染能力.

翻译开始


怎么来结构化redux的store来提高渲染性能?
本文是在React中渲染系列元素的一些优化方法的后续篇

提示: 这篇文章是专门用来优化React+Redux应用的. 并且专门用于优化react-redux v5. 由于react-redux v4的内部监听器太慢,这里的优化方法对他不起作用.

在application中存储一组元素最最普遍的方法就是作为一个数组.

可能你有一个组件需要渲染一组元素.

如果你需要更新其中之一,你就不得不更新整个列表:

每一个条目的更新都会更新整个“TargetList”的视图:

我创建了使用这种渲染方式的代码
http://codepen.io/lavrton/pen/xRgYbL

const {Stage, Layer, Circle, Group} = ReactKonva;


function generateTargets() {
  return _.times(1000, (i) => {//underscore的方法
    return {
      id: i,
      x: Math.random() * window.innerWidth,
      y: Math.random() * window.innerHeight,
      radius: 2 + Math.random() * 5,
      color: Konva.Util.getRandomColor()
    };
  });
}

// for for test case our logic will be very simple
// just one action UPDATE for updating radius of target
//为了测试,只有一个action来改变目标item的半径
function appReducer(state, action) {
  if (action.type === 'UPDATE') {
    const i = _.findIndex(state.targets, (t) => t.id === action.id);//想找的要更新元素的索引
    const updatedTarget = {//构造更新项目
      ...state.targets[i],
      radius: action.radius
    };
    state = {
      targets: [ //执行更新,下面的改进就在这个地方
        ...state.targets.slice(0, i),//js数组插入的方法
        updatedTarget,
        ...state.targets.slice(i + 1)
      ]
    }
  }
  return state;
}

const initialState = {
  targets: generateTargets()
};
// create redux store
const store = Redux.createStore(appReducer, initialState);



class Target extends React.Component {
  shouldComponentUpdate(newProps) {
    return this.props.target !== newProps.target;
  }
  render() {
    const {x, y, color, radius} = this.props.target;
    return (
      <Group x={x} y={y}>
        <Circle
          radius={radius}
          fill={color}
          />
        <Circle
          radius={radius * 1 / 2}
          fill="black"
          />
        <Circle
          radius={radius * 1 / 4}
          fill="white"
          />
      </Group>
    );
  }
}

// top component with list of targets
class App extends React.Component {
  render() {
    const targets = this.props.targets.map((target) => {
      return <Target key={target.id} target={target} />;
    });
    const width = window.innerWidth;
    const height = window.innerHeight;
    return (
      <Stage width={width} height={height}>
        <Layer hitGraphEnabled={false}>
          {targets}
        </Layer>
      </Stage>
    );
  }
}

const AppWrap = ReactRedux.connect((s) => ({ targets: s.targets }))(App);

ReactDOM.render(
  <ReactRedux.Provider store={store}>
    <AppWrap />
  </ReactRedux.Provider>,
  document.getElementById('container')
);

const N_OF_RUNS = 500;
const start = performance.now();
_.times(N_OF_RUNS, () => {
  const id = 1;
  let oldRadius = store.getState().targets[id].radius;
  // update redux store
  store.dispatch({ type: 'UPDATE', id, radius: oldRadius + 0.5 });
});
const end = performance.now();

console.log('sum time', end - start);
console.log('average time', (end - start) / N_OF_RUNS);


更新长度为1000的数组中的其中一条,在我的机器上需要花费大约21ms的时间.
之前的文章中,我描述了一个做类似渲染的优化方法,就是让子组件”智能化”,在React组件的生命周期函数”shouldComponentUpdate”中添加一些逻辑.但是在store中改变state的结构方法更简单,还可以获得同样能的结果.

怎么去优化?

如果你正在使用react-redux,通过改变state的结构就你能简单的提升性能:

然后我们需要改变一下“TargetList”:

请注意:在这个实例中,我正把item ID传递到子视图中,并没有传递给整个item.在这个实例中”TargetView”不能是傻瓜组件.他需要连接到store:

正因为“TargetView”被连接到store,当“目标”数据被更新的时候,他将会自动进行更新.如果我们要更新一个列表中的一项,这种办法非常重要.”TargetList”不会被更新,因为“targetsOrder”是一样的.在同一个实例中,性能有很大的提高
http://codepen.io/lavrton/pen/ZBLrWp

 const {Stage, Layer, Circle, Group} = ReactKonva;



function generateTargets() {
  return _.times(1000, (i) => {
    return {
      id: i,
      x: Math.random() * window.innerWidth,
      y: Math.random() * window.innerHeight,
      radius: 2 + Math.random() * 5,
      color: Konva.Util.getRandomColor()
    };
  });
}

// for for test case our logic will be very simple
// just one action UPDATE for updating radius of target
function appReducer(state, action) {
  if (action.type === 'UPDATE') {
    state = {
      ... state,
      targets: {
        ...state.targets,
        [action.id]: {
          ...state.targets[action.id],
          radius: action.radius
        }
      }
    }
  }
  return state;
}


const initialTargets = generateTargets();
const targets = {};
initialTargets.forEach((t) => {
  targets[t.id] = t;
});

const initialState = {
  targetsOrder: initialTargets.map(t => t.id),
  targets: targets
};
// create redux store
const store = Redux.createStore(appReducer, initialState);



class Target extends React.Component {
  render() {
    const {x, y, color, radius} = this.props.target;
    return (
      <Group x={x} y={y}>
        <Circle
          radius={radius}
          fill={color}
          />
        <Circle
          radius={radius * 1 / 2}
          fill="black"
          />
        <Circle
          radius={radius * 1 / 4}
          fill="white"
          />
      </Group>
    );
  }
}

const TargetWrap = ReactRedux.connect((s, ownProps) => ({target: s.targets[ownProps.targetId]}))(Target);

// top component with list of targets
class App extends React.Component {
  render() {
    const targets = this.props.targetsOrder.map((targetId) => {
      return <TargetWrap key={targetId} targetId={targetId} />;
    });
    const width = window.innerWidth;
    const height = window.innerHeight;
    console.log('render list');
    return (
      <Stage width={width} height={height}>
        <Layer hitGraphEnabled={false}>
          {targets}
        </Layer>
      </Stage>
    );
  }
}

const AppWrap = ReactRedux.connect((s) => ({ targetsOrder: s.targetsOrder }))(App);

ReactDOM.render(
  <ReactRedux.Provider store={store}>
    <AppWrap />
  </ReactRedux.Provider>,
  document.getElementById('container')
);

const N_OF_RUNS = 500;
const start = performance.now();
_.times(N_OF_RUNS, () => {
  const id = 1;
  let oldRadius = store.getState().targets[id].radius;
  // update redux store
  store.dispatch({ type: 'UPDATE', id, radius: oldRadius + 0.5 });
});
const end = performance.now();

console.log('sum time', end - start);
console.log('average time', (end - start) / N_OF_RUNS);


更新一个单独的条目在我的机器上只花费了2.2ms.几乎比使用前一种state的结构快十倍.

译注:实际上是列表中每一项都根据自己的ID订阅了state的变化,当某个item的state变化是,只有单个的item可以根据ID来感知state的变化,从而进行渲染操作,其他item感知不到变化,不会发生渲染,

相关文章

网友评论

    本文标题:翻译|从提高性能角度优化react-redux store

    本文链接:https://www.haomeiwen.com/subject/hcxozttx.html