Vue2(十一):全局事件总线、消息订阅与发布pubsub、TodoList的编辑功能、$nextTick、过渡与动画

一、全局事件总线

1、思路解析

一种组件间通信的方式,适用于任意组件间通信。通俗理解就是一个定义在所有组件之外的公共x,这个x可以有vm或vc上的同款$on、$off、$emit,也可以让所有组件都访问到。

第一个问题:那怎样添加这个x才能让所有组件都看到呢?要想实现这个事情,只能在Vue的原型对象上去添加了!就是在Vue.prototype上添加一个属性,值是vm或vc.

那么Vue.prototype应该放在那里写?应该在main.js中写,因为你在main.js中引入的vue

第二个问题,怎样才能访问到 $on,$off,$emit这些呢?直接去输出x的$on这些,你是找不到的,因为他只是个对象object。其实vue的原型上都有,输出vue.prototype就会发现这些属性全都有。

接下来我们看看如何使用?

 2、安装全局事件总线

安装的话用vc也行用vm也行,用vc的话可以在main.js中这么写:

const Demo = Vue.extend({});
const d = new Demo();
Vue.prototype.$bus = d;//这个d其实就是我们的vc

但其实标准的写法不是这样繁琐的,应该是用vm下面这样:

new Vue({......//放这个函数里,是因为模板还未解析,这个函数在自定义事件定义之前,是不会报错滴beforeCreate() {Vue.prototype.$bus = this //安装全局事件总线,起个名叫$bus,把当前vm给特},......
}) 

 3.使用事件总线

接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身

methods(){demo(data){......}
}
......
mounted() {this.$bus.$on('xxxx',this.demo)
}

4.提供数据

任意一个组件,都可以给上面说的A组件传数据

  methods:{sendStudentName(){this.$bus.$emit('xxxx',this.name)}
}

5.组件销毁前最好解绑

最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
因为接收数据的组件A中定义的回调函数和自定义事件是绑定的,而这个用来接收数据的组件实例A都销毁了,回调函数也没了,那这个xxxx自定义事件也就没用了,你留着会污染全局环境(这块儿有点迷糊)

beforeDestory(){this.$bus.$off('xxxx')
}

二、todolist的孙传父 

 之前我们孙传父都是父亲传给儿子函数,儿子传给孙子函数,然后孙子再调用函数传值,很麻烦,但是现在我们可以用全局事件总线实现孙子给父亲传数据

1.首先安装全局事件中线

new Vue({el: '#app',render: h => h(App),beforeCreate() {Vue.prototype.$bus = this; //创建全局事件总线}
});

2.在App中绑定全局自定义事件,并使用之前写好的回调 

mounted() {//挂载完成后给全局事件总线添加事件this.$bus.$on('changeTodo', this.changeTodo);this.$bus.$on('deleteTodo', this.deleteTodo);
},
beforeDestroy() {//最好在销毁前解绑this.$bus.$off(['changeTodo', 'deleteTodo']);
},

3.Item中触发事件,并把数据id传过去

<input type="checkbox" :checked="todo.done" @click="handleChange(todo.id)" />
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
handleChange(id) {//触发全局事件总线中的事件this.$bus.$emit('changeTodo', id);
},
handleDelete(id) {if (confirm('确定要删除吗?'))  //点确定是true,取消是falsethis.$bus.$emit('deleteTodo', id);
}

 三、消息订阅与发布(pubsub)

1.使用方法

一种组件间的通信方式,适用于任意组件之间。

这玩意儿用的不多,和全局事件总线写法差不多,但是全局事件总线更好,因为是在Vue身上操作,但是这个的话要引入第三方库,库有很多,比如pubsub-js

(1)安装pubsub:npm i pubsub-js
(2)引入:import pubsub from 'pubsub-js

(3)接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
接收两个参数,第一个是消息名字,第二个是传过来的数据

methods(){demo(msgName,data){......}
}
......
mounted() {this.pubsubId = pubsub.subscribe('xxx',this.demo) //订阅消息
},   
beforeDestroy() {pubsub.unsubscribe(this.pubsubId); //销毁时取消订阅
},

(4)提供数据:

methods: {sendStudentName() {// this.$bus.$emit('hello', this.name);pubsub.publish('hello', this.name); //发布消息并传数据}
},

(5) 销毁:

 beforeDestroy(){// this.$bus.$off('hello')pubsub.unsubscribe(this.pubId)}

总结: 

2.todolist案例

(1)在App.vue中


import pubsub from 'pubsub-js'
deleteTodo(_,id)

 deleteTodo要用_,占个位

 mounted(){this.$bus.$on('checkTodo',checkTodo)// this.$bus.$on('deleteTodo', deleteTodo)this.pubId=pubsub.subscribe('deleteTodo',this.deleteTodo)},beforeDestroy(){this.$bus.$off('checkTodo')// this.$bus.$off('deleteTodo')pubsub.unsubscribe(this.pubId)}

(2) MyItem.vue


import pubsub from 'pubsub-js'
handleDelete(id) {if (confirm('确定删除吗?')) {//通知App组件将对应的todo对象删除// this.deleteTodo(id)// this.$bus.$emit('deleteTodo', id)pubsub.publish('deleteTodo',id)}}

四、todolist的编辑功能实现

1.思路

首先得有一个按钮,点击之后出现input框,框里呢是todo.title,而且原来的span要隐藏。然后修改完之后,失去焦点会自动更新数据,并且span出现,input框隐藏。
除此之外还有个细节,那就是点击编辑要自动获取焦点,要不然会有bug(点击编辑,然后突然不想改了,还得手动点一下input,再点下别的地方,才会变回span)

想实现span和input的来回切换,就要给todo添加新的属性,用来标识这个变换,这里起名叫isEdit

所以大致思路:给标签添加事件 => 点击编辑按钮切换span为input => 失去焦点传数据 => App收数据 => 解决焦点bug

2.给标签添加事件

(1)isEdit一上来是没有的,所以todo.isEdit = false,再加上默认上来显示的是span,所以span加个v-show="!todo.isEdit",input加个v-show="todo.isEdit" ,button加v-show="!todo.isEdit"是因为我们一般编辑时,这个按钮应该消失才对,所以和span一致

(2)由于props接过来的数据不能改,所以使用单向数据绑定:value="todo.title" (不过这里很奇怪,明明isEdit也是给todo添加了属性,奇怪奇怪,画个问号?????)

(3)ref="inputTitle" 是为了方便后面拿到input元素然后操作它写的(nextTick)

(4)@blur="handleBlur(todo, $event)"是失去焦点时触发的事件,用来给App传值

<span v-show="!todo.isEdit">{{ todo.title }}</span>
<input 
type="text" 
v-show="todo.isEdit" 
:value="todo.title" 
ref="inputTitle" //这个我没写
@blur="handleBlur(todo, $event)"><button class="btn btn-edit" @click="handleEdit(todo)" v-show="!todo.isEdit">编辑</button>

3.点击编辑按钮切换span为input

点击编辑给todo追加属性,用来切换span为input。这里考虑到给todo追加属性的问题,如果想要让Vue监测到这个属性,那么必须使用$set来添加isEdit,且默认值为true(因为编辑的时候显示的时input啊,想想v-show="todo.isEdit")。

但是这里边有点儿问题,如果已经添加过了isEdit,那每次点击编辑按钮,都会添加一次isEdit属性,这样是不太好的,所以要加个判断,添加过了就改成true,没添加过就添加个true

   handleEdit(todo) {if (todo.isEdit !== undefined) {console.log('todo里有isEdit属性了')todo.isEdit = true;} else {console.log('todo里没有isEdit属性')this.$set(todo, 'isEdit', true);}this.$nextTick(function () {this.$refs.inputTitle.focus();})},

4.失去焦点传数据

失去焦点首先input得变回span,然后使用全局事件总线传值,传值一定要传当前input框的value值,因为这才是你修改后的值,使用事件对象获取。(当然,别忘了id也要传)

handleBlur(todo, e) {todo.isEdit = false;if (!e.target.value.trim()) return alert('值不能为空!');  //trim去掉空格this.$bus.$emit('editTodo', todo.id, e.target.value);
}

5.App收数据

把input框里你写的东西拿过来,给对应的todo.title

//6.实现编辑todo
methods: {......editTodo(id, newVal) {this.todos.forEach((todo) => {if (todo.id === id) { todo.title = newVal }});}}
mounted() {......//实现编辑功能,接收数据this.$bus.$on('editTodo', this.editTodo);
},
beforeDestroy() {//最好在销毁前解绑this.$bus.$off('editTodo');
},

6、代码总览

App.vue

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader @addTodo="addTodo" /><MyList :todos="todos" /><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo" /></div></div></div>
</template><script>
import pubsub from 'pubsub-js'
import MyHeader from './components/MyHeader'
import MyList from './components/MyList'
import MyFooter from './components/MyFooter.vue'export default {name: 'App',components: { MyHeader, MyList, MyFooter },data() {return {//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)todos: JSON.parse(localStorage.getItem('todos')) || []}},methods: {//添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},//勾选or取消勾选一个todocheckTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 更新修改一个todoupdateTodo(id,title) {this.todos.forEach((todo) => {if (todo.id === id) todo.title=title})},//删除一个tododeleteTodo(_,id) {this.todos = this.todos.filter(todo => todo.id !== id)},//全选or取消全选checkAllTodo(done) {this.todos.forEach((todo) => {todo.done = done})},//清除所有已经完成的todoclearAllTodo() {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('updateTodo',this.updateTodo)// this.$bus.$on('deleteTodo', deleteTodo)this.pubId=pubsub.subscribe('deleteTodo',this.deleteTodo)},beforeDestroy(){this.$bus.$off('checkTodo')this.$bus.$off('updateTodo')// this.$bus.$off('deleteTodo')pubsub.unsubscribe(this.pubId)}
}
</script><style>
/*base*/
body {background: #fff;
}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;
}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;
}
.btn-edit {color: #fff;background-color: skyblue;border: 1px solid blue;margin-right: 5px;
}.btn-danger:hover {color: #fff;background-color: #bd362f;
}.btn:focus {outline: none;
}.todo-container {width: 600px;margin: 0 auto;
}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;
}</style>

MyItem.vue

<template><li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)" /><!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props --><!-- <input type="checkbox" v-model="todo.done"/> --><span v-show="!todo.isEdit">{{ todo.title }}</span><input v-show="todo.isEdit" type="text" :value="todo.title"@blur="handleBlur(todo,$event)"></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button><button class="btn btn-edit" @click="handleEdit(todo)" v-show="!todoEdit">编辑</button></li>
</template><script>
import pubsub from 'pubsub-js'
export default {name: 'MyItem',//声明接收todoprops: ['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)}},// 编辑handleEdit(todo) {if(todo.isEdit !== undefined){todo.isEdit = true}else{console.log('@');this.$set(todo, 'isEdit', true)}},// 失去焦点回调(真正执行修改逻辑)handleBlur(todo,e){todo.isEdit=false// console.log('updateTodo', todo.id, e.target.value);if(!e.target.value.trim()) return alert('输入不能为空!')this.$bus.$emit('updateTodo',todo.id,e.target.value)}},
}
</script><style scoped>
/*item*/
li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;
}li label {float: left;cursor: pointer;
}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;
}li button {float: right;display: none;margin-top: 3px;
}li:before {content: initial;
}li:last-child {border-bottom: none;
}li:hover {background-color: #ddd;
}li:hover button {display: block;
}
</style>

五、$nextTick

1、语法:this.$nextTick(回调函数)
2、作用:在下一次 DOM 更新结束,v-for循环结束后执行其指定的回调
3、什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时(如input自动获取焦点),要在nextTick所指定的回调函数中执行。

4、比如刚才点击编辑后,input框没法自动获取焦点,那么我就得先点一下,再点别处儿才能切换回去,如果直接this.$refs.inputTitle.focus();不行,因为这个函数虽然动了isEdit的值,但是模板重新解析也得等这个函数走完啊,那input还没创建出来呢,就focus了,肯定是不行滴。
有个办法就是用异步,也可以解决,但是更好的办法是$nextTick

ref="inputTitle"
 handleEdit(todo) {if(todo.isEdit !== undefined){todo.isEdit = true}else{console.log('@');this.$set(todo, 'isEdit', true)}this.$nextTick(function(){this.$refs.inputTitle.focus()})},

六、过度与动画 

1、作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

2、写法:

准备好样式:

元素进入的样式:
v-enter:进入的起点
v-enter-active:进入过程中
v-enter-to:进入的终点

元素离开的样式:
v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点

使用<transition>包裹要过度的元素,并配置name属性:

<transition name="hello"><h1 v-show="isShow">你好啊!</h1>
</transition>

3、备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

也可以引入第三方库,animate.css.打开终端输入npm i animate.css,下载之后在相应的vue文件中引入inport 'animate.css'就可以使用。

<transition-group name="animate_animated animate_bounce" appearenter-active-class="animate_swing"leave-active-class="animate_backOutUp"><!-- 如果写name下面就要用name-enter-active --><!-- appear意思是一上来就有动画效果 --><h1 v-show="!isShow" key="1">你好啊</h1><h1 v-show="isShow" key="2">椰果</h1></transition-group>

OK今天就到这里结束

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/772610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自省式RAG 与 LangGraph的实践

自省式 RAG 对实现 RAG 的步骤进行逻辑分析&#xff1a;比如&#xff0c;我们需要知道什么时候进行检索&#xff08;基于问题和索引的构成&#xff09;、何时改写问题以提升检索效率&#xff0c;或者何时抛弃无关的检索结果并重新检索。因此提出了自省式 RAG&#xff0c;自省式…

【面经八股】搜广推方向:面试记录(十)—最近的一些面试汇总

【面经&八股】搜广推方向:面试记录(十)—最近的一些面试汇总 文章目录 【面经&八股】搜广推方向:面试记录(十)—最近的一些面试汇总0. AB1. 编程1.1 大数加减法1.2 树的序列化与反序列化1.3 手写 kmeans 聚类1.4 二叉树的前中后序遍历(非递归实现)2. 感觉这个经…

[医学分割大模型系列] (3) SAM-Med3D 分割大模型详解

[医学分割大模型系列] -3- SAM-Med3D 分割大模型解析 1. 特点2. 背景3. 训练数据集3.1 数据集收集3.2 数据清洗3.3 模型微调数据集 4. 模型结构4.1 3D Image Encoder4.2 3D Prompt Encoder4.3 3D mask Decoder4.4 模型权重 5. 评估5.1 评估数据集5.2 Quantitative Evaluation5.…

【详细讲解yarn的安装和使用】

&#x1f308;个人主页:程序员不想敲代码啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

题目:用*号输出字母C的图案。

题目&#xff1a;用*号输出字母C的图案。 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should lea…

面向对象【Annotation注解】

文章目录 注解概述注解与注释常见的 Annotation最基本的注解使用@Override@Override@SuppressWarnings元注解@Retention@Target@Documented@Inherited自定义注解格式定义使用注解概述 注解(Annotation)是从 JDK5.0 开始引入,以“@注解名”在代码中存在。例如: @Override @D…

Git入门(Git快速下载,安装,配置,远程仓库,本地仓库,IDEA提交代码,VScode提交代码使用方案一体)

Git快速下载 通过阿里镜像可以自由挑选版本并快速下载CNPM Binaries Mirrorhttp://npm.taobao.org/mirrors/git-for-windows/ 这里安装最新版本 下载安装文件 安装完后双击文件即可开始安装git 安装 git的安装傻瓜式Next即可 配置 打开git&#xff1a;桌面空白处右击&#…

JVM(四)——编译期的处理

编译期处理(语法糖) 所谓的语法糖 &#xff0c;其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中&#xff0c;自动生成 和转换的一些代码&#xff0c;主要是为了减轻程序员的负担。大多数是在 jdk5 及 jdk7 做的处理。 1&#xff09;默认构造器 创建一个…

SolidWorks教育版本的优势是什么

随着工程设计和制造技术的不断进步&#xff0c;计算机辅助设计&#xff08;CAD&#xff09;软件在教育领域的应用越来越广泛。SolidWorks教育版本作为一款专为教育机构和学生设计的三维CAD软件&#xff0c;具有许多明显的优势。本文将探讨SOLIDWORKS教育版本的主要优势&#xf…

谁再问你数据库三范式,这篇文章甩给他!!!

前几天有粉丝私信说面试被问到了数据库三范式&#xff08;面试问这种的不去也好&#xff09;&#xff0c;今天我们就来聊聊。在数据库设计的过程中&#xff0c;为了确保数据的准确性和完整性&#xff0c;我们通常遵循一定的规则和标准&#xff0c;其中最为人所熟知的便是“数据…

linux文本三剑客 --- grep、sed、awk

1、grep grep&#xff1a;使用正则表达式搜索文本&#xff0c;将匹配的行打印出来&#xff08;匹配到的标红&#xff09; 命令格式&#xff1a;grep [option] pattern file <1> 命令参数 -A<显示行数>&#xff1a;除了显示符合范本样式的那一列之外&#xff0c;并…

类,并快乐着---python中的类

类&#xff0c;并快乐着---python中的类 争取让你获取类的快乐&#xff01;\ 在Python中&#xff0c;类是一种用来创建对象的蓝图或模板。类定义了对象的属性和方法&#xff0c;可以通过类来创建多个对象实例。 所谓类&#xff0c;并快乐着。让你欲罢不能。 下面是Python中类…

MongoDB 的索引有哪些 nestjs mongoose示例

MongoDB 的索引有哪些 nestjs mongoose示例 复合索引&#xff08;Compound Index&#xff09;&#xff1a; 索引多个字段&#xff0c;允许对这些字段的组合进行高效查询。例如&#xff0c;您可以创建一个索引 { name: 1, age: 1 }&#xff0c;以便可以快速查询按姓名和年龄排序…

解不开的心结就让他系成蝴蝶结吧!

解不开的心结&#xff0c;就让它变成蝴蝶结吧 生活中&#xff0c;我们都会遇到一些难以解开的心结。它们像一块块绊脚石&#xff0c;让我们在前进的路上跌跌撞撞。面对这些心结&#xff0c;我们往往陷入了深深的思考和无尽的纠结。但是&#xff0c;有没有一种更优雅、更积极的…

【C语言】鸡兔同笼,鸡和兔共 100 只,共 284 只脚,求鸡和兔的个数。

鸡兔同笼&#xff0c;鸡和兔共 100 只&#xff0c;共 284 只脚&#xff0c;求鸡和兔的个数。 int main() {for (int i 0; ; i){if (2 * i 4 * (100 - i) 284){printf("鸡的数量&#xff1a;%d,兔子的数量&#xff1a;%d", i, 100 - i);break;} } }这里直接算出题…

开源博客项目Blog .NET Core源码学习(11:App.Core项目结构分析)

开源博客项目Blog的App.Core项目主要定义数据库表对应的数据类&#xff0c;同时定义配置文件读取、日志记录、辅助缓存等辅助类。App.Core项目安装的Nuget包不多&#xff0c;仅包括SqlSugarCore和Microsoft.Extensions.DependencyInjectio两类。   App.Core项目的顶层文件夹如…

Git本地项目开发流程记录

背景 基于Git Bash本地创建项目&#xff0c;了解Git项目开发的基本流程&#xff0c;便于管理和记录算法开发流程&#xff0c;规范代码结构。相关概念 Git分区&#xff1a;工作区&#xff0c;缓存区&#xff0c;版本区。工作区即代码开发的本地文件&#xff0c;缓存区为使用Git …

TouchGFX之Drawable

TouchGFX框架中的所有控件均为Drawable类的子类。 该类别包含控制大小和位置的一般方法。 #ifndef TOUCHGFX_DRAWABLE_HPP #define TOUCHGFX_DRAWABLE_HPP #include <touchgfx/Bitmap.hpp> #include <touchgfx/events/ClickEvent.hpp> #include <touchgfx/event…

AI智能客服:引领企业客户服务新篇章

AI智能客服&#xff1a;高效处理客户咨询的新选择 AI智能客服能够自动识别客户的语音或文字信息&#xff0c;通过自然语言处理技术理解其意图和需求&#xff0c;并快速给出准确的回答或建议。无论是常见的产品查询、订单状态确认&#xff0c;还是复杂的投诉建议&#xff0c;AI…

WEB测试之兼容性测试

1. 软件兼容性测试 兼容性测试是指待测试项目在特定的硬件平台上&#xff0c;不同的应用软件之间&#xff0c;不同的操作系统平台上&#xff0c;在不同的网络等环境中能正常的运行的测试。 兼容性测试的目的&#xff1a;待测试项目在不同的操作系统平台上正常运行&#xff0c…