【Vue】data属性,v-for,diff算法,composition API

❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

Vue3

在这里插入图片描述

  • MVC和MVVM模型
    MVC是Model-View-Controller,MVVM是Model-View-ViewModel,html就相当于是view,javaScript就相当于controller,通过axios获取数据,获取的数据就是model,然后通过model返回给view,这样就是MVC模式;
    Vue就是MVVM的结构,ViewModel就是将模型里的数据绑定到View上了,如果view上发生了一些事件,通过ViewModel触发DOM Listeners之后操作model里面的值

data属性

  • Vue2中可以不写函数,Vue3的版本必须的是一个函数,Vue2通过Object.defineProperty实现,Vue3通过new Proxy实现
  • 注意数组更新的检测,如果是使用一些原生的数组方法,是直接修改了数组的值,如果是使用map,filter等高阶函数,会生成一个新的函数,不会修改原函数的值

插值语法

  • 双花括号中不能插入html代码,为了避免xss的攻击,通过v-html可以实现

修饰符

  • prevent修饰符会告知v-on指令对触发事件调用event.preventDefault(),用来阻止事件的默认行为,例如a标签默认跳转连接等等
  • enter修饰符,自动添加回车确定事件
  • passive:被动事件表示该事件处理函数不会调用preventDefault(),浏览器可以不必等到这个函数运行完才执行默认行为,可以让默认行为更早的发生,让页面看起来更流畅
  • once:运行一次旧销毁

v-on的修饰符

  • .stop:调用event.stopPropagation(),用于阻止事件冒泡
  • .prevent:调用event.preventDefault(),用于阻止默认事件
  • .capture:添加事件监听器到指定事件的捕获阶段,正常事件触发的时候是在冒泡阶段,如果加了这个修饰符,点击父元素的时候就可以触发事件了

v-model

  • 本身就是语法糖,负责监听用户的输入事件来更新数据,可以在表单input,textarea,select,checkbox等元素上创建双向数据绑定,会根据控件的类型自动选取正确的方法来更新元素
  • 通过v-bind帮点value属性的值;绑定input事件或者change事件监听函数,函数会获取最新的值赋值到绑定的属性中
  • 默认情况下,v-model绑定的是input事件,如果在v-model后面加上lazy修饰符,就会切换为change事件,只要在提交(比如回车)才会触发
    -

事件总线

  • 全局事件总线是一种用于在组件之间进行通信的机制,通过创建一个中央时间总线或事件中心,用于在应用程序中的任何组件之间传递消息,无需显式的在组件之间传递props。
  • 在App.vue中监听事件,通过eventBus.$on方法,然后再子组件中通过emit触发事件
    在这里插入图片描述

v-for

  • 在使用v-for进行列表渲染的时候,通常会和key属性一起使用,key属性主要用于Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes
  • 首先认识一下VNode,VNode就是虚拟节点,无论是组件还是元素,他们最终在Vue中表示出来的都是一个个VNode,VNode本质是一个JS对象
    在这里插入图片描述
  • 元素先转化成VNode,然后VNode之后转换成真实的DOM
    在这里插入图片描述
  • 那么如果我们不只是一个简单的div,而是有一大堆的元素,那么他们应该会形成一个VNode Tree,多个VNode形成一个虚拟DOM
  • 那么为什么有一个虚拟DOM呢,方便我们后续的diff算法,也方便我们跨平台
    在这里插入图片描述

虚拟DOM

  • 虚拟DOM就在Vue的实例上面,叫做_vnode,首先tag是根元素div,div的id在data中的attrs展示是container,里面有一个children,里面就是一些子节点(包括文本节点)
    在这里插入图片描述
    在这里插入图片描述
  • vue的模板字符串编译成一个模板函数,vue里面自带一个函数可以实现这个,vue.compile('div @click=“foo()”></ div>),这个函数里面还有一个cache缓存函数,如果转化过了就不转化了,原理就是先通过模板生成一个抽象语法树 ,然后根据抽象语法树变成模板函数(这是一个匿名函数 ,通过With关键字传this),然后模板函数获得值再返回给你,这个东西才是我们真正可以用的东西,这个模板函数就是render函数,render 函数是用于生成组件的虚拟 DOM(Virtual DOM)的函数。
    在这里插入图片描述
  • 当修改界面上的东西的时候,就会在render函数中调用这句话创建一个新的vnode节点,这句话vm._renderProxy是vue对象,后面的createElement就相当于elt函数,这个函数里面有一个_c在这里插入图片描述通过这个_c函数就可以创建虚拟DOM节点,_v是创建文本节点,_c传进去三个参数,标签名,属性attrs,子节点在这里插入图片描述
  • 当我们创建完新的虚拟DOM节点之后,就会调用vm._update函数在这里插入图片描述
  • 把旧的vnode赋值给prevVnode,然后把新的vnode赋值给vm._vnode
    在这里插入图片描述
    -如果是第一次渲染,就直接创建真实的DOM节点,然后在页面上展示,如果不是第一次渲染,通过vm_patch_函数,进行打补丁,将两者的差异在真实的DOM上打上补丁,之后进行更新,在patch函数中有一个updateChildren函数,主要目的是对比新旧子节点并进行高效地更新
    在这里插入图片描述

diff算法

  • 我们先在有一个案例,假如当我点击按钮的时候,会在一个数组中间加一个f,然后通过v-for展示出来,在Vue中,对于相同给父元素的子元素节点并不会重新渲染整个列表,因为对于列表中abcd,他们都是没有变化的,在操作真实DOM的时候,我们只需要在中间插入一个f的li即可
  • Vue中对于有key和没有key会调用两个不同的方法,如果有key,就会使用patchKeyedChildren方法,如果没有key,就会使用patchUnkeyedChildren方法
    在这里插入图片描述
  • 子节点更新策略:
    四种命中查找
    1.新前与旧前
    2.新后与旧后
    3.新后于旧前
    4.新前于旧后
    先准备四个指针,旧前,旧后,新前,新后,首先对比新前与旧前是不是一个节点,如果相同就不执行节点移动操作,命中了一种就不再进行命中判断了,旧前指针往下移动,新前指针也往下移动,循环的条件就是新前<=新后 && 旧前<=旧后,如果旧节点先循环完毕了,说明新节点是有剩余的,就说明了新前和新后之间的节点都是新增的节点,这是新增的情况
    如果是删除的情况,前前比较,命中,指针下移,直到前前比较,不命中,让后后比,命中,指针都上移动,如果新节点先循环完毕,说明老节点中还有剩余节点,说明旧前和旧后之间卡住的节点就是他们是要被删除的节点
    如果都没有命中,就需要用循环来寻找,循环旧节点秒如果找到了,虚拟DOM就会给该节点打上undefined,但是真实DOM就会移动位置到旧前之前的位置
    当新前与旧后命中的时候,就会移动新前指向的这个节点在老节点中对应的节点,把老节点中这个节点移动到旧前的前面,然后新前往后移,旧后往前移
    当新后和旧前命中的时候,就会移动新后指向的这个节点在老节点中对应的节点,把老节点中这个节点移动到旧后的后面

响应式原理

  • v2是通过getter和setter,v3是通过proxy对象修改,通过代理对象,当对对象继续操作之后,先告诉proxy,由proxy进行操作
  • v3中数据的操作都是通过proxy的,原来的对象还是不变的,变的都是proxy对应的代理对象

v-for中的key

  • 文档中说,vue默认是按照“就地更新“的策略来更新通过v-for渲染的元素列表,当数据项的顺序改变时,vue不会移动DOM元素的顺序,而是就地更新每个元素,确保他们在原本指定的索引位置上渲染。

nextTick

  • vue 中的 nextTick 主要用于处理数据动态变化后,DOM 还未及时更新的问题,用nextTick 就可以获取数据更新后最新 DOM 的变化。 nextTick 是将回调函数推入微任务队列中,以便在当前宏任务执行完毕后执行。它能够在 Vue 完成一次 DOM 更新后执行回调函数,用于处理与 DOM 更新相关的操作。
  • 在旧版浏览器中,通过MutationObserver可以监控DOM对象的变化,可以监听属性attributes,子代节点childList
    在这里插入图片描述

composition API

  • 在Vue2中,我们编写组件的方式是OptionsAPI,OptionsAPI一大特点就是在对应的属性中编写对应的功能模块,比如data定义数据,methods定义方法
  • 但是这种代码有很大的弊端:当我们实现某一个功能的时,这个功能对应的代码逻辑会被拆分到各个属性中,当我们的组件变得更大更复杂的时候,同一个功能的逻辑就会被拆分的很分散。
  • 如果我们能够将同一个逻辑关注点的代码收集在一起会更好。
  • 这个就是Composition API想做的事情,这个API我们就要在setup函数中编写。

setup函数

  • setup函数有两个参数,第一个参数是props,第二个参数是context
  • props其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取,我们的setup中是没有this的
  • context是一个对象,有三个属性,attrs,slots,emit,slots是父组件传递过来的插槽,emit是我们组件内部需要发出事件的时候,会用到emit(但是不可以通过this.$emit发出事件)
<template>
  <div class="app">
    <h2>当前计数:{{ counter }}</h2>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  setup() {
    let counter = ref(100);
    const increment = () => {
      counter.value += 1;
    };
    const decrement = () => {
      counter.value -= 1;
    };
    return {
      counter,
      increment,
      decrement,
    };
  },
};
</script>


<style lang="scss">
</style>

  • ref是将数据变成响应式

reactive函数

  • 用于定义复杂类型的数据,可以实现在setup中定义的数据提供响应式的特性
  • ref用于定义简单类型的数据,也可以定义复杂的数据
<template>
  <div>message:{{ message }}</div>
  <button @click="changeMessage">修改message</button>
  <hr />
  <h2>账号:{{ account.username }}</h2>
  <h2>密码:{{ account.password }}</h2>
  <button @click="changAccount">修改账号</button>
</template>

<script>
import { ref, reactive } from "vue";
export default {
  setup() {
    let message = ref("hello world");
    const changeMessage = () => {
      message.value = "你好,世界";
    };

    const account = reactive({
      username: "coderwhy",
      password: "12345",
    });
    const changAccount = () => {
      account.username = "limingpu";
    };

    return { message, changeMessage, account, changAccount };
  },
};
</script>

<style>
</style>

Ref

  • reactiveAPI对传入的类型是有限制的,只能是一个对象或者数组,如果我们传入一个基本类型就会报警告
  • 提供了另外一个可变的响应式对象,ref的值是用value属性维护的

readonly函数

  • 如果要在子组件中修改父组件的内容,需要通过emit函数,在父组件中进行操作,emit在setup函数中的centext参数
  • 但是在vue3中,专门设计了一个readonly函数,用readonly函数包过的数据,是修改不了的,只能是从子组件将要修改的数字传到父组件,然后修改父组件中用readOnly包裹的值即可
    在这里插入图片描述

toRefs、toRef函数

  • 当我们有一个被reactive包裹的对象,我们想对其中的元素进行解构,解构后的值就失去了双向绑定的特性,这时候我们就需要用toRefs函数将reactive包裹的对象的值再进行包裹,解构后的值才能实现响应式,如果想结构其中一个字段,通过toRef函数就可以实现了在这里插入图片描述

computed

  • computed值返回的是一个ref,所以需要.value
    在这里插入图片描述

在这里插入图片描述

ref获取元素方式

  • 挂载函数变成onMounted了,如果要获取ref元素,ref=’titleRef‘,然后再setup函数中const titleRef = ref(),获取的时候点value就可以了

在这里插入图片描述

provide函数和inject函数

在这里插入图片描述

在这里插入图片描述

watch函数/watchEffect函数

  • watchEffect:自动收集响应式数据的依赖,不需要显示指定要监视的数据源,默认情况下第一次立即执行回调函数;watch:需要手动指定侦听的数据源,只要当被侦听的源发生变化时才会执行回调,默认情况下初始渲染时不执行回调函数
    在这里插入图片描述

在这里插入图片描述

记住滚动条位置的封装

在这里插入图片描述

script setup语法糖

  • 在script旁边加一个setup,就可以直接使用composition API,也不需要返回了。
  • 更少的样板内容,更简洁的代码;更好的运行时性能。

在这里插入图片描述

  • 导入的组件可以直接使用,不需要components
  • 定义props通过defineProps,这个函数不需要导入,就在当前的作用域中,如果需要发送事件,通过defineEmits([])
    在这里插入图片描述
    在这里插入图片描述
  • 如果想拿到一个组件的实例,调用子组件的方法,通过ref,也就是所谓的父组件想用子组件中的东西,需要在子组件中通过defineExpose函数暴露给父组件才可以使用
  • 子组件
    在这里插入图片描述
  • 父组件
    在这里插入图片描述

————————————————————————
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李小浦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值