一、全局事件总线!!
任意组件间通信
比如a想收到别的组件的数据,那么就在a里面给x绑定一个demo自定义事件,所以a里面就得有一个回调函数吧,然后我要是想让d组件给a穿数据,那就让d去触发x的自定义事件,并且传参数进去,那么触发自定义事件就也会调用回调函数,就把数据给到a了
1.安装全局事件总线
那么x应该放到哪里呢?之前学prototype的时候说它可以让vc、vm都能找到,x放在vue的原型对象上,x写到main.js中
new Vue({el:'#app',render: h => h(App),beforeCreate() {Vue.prototype.$bus=this},
})
注意$on、$off只有vm、vc才能用,如果把创建为一个新的vc可以实现,但是代码有点多,我们就把它创建成一个vm,有一个现成的vm让x直接等于它,写在new Vue后边呢在模版解析完之后才能赋值太晚了,所以就用钩子来赋值。(x一般写成$bus)
2.使用事件总线
a组件要绑定自定义事件
mounted(){this.$bus.$on('hello',(data)=>{console.log('我是school,我收到了',data)})}
3.提供数据
x中开始调用触发
<button @click="sendName">点我把数据传给school</button>
methods:{sendName(){this.$bus.$emit('hello',666)}}
注意:1.我们会有很多条线调用两个组件一个方法名,这些方法名不能重复
2.我们在销毁之前要把事件解绑
4.解绑事件
在哪儿绑的在哪儿解,为啥之前的自定义事件销毁之前不去解绑呢?因为之前那些组件销毁的话身上的自定义事件就一起嘎了,但是这个无论哪个组件销毁x都一直在,组件都销毁了还留着事件那x太累了
beforeDestroy(){this.$bus.$off('hello')}
5.TodoList中的孙传父
app里传给list再传给item的函数全都不要了
mounted(){// this.$bus.on('checkTodo',(id)=>{// this.checkTodo(id)// }), 不是这么写啊,这里是事件名和事件函数,我本来就是要触发那个函数,而且函数已经写好了直接用this.$bus.$on('deleteTodo',this.deleteTodo)this.$bus.$on('checkTodo',this.checkTodo)},beforeDestroy(){this.$bus.$off(['checkTodo','deleteTodo'])}
item:
methods: {handleCheck(id){this.$bus.$emit('checkTodo',id)},handleDelete(id){this.$bus.$emit('deleteTodo',id)}},
检查vue中如果sourse为root那一般就是$bus
二、消息订阅与发布(pubsub)
在vue里用的不多
在a(需要消息)组件里我们订阅了一个demo的消息,内容是test,然后c(提供数据、发布消息)一旦发布demo的消息并传参666,那么a就会立马收到
1.使用方法
需要引入pubsub-js库,可以在任何一个框架使用
安装pubsub:npm i pubsub-js
引入:import pubsub from 'pubsub-js
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
mounted(){// this.$bus.$on('hello',(data)=>{// console.log('我是school,我收到了',data)// })this.pubid=pubsub.subscribe('hello',(msgName,data)=>{console.log('dingyuele ',msgName,data)})//两个参数第一个是事件名,第二个是(事件名,数据)},beforeDestroy(){// this.$bus.$off('hello')pubsub.unsubscribe(this.pubid)//括号里面写‘hello’不对,每次订阅消息都有一个订阅id,类似定时器//上面和这里不写this找不到}
提供数据:
methods:{sendName(){pubsub.publish('hello',666)}}
2.TodoList中用pubsub
第三方Vue中不支持查看
我们来修改一下todolist 的delete方法,app:
import pubsub from 'pubsub-js'//mounted:this.pubId=pubsub.subscribe('deleteTodo',this.deleteTodo)},beforeDestroy(){//this.$bus.$off(['checkTodo'])pubsub.unsubscribe(this.pubId)}
item:
handleDelete(id){//this.$bus.$emit('deleteTodo',id)pubsub.publish('deleteTodo',id)}
然后我们之前不是说会传参过来两个吗,一个是函数名,一个是形参,但是函数名我们其实不太需要,这个里面调的是methods里面我们写好的函数,所以就用_占位一下
deleteTodo(_,id)
//传不都是传两个参数吗,用这个_占位
这里没有用箭头函数是我们的函数写在了methods里面,this(delete里的this)就是vm
(为啥要让里面函数的this指向vm?存个疑)
三、TodoList的编辑功能
1.点击获取
首先实现在每个爱好的后面再加一个按钮编辑,让文字处于input框中
在处于input框中的时候,就应该是文字在框里面,编辑完成之后又得恢复成文字样式,所以还得设置一个todo.isEdit来判断当前的todo是不是在编辑中
<span v-show="!todo.isEdit">{{todo.title}}</span><input v-show="todo.isEdit" type="text" :value="todo.title" >
isEdit为true的时候,表单就出来,否则就隐藏,因为变换比较多的问题这里不用v-if
然后开始添加点击之后让isEdit为true
handleEdit(todo){//todo.isEdit=true 这么加只是表层加了,isEdit没有getter、setter//这样每次都追加的写法有点不好,因为你第一次编辑确实得加这个属性,但是第二次就没必要了this.$set(todo,'isEdit',true)},
但是这样会让每次点击都重新添加isEdit属性不管你以前有没有
handleEdit(todo) {if (todo.isEdit !== undefined) {todo.isEdit = true;} else {this.$set(todo, 'isEdit', true);}},
老师讲的是hasOwnProperty方法,但是报错说我电脑没有这个我就没用
2.失去焦点
失去焦点之后,isEdit为false,表单消失,而且之前修改的内容需要改到我们的文字里面,注意传的是input表单的value而不是todo.title
<input v-show="todo.isEdit" type="text" :value="todo.title" @blur="handleBlur(todo,$event)">
//失去焦点真正执行修改handleBlur(todo,e){//现在的todo已经有isEdit属性了todo.isEdit=falsethis.$bus.$emit('updataTodo',todo.id,e.target.value)}
用全局事件来修改app里的数据,在item中触发updateTodo事件
app:遍历数组看谁的id和我传的一样就修改它的title
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('editTodo');
},
四、$nextTick
1、语法:this.$nextTick(回调函数)
2、作用:在下一次 DOM 更新结束,v-for循环结束后
执行其指定的回调。
3、什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时(如input自动获取焦点),要在nextTick所指定的回调函数中执行。
我点击了编辑按钮之后我想让他自动获取焦点,如果直接写this.$refs.inputTitle.focus()的话,模版解析等这个函数走完了才显示在页面上,但是那个时候就focus完了,在handleEdit(todo)用$nextTick。
this.$nextTick(function (){this.$refs.inputTitle.focus()});
五、动画
1.动画效果
transition标签在html中不显示,是vue提供的,可以在style设置来去的动画,appear默认打开页面就出现 来 的动画
<button @click="isShow = !isShow">显示/隐藏</button><transition name="hello" :appear="true"><!-- appear="true"这样会报错因为人家要的是布尔值不是字符串 --><!-- :appear="true"简写为appear --><h1 v-show="isShow">你好啊</h1></transition>
.hello-enter-active{animation: atguigu 1s;
}
.hello-leave-active{animation: atguigu 1s reverse;
}
@keyframes atguigu {from{transform: translateX(-100%);}to{transform: translateX(0px);}
}
2.用过渡实现以上效果
元素进入的样式:
v-enter:进入的起点
v-enter-active:进入过程中
v-enter-to:进入的终点
元素离开的样式:
v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点
.hello-enter,.hello-leave-to{/* 进入的起点 */transform: translateX(-100%);}
.hello-enter-to,.hello-leave{
/* 进入的终点 */
transform: translateX(0);
}
.hello-enter-active,.hello-leave-active{transition: 0.5s linear;
}
3.多个元素实现过渡
transition-group
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition-group name="hello" :appear="true"><!-- appear="true"这样会报错因为人家要的是布尔值不是字符串 --><!-- :appear="true"简写为appear --><h1 v-show="isShow" key="1">你好啊</h1><h1 v-show="isShow" key="2">你好啊</h1></transition-group></div>
</template>