美文网首页react
react项目优化

react项目优化

作者: 你的时间非常值钱 | 来源:发表于2018-09-27 01:31 被阅读0次

    前言

    传统的Diff算法O(N3),React Diff基于三大前提将复杂度降为O(N)

    1.tree diff,跨层dom操作比较少,结构不同直接销毁
    2.component diff,相同类型节点有相同树形结构,只做更新,不同类型直接销毁
    3.element diff,同层子元素根据唯一标识做区分

    一.代码优化

    1.key

    设置唯一稳定的key
    render() {
      return (
        <div>
          this.state.user.map(item => <div key={item.id}>{item.name}</div>)
        </div>  
      )
    }
    

    2.节点内容

    节点类型分两大类,一类是DOM元素类型(如div,span),另一类是React组件

    Dom元素比较属性和内容
    <div style={{color: 'red'}} onClick={() =>this.handleClick()}>变化前</div>
    ...
    // 变化后属性和内容都变化了
    <div style={{color: 'red'}} onClick={() =>this.handleClick()}>变化后</div>
    

    注意引用类型的比较

     {color: 'red'} === {color: 'red'}  // false
     var fun1 = () => {}
     var fun2 = () => {}
     fun1 === fun2  // false
    

    优化后,属性指向都不变

    const style = {color: 'red'}
    ...
    constructor(props) {
      super(props)
      this.handleClick = this.handleClick.bind(this)
    }
    ...
    render() {
      return {
        <div style={style} onClick={this.handleClick}>更改后</div>
      }
    }
    

    redux版本优化前,

    <TodoItem
      key={item.id}
      onRemove={() => onRemoveTodo(item.id)}
      ...
    
    ...
    

    redux版本优化后

    // 方法1
    <TodoItem
      key={item.id}
      onRemove={onRemoveTodo}
      id={item.id}
      ...
    >
    ...
    
    const mapDispatchToProps = (dispatch,ownProps) => ({
      onRemoveTodo: ownProps.onRemoveTodo(ownProps.id)),
    })
    
    // 方法2
    <TodoItem
      key={item.id}
      id={item.id}
      ...
    >
    ...
    const mapDispatchToProps = (dispatch,ownProps) => ({
      onRemoveTodo: () => dispatch(onRemoveTodo(ownProps.id)),
    })
    

    3.shouldComponentUpdate(nextProps,nextState)

    为了避免浪费多余的渲染,return false可以阻止组件的更新
    shouldComponentUpdate(nextProps,nextState) {
      return nextProps.isChange !== this.props.isChange   // true or false
    }
    

    PureComponent类内部也是用了shouldComponentUpdate

    // PureComponent内部
    if (this._compositeType === CompositeTypes.PureClass) {
      shouldUpdate = !shallowEqual(prevProps, nextProps)
      || !shallowEqual(inst.state, nextStat e);
    }
    return shouldUpdate;
    

    shallowEqual(浅比较),只做简单类型的判断。

    state = {
      arr = [1,2,3,4,5]
    }
    shouldComponentUpdate(nextProps,nextState) {
      return nextState.arr !== this.state.nextState // false
    }
    handleClick = () => {
     const { arr } = this.state
      arr.push(6)
        this.setState({
          arr,
      })
    }
    
    render () {
      ...
    }
    

    解决方法deepEqual,但如果变量嵌套深会对性能有消耗

    二.工具使用

    1.immutable(一种不可更改的数据)

    网上一般写法

    import { is } from 'immutable' 
    ...
      shouldComponentUpdate = (nextProps = {} , nextState = {}) => {
        if(Object.keys(this.props).length !== Object.keys(nextProps).length ||
            Object.keys(this.state).length !== Object.keys(nextState).length
        ) {
          return true
        }
        for(const key in nextProps) {
          if(this.props[key] !== nextProps[key] || !is(this.props[key],nextProps[key])) {
            return true
          }
        }
        for(const key in nextState) {
          if(this.state[key] !== nextState[key] || !is(this.state[key],nextState[key])) {
            return true
          }
        }
        return false
      }
    ...
    

    我项目中优化前后对比

    
      shouldComponentUpdate = (nextProps = {} , nextState = {}) => {
        if(!is(nextState,this.state)) {
          return true
        }
        return false
      }
    
    优化前.png
    优化后.png

    使用了结构共享,避免deepCopy,可节省内存

    2.reselect

    一种选择器中间件,认为输入参数state相同,就没必要进行计算,直接抽取以往的值

    const mapState = (state)=>({
      todos:state.todos,
      filter:state.filter,
      visibleTodos:getVisibleTodos(state.todos,state.filter)
    });
    //selector.js
    export const todosSelector = (state) => state.todos;
    export const filterSelector = (state) => state.filter;
    export const visibleTodosSelector = createSelector(
      [todosSelector,filterSelector],
      (todos,filter)=>{return getVisibleTodos(todos,filter)}  //这里假设已经定义一个getVisibleTodos函数用来返回要显示哪些todo项
     );
     
    //container.js
    import {visibleTodosSelector}
    const mapState = (state)=>({
      todos:visibleTodosSelector(state)
    });
    
    

    3.其他

    少用{...props}
    适当拆分组件
    压缩,合并,commonChunksPulgin(webpack4里面移除了commonChunksPulgin插件,放在了config.optimization里面)

    css预处理语言写法,防止多余编译

    三.Fiber架构迁移

    以往的react渲染是一气呵成,不能打断,同步渲染计算大的话容易阻塞UI线程。
    react16后提出Fiber,使react从Stack reconciler转变为fiber reconciler


    Fiber两个Phase.png

    将渲染分割成多个事务,使得以往的栈结构可以定制优先级,暂停,复用,其中第一个阶段是可以随时被打断的阶段,这使得某部分旧的生命周期函数造成不安全的危害
    componentWillMount
    componentWillReceiveProps
    componentWillUpdate
    用其它生命周期函数再结合两个新的生命周期函数,足以替代它们的业务场景

    getDerivedStateFromProps(nextProps, prevState)

      static getDerivedStateFromProps(nextProps, prevState) {
        if (nextProps.selectCodeId !== prevState.codeId) {
          return {
            codeId: nextProps.selectCodeId,
            first: false,
          };
        }
        return null;
      }
    

    getDerivedStateFromProps没有附加渲染的情况下更新状态的唯一方法,可以用来替代componentWillReceiveProps

    getSnapshotBeforeUpdate(prevProps, prevState)

    export default class ScrollingList extends Component {
      constructor(props) {
        super(props)
        this.scrollRef = null
      }
    
      getSnapshotBeforeUpdate(prevProps, prevState) {
        if (prevProps.list.length < this.props.list.length) {
          return (
            this.scrollRef.scrollHeight - this.scrollRef.scrollTop
          );
        }
        return null;
      }
    
      componentDidUpdate(prevProps, prevState, snapshot) {
        if (snapshot !== null) {
          this.scrollRef.scrollTop =
            this.scrollRef.scrollHeight - snapshot;
        }
      }
    
      render() {
        return (
          ....
        );
      }
    
    
    }
    
    react16.png

    相关文章

      网友评论

        本文标题:react项目优化

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