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

news2024/12/27 13:38:24

脚手架搭建

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>

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

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

相关文章

狗都能看懂的DBSCAN算法详解

文章目录 DBSCAN简介DBSCAN算法流程运行机制举个实例 DBSCAN算法特点DBSCAN参数选取技巧 ϵ \epsilon ϵ的选取&#xff1a;找突变点MinPts的选取 DBSCAN简介 DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff0c;具有噪声的基于密度的…

仿Photoshop利用曲线对图像调整亮度与色彩

曲线调整是Photoshop的最常用的重要功能之一。对于一个RGB图像, 可以对R, G, B 通道进行独立的曲线调整&#xff0c;即&#xff0c;对三个通道分别使用三条曲线&#xff08;Curve&#xff09;。还可以再增加一条曲线对 三个通道进行整体调整。 因此&#xff0c;对一个图像&a…

Blast L2空投教学,好用的Blast钱包推荐bitget

什么是 Blast L2&#xff1f; Blast&#xff08;web3.bitget.com/en/&#xff09;是一个与 EVM 兼容的第 2 层 (L2) 区块链网络&#xff0c;旨在通过原生收益产生收益。该项目由匿名联合创始人 PacmanBlur 领导&#xff0c;并已成功从 Paradigm 和 Standard Crypto 等知名投资…

早餐店小程序开发

在快节奏的城市生活中&#xff0c;早餐对于许多人来说是一天中最重要的一餐。然而&#xff0c;传统的早餐店在经营过程中常常面临客流量不稳定、服务效率低下等问题。为了解决这些问题&#xff0c;越来越多的早餐店老板开始寻求利用科技手段提升经营效率。早餐店小程序作为一种…

Energy-based PINN在固体力学中的运用

简介 物理信息神经网络&#xff08;Physic informed neural network&#xff0c;PINN&#xff09;已经成为在有限差分、有限体积和有限元之后的另一种求解偏微分方程组的范式&#xff0c;受到学者们广泛关注。 在固体力学领域有两类不同的PINN: &#xff08;1&#xff09;PDE…

【D3.js in Action 3 精译】1.2 D3 生态系统——入门须知

1.2 D3 生态系统——入门须知 D3.js 从不单打独斗&#xff0c;而是作为 D3 生态系统的一员&#xff0c;与生态内的一系列技术和工具相结合来创建丰富的 Web 界面。与其他网页一样&#xff0c;D3 项目也是充分利用 HTML5 的强大功能在 DOM 内构建出来的。尽管 D3 也可以创建并操…

栈,ASCII编码

栈 LinkedList stack new LinkedList<>(); int i 0; while (i < s.length()) { char c s.charAt(i); if (c <) {if (stack.isEmpty()) {i;continue;}stack.removeLast(); //从栈的末尾移除一个元素} else {stack.addLast(c); //压入栈的末尾栈是只允许在一端…

二叉树——另一颗树的子树

目录 1&#xff1a;题目分析及思路 2&#xff1a;代码实现和分析 1&#xff1a;代码 2&#xff1a;分析 1&#xff1a;题目分析及思路 给我们两棵二叉树&#xff0c;分别是 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&…

ZGC垃圾收集的主要流程

值得说明的是&#xff0c;在执行就地迁移时&#xff0c;ZGC 必须首先压缩指定为对象迁移区域内的对象&#xff0c;这可能会对性能产生负面影响。增加堆大小可以帮助 ZGC 避免使用就地迁移。 如上图&#xff0c;ZGC 的工作流程主要包括以下几个步骤&#xff1a; &#xff08;STW…

昇思25天学习打卡营第8天|保存与加载

一、简介&#xff1a; 上一章节主要介绍了如何调整超参数&#xff0c;并进行网络模型训练。在训练网络模型的过程中&#xff0c;实际上我们希望保存中间和最后的结果&#xff0c;用于微调&#xff08;fine-tune&#xff09;和后续的模型推理与部署&#xff0c;本章节我们将介绍…

drozer中文乱码解决方法

drozer简介 drozer 是 Android 的安全测试框架。 drozer 允许您通过扮演应用的角色并与 Android 运行时、其他应用的 IPC 端点和底层操作系统进行交互来搜索应用和设备中的安全漏洞。 drozer 提供了一些工具来帮助您使用、分享和理解公共 Android 漏洞。 drozer 是开源软件…

stm32学习笔记---TIM输出比较(代码部分)PWM驱动LED呼吸灯/舵机/直流电机

目录 第一个工程&#xff1a;PWM驱动LED呼吸灯 PWM.c 初始化PWM步骤 TIM的库函数 TIM_OCStructInit TIM_CtrlPWMOutputs TIM_CCxCmd和TIM_CCxNCmd TIM_SelectOCxM 四个单独更改CCR寄存器值的函数 四个初始化定时器的通道的函数 给结构体一次性都赋初始值的函数 如何…

nginx的基本配置

#user nobody;#工作进程数量 worker_processes 4;events {#子进程最大连接数worker_connections 1024; }http {#囊括的文件类型include mime.types;default_type application/octet-stream;sendfile on;#长连接多长时间没沟通后断开keepalive_timeout 65;#服…

光伏储能为什么变得那么受欢迎?

在当今这个追求可持续发展和清洁能源的时代&#xff0c;光伏储能技术逐渐崭露头角&#xff0c;并成为了能源领域的热门话题。其受欢迎程度不断攀升&#xff0c;背后有着多方面的原因。光伏储能技术的优点众多&#xff0c;涵盖了多个方面&#xff0c;以下是关于其安全、寿命等关…

建筑信息模型(BIM)的应用与案例研究:塑造建筑与施工的新纪元

在数字化浪潮的推动下&#xff0c;建筑信息模型&#xff08;Building Information Modeling, BIM&#xff09;已从一项新兴技术发展为建筑与施工领域不可或缺的核心工具。本文将深入探讨BIM技术的应用原理&#xff0c;分享实际案例&#xff0c;揭示其如何引领建筑行业走向更加高…

C语言学习记录20240626

飞船无论朝哪边行驶&#xff0c;都能通过结构体记录获取它的初始坐标、转向角度和在该方向行进的距离&#xff0c;需要根据这些信息计算飞船移动后的坐标。 向量(vector)指具有大小(magnitude)和方向(direction)的量&#xff0c;可以理解为有方向的线段。 标量或纯量(scalar)指…

MySQL 7种Join的定义图解示范结果(所有join类型)

文章目录 MySQL 7种Join的定义&图解&示范&结果&#xff08;所有join类型&#xff09;基本知识笛卡尔积 建表&填充数据1-Join不带条件account筛选 1-Inner Join 内连接不带条件account相同where筛选玩点特殊的 2-Left Join 左连接不带条件account筛选 3-Right J…

django 和 pyecharts实现可视化大屏(完整代码)

1.配置settings文件 &#xff08;1&#xff09;注意&#xff1a;需要先创建app(djnago-admin startapp app名称) &#xff08;2&#xff09;配置模板文件 DIRS: [os.path.join(BASE_DIR, templates)], &#xff08;3&#xff09;配置静态文件(这里我由于存放清洗好的需要进行可…

基于Java+MySQL停车场车位管理系统详细设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

基于51单片机密码锁—六位密码LCD1602显示

基于51单片机密码锁设计 &#xff08;仿真&#xff0b;程序&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.密码为六位数字&#xff0c;有键按下&#xff0c;LCD1602就会显示出字符 “*”&#xff1b; 2.当用户键入正确密码后&#xff0c;再按确认键&a…