vue-bus
一个 Vue.js 事件中心插件,同时支持 Vue 1.0 和 2.0
原因
Vue 2.0 重新梳理了事件系统,因为基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。虽然依然保留了父子组件间的事件流,但有诸多限制,比如不支持跨多层父子组件通信,也没有解决兄弟组件间的通信问题。
Vue 推荐使用一个全局事件中心来分发和管理应用内的所有事件,详见文档。这是一个最佳实践,同时适用于 Vue 1.0 和 2.0。你当然可以声明一个全局变量来使用事件中心,但你如果在使用 webpack 之类的模块系统,这显然不合适。每次使用都手动 import 进来也很不方便,所以就有了这个插件:vue-bus
vue-bus 提供了一个全局事件中心,并将其注入每一个组件,你可以像使用内置事件流一样方便的使用全局事件。
安装
$ npm install vue-bus
如果在一个模块化工程中使用它,必须要通过 Vue.use()
明确地安装 vue-bus:
import Vue from 'vue';
import VueBus from 'vue-bus';
Vue.use(VueBus);
如果使用全局的 script 标签,则无须如此(手动安装)。
使用
监听事件和清除监听
// ...
created() {
this.$bus.on('add-todo', this.addTodo);
this.$bus.once('once', () => console.log('这个监听器只会触发一次'));
},
beforeDestroy() {
this.$bus.off('add-todo', this.addTodo);
},
methods: {
addTodo(newTodo) {
this.todos.push(newTodo);
}
}
触发事件
// ...
methods: {
addTodo() {
this.$bus.emit('add-todo', { text: this.newTodoText });
this.$bus.emit('once');
this.newTodoText = '';
}
}
实例
App.vue文件:
<template> <div id="app"> <h1>{{title}}</h1> <h2 v-text="title"></h2> <h3 v-html="title"></h3> <input v-model="newItem" v-on:keyup.enter="addNew"> <ul> <li v-for="item in items" :key="item.label" v-bind:class="{finished: item.isFinished}" v-on:click="toggleFinish(item)"> {{item.label}} </li> </ul> <p>children tells me: {{childrenWords}}</p> <component-a msgFromFather="I am father!" v-on:children-tell-me-something="listenToChildren"></component-a> <!--<component-a msgFromFather="I am father!" v-on:children-tell-me-something="listenToChildren" v-on:add-todo="addTodo" v-on:delete-todo="deleteTodo"></component-a>--> </div> </template> <script> import Vue from 'vue' import Storage from './storage' import ComponentA from './components/componentA' import VueBus from 'vue-bus' // 如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装 vue-bus Vue.use(VueBus) // export相当于 new Vue({}); export default { // 相当于 date: function(){} 下面是ES6语法 _vue-bus@1.2.0@vue-bus created () { console.log('生命周期') this.$bus.on('add-todo', this.addTodo) this.$bus.on('delete-todo', this.deleteTodo) this.$bus.once('once', () => console.log('这个监听器只会触发一次')) }, beforeDestroy () { this.$bus.off('add-todo', this.addTodo) this.$bus.off('delete-todo', this.deleteTodo) }, data () { return { title: '<span>渲染HTML</span> this is a todo list', items: Storage.fetch(), newItem: '', childrenWords: '' } }, components: { ComponentA }, watch: { items: { handler: function (items) { Storage.save(items) }, deep: true } }, methods: { toggleFinish: function (item) { console.log(item.isFinished = !item.isFinished) }, addNew: function () { this.items.push({ label: this.newItem, isFinished: false }) console.log(this.newItem) this.newItem = '' }, listenToChildren: function (msg) { // console.log(msg) this.childrenWords = msg }, addTodo: function (newTodo) { console.log('add') console.log(newTodo) this.items.push(newTodo) }, deleteTodo: function (todoId) { console.log('del') this.items = this.items.filter(function (todo) { return todo.label !== todoId }) } } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } .finished { text-decoration: underline; } </style>
Storage.js
const STORAGE_KEY = 'todos-vuejs' export default { fetch: function () { return JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]') }, // es6写法 save (items) { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(items)) } }
ComponentA.vue组件
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>父组件->子组件的props属性:{{msgFromFather}}</h2> <input v-model="msgFromChildren"/> <button v-on:click="onClickMe">子组件->父组件($emit)</button> <button v-on:click="onClickDispatch">子组件->父组件($dispatch)</button> <hr> <input v-model="newTodoText"/> <button v-on:click="addTodo">添加</button> <button v-on:click="deleteTodo(newTodoText)">删除</button> </div> </template> <script> export default { name: 'HelloWorld', data () { return { msg: 'Hello from component a!', msgFromChildren: '', newTodoText: '' } }, props: ['msgFromFather'], methods: { onClickMe: function () { // console.log(this.msgFromFather) this.$emit('children-tell-me-something', this.msgFromChildren) }, onClickDispatch: function () { console.log('Vue 1.0升级到2.0中去除了$broadcast和$dispatch方法!') // Vue 1.0升级到2.0中去除了$broadcast和$dispatch方法。 // this.$dispatch('children-tell-me-dispatch', this.msgFromChildren) }, addTodo: function () { console.log(this.newTodoText) // this.$emit('add-todo', { label: this.newTodoText, isFinished: false }) // 事件中心bus this.$bus.emit('add-todo', { label: this.newTodoText, isFinished: false }) this.$bus.emit('once') this.newTodoText = '' }, deleteTodo: function (id) { console.log(id) // this.$emit('delete-todo', id) this.$bus.emit('delete-todo', id) this.newTodoText = '' } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h1, h2 { color: #42b983; font-weight: normal; } </style>
效果图:
总结:
一开始使用$on和$emit进行事件的分发,后面试着使用$dispatch和$broadcast时候发现demo是vue.2.x的,已经不支持了,Vue 1.0 到2.0的升级中已经不支持events了,大中型项目推荐使用Vuex(还没撸到),所以简单撸个vue-bus来玩玩,在这里踩个坑记记,各位看官大佬不喜勿喷(ps:此时一名小码农正在路过...)。