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代码优化策略(持续更新) #6

Open
Kehao opened this issue Apr 10, 2020 · 0 comments
Open

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

Kehao opened this issue Apr 10, 2020 · 0 comments

Comments

@Kehao
Copy link
Owner

Kehao commented Apr 10, 2020

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.继承

不推荐,但有时更顺手。

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

1 participant