Skip to content
New issue

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 and Redux 性能,框架优化总结 #2

Open
aototo opened this issue Mar 10, 2017 · 3 comments
Open

React and Redux 性能,框架优化总结 #2

aototo opened this issue Mar 10, 2017 · 3 comments

Comments

@aototo
Copy link
Owner

aototo commented Mar 10, 2017

React and Redux 性能,框架优化总结


React

  1. 利用React Server Render 提高首屏的渲染速度

    • 利于SEO
    • 加速首屏的渲染时间
    • 前后端共享数据源

    使用Reacr.renderToString, React.renderToStaticMarkup

  2. 请将方法的bind一律置于constructor,避免多次bind。

  3. 请只传递component需要的props ,切勿一股脑的<Component {...props} />

  4. 不需要传入状态的component写成const element的形式,这样能加快这个element的初始渲染速度。

  5. dom上设置可被react识别的同级唯一key,否则情况可能不会重新渲染。

  6. 使用`Stateless Functional Component 无状态组件

    • Class并无必要
    • 没有this关键字
    • 无状态组件写起来代码量更少,
    • 便于测试

React 的无状态组件优雅的实现可复用组件的方式。
栗子如下:

const Pane = (props) => <div>{props.children}</div>;

Pane.propTypes = {
  label: React.PropTypes.string.isRequired,
  children: React.PropTypes.element.isRequired
};
  1. 使用pureRender,避免组件没有意义的渲染,配合immutable,减少渲染。

  2. 使用react-css-modules,解决了命名混乱,全局污染以及依赖管理的问题,多人协同开发有时候难免会发生样式上的冲突。
    有个需要注意的地方,下面的2个顺序如果颠倒,就会出错。

@connect(mapStateToProps, mapDispatchToProps)
@CSSModules(styles)

React-router && Webpack

按需加载模块

把这个按需写着这里,本身需要react-router支持,索性就放在这边了。
加载函数:

require.ensure(dependencies, callback, chunkName)

//这里react-router 使用require.ensure,当然了webpack需要配置一下。
<Route path="home" getComponent={(location, callback) => {
  require.ensure([], require => {
    callback(null, require('modules/home'))
  }, 'home')  
}}></Route>

具体看webpack官方
看不懂看这篇webpack 按需打包


Redux

Data

项目数据扁平化,不扁平化带来的问题:

  1. 数据拷贝比较更耗时
  2. 获取数据的时候比较麻烦

通过redux 的combineReducers 可以很好的扁平化数据。如果使用immutable的话整个侵入性非常的强,不仅要修改combineReducers(因为combineReducers实现就是可变的数据),还需要注意获取数据的时候是否是不可变,以免是null。如果使用immutable可以推荐使用redux-immutable。

return {
	...state,
	newData
}

上面这种写法本身也算是一种immutable,但是要求数据层级不能太深。如果数据相对复杂建议使用immutable。

推荐pure-render-decorator方便的来控制组件渲染。

	import pureRender from 'pure-render-decorator';
	@pureRender
	class ...

使用immutable的时候数据转换如下:

  • 可变转不变可以在redux的reducer执行
  • 不变转可变在mapStateToProps and getState()执行

immutableJs
Immutable 详解及 React 中实践
redux-immutable
seamless-immutable体积更小,兼容相对好。只支持Arrays and Objects。


数据筛选reselect

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

每当store发生改变的时候,connect就会触发重新计算,为了减少重复的不必要计算,减少大型项目的性能开支,需要对selector函数做缓存。推荐使用reactjs/reselect, 缓存的部分实现代码如下。

export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
  let lastArgs = null
  let lastResult = null
  const isEqualToLastArg = (value, index) => equalityCheck(value, lastArgs[index])
  return (...args) => {
    if (
      lastArgs === null ||
      lastArgs.length !== args.length ||
      !args.every(isEqualToLastArg)
    ) {
      lastResult = func(...args)
    }
    lastArgs = args
    return lastResult
  }
}

If the values of the input-selectors are the same as the previous call to the selector, it will return the previously computed value instead of calling the transform function.

假如state.todos 中todos 提供的数据没有发生改变,就会return之前计算好的结果,这样就可以少去非常多的计算成本。
具体的实现可以去看https://github.com/reactjs/reselect#creating-a-memoized-selector。

具体用法:以下修改reudx官方的的demo

import { createSelector } from 'reselect'

const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos

export const getVisibleTodos = createSelector(
  [ getVisibilityFilter, getTodos ],
  (visibilityFilter, todos) => {
    switch (visibilityFilter) {
      case 'SHOW_ALL':
        return todos
      case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed)
      case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed)
    }
  }
)

Batched actions

如果我们需要同时发送很多action,比如:

dispatch(action1)
dispatch(action2)
dispatch(action3)

可以减少不必要的计算,推荐用到redux-batched-actions

dispatch(batchActions[action1, action2, action3])

源码很简单
https://github.com/tshelburne/redux-batched-actions/blob/master/src/index.js#L7


Redux DevTools

开发中使用DevTools,建议使用谷歌的插件,不建议在页面结构中插入DevTools。
redux-devtools-extension

在开发环境以及产品环境中移除devTools,避免不必要性能开销和文件大小。
栗子如下:

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./configureStore.prod');
} else {
  module.exports = require('./configureStore.dev');
}

注意: 需要在webpack中使用DefinePlugin 插件。

new webpack.DefinePlugin({
  'process.env': {
    NODE_ENV: JSON.stringify('production')
  }
}),

相关

  • 移动端开发的时候请禁止redux-devtools,会造成卡顿

以往收集的文章,博客

阮一峰的redux 入门
ReactJS组件间沟通的一些方法
聊一聊基于Flux的前端系统
React 性能工程
聊一聊基于Flux的前端系统
React性能工程-- 深入研究React性能调试
A Better File Structure For React/Redux Applications
React服务器端渲染实践小结
dva 项目解决方案
Getting Started with Redux
React 实践心得:react-redux 之 connect 方法详解
REACT&REDUX中SCROLL LIST封装实践
redux-axios-middleware axios兼容ie9,提供promise ,很方便。
深入理解 react-router 路由系统
Immutable 详解及 React 中实践
webpack+ react-router 按需加载

大概的整个项目具体的优化就这些,细节的插件和实施大家自己去看文档。后续继续更新,喜欢的朋友star支持一下。

@aototo aototo changed the title React and Redux 性能优化总结 React and Redux 性能,框架优化总结 Mar 10, 2017
@tangkunyin
Copy link

@asd0102433

通过redux 的combineReducers 可以很好的扁平化数据。如果使用immutable的话整个侵入性非常的强,不仅要修改combineReducers(因为combineReducers实现就是可变的数据),还需要注意获取数据的时候是否是不可变,以免是null。如果使用immutable可以推荐使用redux-immutable

这是说引入了redux-immutable替代了redux自己的combineReducers后,还需要手动引入immutablejs库?

@aototo
Copy link
Owner Author

aototo commented Apr 10, 2017

@tangkunyin 需要哈!

@tangkunyin
Copy link

@asd0102433 三克斯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants