Vue知识点整理(三)- Vue脚手架(6)- 全局事件总线、消息订阅与发布

目录

一、全局事件总线(GlobalEventBus)

1.1 全局事件总线前需了解的内容

1.1.1 Vue原型对象上包含事件处理的方法

1.1.2 重要的内置关系

1.2 全局事件总线

二、TodoList案例_全局事件总线

三、消息订阅与发布

四、TodoList案例_消息订阅与发布

五、TodoList案例 - 添加编辑功能

5.1 编辑功能

5.1.1 添加编辑按钮

5.1.2 实现编辑功能

5.1.3 存在的bug

5.1.4 bug处理 - $nextTick


一、全局事件总线(GlobalEventBus)

1.1 全局事件总线前需了解的内容

1.1.1 Vue原型对象上包含事件处理的方法

  1. $on(eventName, listener):绑定自定义事件,监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数
  2. $emit(eventName, data):触发当前实例上的事件。附加参数都会传给监听器回调
  3. $off(eventName):移除自定义事件监听器
    1. 如果没有提供参数,则移除所有的事件监听器
    2. 如果只提供了事件,则移除该事件所有的监听器
    3. 如果同时提供了事件与回调,则只移除这个回调的监听器
  4. $once(eventName, listener):监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除

1.1.2 重要的内置关系

详细内容可看:Vue知识点整理(二)- Vue组件化编程(1)中的第六节

所有组件实例对象的 原型对象的 原型对象 是 Vue的原型对象

  • 所有组件对象都能看到 Vue原型对象上的属性和方法

1.2 全局事件总线

1. 一种组件间的通信方式,适用于任意组件间通信

2. 安装全局事件总线(在main.js中):

new Vue({
    .....
    beforeCreate(){
        Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前应用的vm
    },
    .....
})

3. 使用事件总线

(1)接收数据:A组件想要接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身

methods(){
    demo(data){.....}
}
.....
mounted(){
    this.$bus.$on('xxx', this.demo) // xxx指的是自定义事件,demo指的是回调
}

(2)提供数据:

this.$bus.$emit('xxx', 数据) // xxx指的是自定义事件

4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件

beforeDestroy(){
    this.$bus.$off('xxx') // xxx指的是自定义事件
}

二、TodoList案例_全局事件总线

TodoList案例

首先在main.js中安装全局事件总线

// 引入Vue
import Vue from "vue";
// 引入App
import App from "./App.vue";

Vue.config.productionTip = false;

// 创建vm
new Vue({
  el: "#app",
  render: (h) => h(App),
  // 安装全局事件总线
  beforeCreate() {
    Vue.prototype.$bus = this;
  },
});

清除App.vue 和 UserList.vue 中组件绑定的自定义事件,以及 UserList.vue 和UserItem.vue 中 props属性 接收的自定义事件

App.vue

        <!-- <UserList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></UserList> -->
        
        <UserList :todos="todos"></UserList>

UserList.vue

    <!-- <UserItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
      :checkTodo="checkTodo"
      :deleteTodo="deleteTodo"
    ></UserItem> -->

    <UserItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
    ></UserItem>

在App.vue中采用全局总线方法绑定自定义事件,以及设置vm销毁时解绑自定义事件

  mounted() {
    // 全局事件总线 - 绑定自定义事件
    this.$bus.$on("checkTodo", this.checkTodo);
    this.$bus.$on("deleteTodo", this.deleteTodo);
  },
  beforeDestroy() {
    // 全局事件总线 - 解绑自定义事件
    this.$bus.$off("checkTodo");
    this.$bus.$off("deleteTodo");
  },

在UserItem.vue中 使用全局事件总线触发当前实例上的事件

  methods: {
    // 勾选或取消勾选
    handleCheck(id) {
      // 通过App组件将对应的todo对象的done值取反
      // this.checkTodo(id);
      // 全局事件总线 - 触发当前实例上的事件
      this.$bus.$emit("checkTodo", id);
    },
    handleDelete(id) {
      if (confirm("您确认删除吗?")) {
        // this.deleteTodo(id);
        // 全局事件总线 - 触发当前实例上的事件
        this.$bus.$emit("deleteTodo", id);
      }
    },
  },

由图示可看出,采用全局事件总线,成功调用了 checkTodo自定义事件 和 deleteTodo自定义事件,且不需要让UserList.vue充当 App.vue 和 UserItem.vue之间通行的媒介,也不需要在组件标签上绑定多个自定义事件

三、消息订阅与发布

1. 一种组件间的通信的方式,适用于任意组件间通信

2. 使用步骤:

(1)安装pubsub(在终端输入):

npm i pubsub-js

(2)引入:

import pubsub from 'pubsub-js'

(3)接收数据:A组想接收数据,则在A组中订阅消息,订阅的回调留在A组件自身(对应绑定事件监听)

methods:{
    demo(data){...}
}
...
mounts(){
    // 回调写在methods中
    this.pid = pubsub.subscribe('msgName',this.demo) // 订阅消息
    // 回调直接写在内部
    this.pid = pubsub.subscribe('msgName',function(msgName,data){})
    // 一般使用箭头函数,确保this指向vc
    this.pid = pubsub.subscribe('msgName',(msgName,data)=>{})
}

(4)提供数据,即发布消息,触发订阅的回调函数调用:

pubsub.publish('msgName', data)

(5)取消消息的订阅,最好在beforeDestroy钩子中去取消订阅

pubsub.unsubscribe(pid)

四、TodoList案例_消息订阅与发布

在TodoList案例中,UserItem组件内的删除功能采用 消息订阅与发布 的方式

在App.vue 中引入pubsub,并订阅消息,以及订阅的回调

<script>
import pubsub from "pubsub-js";
import UserHeader from "./components/UserHeader.vue";
import UserList from "./components/UserList.vue";
import UserFooter from "./components/UserFooter.vue";

export default {
  name: "App",
  components: { UserHeader, UserList, UserFooter },
  data() {
    ...
  },
  methods: {
    ...
    // 删除一个todo
    deleteTodo(_, id) {
      // '_'起到占位作用
      this.todos = this.todos.filter((todo) => todo.id !== id);
    },
    ...
  },
  watch: {
    ...
  },
  mounted() {
    // 全局事件总线 - 绑定自定义事件
    this.$bus.$on("checkTodo", this.checkTodo);
    // 订阅 - 接收数据
    this.pubId = pubsub.subscribe("deleteTodo", this.deleteTodo);
  },
  beforeDestroy() {
    // 全局事件总线 - 解绑自定义事件
    this.$bus.$off("checkTodo");
    // 解除订阅
    pubsub.unsubcribe("pubId");
  },
};
</script>

在UserItem.vue 中 引入pubsub,并发布消息

<script>
import pubsub from "pubsub-js";
export default {
  name: "UserItem",
  props: ["todo"],
  methods: {
    // 勾选或取消勾选
    handleCheck(id) {
      // 通过App组件将对应的todo对象的done值取反
      // this.checkTodo(id);
      // 全局事件总线 - 接收数据
      this.$bus.$emit("checkTodo", id);
    },
    handleDelete(id) {
      if (confirm("您确认删除吗?")) {
        // this.deleteTodo(id);

        // 全局事件总线 - 接收数据
        // this.$bus.$emit("deleteTodo", id);

        // 发布消息
        pubsub.publish("deleteTodo", id);
      }
    },
  },
};
</script>

由图示可以看出,删除功能可以顺利完成。与全局事件总线不同的是,在Vue开发者工具中,看不到 订阅或发布了哪些消息,因为pubsub是第三方库。

五、TodoList案例 - 添加编辑功能

5.1 编辑功能

首先在UserItem.vue中添加编辑按钮,并在App.vue中设置样式

5.1.1 添加编辑按钮

UserItem.vue

    <button class="btn btn-edit">编辑</button>

App.vue

.btn-edit {
  color: #fff;
  background-color: skyblue;
  border: 1px solid rgb(65, 155, 190);
  margin-right: 5px;
}

5.1.2 实现编辑功能

点击编辑按钮,使输入框显示出来,并且实现修改输入框内容,在离开焦点时改变数据

App.vue 内

  • 定义了更新事件的回调 updateTodo,并对其绑定 以及 销毁后的解绑
  • 对编辑按钮的样式的编写
  methods: {
    ...
    // 更新功能
    updateTodo(id, title) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.title = title;
      });
    },
    ...
  },
  mounted() {
    // 全局事件总线 - 绑定自定义事件
    ...
    // 编辑
    this.$bus.$on("updateTodo", this.updateTodo);
    ...
  },
  beforeDestroy() {
    ...
    // 解绑编辑
    this.$bus.$off("updateTodo");
    ...
  },

UserItem.vue内

  • 添加编辑按钮以及点击编辑按钮出现的输入框,并用v-show限制其是否显示
  • 编辑功能handleEdit
  • 输入框失去焦点后的回调 handleBlur(真正执行修改逻辑)

添加编辑按钮以及点击编辑按钮出现的输入框

<template>
  <li>
    <label>
      <input
        type="checkbox"
        :checked="todo.done"
        @change="handleCheck(todo.id)"
      />
      <span v-show="!todo.isEdit">{{ todo.title }}</span>
      <!-- 输入框 -->
      <!-- $event:获取到该事件的事件对象 -->
      <input
        type="text"
        :value="todo.title"
        v-show="todo.isEdit"
        @blur="handleBlur(todo, $event)"
      />
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
      <!-- 编辑按钮 -->
    <button
      class="btn btn-edit"
      @click="handleEdit(todo)"
      v-show="!todo.isEdit"
    >
      编辑
    </button>
  </li>
</template>

编辑功能handleEdit 以及 输入框失去焦点后的回调 handleBlur

  methods: {
    ...
    // 编辑功能
    handleEdit(todo) {
      // if判断条件 'isEdit' in todo 或 Object.prototype.hasOwnProperty.call(todo, "isEdit")
      if (Object.prototype.hasOwnProperty.call(todo, "isEdit")) {
        // todo身上有 isEdit属性
        // console.log("todo身上有isEdit属性");
        todo.isEdit = true;
      } else {
        // todo身上没有 isEdit属性
        this.$set(todo, "isEdit", true);
      }
    },
    // 失去焦点回调(真正执行修改逻辑)
    handleBlur(todo, e) {
      todo.isEdit = false;
      if (!e.target.value.trim()) return alert("输入不能为空");
      this.$bus.$emit("updateTodo", todo.id, e.target.value);
    },
  },

5.1.3 存在的bug

由图示可看出,在不手动点击输入框后,在未手动获得输入框的焦点时,进行了其他操作,输入框仍会显示

5.1.4 bug处理 - $nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

对于 5.1.3 中bug的处理,可以通过点击按钮后,输入框会自动获取焦点

首先为输入框设置ref属性

      <input
        type="text"
        :value="todo.title"
        v-show="todo.isEdit"
        @blur="handleBlur(todo, $event)"
        ref="inputTitle"
      />

不能直接在编辑功能handelEdit内添加 this.$refs.inputTitle.focus() 的原因:

  • 因为第一次解析模板时,使用了v-show在控制输入框是否显示,所以在 执行到 this.$refs.inputTitle.focus() 时,input输入框还是处于隐藏状态,并不能获取焦点

如何解决该问题?

方法一:设置定时器

    // 编辑功能
    handleEdit(todo) {
      ...
      // 定时器
      setTimeout(() => {
        this.$refs.inputTitle.focus();
      }, 500);
    },

方法二:$nextTick,$nextTick所指定的回调,会在DOM节点更新完毕之后再执行(推荐写法)

    // 编辑功能
    handleEdit(todo) {
      ...
      this.$nextTick(function () {
        this.$refs.inputTitle.focus();
      });
    },

由图示可以看出,在点击编辑按钮后,输入框自动获取了焦点 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JHY97

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值