组件的自定义事件-解绑

157 阅读1分钟

我们上一个文章写的是给自定义事件绑定,现在我们写的是给绑定的自定义事件解绑,一般来说如果我们绑定了事件,如果不解绑,它就会一直存在,就算不使用也存在,所以我们要如何解绑自定义事件呢?

解绑自定义事件

一般来说,你给谁绑定的事件,你就给谁解绑,我们在MyStudent绑定的事件,就在MyStudent解绑事件,我们先在MyStudent加一个按钮,给按钮绑定一个点击事件,写一个unbind方法
image.png

解绑一个自定义事件

然后在unbind方法里,我们要在实例对象一个$off的api来解绑事件
image.png

网页上如果你不点击解绑按钮,一直点击上传学生名按钮,App会一直收到学生名
image.png

一旦你点击了解绑按钮
image.png

你后面再怎么点击上传学生名按钮,App都不会再收到学生名
image.png

解绑多个自定义事件

但是这样只适用解绑一个自定义事件,如果我们要解绑多个自定义事件呢?在App.vue上我们再给student组件加上一个自定义事件demo
image.png

在MyStudent触发一下demo,我们不再另写按钮,直接在上传学生名按钮下,触发xuesheng的同时触发demo,记得不要写mi,mi只是demo的回调名,要写demo
image.png

网页上点击上传按钮后,两个事件就同时被触发了
image.png

如果要同时要解绑两个事件,就要把两个事件放在一个数组了,再用$off来解绑
image.png

网页上没有点击解绑按钮前,一直点击上传学生名按钮,两个事件都会被触发
image.png

一旦你点击了解绑按钮
image.png

你后面再怎么点击上传学生名按钮,App都不会再收到学生名和demo
image.png

解绑所有自定义事件

$off后面不加任何参数,这样就直接解绑所有的自定义事件
image.png

网页上没有点击解绑按钮前,一直点击上传学生名按钮,所有事件都会被触发
image.png

一旦你点击了解绑按钮
image.png

你后面再怎么点击上传学生名按钮,App都不会再收到任何自定义事件的回调
image.png

生命周期的beforeDestroy和Destroyed

image.png

接下来我们来讲一下我们之前写到过的生命周期(《17.生命周期》),vm被销毁后就拆除掉了监视属性、子组件和事件监听器(指的是自定义事件不包括DOM的原生事件);除了vue的实例对象vm可以用生命周期的钩子,组件的实例对象vc也可以使用生命周期的钩子,接下来我们来实验一下:

销毁MyStudent的vc

image.png

添加一个销毁按钮
image.png

组件实例对象vc可以使用生命周期的钩子
image.png

为了展示得更明白些,我添加了一些代码
image.png

网页上,点击number++按钮,add会被调用,页面上的“当前求和为:”也在++,就是说响应式在的
image.png

但当我点击销毁按钮后
image.png

再去点击number++按钮,add(DOM的原生事件)依旧被调用,但是响应式丢了,页面上的“当前求和为:”不再变化依旧是0
image.png

由于 vue.js 版本的问题,即2.7.0 及以上版本执行完 this.$destory()  指令后,原生DOM的响应事件不会被触发,原生 DOM 事件无法被调用了。执行完npm list vue,我发现我的版本是2.7.0 及以上版本,
image.png

所以执行完 this.$destory()  指令后,我的原生DOM事件无法被调用
image.png

而且在我点销毁按钮前,我是可以上传学生名的
image.png

但当我点击销毁按钮后,
image.png

再点击上传按钮后,我无法上传学生名了,这更加印证了vc销毁后,它身上的自定义事件也就不奏效了
image.png

销毁root的vm

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed5f0dc782e14defb0008dee76c3a40b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=747&h=288&s=24003&e=png&b=fefefe)

在main.js的vm上,加一个mounted钩子,里面添加一个计时器,3秒后销毁vm
image.png

网页上前3秒,我点上传学生名按钮,组件的自定义事件可以被调用,但当3秒后,再怎么点击该按钮,组件的自定义事件不能被调用了
image.png

这样我们就验证了下面这两个了
image.png

这结主要的代码如下: MyStudent.vue

<template>
  <div class="student">
    <h1>学校姓名:{{ name }}</h1>
    <h1>学生年龄:{{ age }}</h1>
    <h1>当前求和为:{{ number }}</h1>
    <button @click="add">number++</button>
    <button @click="sendStudentname">上传学生名给App</button>
    <button @click="unbind">解绑xuesheng事件</button>
    <button @click="death">销毁当前Student组件的实例对象(vc)</button>
  </div>
</template>

<script>
// 组件交互相关的代码(数据、方法等等)
export default {
  name: "MyStudent",
  data() {
    return {
      name: "小蒲",
      age: 18,
      number: 0,
    };
  },
  methods: {
    add() {
      console.log('Dom原生事件add被调用')
      this.number++;
    },
    sendStudentname() {
      // 触发Student组件实例对象上的xuesheng事件
      this.$emit("xuesheng", this.name, 111, 222, 333);
      this.$emit("demo");
    },
    unbind() {
      this.$off("xuesheng"); //只适用解绑一个自定义事件
      this.$off(["xuesheng", "demo"]); //只适用解绑多个自定义事件
      this.$off(); //解绑所有自定义事件
    },
    death() {
      this.$destroy();//销毁了当前Student组件的实例,销毁后所以Student实例的自定义事件全都不奏效了
    },
  },
};
</script>

<style lang="less">
.student {
  background-color: pink;
  padding: 5px;
  margin-top: 30px;
}
</style>

App.vue

<template>
  <div class="App">
    <h1>{{ msg }}</h1>
    <!-- <h2>{{ Schoolname }}</h2> -->
    <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
    <School :getSchoolname="getSchoolname" />
    <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on)-->
    <!-- <Student v-on:xuesheng="getStudentname" /> -->
    <Student @xuesheng="getStudentname" @demo="m1" />
    <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref)-->
    <!-- <Student ref="student" /> -->
  </div>
</template>

<script>
// 引入组件
import School from "./components/MySchool.vue";
import Student from "./components/MyStudent.vue";

export default {
  name: "App",
  components: { School, Student },
  data() {
    return {
      msg: "你好啊",
      Schoolname: "",
    };
  },
  methods: {
    getSchoolname(name) {
      console.log("App收到了学校名", name);
      // this.Schoolname = name;
    },
    getStudentname(name, ...params) {
      console.log("App收到了学生名", name, params);
    },
    m1(){
      console.log('demo事件被触发了!')
    }
  },
  // mounted() {
  //   setTimeout(() => {
  //     // this.$refs.student.$on("xuesheng", this.getStudentname);
  //     this.$refs.student.$once("xuesheng", this.getStudentname)(一次性);
  //   }, 3000);
  // },
};
</script>

<style>
.App {
  background-color: gray;
  padding: 5px;
}
</style>

main.js

import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
    render: h => h(App),
    mounted(){
        setTimeout(()=>{
            this.$destroy()
        },(3000))
    }
}).$mount('#app')