Vue@2.X --- 官方文档上没有的抽象(abstract)组件

1,080 阅读3分钟

写在前面

生如夏花,理当分外光明!!!

抽象组件

Vue.js提供了一个abstract开关,在组件配置项中添加一个abstract的选项设置为true,就可以让组件成为一个抽象组件,抽象组件它自身不会渲染一个DOM元素,也不会出现在组件的父组件链中。

在抽象组件的生命周期过程中,我们可以对包裹的子组件监听的事件进行拦截,也可以对子组件进行Dom 操作,从而可以对我们需要的功能进行封装,而不需要关心子组件的具体实现

定义一个防抖组件(Debounce)

下面我们将定义一个具体的防抖组件来理解什么是抽象组件 --->

还是老套路,首先看目录树:

image.png

  1. 我们希望,定义一个全局的防抖组件,也就是一个button按钮,在按钮上绑定一个点击事件,每次点击,产生一个随机数。
  2. 首先,在我们的view目录下的index.vue注册一个button按钮,为其绑定一个clickHeader点击事件,将其事件注入到methods方法中,生成一个随机数。
<template>
  <div>
      <button @click="clickHandler">你来点我呀~~</button>
  </div>
</template>

<script>
export default {
  methods: {
    clickHandler: () => {
      console.log('绑定给Button的回调函数', Math.random());
    },
  },
};
</script>

<style>
</style>
  1. 然而,我们应该设置其防抖 [debounce],使它不能频繁的被点击,点击事件只在最后应该执行。
  2. 这时候有人就会默默的开始写定时器,哈哈哈哈,作为一个合格的搬砖人,我们当然不能用这么低效率的代码开发,推荐大家一个工具库(Lodash)☞看官方文档 Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。

安装☞

npm i lodash -S
  1. 导入lodash工具库里的debounce 打印的bounce,就可以看到,该函数有两个参数,第一个参数是一个执行函数,第二个参数是设置秒数。那么据此修改一下我们刚刚定义的点击事件,点击事件的防抖功能就做好啦!
<template>
  <div>
      <button @click="clickHandler">你来点我呀~~</button>
  </div>
</template>

<script>
//导入debounce
import {debounce} from 'lodash'
export default {
  methods: {
  //使用debounce,传参
    clickHandler: debounce(() => {
      console.log('绑定给Button的回调函数', Math.random());
    },500)
  },
};
</script>

<style>
</style>
  1. 现在有这一个需求,我们有50个页面都需要这么一个防抖按钮,我们当然不可能一个个定义,一个个导入吧。☚☚[大怨种行为],那么我们应该怎么做呢?

  2. 我们可以想到,利用一个全局组件[Debounce],将该防抖按钮包裹起来,在main.js里面导入,并注册到 Vue.use() 里面并且,我们不希望修改原先的点击事件的逻辑,在其 [debounce] 组件上设置秒数属性,就可以实现一个全局的防抖组件。而且,我们还希望,当前组件与button按钮不会发生标签嵌套,浏览器也不会给我们正常渲染出< Debounce >标签链,也就是说,在button上通过this.$parent获取不到< Debounce >

  3. 这时候,我们就终于到了开始定义一个抽象组件的第一步,重点抽象组件是Vue中唯一一个不会正常渲染到页面,也没有template模板的组件☞[只让牛拉车不让牛吃草系列] 哈哈哈哈~~~

修改渲染页面代码:

<template>
  <div>
    <cl-debounce :wait="500">
      <button @click="clickHandler">你来点我呀~~</button>
    </cl-debounce>
  </div>
</template>

<script>
export default {
  methods: {
    clickHandler: () => {
      console.log('绑定给Button的回调函数', Math.random());
    },
  },
};
</script>

<style>
</style>

你以为这就完了嘛?不不不

  1. 回到Debounce文件下的index.js,我们知道一个js文件就是一个组件,而我们的抽象组件不需要template,那它也就同时不需要Style

  2. 默认导出一个对象,那么,我们怎么才能确定一个组件就是抽象组件呢?这里我们就会用到 [abstract:true],那么我们设置它有什么作用呢 声明一个组件为抽象组件

  3. 因为我们还需要对接全局组件Vue.use的接口,所以我们还应该定义install方法.

  4. 其实,刚刚的 <debounce.> 内部的button组件就是它的应该插槽,据此我们知道,我们不渲染 <debounce>组件,但要渲染其内部的标签节点,那么我们在导出时还需要定义应该 render函数,render返回谁就渲染谁,那么,我们想要渲染 默认插槽,就需要在rander函数内通过 this.$slot.default[0] 获取默认插槽,可以打印一下看看,得到一个vNode虚拟节点

  5. 这时候,我们就还应该利用 (vNode.data.on.click()) 拦截前面页面绑定的点击事件,当前页面导入lodash的denounce,同时对拦截到的该点击事件修改成为防抖函数。并且删除页面的对点击事件防抖的操作。

  6. 为了在写组件时就可以设置wait,我们还应该定义props,设置wait的类型,默认值 完整代码:

// 定义一个防抖组件

//导入工具库
import { debounce } from 'lodash';

export default {
 name: 'cl-debounce',
 // 声明该组件为抽象组件
 abstract: true,
 
 props: {
   wait: {
     type: Number,
     default: 350,
   },
 },
 
 // render返回什么就渲染什么
 render() {
  
   // 拦截该函数,重新赋值 设置成为防抖函数
   VNode.data.on.click = debounce(VNode.data.on.click, this.wait);
   // 返回该虚拟节点 渲染页面
   return VNode;
 },
 
 // 适配Vue.use
 install(Vue) {
   Vue.component(this.name, this);
 },
};

❀❀❀ 到这里我们的全局防抖的一个抽象组件就完成啦!!!