写给vue转react的同志们(5)

27,872

本系列文章将由浅慢慢深入,一步步带你领略react和vue的同工异曲之处,让你左手react,右手vue无忧。

前提要顾: 点击查看该系列专栏

Vue 与 React 的高阶组件

我们知道 React 中使用高阶组件(下面简称HOC)来复用一些组件的逻辑。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。

组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

上面出自 React 官方文档。

那在 Vue 中 复用组件逻辑实际上比较简单,利用 Mixins 混入复用组件逻辑,当 Mixins 中的逻辑过多时(比如方法和属性),在项目当中使用时追述源代码会比较麻烦,因为他在混入后没有明确告诉你哪个方法是被复用的。

//mixins.js(Vue 2 举例)
export defalut {
    data() {
        return {
            text: 'hello'
        }
    }
}
// a.vue
import mixins from './mixins.js'
export defalut {
    mixins: [mixins]
    computed: {
        acitveText() {
            return `来自mixins的数据:${this.text}`
        }
    }
}

可以看到除了在开头引入并挂载混入,并没有看到this.text是从哪里来的,混入虽然好用,但当逻辑复杂时,其阅读起来是有一定困难的。

那你想在 Vue 中强行使用像 React 那样的高阶组件呢?那当然可以。只是 Vue 官方不怎么推崇 HOC,且 Mixins 本身可以实现 HOC 相关功能。

简单举个例子:

// hoc.js
import Vue from 'Vue'

export default const HOC = (component, text) => {
    return Vue.component('HOC', {
        render(createElement) {
            return createElement(component, {
                on: { ...this.$listeners },
                props: {
                    text: this.text
                }
            })
        },
        data() {
            return {
                text: text,
                hocText: 'HOC'
            }
        },
        mounted() {
            // do something ...
            console.log(this.text)
            console.log(this.hocText)
        }
    })
}

使用高阶组件:

// user.vue
<template>
  <userInfo/>
</template>

<script>
import HOC from './hoc.js'
// 引入某个组件
import xxx from './xxx'

const userInfo = HOC(xxx, 'hello')

export default {
  name: 'user',
  components: {
    userInfo
  }
}
</script>

是不是相比 Mixins 更加复杂一点了?在 Vue 中使用高阶组件所带来的收益相对于 Mixins 并没有质的变化。不过话又说回来,起初 React 也是使用 Mixins 来完成代码复用的,比如为了避免组件的非必要的重复渲染可以在组件中混入 PureRenderMixin

const PureRenderMixin = require('react-addons-pure-render-mixin')
const component = React.createClass({
    mixins: [PureRenderMixin]
})

后来 React 使用shallowCompare 来 替代 PureRenderMixin

const shallowCompare = require('react-addons-shallow-compare')
const component = React.createClass({
    shouldComponentUpdate: (nextProps, nextState) => {
        return shallowCompare(nextProps, nextState)
    }
})

这需要你自己在组件中实现 shouldComponentUpdate 方法,只不过这个方法具体的工作由 shallowCompare 帮你完成,我们只需调用即可。

再后来 React 为了避免总是要重复调用这段代码,React.PureComponent 应运而生,总之 React 在慢慢将 Mixins 脱离开来,这对他们的生态系统并不是特别的契合。当然每种方案都各有千秋,只是是否适合自己的框架。

那我们回归 HOC,在 React 中如何封装 HOC 呢?

实际上我在往期篇幅有提到过: 点击传送

但是我还是简单举个例子:

封装 HOC:

// hoc.js
export default const HOC = (WrappedComponent) => {
    return Class newComponent extends WrappedComponent {
        constructor(props) {
            super(props)
            // do something ...
            this.state = {
                text: 'hello'
            }
        }
        componentDidMount() {
            super.componentDidMount()
            // do something ...
            console.log('this.state.text')
        }
        render() {
            // init render
            return super.render()
        }
    }
}

使用 HOC:

// user.js
import HOC from './hoc.js'
class user extends React.Component {
    // do something ...
}
export defalut HOC(user)

装饰器写法更为简洁:

import HOC from './hoc.js'
@HOC
class user extends React.Component {
    // do something ...
}
export defalut user

可以看到无论 Vue 还是 React 亦或是 HOC 或 Mixins 他们都是为了解决组件逻辑复用应运而生的,具体使用哪一种方案还要看你的项目契合度等其他因素。

技术本身并无好坏,只是会随着时间推移被其他更适合的方案取代,技术迭代也是必然的,相信作为一个优秀的程序员也不会去讨论一个技术的好或坏,只有适合与否。

最后

都看到这里了,不点个赞再走吗?

欢迎在下方给出你的建议和留言。