尚硅谷vue2的todolist案例解析,基本概括了vue2所有知识点,结尾有具体代码,复制粘贴学习即可

脚手架搭建

1-初始化脚手架(全局安装)

npm install -g @vue/cli

2-切换到创建项目的空目录下

vue create xxxx

整体结构

整体思路

App定义所有回调方法 增删改查 还有统一存放最终数据,所有子组件不拿数据,由App下发数据,类似于数据中心

打个比方,所有子组件只是个可以反复利用的壳子,类似于高达积木,而正在要来拼接他们的是我们的APP组件,有大脑想法,可以决定怎么拼好看,且怎么上色也就是怎么赋予数据得当

当然下放数据可以给其第二个大脑List

第二个数据中心为App给List传的数据todos,其遍历出来的每一个todo下发每一个Item组件,也就是在Item组件里v-for,该item里面的数据隔离开其他数据,可以进行编辑,你也可也在一个List页面同时写了ul,li,此时遍历出来的每个li如果要控制他们单独编辑状态还必须v-if=“showInput&&currRow=i” 来把所有的input同时遍历出来,然后当点击了按钮后传currRow当前编辑行设置为第几个i,然后showInput变为true,

删除操作就传递id然后触发事件遍历过滤出去就行

所有子组件触发的事件 App全部监听 事件可以通过pubsub消息订阅,事件总线,子父组件事件触发和监听方法实现, 父给子传递数据就使用 <sonComponent :子组件里的props接收名=“父组件的数据字段名”> 也可以传递一个方法。子组件props:[‘数据名’]接收

依赖安装

第三方的依赖引入import放在最上面

名称作用使用场合
nanoid生成唯一的Id,防止id冲突不用手动递增ID
pubsub消息订阅与发布孙组件给爷组件传递数据时

依赖用法

nanoid

 npm i nanoid
 import { nanoid } from "nanoid";
 nanoid()

pubsub

npm i pubsub
import pubsub from 'pubsub-js'

父组件挂载时订阅,销毁时取消订阅

  mounted() {
        this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo)
    },
    beforeDestroy() {
        pubsub.unsubscribe(this.pubId)
    },

子组件传递数据,

      //删除
        handlerDelete(id){
        if (confirm('确定要删除吗?')) {
          pubsub.publish('deleteTodo',id)
        }

注意事项

当你发现一个组件很难命名的时候,可能该组件包含了多个功能,需要更细致的拆分组件

例如Header用来输入信息,List用来展示,编辑,删除操作,这两个就必须拆分开

h5自身有header,head组件,不能起和这俩一样的名字,会有警告,可以在前面加个前缀或直接叫Top还是别的名字

要求

孙组件与父组件通信时使用全局事件总线和消息订阅与发布

update和check使用全局事件总线

deleteTodo使用消息订阅与发布

命名要求

组件

MyHeader
列表MyList
每一个itemMyItem
页脚MyFooter

方法

添加addTodo/handleAdd
勾选checkTodo/handleCheck/checkAllTodo
编辑updateEdit/handleEdit
删除deleteTodo/deleteAllTodo/handleDelete
全选checkAllTodo

数据

所有todotodos
单个todotodo
编辑状态isEdit
是否全选isAll
完成状态done
todo标题title
todoIdtodoId
todo对象todoObj
已完成doneTotal
所有total

涉及的操作 主要内容

$event

传递原生事件,属性target获取触发该事件的DOM对象,DOM对象的value获取其值,用于输入框失焦所触发事件获取输入框值

本地存储+watch深度监视

搭配watch监听器,监听某个数据对象,深度监视为真,表示监视该对象属性

内部设置handler处理函数当数据发生变化时触发,接收两值分别为,newVal变化后值、oldVal变化前值

 watch: {
        todos: {
            deep: true,
            handler(value) {
                localStorage.setItem('todos', JSON.stringify(value))
            }
        }
    }

只需要第一个newVal,该值为todos对象,由于localStorage存储必须为JSON字符串不能为js对象,需要先转,且取值的时候需要从json字符串转为js对象

localStorage.setItem('todos', JSON.stringify(value))

钩子函数/全局事件总线/ref命名/vue原型

vue实例创建前,给vm原型绑定一个全局变量bus,值为vm本身,这样后续所有父组件可以在bus上监听,子组件在上面触发

$bus.on监听(‘事件名’,回调)

$bus.emit触发(‘事件名’,参数)

打个比方,on相当于耳朵在听,听到了就根据听到的内容做出对应的动作,也就是回调

而emit相当于说,说给耳朵听, 之前单纯的emit只能在父子组件通信,现在引入了一个传话的,隔代通信,事件名相当于一个人名,和谁通信的标识,当然这个on必须在挂载后执行类似于父组件监听子组件的 <Son @Event=回调 />

当然,该传话人必须让所有组件都能看到,访问到,往VueComponent.protoType身上放?每一个标签都会由Vue.extend生成一个新的VueComponent,这样所有组件都能看到this.$bus了?

image-20240624142833094

以下的VueComponent简写成vc,vue实例对象简写成vm (viewModule命名来源于Model-View-ViewModule)

MVVM扫盲:

Module——数据

View——视图(div,input,span…)

ViewModule——方法(视图通过方法来获取到数据的途径, 视图数据间的桥梁)

但是该vc是vue实例对象也就是vm.extend后才有的。一开始没有的,所以该绑定无效,当然你也可以直接改源码,每次extend生成的VueComponent都有该共同传话人(不建议)

可以将其放置在vc的缔造者,vm身上 this。。如果子组件身上的this里没有bus属性则找vm的this,类似于java里的extend继承父类,父类vm有的所有属性子类都有这样就能通过this.$bus.on/emit调用,思路有了。该如何写呢?这里先插入解释一下之前 父子通信的过程

上面听不懂的你就这样记。我们在找一个大家都能 看到的人,委托他帮忙传话,
举个例子, 你要和校长聊天,但是没校长联系方式。你这个时候得找你们班主任 转达你要说的内容给校长,而校长要和你聊天,他也得通过班主任

image-20240624145314436
image-20240624143557840
这里的原型对象和java里的类相似,而实例对象等于类似于java里new出来的实例对象
知道了 o n , e m i t , o f f 在原型对象上了之后且,原型对象 v c 和 v m 都能看到,那问题就好办了,挑一个 v c > 还是 v m 设置为 on,emit,off在原型对象上了之后且,原型对象vc和vm都能看到,那问题就好办了,挑一个vc>还是vm设置为 on,emit,off在原型对象上了之后且,原型对象vcvm都能看到,那问题就好办了,挑一个vc>还是vm设置为bus的属性即可

第一种vc,(好像有点麻烦)


第二种vm

image-20240624152300766
总结一句话, 在原型上的bus属性放置了 vm自身(携带方法on emit off) 所有组件可以通过this. b u s . bus. bus.on 通信

为什么叫$bus? 而不是随便起一个x还是什么乱七八糟的名字。bus翻译过来有总线的意思 而this为全局, 所以称全局事件总线

$ 符号意为 规范用法 vue原型身上的api都用 $ 例如import request from axios.js |||| Vue.prototype.$request=request等等…

插入一句话——————

父组件这样@事件名=回调其实等同于在子组件里 this. $ on(‘事件名’,回调) 监听, ,子组件里this. $ emit触发…都是在同一个VC下使用原型的 $ on emit属性(当然还有一个解除绑定this.$off)所以父子可以通信

当然子组件监听子组件触发 不是有点废话?一般用于同级组件也称兄弟组件,先ref组件命名再指定哪个组件进行监听on,on需在mounted挂载之后执行

image-20240624153935379

image-20240624153911998

注意
on不能重名,可以将其分模块到一个文件下统一constant常量

$ on 销毁前需 $ off 掉 为什么?因为当子组件销毁时 不会自动取消父组件的订阅,你还隔这占着 $ bus傀儡中间人,数量一多,整个vue服务器容易性能下降,好比数据库不使用连接池,你连接多了不用又不释放,占用资源,时间长卡顿甚至数据库崩溃,那为什么vc不用off?,因为vc关闭事件也跟着没,而这里的事件监听on是在 原型上的,一直存在的不受vc关闭而关闭

beforeCreate() {
Vue.prototype.$bus = this
},
image-20240624140051368

挂载后**mounted**,订阅消息,事件总线监听

image-20240625100416758

销毁前**beforeDestroy**,关闭所有订阅,和事件总线监听

image-20240625100423771

至此,我们可以再任意组件之间通信

父子组件传值(数据/方法)

image-20240624154948689

:子组件内部值, =号右边为 父组件自身数据

image-20240624155045577

也可以通过插槽

image-20240624160055792

image-20240624160043944

子< slot name=“body” :rows=“data”> :传递出去的数据名=自身组件数据名

父 < template v-slot:body=“scope”> 子组件所有:传出去的数据为一个对象,父组件使用scope统一接收 例如使用row数据则scope.row

该例子为具名插槽,如果没有name,则使用v-slot:default= 默认插槽,也可以直接写成v-slot=“” v-slot:body 可以简写为#body

element ui的table使用到了该原理, 先给table 绑定数据 :data=xxx 再通过template v-slot="scope.row"获取 当前行的数据

filter过滤删除,foreach循环设置状态

页脚计算属性统计完成数量和总数

全选框根据isAll 中get属性计算返回 《total和done是否相等,且total>0 》 不然total 为0,done也为0也全选了

set属性 意味着每次isAll变化都会传进来复选框的布尔值,以此改变所有的todo.done为true/false

total 计算todo.length

doneTotal计算所有 done为真的 使用todos.reduce累加方法,传递两个形参,第一个为回调函数,接收两个形参pre积累的值,遍历出来的每一个todo, 方法体为 pre+ done? 1:0 ,第二个值为pre的初始值
image-20240624164750106

下划线占位符_

当消息订阅触发的回调函数传来两个参数第一个是消息是名称,第二个是消息传递过来的参数,这时候我们可以选中使用一个XXX占用第一个形参但是会变灰,且提示unuse,也可以使用 占位符,会发光且不提示错误信息

blur 输入框失焦

传递todo,和event事件获取输入框的值,和todo id进行更新对应的todo的title

confirm确认

对象的封装传递 add

单个复选框的change绑定事件

传id,触发事件 遍历勾选Done值

编辑 nextTick/$set /hasOwnProperty/event

input

与title同样位置来一个input 标签绑定数据为 todo.title,v-show与 title互斥 一个为!xxx / 一个为xxx

isEdit属性

给todo追加一个属性isEdit,但是直接给对象添加属性,该属性不是响应式的没有get/set,vue不认可, 模板不会解析(开发遇到的各种问题)

添加响应式属性使用this.$set(todo,‘属性名’,初始值)

二次点击编辑按钮

下一次编辑的时候做判断有了isEdit属性就直接变为true即可,没有的话就set设置属性

判断todo.hasOwnProperty(‘isEdit’) 不能直接判断todo.isEdit 因为有属性但是为false永远不会走if为真的路线

编辑时编辑按钮不显示

编辑状态时编辑按钮不显示. v-show=!isEdit
提交编辑

传递todo id和输入框的值,接用event事件,当失焦时修改, 修改时判断值是否为空!xxx return alert… 成功后isEdit为false

获取焦点

input起名ref=‘名字’, handlerEdit里this.$ref.名字.focus()但是 当点击编辑后他会执行完方法再去渲染视图,导致视图还没出来就聚焦了,聚焦失败,可以使用一个定时器,类似于异步操作,先渲染,然后等一会再聚焦 ,不过官方考虑到了这一点,提供了一个API

this.$nextTick (回调函数),作用是解析完前面代码影响的模板视图再来 执行该函数

image-20240624174516443
官方解释

语法: this.$nextTick(回调)

作用: 下一次DOM更新完执行(上述代码影响后要渲染的DOM)

时机: 需要基于渲染后的DOM进行数据操作时, 例如上述的先要等input出来在聚焦的操作

计算属性的get/set

写成一个对象 内有get/set方法 get来计算值,set来设置值,例如每次复选框的变化 就会触发set,形参为复选框的布尔值

foreach没有返回值,默认为undefined

具体代码 (无样式版)

MyHeader

<template>
    <div>
        <input v-model="inputTitle" placeholder="输入完回车确认" @keyup.enter="handleAdd"/>
    </div>
</template>

<script>
import {nanoid} from "nanoid";

export default {
    name: "MyHeader",
    data() {
        return {
            inputTitle: ''
        }
    },
    methods: {
        handleAdd() {
            if (!this.inputTitle.trim()) return alert('输入不能为空')
            const todoObj = {
                id: nanoid(),
                title: this.inputTitle,
                done: false
            }
            this.$emit('addTodo', todoObj)
            this.inputTitle = ''
        }
    }
}
</script>

<style scoped>

</style>

MyList

<template>
    <div>
        <ul>
            <MyItem v-for="todoObj in todos"
                    :key="todoObj.id"
                    :todo="todoObj"></MyItem>
        </ul>
    </div>
</template>

<script>
import MyItem from "@/components/MyItem.vue";

export default {
    name: "MyList",
    props: ['todos'],
    components: {MyItem}

}
</script>

<style scoped>

</style>

MyItem

<template>
    <div>
        <ul>
            <MyItem v-for="todoObj in todos"
                    :key="todoObj.id"
                    :todo="todoObj"></MyItem>
        </ul>
    </div>
</template>

<script>
import MyItem from "@/components/MyItem.vue";

export default {
    name: "MyList",
    props: ['todos'],
    components: {MyItem}

}
</script>

<style scoped>

</style>

MyFooter

<template>
    <div>
        <ul>
            <MyItem v-for="todoObj in todos"
                    :key="todoObj.id"
                    :todo="todoObj"></MyItem>
        </ul>
    </div>
</template>

<script>
import MyItem from "@/components/MyItem.vue";

export default {
    name: "MyList",
    props: ['todos'],
    components: {MyItem}

}
</script>

<style scoped>

</style>

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus=this
  }
}).$mount('#app')

App.vue

<template>
    <div id="app">
        <MyHeader @addTodo="addTodo"/>
        <MyList :todos="todos"/>
        <MyFooter :todos="todos"
                  @checkAllTodo="checkAllTodo" @deleteAllTodo="deleteAllTodo"/>
    </div>
</template>

<script>
import MyHeader from "@/components/MyHeader.vue";
import MyList from "@/components/MyList.vue";
import MyFooter from "@/components/MyFooter.vue";
import pubsub from "pubsub-js";

export default {
    name: 'App',
    components: {
        MyHeader, MyList, MyFooter
    },
    data() {
        return {
            //如果 本地存储有数据则使用。没有则初始化空数组,防止reduce计算属性找不到todos.length
            todos: JSON.parse(localStorage.getItem('todos')) || []
        }
    },
    watch: {
        todos: {
            deep: true,
            handler(newValue) {
                localStorage.setItem('todos', JSON.stringify(newValue))
            }
        }
    },
    methods: {
        addTodo(todoObj) {
            this.todos.unshift(todoObj)
        },
        checkTodo(todoObj) {
            this.todos.forEach((todo) => {
                    if (todo.id == todoObj.id) {
                        todo.done = !todo.done
                    }
                }
            )
        },
        updateTodo(todoObj, title) {
            this.todos.forEach((todo) => {
                    if (todo.id == todoObj.id) {
                        todo.title = title
                    }
                }
            )
        },
        deleteTodo(messageName, todoObj) {
            this.todos = this.todos.filter((todo) => {
                return todo.id !== todoObj.id
            })
        },
        checkAllTodo(value) {
            this.todos.forEach((todo) => {
                todo.done = value
            })
        },
        deleteAllTodo() {
            //用错了 把filter用成了forEach,而forEach只是个操作,不返回任何值也就是undefined
            //所以最终会将一个undefined赋值给todos
            this.todos=this.todos.filter((todo) => {
                return !todo.done
            })
        }
    },
    mounted() {
        this.$bus.$on('checkTodo', this.checkTodo)
        this.$bus.$on('updateTodo', this.updateTodo)
        this.pubId=pubsub.subscribe('deleteTodo', this.deleteTodo)
    },
    beforeDestroy() {
        this.$bus.$off('checkTodo')
        this.$bus.$off('updateTodo')
        pubsub.unsubscribe(this.pubId)
    }
}
</script>

<style>

</style>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 集成 H2 数据库

1. 引言 Spring Boot 以其简洁的配置和快速开发能力&#xff0c;成为现代微服务架构的首选框架之一。而H2数据库作为一个轻量级的内存数据库&#xff0c;非常适合开发阶段作为嵌入式数据库进行单元测试和功能验证。本文将手把手教你如何在Spring Boot项目中集成H2数据库&#…

Mybatis 到 MyBatisPlus

Mybatis 到 MyBatisPlus Mybatis MyBatis&#xff08;官网&#xff1a;https://mybatis.org/mybatis-3/zh/index.html &#xff09;是一款优秀的 持久层 &#xff08;ORM&#xff09;框架&#xff0c;用于简化JDBC的开发。是 Apache的一个开源项目iBatis&#xff0c;2010年这…

DC/AC电源模块一种效率与可靠性兼备的能源转换解决方案

DC/AC电源模块都是一种效率与可靠性兼备的能源转换解决方案 DC/AC电源模块是一种能够将直流电源&#xff08;DC&#xff09;转换为交流电源&#xff08;AC&#xff09;的设备。它在现代电子设备中扮演着非常重要的角色&#xff0c;因为许多设备需要交流电源才能正常运行。无论…

VS Code修改菜单栏字体大小

修改方法 打开VS Code&#xff0c;快捷键 CtrlShiftP&#xff0c;在弹出的输入框中输入 setting&#xff0c;找到带有JSON的一项&#xff0c;如图所示&#xff1a; 原文链接 window.zoomLevel 前后变化 终端字体大小 File -> Preferences -> Settings -> Features…

云计算运维工程师的突发状况处理

云计算运维工程师在应对突发的故障和紧急情况时,需要采取一系列迅速而有效的措施来最小化服务中断的时间并恢复系统的稳定性。 以下是一些关键步骤和策略: 快速响应: 立即识别并确认故障的性质和范围。通知团队成员和相关的利益相关者,确保所有人了解当前情况。故障诊断:…

迅为iTOP-2K1000开发板龙芯中科国产64位Loognix主板

硬件配置 国产龙芯处理器&#xff0c;双核64位系统&#xff0c;板载2G DDR3内存&#xff0c;流畅运行Busybox、Buildroot、Loognix、QT5.12 系统! 接口全板载4路USB HOST、2路千兆以太网、2路UART、2路CAN总线、Mini PCIE、SATA固态盘接口、4G接口、GPS接口WIF1、蓝牙、Mini H…

环路滤波器

块效应产生的原因 块效应指视频边界不连续的变化,我们在观看视频的时候,在运动剧烈的场景常能观察到图像出现小方块,小方块在边界处呈现不连续的效果(如下图),这种现象被称为块效应(blocking artifact)。 造成这种现象的主要原因有两点: DCT量化误差导致运动补偿导致…

工业网关的功能与作用解析-天拓四方

在工业4.0和智能制造的时代背景下&#xff0c;工业网关作为连接现场设备与云端平台的桥梁&#xff0c;正发挥着日益重要的作用。它不仅为工业设备的远程监控和管理提供了可能&#xff0c;还为企业实现数字化转型和智能化升级提供了有力支持。本文将对工业网关的功能与作用进行解…

深入理解PHP命名空间

在PHP项目中&#xff0c;命名空间&#xff08;namespace&#xff09;是一个非常重要的特性。它不仅帮助开发者组织代码&#xff0c;还能避免类、函数、常量等命名冲突问题。本文将详细介绍PHP命名空间的概念、使用方法和最佳实践。 一、什么是命名空间&#xff1f; 命名空间…

【PyQt5】一文向您详细介绍 setContentsMargins() 的作用

【PyQt5】一文向您详细介绍 setContentsMargins() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通…

EWM学习之旅-1-EWM100

系统学习一个业务模块已经变得越来越重要&#xff0c;开始吧&#xff0c;EWM&#xff01; EWM的Learning Journey中包括7本 ebook,100/110/115/120/125/130/140&#xff0c;一本一本的啃吧&#xff0c;相信很多内容是重复的。 EWM100很适合初学者&#xff0c;了解概念术语&…

charles破解

一、Charles官网下载安装包二、安装charles三、charles破解 一、Charles官网下载安装包 根据自己电脑系统 官网下载即可。 链接: https://www.charlesproxy.com/download/latest-release/ 二、安装charles 点击下载的安装包&#xff0c;然后进行安装。 三、charles破解 打…

[解决方案]使用微软拼音打中文卡顿到离谱

去这里看&#xff0c;发现有65535个文件&#xff0c;基本都是临时文件。删除后测试了一下&#xff0c;不会卡顿了但是只要打中文还是会疯狂生成tmp临时文件。 问题&#xff1a;输入法不兼容 解决方案 先把上面那个文件夹里的tmp文件全删了 直接点是&#xff0c;其他的文件会…

BEVM基于OP-Stack发布首个以WBTC为GAS连接以太坊和比特币生态的中继链

为了更好的连接以太坊和比特币生态&#xff0c;BEVM团队正在基于OPtimism的OP Stack来构建一个以WBTC为GAS兼容OP-Rollup的中继链&#xff0c;这条中继链将作为一种完全去中心化的中间层&#xff0c;把以太坊上的主流资产(WBTC/ ETH/USDC/USDT等)引入到BEVM网络。 不仅如此&am…

3.优化算法之二分查找1

二分查找简介 1.特点 最简单最恶心&#xff0c;细节最多&#xff0c;最容易写出死循环的算法 2.学习中的侧重点 1&#xff09;算法原理 数组有序的情况 2&#xff09; 模板 不要死记硬背 ->理解之后再记忆 1.朴素的二分模板 2.查找左边界的二分模板 3.查找右边界的二分模板 …

同步时钟系统为何能成为机场时间管理的好伙伴?

在机场这个分秒必争的环境中&#xff0c;精准的时间管理至关重要。同步时钟系统的出现&#xff0c;成为了机场时间管理的得力助手&#xff0c;为机场的高效运行和服务质量的提升发挥了关键作用。 一、同步时钟系统简介 同步时钟系统是一种通过网络技术实现时间同步的高精度计时…

python3使用ast.parse详解

使用ast库分析python3脚本, 并对脚本进行一些处理, 比如注释pirnt语句 一.基础知识 官方网址连接 ast — Abstract Syntax Trees ast库可以方便的分析python代码结构, 并做一些处理, 很适合对大量脚本文件做批量处理, 比如把print语句全部注释等. 直观的打印出代码结构 impo…

洞察用户需求,Xinstall数据统计App让你的App运营如虎添翼

在互联网时代&#xff0c;App推广和运营面临着前所未有的挑战。流量红利逐渐衰退&#xff0c;用户获取成本不断攀升&#xff0c;如何确保在多变的互联网环境下&#xff0c;迅速搭建起能时刻满足用户需求的运营体系&#xff0c;成为众多企业急待解决的问题。今天&#xff0c;我们…

简易人工智能入门(2)

上篇文章讨论过了人工智能的几个核心概念&#xff0c;线性模型&#xff0c;损失函数和梯度下降。下面我们继续探讨。 一、几种梯度下降的方式 1、批量梯度下降法&#xff08;Batch Gradient Descent&#xff0c;简称BGD&#xff09;是梯度下降法最初的形式&#xff0c;在更新…

RabbitMQ 入门

目录 一&#xff1a;什么是MQ 二&#xff1a;安装RabbitMQ 三&#xff1a;在Java中如何实现MQ的使用 RabbitMQ的五种消息模型 1.基本消息队列&#xff08;BasicQueue&#xff09; 2.工作消息队列&#xff08;WorkQueue&#xff09; 3. 发布订阅&#xff08;Publish、S…