【1】 组件通信的5种方式
props vue的自定义事件 pubsub第三方库 slot vuex
① props
父子组件间通信的基本方式 属性值的2大类型: 一般: 父组件-->子组件 函数: 子组件-->父组件 隔层组件间传递: 必须逐层传递(麻烦) 兄弟组件间: 必须借助父组件(麻烦)
② vue自定义事件
子组件与父组件的通信方式 用来取代function props 不适合隔层组件和兄弟组件间的通信
③ pubsub第三方库(消息订阅与发布)
适合于任何关系的组件间通信
④ slot
通信是带数据的标签,注意: 标签是在父组件中解析。
⑤ vuex
多组件共享状态(数据的管理),组件间的关系也没有限制。功能比pubsub强大, 更适用于vue项目。
【2】组件引入与组件传值
① 父页面
<template> <div class="todo-container"> <div class="todo-wrap"> <!-- 给子组件绑定方法 --> <TodoList :todos="todos"/> <!-- 给子组件绑定方法并传值 --> <TodoFooter :todos="todos" :deleteCompleteTodos="deleteCompleteTodos" :selectAll="selectAll"/> </div> </div> </template>
② 脚本
父页面需要引入并声明组件:
<script> //import引入组件 import TodoHeader from './components/TodoHeader.vue' import TodoList from './components/TodoList.vue' import TodoFooter from './components/TodoFooter.vue' import storageUtils from './utils/storageUtils' export default { //声明组件 components: { TodoHeader, TodoList, TodoFooter } //数据 data () { return { todos: storageUtils.readTodos() } }, methods: { addTodo (todo) { this.todos.unshift(todo) }, deleteTodo (index) { this.todos.splice(index, 1) }, // 删除所有已完成的 deleteCompleteTodos () { this.todos = this.todos.filter(todo => !todo.complete) }, // 全选/全不选 selectAll (isSelectAll) { this.todos.forEach(todo => { todo.complete = isSelectAll }) } } } </script>
export 用来导出模块,Vue 的单文件组件通常需要导出一个对象,这个对象是 Vue 实例的选项对象,以便于在其它地方可以使用 import 引入。而 new Vue() 相当于一个构造函数,在入口文件 main.js 构造根组件的同时,如果根组件还包含其它子组件,那么 Vue 会通过引入的选项对象构造其对应的 Vue 实例,最终形成一棵组件树。
使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名。
default只能有一个值,所以一个文件内不能有多个export defaul。
export default输出一个叫做default的变量,然后系统允许你为它取任意名字。所以可以为import的模块起任何变量名,且不需要用大括号包含export default写法顺序
name components props data created methods mounted computed watch
③ 子页面
子页面通过props接受父组件的传值:
<template> <div class="todo-footer"> <label> <input type="checkbox" v-model="checkAll"/> </label> <span> <span>已完成{{completeSize}}</span> / 全部{{todos.length}} </span> <button class="btn btn-danger" v-show="completeSize" @click="deleteAllCompleted">清除已完成任务</button> </div> </template> <script> export default { //接收父组件的值传递与方法传递 props: { todos: Array, deleteCompleteTodos: Function, selectAll: Function }, computed: { completeSize () { return this.todos.reduce((preTotal, todo) => preTotal + (todo.complete?1:0) ,0) }, checkAll: { get () { // 决定是否勾选 return this.completeSize===this.todos.length && this.completeSize>0 }, set (value) {// 点击了全选checkbox value是当前checkbox的选中状态(true/false) this.selectAll(value) } }, }, methods: { deleteAllCompleted () { if(window.confirm('确定清除已完成的吗?')) { this.deleteCompleteTodos() } } } } </script>
【3】 props的三种写法
① 第一种简写
export default { // 声明接收标签属性 props: ['todos'], // 会成为当前组件对象的属性, 可以在模板中直接访问, 也可以通过this来访问 components: { TodoItem } }
② 第二种写法
export default { props: {// 指定属性名和属性值的类型 todo: Object, index: Number } }
③ 第三种复杂写法
export default { props: { todo: { type:Function, required:true, default: function () { return { message: 'hello' } }, validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } }, index: Number } }
type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组
。
会检查一个 prop 是否是给定的类型,否则抛出警告。Prop 类型的更多信息如下所示:
props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise // or any other constructor }
default:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
required:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。
validator:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。你可以在这里查阅更多 prop 验证的相关信息。
props更多文档参考:https://cn.vuejs.org/v2/guide/components-props.html
【4】组件通信之消息发布订阅
消息订阅与发布(PubSubJS 库),此方式可实现任意关系组件间通信(数据)。
需要先安装PubSubJS 库,npm install --save pubsub-js
然后引入:
import PubSub from 'pubsub-js'
① 发布消息
PubSub.publish('msg', data)
触发事件(发布消息)
DOM 事件: 用户在浏览器上对应的界面上做对应的操作 自定义: 编码手动触发
实例如下:
deleteItem () { // this.deleteTodo(this.index) // 发布消息(deleteTodo) PubSub.publish('deleteTodo', this.index) }
② 订阅消息
PubSub.subscribe('msg', function(msg, data){})
绑定事件监听(订阅消息)
目标: 标签元素<button> 事件名(类型): click/focus 回调函数: function(event){}
实例如下:
mounted () { // 绑定自定义事件(addTodo)监听 this.$refs.header.$on('addTodo', this.addTodo) // 订阅消息(deleteTodo) PubSub.subscribe('deleteTodo', (msg, index) => { this.deleteTodo(index) }) },
【5】组件间通信之slot
slot主要用于父组件向子组件传递标签数据。
父组件实例
<TodoFooter> <input slot="checkAll" type="checkbox" v-model="checkAll" /> <span slot="size">已完成{{completeSize}} / 全部{{todos.length}}</span> <button slot="delete" class="btn btn-danger" v-show="completeSize" @click="deleteAllCompleted" >清除已完成任务</button> </TodoFooter>
子组件实例
<template> <div class="todo-footer"> <label> <!--<input type="checkbox" v-model="checkAll"/>--> <slot name="checkAll"></slot> </label> <span> <slot name="size"></slot> <!-- <span>已完成{{completeSize}} / 全部{{todos.length}}</span>--> </span> <slot name="delete"></slot> <!-- <button class="btn btn-danger" v-show="completeSize" @click="deleteAllCompleted">清除已完成任务</button>--> </div> </template>
【6】自定义事件
① v-on指令监听事件
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。v-on:事件名, 可以缩写为: @事件名
事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop .prevent .capture .self .once .passive
代码示例如下
<!-- 阻止单击事件继续传播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件监听器时使用事件捕获模式 --> <!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 --> <div v-on:click.capture="doThis">...</div> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div v-on:click.self="doThat">...</div>
② 自定义事件监听
第一种方式
#给TodoHeader 标签对象绑定addTodo事件监听 <TodoHeader @addTodo="addTodo"/>
那么子组件如何触发事件呢?
//this.addTodo(todo)---原先使用props通信 /*触发自定义事件: addTodo*/ this.$emit('addTodo', todo)
第二种方式
<TodoHeader ref="header"/> mounted () { // 绑定自定义事件(addTodo)监听 this.$refs.header.$on('addTodo', this.addTodo) }
需要注意的是上面函数传递方式只适用于父-子
,也就是A-B;A-B-C则不适用。