消息订阅和发布、基础知识和实战应用

时间:2022-12-13 11:15:30

1、基础知识

类别引用说明:订阅报纸。用户作为订阅者订购报纸,邮局派送报纸。

1、一种组件间通信的方式,适用于<span style="color:red">任意组件间通信</span>。

2、使用步骤:

  1. 安装pubsub:npm i pubsub-js(安装不成功、使用管理员权限运行) 消息订阅和发布、基础知识和实战应用

  2. 引入: import pubsub from 'pubsub-js'(订阅和发布都要引入) 消息订阅和发布、基础知识和实战应用

3、接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的<span style="color:red">回调留在A组件自身。</span>

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }

4、提供数据:pubsub.publish('xxx',数据)(这里的传参传两个、第一个是订阅名、第二个是数据)

5、最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去<span style="color:red">取消订阅。</span>

2、代码实例

实现的效果:student组件中的信息发送到School组件中。student作为信息的发布者,School组件订阅信息。

2.1 main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
})

<hr>

2.2 School.vue

<template>
	<div class="school">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
	</div>
</template>

<script>
	import pubsub from 'pubsub-js'
	export default {
		name:'School',
		data() {
			return {
				name:'尚硅谷',
				address:'北京',
			}
		},
		mounted() {
			// console.log('School',this)
			/* this.$bus.$on('hello',(data)=>{
				console.log('我是School组件,收到了数据',data)
			}) */
			this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
				console.log(this)
				// console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
			})
		},
		beforeDestroy() {
			// this.$bus.$off('hello')
			pubsub.unsubscribe(this.pubId)
		},
	}
</script>

<style scoped>
	.school{
		background-color: skyblue;
		padding: 5px;
	}
</style>

<hr>

2.3 Student.vue

<template>
	<div class="student">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	import pubsub from 'pubsub-js'
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男',
			}
		},
		mounted() {
			// console.log('Student',this.x)
		},
		methods: {
			sendStudentName(){
				// this.$bus.$emit('hello',this.name)
				pubsub.publish('hello',666)
			}
		},
	}
</script>

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

<hr>

2.4 App.vue

<template>
	<div class="app">
		{{msg}}
		<School/>
		<Student/>
	</div>
</template>

<script>
	import Student from './components/Student'
	import School from './components/School'

	export default {
		name:'App',
		components:{School,Student},
		data() {
			return {
				msg:'你好啊!',
			}
		}
	}
</script>

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

3、全局事件总线通信改为消息的订阅和发布

3.1 核心部分代码

这里只修改一处、删除列表项的数据方式改为消息订阅和发布的形式

3.1.1 TheItem.vue(样式省略)、发布消息

消息发布、将要删除的id传递给App.vue

<template>
  <li>
    <label>
      <input
        type="checkbox"
        :checked="todo.done"
        @change="handleCheck(todo.id)"
      />
      <span>{{ todo.title }}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
  name: "MyItem",
  //声明接收todo、checkTodo、deleteTodo
  props: ["todo"],

  methods: {
    //勾选or取消勾选
    handleCheck(id) {
      //通知App组件将对应的todo对象的done值取反
    //   this.checkTodo(id);
	  this.$bus.$emit('checkTodo',id)
    },
    //删除
    handleDelete(id) {
      if (confirm("确定删除吗?")) {
        //通知App组件将对应的todo对象删除
        // this.deleteTodo(id);
		// this.$bus.$emit('deleteTodo',id)
		// 消息发布
		pubsub.publish('deleteTodo',id)
      }
    },
  },
};
</script>

<hr>

3.1.2 App.vue 订阅消息

<template>
  <div >
    <div class="todo-container">
      <div class="todo-wrap">
        <TheHeader @addTodo="addTodo" />
        <TheList :todos="todos" />
        <TheFooter
          :todos="todos"
          @checkAllTodo="checkAllTodo"
          @clearAllTodo="clearAllTodo"
        />
      </div>
    </div>
  </div>
</template>

<script>
import TheHeader from "./components/TheHeader";
import TheList from "./components/TheList";
import TheFooter from "./components/TheFooter.vue";
import pubsub from "pubsub-js";
export default {
  name: "App",
  components: { TheHeader, TheList, TheFooter },
  data() {
    return {
      msg: "你好啊!",
      studentName: "",
      //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
      todos: JSON.parse(localStorage.getItem("todos")) || [],
      // todos: [
      //   { id: "001", title: "吃饭", done: true },
      //   { id: "002", title: "睡觉", done: false },
      //   { id: "003", title: "打豆豆", done: true },
      // ],
    };
  },
  methods: {
    //添加一个todo
    addTodo(todoObj) {
      this.todos.unshift(todoObj);
    },
    //勾选or取消勾选一个todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.done = !todo.done;
      });
    },

    //删除一个todo
    deleteTodo(_, id) {
      this.todos = this.todos.filter((todo) => todo.id !== id);
    },
    //全选or取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    //清除所有已经完成的todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
  },

  watch: {
    todos: {
      deep: true,
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
  mounted() {
    this.$bus.$on("checkTodo", this.checkTodo);
    // this.$bus.$on('deleteTodo',this.checkTodo)
    //订阅消息、调用方法里边的函数、参数两个。第一个订阅消息名称、第二个是数据。也可以直接写成箭头函数
    this.pubId = pubsub.subscribe("deleteTodo", this.deleteTodo);
  },
  beforeDestroy() {
    this.$bus.$off("checkTodo");
    //  this.$bus.$off('deleteTodo')
    // 取消订阅
    pubsub.unsubscribe(this.pubId);
  },
};
</script>


<hr>

4、测试结果说明

消息订阅和发布、基础知识和实战应用消息订阅和发布、基础知识和实战应用