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

Vue Mixins 高级组件 与 Vue HOC 高阶组件 #685

Open
libin1991 opened this issue Jan 3, 2019 · 0 comments
Open

Vue Mixins 高级组件 与 Vue HOC 高阶组件 #685

libin1991 opened this issue Jan 3, 2019 · 0 comments
Labels

Comments

@libin1991
Copy link
Owner

在项目里,我们经常会使用组件库进行快速开发,然而在过程中,又难免会遇到对组件库的改造和拓展,如何优雅且简单的进行重构,下面让我们从一个简单需求来探索组件的奇技淫巧--Mixins和HOC

项目中使用组件库遇到的需求

需求: 实现所有页面按钮的点击事件节流控制

随便选用一套组件库,在这次例子里,我选用iview进行开发

iview官网的Button组件的使用方法如下

<template>
    <Button @click="click">Default</Button>
</template>
<script>
    export default {
        methods: {
            click () {
                console.log('yes')
            }
        }
    }
</script>
复制代码

Button的源码也很简单,在这里我剔除了不相关的内容

<template>
    <button @click="handleClick"></button>
</template>
<script>
    export default {
        name: 'Button',
        components: { Icon },
        props: {
        },
        data () {
        },
        computed: {
        },
        methods: {
            handleClick (event) {
                this.$emit('click', event);
            }
        },
        mounted () {
        }
    };
</script>
复制代码

从源码里可以得到以下信息

  1. iview的Button组件封装了原生的button组件
  2. 对原生的button组件,进行了click事件的绑定
  3. click事件触发时,向组件emit了click事件

需求怎么实现

再看看我们的需求 实现所有页面按钮的点击事件节流控制,这里有几个关键点

  1. 点击事件,iview的Button组件里已经对原生的button的click进行了绑定,我们需要劫持这段绑定,进行节流
  2. 节流控制,节流(debounce)函数,网上有很多示例,容易实现

那么需求的难点就在于,如何实现点击事件的劫持?在这里有两种方案

Mixins

1. Mixin是什么

Mixins 在官方Vue文档中已经有了很详细的介绍,不熟悉的朋友们可以看看,用一句话来理解,即合并组件的组件

官方介绍

用一张图来表示

2. 怎么解决需求

直接上源码

// 节流函数
function debounce (func, delay, context, event) {
  clearTimeout(func.timer)
  func.timer = setTimeout(function () {
    func.call(context, event)
  }, delay)
}
// iview中click方法拷贝
function _handleClick (event) {
  this.$emit('click', event)
  const openInNewWindow = event.ctrlKey || event.metaKey
  this.handleCheckClick(event, openInNewWindow)
}
// 导出新组件
export default {
  props: {
  },
  mixins: [Vue.options.components.Button], // iview 中Button组件
  data () {
    return {}
  },
  mounted () {
    console.log('mixins succeed')
  },
  methods: {
    handleClick (event) {
      let that = this
      console.log('debounce')
      debounce(_handleClickLink, 300, that, event)
    }
  }
}

复制代码

3. 原理

mixins的原理很容易理解,上列源码我们做了这些操作,来实现合并ivew Button组件,劫持click事件

  1. 创建debounce节流函数
  2. 复制iview Button组件中handleClick方法为_handleClick
  3. 导出对象,methods里重写handleClick方法,进行节流控制

使用mixins 来实现我们需求很简单,但也因此会有许多问题

  1. 需要知道Button源码结构
  2. 带来了隐式依赖,如果mixins嵌套,会很难理解

那么有没有更好的方法?

HOC

1.什么是HOC?

所谓高阶组件其实就是高阶函数,React 和 Vue 都证明了一件事儿:一个函数就是一个组件。所以组件是函数这个命题成立了,那高阶组件很自然的就是高阶函数,即一个返回函数的函数

HOC的详细介绍和实现,这篇文章探索Vue高阶组件有详细介绍,用一句话来理解,即包裹组件的组件

用一张图来表示

2. 怎么解决需求

直接上源码

// 节流函数
function debounce (func, delay, context, event) {
  clearTimeout(func.timer)
  func.timer = setTimeout(function () {
    func.call(context, event)
  }, delay)
}
// 导出新组件
export default {
  props: {},
  name: 'ButtonHoc',
  data () {
    return {}
  },
  mounted () {
    console.log('HOC succeed')
  },
  methods: {
    handleClickLink (event) {
      let that = this
      console.log('debounce')
      // that.$listeners.click为绑定在新组件上的click函数
      debounce(that.$listeners.click, 300, that, event)
    }
  },
  render (h) {
    const slots = Object.keys(this.$slots)
      .reduce((arr, key) => arr.concat(this.$slots[key]), [])
      .map(vnode => {
        vnode.context = this._self
        return vnode
      })
    return h('Button', {
      on: {
        click: this.handleClickLink //新组件绑定click事件
      },
      props: this.$props,
      // 透传 scopedSlots
      scopedSlots: this.$scopedSlots,
      attrs: this.$attrs
    }, slots)
  }
}

复制代码

3. 原理

HOC的特点在于它的包裹性,上列源码我们做了这些操作,来实现包裹iview的Button组件,劫持click事件

  1. 创建debounce节流函数
  2. 导出新的组件
  3. render渲染出iview Button
  4. Button 绑定debounce后的click方法

HOC的包裹性同时也会带来几个问题

  1. 组件之间通信会被拦截,比如子组件访问父组件的方法(this.$parent.methods)
  2. vue官方并没有推荐使用HOC :(

总结

Mixins 和 HOC 都能实现这个简单的需求,希望大家能理解这两种技巧,解决项目中的问题

源码:github.com/warpcgd/mix…

demo:warpcgd.github.io/mixinAndHoc…

@libin1991 libin1991 added the Vue label Jan 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant