Skip to content

关于react代码优化策略(持续更新) #6

@Kehao

Description

@Kehao

1. React.mixin

React mixin 是通过React.createClass创建组件时使用的,现在主流是通过ES6方式创建react组件,官方因为mixin不好追踪变化以及影响性能,所以放弃了对其支持,同时也不推荐使用。这里简单介绍下mixin。

mixin的原理其实就是将[mixin]里面的方法合并到组件的prototype上。

var logMixin = {
    alertLog:function(){
        alert('alert mixin...')
    },
    componentDidMount:function(){
        console.log('mixin did mount')
    }
}

var MixinComponentDemo = React.createClass({
    mixins:[logMixin],
    componentDidMount:function(){
        document.body.addEventListener('click',()=>{
            this.alertLog()
        })
        console.log('component did mount')
    }
})

// 打印如下
// component did mount
// mixin did mount
// 点击页面
// alert mixin

mixin就是将logMixn的方法合并到MixinComponentDemo组件中,如果有重名的生命周期函数都会执行(render除外,如果重名会报错)。
但是由于mixin的问题比较多, 所以被react放弃。

  • mixins 引入了不清晰的依赖关系

组件采用了mixins的state和方法,mixins采用了组件的方法,或者mixins又依赖了其他的mixins。这样导致组件和mixins有强耦合的关系,而这些关系不是存在同一个文件中,修改组件或者修改mixins都是非常危险的行为

  • mixins 导致命名空间的冲突
  • mixins 导致滚雪球般的复杂度

2. 高阶组件

function logTimeHOC(WrappedComponent,options={time:true,log:true}){
    return class extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                index: 0
            }
            this.show = 0;
        }
        componentDidMount(){
            options.time&&this.timer = setInterval(()=>{
                this.setState({
                    index: ++index
                })
            },1000)
            options.log&&console.log('组件渲染完成----')
        }
        componentDidUpdate(){
            options.log&&console.log(`我背更新了${++this.show}`)
        }
        componentWillUnmount(){
            this.timer&&clearInterval(this.timer)
            options.log&&console.log('组件即将卸载----')
        }
        render(){
            return(<WrappedComponent {...this.state} {...this.props}/>)
        }
    }
}
class InnerLogComponent extends React.Component{
    render(){
        return(
            <div>我是打印日志组件</div>
        )
    }
}
// 使用高阶组件`logTimeHOC`包裹下 
export default logTimeHOC(InnerLogComponent,{log:true})

这样不仅复用了业务逻辑提高了开发效率,同时还方便后期维护。react里的connect, withRouter都是高阶组件。

export default withRouter(connect(({ login, loading }) => ({ login, loading: loading.models.login }))(Form.create()(Login)))

但也有一些缺陷:

  • 高阶组件的props都是直接透传下来,无法确实子组件的props的来源。
  • 可能会出现props重复导致报错。
  • 组件的嵌套层级太深。
  • 会导致ref丢失。

3. React Hook

它不仅仅解决了功能复用的问题,还让我们以函数的方式创建组件,摆脱Class方式创建,从而不必在被this的工作方式困惑,不必在不同生命周期中处理业务。
是目前比较完美的代码封装方式。

import React,{ useState, useEffect } from 'react'
function useLogTime(data={log:true,time:true}){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        data.log && console.log('组件渲染完成----')
        let timer = null;
        if(data.time){
            timer = setInterval(()=>{setCount(c=>c+1)},1000)
        } 
        return ()=>{
            data.log && console.log('组件即将卸载----')
            data.time && clearInterval(timer)
        }
    },[])
    return {count}
}
export default function LogComponent(){
    useLogTime({log:true})
    return(
        <div>我是打印日志组件</div>
    )
}
// 判断是否在视口里面
function isInWindow(el){
    const bound = el.getBoundingClientRect();
    const clientHeight = window.innerHeight;
    return bound.top <= clientHeight + 100;
}
// 加载图片真实链接
function loadImg(el){
    if(!el.src){
        const source = el.getAttribute('data-sourceSrc');
        el.src = source;
    }
}
// 加载图片
function checkImgs(className){
    const imgs = document.querySelectorAll(`img.${className}`);
    Array.from(imgs).forEach(el =>{
        if (isInWindow(el)){
            loadImg(el);
        }
    })
}
function useImgLazy(className){
    useEffect(()=>{
        window.addEventListener('scroll',()=>{
            checkImgs(className)
        });
        checkImgs(className);

        return ()=>{
            window.removeEventListener('scroll')
        }
    },[])
}
function App(){
    // ...
    useImgLazy('lazy-img')
    // ...
    return (
        <div>
            // ...
            <img className='lazy-img' data-sourceSrc='真实图片地址'/>
        </div>
    )
}

4.继承

不推荐,但有时更顺手。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions