一、全局事件总线!!
任意组件间通信
比如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=false
this.$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>