04-Vue3 学习笔记之 Vue 核心基础

数据模板绑定

  模板数据绑定渲染可生成动态的 HTML 页面,页面中使用嵌入 Vue 语法可动态生成:

  • {{xxx}} 双大括号文本绑定
  • v-xxxv- 开头用于标签属性绑定,称为 指令

双大括号语法

  • 格式:{{ 表达式 }}
  • 作用:
    • 使用在标签中,用于获取数据
    • 可以使用 JavaScript 表达式
<script setup lang="ts">
import { ref } from 'vue';

const message = ref("Hello World!")
const number = ref(100)
</script>

<template>
  <div>
    <!-- 双大括号语法 -->
    <!-- 1. 文本内容 -->
    <p>文本内容:{{message}}</p>
    <!-- 2. JS 表达式 -->
    <p>JS表达式:{{number / 10}}</p>
  </div>
</template>

在这里插入图片描述

文本显示指令 v-text 和 v-cloak

  v-text 等价于 {{}} 用于显示内容,并且会覆盖元素中现有的内容。区别在于 {{}} 会有闪烁问题,v-text 不会闪烁

提示:{{}} 闪烁的原因是浏览器从上往下依次解析,会先把 {{ message }} 当作标签体直接先渲染, 然后 Vue 再进行解析 {{ message }} 变成了 message 的值。

<script setup lang="ts">
import { ref } from 'vue';

const message = ref("Hello World!")
</script>

<template>
  <div>
    <!-- v-text -->
    <p v-text="message"></p>
  </div>
</template>

在这里插入图片描述

  v-cloak 用于隐藏尚未完成编译的 DOM 模板,默认一开始被 Vue 管理的模板是隐藏着的,当 Vue 解析处理完 DOM 模板之后,会自动把这个样式去除,然后再显示出来。如果想用 {{}} 但又不想有闪烁问题,可以使用 v-cloak 来处理:

  • 添加一个属性选择器 [v-cloak]CSS 隐藏样式:[v-cloak] {display: none;}
  • 在被 Vue 管理的模板入口节点上作用 v-cloak 指令(也可作用到子元素上)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <style>
        /*
        1. 定义样式:将带有 v-clock 指令的标签隐藏
        2. 在被 Vue 管理的模板入口节点上作用 v-cloak 指令(也可作用到子元素上)
        */
        [v-cloak] {
            display: none;
        }
    </style>
</head>
<body>
    <!-- 1.定义根节点元素,作用 v-cloak 指令 -->
    <div id="app" v-cloak>
        <h2>1、双大括号输出文本内容</h2>
        <p>普通文本:{{ name }}</p>
        <p>JS表达式1:{{ salary + 10 }}</p>
    </div>
    <!-- 2. 借助 script 标签引入 Vue 库 -->
    <script src="./node_modules/vue/dist/vue.global.js"></script>
    <script type="text/javascript">
        const { createApp } = Vue;
        // 创建应用实例
        const app = createApp({
            data() {
                return {
                    name: 'Jack',
                    salary: -100
                }
            }
        }).mount('#app');
    </script>
</body>
</html>

输出 HTML 指令

  • 格式:v-html = ‘xxx’
  • 作用:如果是 HTML 格式数据,双大括号会将数据解释为普通文本,为了输出真正的 HTML,需要使用 v-html 指令;Vue 为了防止 XSS 攻击,在此指令上做了安全处理,当发现输出内容有 script 标签时,则不渲染
<script setup lang="ts">
import { ref } from 'vue';

const message = ref('<span style="color:red">红色字体内容<script>alert("hello vue")<\/script></span>');
</script>

<template>
  <div>
    <!-- 没有 v-html,原格式输出 -->
    <p style="color: red;">下面是没有使用 v-html:</p>
    <p>{{message}}</p>

    <!-- 使用 v-html -->
    <p style="color: red;">下面是使用 v-html:</p>
    <p v-html="message"></p>
  </div>
</template>

在这里插入图片描述

提示:XSS 攻击主要是利用 JS 脚本注入到网页中,读取 Cookie 值(Cookie 一般存储了登录身份信息),读取到了发送到黑客服务器,从而黑客可以使用你的账户做非法操作。XSS 攻击还可以在你进入到支付时,跳转到钓鱼网站。

一次性插值

  通过使用 v-once 指令也可以执行一次性地插值,当数据改变的时候,插值处的内容不会发生更新:

<script setup lang="ts">
import { ref } from 'vue';

// 定义被操作的变量
const number = ref(10);

// 定义操作变量的事件函数
const handleAdd = () => {
  number.value++;
}

</script>

<template>
  <div>
    <!-- 没有 v-once,number会一直变化 -->
    <p>非 v-once:{{number}}</p>
    <!-- v-once,number初始化为10后,不再发生变化 -->
    <p v-once>v-once:{{number}}</p>
    <!-- 点击 +1 -->
    <button @click="handleAdd">+1</button>
  </div>
</template>

在这里插入图片描述

属性的动态绑定

v-bind 的使用说明

  • 完整格式:v-bind:元素的属性名 = ‘xxx’
  • 缩写格式::元素的属性名 = ‘xxx’
  • 作用:将数据动态绑定到指定的元素上
<script setup lang="ts">
import { ref } from 'vue';

const url = ref("https://www.baidu.com")
</script>

<template>
  <div>
    <!-- 为 a 标签的 href 属性绑定百度的链接 -->
    <a :href="url">百度</a>
  </div>
</template>

在这里插入图片描述

  能够支持以对象的形式动态绑定多个属性值:

<script setup lang="ts">
import { reactive } from 'vue';

interface Attrs {
  id: string;
  class: string
}

const attrs = reactive<Attrs>({
  id: "red",
  class: "big"
})
</script>

<template>
  <div>
    <!-- 使用对象为 p 标签同时绑定多个属性 -->
    <p v-bind="attrs">这里有一段文字。</p>
  </div>
</template>

<style scoped>
#red {
  color: red;
}

.big {
  font-size: 100px;
}
</style>

在这里插入图片描述

  能够支持动态属性名:

<script setup lang="ts">
import { ref } from 'vue';

const url = ref("http://www.baidu.com")
const key = ref("href")
</script>

<template>
  <div>
    <a :[key]="url">百度</a>
  </div>
</template>

  还可以使用修饰符:

  • .camel: 可以将短横线命名的属性转变为驼峰式命名。由于 HTML 的特性是不区分大小写的,.camel 修饰符允许在使用 DOM 模板时将 v-bind 属性名称驼峰化:
<!--如果是 :viewBox="viewBoxData" 绑定数据这将导致渲染失败-->
<svg :view-box.camel="viewBoxData"></svg>
  • .attr:强制绑定为 DOM 原生属性,如果子组件声明的 prop 属性和标签的原生属性名相同时可用于区分
  • .prop:强制绑定为子组件的 prop 属性,如果子组件声明的 prop 属性和标签的原生属性名相同时可用于区分

class 与 style 绑定

  • class 绑定的语法格式:v-bind:class=‘表达式’:class=‘表达式’,其中的表达式有以下几种:
    • 字符串::class=‘activeClass’`
    • 对象::class=‘{active:isActive, error:hasError}’`
    • 数组::class=[‘active’, ‘error’]需要加上单引号,否则是获取 data 中的值
  • style 绑定的语法格式::style={color:activeColor, fontSize: fontSize+‘px’}activeColorfontSizedata 中的属性
<script setup lang="ts">
import {ref} from 'vue'

const activeClass = ref<string>("active");
const isDelete = ref<boolean>(true);
const hasError = ref<boolean>(false);
const activeColor = ref<string>("red");
const fontSize = ref<number>(20);
</script>

<template>
  <div>
    <h2>绑定 class 属性</h2>
    <p :class="activeClass">字符串表达式</p>
    <p :class="{delete: isDelete, error: hasError}">对象表达式</p>
    <p :class="['delete', 'error']">数组表达式</p>
    <hr>
    <h2>绑定 style 属性</h2>
    <p :style="{color: activeColor, fontSize: fontSize + 'px'}">style 绑定方式</p>
  </div>
</template>

<style>
.active {
    color: green;
}
.delete {
    background-color: red;
}
.error {
    font-size: 30px;
}
</style>

在这里插入图片描述

事件绑定指令

事件处理方法

  v-on 用于给元素绑定事件监听器(如点击事件 onclick、输入框失去事件 onblur等),当用于普通元素时,只监听原生 DOM 事件;当用于自定义元素组件,则监听子组件触发的自定义事件。

  • 完整格式:v-on:事件名=‘函数名’v-on:事件名=函数名(参数…)
  • 缩写格式:@事件名=‘函数名’@事件名=函数名(参数…)@后面没有冒号
  • event:函数中的默认行参,代表原生 DOM 事件;当调用的函数有多个参数传入的时候,需要使用原生的 DOM 事件,可以通过 $event 作为实参传入
  • 作用:用于监听 DOM 事件
<script setup lang="ts">

const handleError = (event: any) => {
  alert(event.target.innerHTML)
}

const handleWarn = (message: string, event: any) => {
  alert(message + ": " + event.target.innerHTML)
}
</script>

<template>
  <div>
    <button @click="handleError">错误</button>
    <button @click="handleWarn('Hello', $event)">警告</button>
  </div>
</template>

在这里插入图片描述

事件修饰符

  • .stop:阻止单击事件继续传播 event.stopPropagation()
  • .prevent:阻止事件默认行为 event.preventDefault()
  • .once:点击事件将只会触发一次
  • .passive:每次事件产生,浏览器都会去查询一下是否有 preventDefault 阻止该次事件的默认动作。我们加上 passive 就是为了告诉浏览器不用查询了,我们没用 preventDefault 阻止默认动作。这里一般用在滚动监听
<script setup lang="ts">

const handleClick = () => {
  alert("打印消息....")
}
</script>

<template>
  <div>
    <!-- a 的默认行为被阻止了,这里只弹框打印消息 -->
    <a href="https://www.baidu.com" @click.prevent="handleClick">百度</a>
  </div>
</template>

在这里插入图片描述

按键修饰符

  • 格式:v-on:keyup.按键名@keyup.按键名
  • 常用按键名:.enter.tab.delete.esc.space.up.down.left.right
  • 可以使用以下系统按键修饰符来触发鼠标或键盘事件监听器,只有当按键被按下时才会触发:.ctrl.alt.shift.meta
  • .exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符
  • 鼠标按键修饰符:.left.right.middle
<script setup lang="ts">

const handleEnter = (event : any) => {
  alert(event.target.value)
}
</script>

<template>
  <div>
    <input type="text" @keyup.enter="handleEnter">
  </div>
</template>

表单数据双向绑定

  双向绑定是指数据变的时候视图也相应改变;同时视图变的时候,数据也跟着改变。v-model 指令用于表单数据的双向绑定,主要针对以下类型:text(文本)、textarea(多行文本)、radio(单选按钮)、checkbox(复选框)、select(下拉框)

<script setup lang="ts">
import {reactive, ref, toRefs} from 'vue'

interface Person {
  id: number;
  name: string;
}

interface Props  {
  textModel: string;
  textareamodel: string;
  gender: string;
  isAll: boolean;
  hab: Array<string>;
  persons: Array<Person>;
  person: Person | undefined;
}

const state: Props = reactive({
  textModel: "Hello world",
  textareamodel: "",
  gender: "male",
  isAll: false,
  hab: [],
  persons: [ {id: 1, name: 'John'}, {id: 2, name: 'Jack'}],
  person: undefined
})

const {textModel, textareamodel, gender, isAll, hab, persons,person} = {...toRefs(state)}

</script>

<template>
  <div>
    <h3>text文本</h3>
    <input type="text" v-model="textModel">
    <p>text的当前文本:{{textModel}}</p>

    <h3>多行文本</h3>
    <textarea v-model="textareamodel"></textarea>
    <p>{{ textareamodel }}</p>

    <h3>radio</h3>
    <input type="radio" v-model="gender" value="male">:男
    <input type="radio" v-model="gender" value="female">:女 
    <p>radio当前选择了:{{gender}}</p>

    <h3>checkbox</h3>
    <!-- 单个checkbox 可以绑定 boolean 对象 -->
    <input type="checkbox" v-model="isAll"> 全选 <br>
    <p>是否全选:{{isAll}}</p>

    <!-- 多个的时候可以绑定数组 -->
    <input type="checkbox" v-model="hab" value="football"> 足球
    <input type="checkbox" v-model="hab" value="basketball"> 篮球
    <p>checkbox选择了:{{hab}}</p>

    <h3>select</h3>
    <select v-model="person">
      <option v-for="(person,index) in persons" :value="person" :key="index">{{person.name}}</option>
    </select>
    <p>选择了:{{person}}</p>
  </div>
</template>

常用表单控件修饰符

  • .lazy 失去焦点同步一次
  • .number 格式化数字
  • .trim 去除首尾空格

条件渲染

  条件渲染涉及到的指令如下:

  • v-if:是否渲染当前元素,另外还提供了 v-elsev-else-if 指令,实现不同条件的渲染
  • v-show:v-if 类似,只是元素始终被渲染并保留在 DOM 中,只是简单切换元素的 CSS 属性 display 来显示或隐藏
<script setup lang="ts">
import {ref} from 'vue'
import type { Ref } from 'vue';

const isShow: Ref<boolean> = ref(true)
</script>

<template>
  <div>
    <p><input type="checkbox" v-model="isShow">是否显示</p>
    <p>v-if 实现</p>
    <div class="box" v-if="isShow"></div>
    <p>v-show 实现</p>
    <div class="box" v-show="isShow"></div>
  </div>
</template>

<style>
.box {
   width: 100px;
   height: 100px;
   background-color: red;
}
</style>

在这里插入图片描述

多个元素的条件

  如果涉及到多个元素,可以使用 <template>,该元素是一个不可见的元素:<template v-if=“false”>包含的多个元素 </template>。在使用 <template> 包含多个元素的时候,使用 v-if,不能使用 v-show

v-if 和 v-show 比较

  • 什么时候元素被渲染

    • v-if:如果初始条件为假,则什么也不做;每当条件为真时,都会重新渲染条件元素
    • v-show: 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换
  • 使用场景选择

    • v-if 有更高的切换开销
    • v-show 有更高的初始渲染开销

  因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行后条件很少改变,则使用 v-if 较好。

v-if 和 v-for

   当 v-ifv-for 同时存在于一个元素上的时候, v-if 会首先被执行,这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名。

  • Vue 2 版本中在一个元素上同时使用 v-ifv-for 时, v-for 会优先作用

  • Vue 3 版本中 v-if 总是优先于 v-for 生效

  由于语法上存在歧义,建议避免在同一元素上同时使用两者。

列表渲染

迭代数组

  可以使用 v-for 来迭代数组,其语法如下:v-for=“(alias, index) in array”

  • alias:数组元素迭代的别名
  • index:数组索引值,从 0 开始(可以省略
<script setup>
import {reactive} from 'vue'
const employees = reactive([
                        {id: 1, name: '张三', age: 12},
                        {id: 2, name: '李四', age: 22},
                        {id: 3, name: '王五', age: 32}
                    ]); 

</script>

<template>
  <div>
    <ul>
      <li v-for="(employee,index) in employees" :key="employee.id">
        {{index}}. {{employee.id}} - {{employee.name}} - {{employee.age}}
      </li>
    </ul>
  </div>
</template>

在这里插入图片描述

注意

  • 使用 key 特殊属性, 它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素,一般指定的 key 值是 id
  • 可以使用 of 替代 in

关于数组更新检测

  • 使用以下方法操作数组,可以检测变动:pushpopshiftunshiftsortreversesplice()
  • 使用以下方法需要用新数组替换旧数组:filterconcatslicemap
  • Vue3 可以检测到类似下面的数据变动:array[1] =1;

迭代对象

  可以使用 v-for 来迭代对象,其语法如下:v-for=“(value, key, index) in object”

  • value:每个对象的属性值
  • key:每个对象的属性名(可以省略
  • index:索引值(可以省略
<script setup lang="ts">
import {reactive} from 'vue'

interface Person {
  id: number;
  name: string;
  age: number;
}

const person = reactive<Person>({ 
                        id: 1,
                        name: 'John',
                        age: 12 
                    }); 
</script>

<template>
  <div>
    <ul>
      <li v-for="(value, key, index) in person" :key="index">
        {{key}} - {{value}}
      </li>
    </ul>
  </div>
</template>

在这里插入图片描述

注意:

  • 在遍历对象时是按 Object.keys() 的结果遍历,不能保证它的结果在不同的 JavaScript 引擎下是顺序一致的
  • 可以使用 of 替代 in

v-memo

  v-memo 是 Vue 3.2+ 版本新增的指令,它的值为数组,用于缓存一个节点及子节点,在元素和组件上都可以使用,只作性能的提升。

  • 该指令需要传入一个固定长度的依赖值数组进行比较,如果数组里的每个值都与最后一次的渲染相同,那么整个子节点的更新将被跳过
  • v-memo 中依赖的值若不发生变化,当前 DOM 及整个子树 DOM 都不会重新渲染,直接使用最后一次缓存的结果,它存储先前渲染的结果以加速未来的渲染
<script setup lang="ts">
import { nextTick, ref } from 'vue';

const count = ref(1)
const valueA = ref(88)
const valueB = ref(99)

const handleUpdate = async () => {
  count.value++
  valueA.value = 188
  await nextTick()
  console.log(count.value);
}
</script>

<template>
   <div v-memo="[valueA, valueB]">
    <p>count: {{count}}</p>
    <button @click="handleUpdate">更新数据</button>
   </div>
</template>

在这里插入图片描述

  • v-memo 传入空依赖数组(v-memo="[]")与 v-once 的效果相同,一次性插值
<script setup lang="ts">
import { nextTick, ref } from 'vue';

const count = ref(1)

const handleUpdate = async () => {
  count.value++
  await nextTick()
  console.log(count.value);
}
</script>

<template>
   <div v-memo="[]">
    <p>count: {{count}}</p>
    <button @click="handleUpdate">更新数据</button>
   </div>
</template>

在这里插入图片描述

  • v-for 中使用 v-memo 时,两者都要绑定在同一个元素上,v-memo 不能用在 v-for 内部
<script setup lang="ts">
import { nextTick, ref } from 'vue';

interface emp {
  id: number;
  name: string;
  status: boolean;
}

const emps = ref<Array<emp>>([]);

emps.value = Array.from({ length: 200 }, (_, index) => {
  return { id: index, name: "emp" + index, status: true }
})


const updateStatus = (item: any) => {
  const {id, status} = item
  emps.value = Array.from({ length: 200 }, (_, index) => {
    const newStatus = index == id ? !item : true
    console.log(newStatus)
    return { id: index, name: "emp" + index, status: newStatus }
  })
  console.time()
  nextTick(() => {
    console.timeEnd()
  })
}
</script>

<template>
  <div>
    <li v-for="(item, index) in emps" :key="item.id" v-memo="[item.status]">
      <span>{{ item.name }}</span> &nbsp;
      <span>{{ item.status }}</span> &nbsp;
      <button @click="updateStatus(item)">更新状态</button>
    </li>
  </div>
</template>

计算属性

  模板中的表达式虽然方便,但也只能用来做简单的操作,对于复杂逻辑推荐使用计算属性来描述:

<script setup lang="ts">
import { computed, ref } from 'vue'

const engScore = ref(100)
const mathScore = ref(100)

const total = computed(() => {
   return (engScore.value - 0) + (mathScore.value - 0)
})
</script>

<template>
   <div>
      <p>数学分数:<input type="text" v-model="mathScore"></p>
      <p>英语分数:<input type="text" v-model="engScore"></p>
      <p>总分:{{total}}</p>
   </div>
</template>

在这里插入图片描述

  案例中定义了一个计算属性 totalcomputed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref,可以通过 total.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .valueVue 的计算属性会自动追踪响应式依赖。它会检测到 total 依赖于 engScoremathScore,所以当它们改变时,任何依赖于 total 的绑定都会同时更新。

计算属性 VS 方法

  调用一个函数也会获得和计算属性相同的结果,不同之处在于计算属性值会基于其响应式依赖被缓存,一个计算属性仅会在其响应式依赖更新时才重新计算。

  计算属性默认是只读的,当尝试修改一个计算属性时会收到一个运行时警告。只有在某些特殊场景中才需要用到可写的属性,可以通过同时提供 gettersetter 来创建:

<script setup>
import { computed, ref } from 'vue'

const engScore = ref(100)
const mathScore = ref(100)

const total = computed({
   get () {
      return (engScore.value - 0) + (mathScore.value - 0)
   },
   set (newValue) {
      engScore.value = (newValue - 0) / 2
      mathScore.value = (newValue - 0) / 2
   }
})
</script>

<template>
   <div>
      <p>数学分数:<input type="text" v-model="mathScore"></p>
      <p>英语分数:<input type="text" v-model="engScore"></p>
      <p>总分:<input type="text" v-model="total"></p>
   </div>
</template>

在这里插入图片描述

提示:

  • Getter 不应有副作用:getter 的职责应该仅为计算和返回该值,不要在 getter 中做类似异步请求或更改 DOM 的操作
  • 避免直接修改计算属性值:从计算属性返回的值是派生状态,可以把它看作是一个临时快照,每当源状态发生变化时,就会创建一个新的快照,因此计算属性的返回值应该被视为只读的

监听器

  有些情况下需要在状态变化时执行一些副作用,在组合式 API 中可以使用 watch 函数在每次响应式状态发生变化时触发回调函数,它的第一个参数可以是不同形式的数据源,可以是 一个 ref(包括计算属性)、一个响应式对象、一个 getter 函数或多个数据源组成的数组

<script setup>
import {ref, reactive, watch} from 'vue'

// 定义变量
const x = ref(0)
const y = ref(0)

// 定义一个响应式对象
const obj = reactive({
  name : "Tom"
})

// 监听变量
watch(x, (newValue) => {
  console.log(`watch x : ${x.value}`)
})

// 监听数组
watch([x, y], ([newX, newY]) => {
  console.log(`watch [x, y] x: ${x.value}, y: ${y.value}`)
})

// 监听对象
watch(obj, (newObj) => {
  console.log('监听对象')
})

const handleNumber = () => {
  x.value++
  y.value++
}

const handleObj = () => {
  obj.value = {}
}
</script>

<template>
   <div>
      <button @click="handleNumber">changeNumber</button>
      <button @click="handleObj">changeObj</button>
   </div>
</template>

在这里插入图片描述

  不能直接侦听响应式对象的属性值,这里需要用一个返回该属性的 getter 函数:

<script setup lang="ts">
import {reactive, watch} from 'vue'

const obj = reactive({
  name : "Tom"
})

watch(
  () => obj.name, (newValue) => {
    console.log(`name is ${newValue}`)
  }
)

const handleObj = () => {
  obj.name = "Jack"
}

</script>

<template>
  <div>
    <p>{{obj.name}}</p>
    <button @click="handleObj">changeObj</button>
  </div>
</template>

在这里插入图片描述

  也可以给上面这个例子显式地加上 deep 选项,强制转成深层侦听器。但深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时开销很大,因此只在必要时才使用它,并且要留意性能:

<script setup>
import {reactive, watch} from 'vue'

const obj = reactive({
  name : "Tom"
})

watch(
  obj, 
  (newValue) => {
    console.log(`name is ${newValue.name}`)
  },
  {
    deep: true
  }
)

const handleObj = () => {
  obj.name = "Jack"
}

</script>

<template>
  <div>
    <p>{{obj.name}}</p>
    <button @click="handleObj">changeObj</button>
  </div>
</template>

  watch 默认是懒执行的,仅当数据源变化时才会执行回调。但在某些场景中希望在创建侦听器时立即执行一遍回调,可以通过传入 immediate: true 选项来强制侦听器的回调立即执行:

watch(
  source, 
  (newValue, oldValue) => {
 
	}, 
  { immediate: true }
)

  当你更改了响应式状态,它可能会同时触发监听器回调和 Vue 组件更新。默认情况下监听器回调都会在 Vue 组件更新之前被调用。这意味着在监听器回调中访问的 DOM 将是被 Vue 更新之前的状态。如果想在监听器回调中能访问被 Vue 更新之后的 DOM,需要指明 flush: ‘post’ 选项(默认flush:'pre'):

<script setup lang="ts">
import { reactive, ref, watch } from 'vue';

const query = reactive({
    username: '12'
})

const spanRef =  ref();

const handleClick = () => {
    query.username = "Jack"
}

watch(query, (newValue, oldValue) => {
    console.log('spanRef', spanRef.value.innerHTML)
    console.log('深度监听对象query', newValue, oldValue);
}, {
    deep: true,
    flush: 'pre'
})

</script>

<template>
    <!-- ref 是一个特殊的属性,类型元素上的id属性,通过ref值直接引用操作此 DOM 元素或组件 -->
    <span ref="spanRef">显示:{{ query.username }}</span>
    <button @click="handleClick">click</button>
</template>

在这里插入图片描述

  将 flush: 'pre' 改成 flush: 'post' 之后的效果如下:

在这里插入图片描述

  在组件模板中,针对要操作的元素上使用 ref 属性(ref 是一个特殊的属性),将元素挂载到 Vue 实例上,通过 const spanRef = ref(); 获取挂载的元素,从而进行操作元素。

关于 watchEffect 和 watch

  watchwatchEffect 都能响应式地执行有副作用的回调,它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机
  • watchEffect 则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确(watchPostEffect 在响应式状态更新到模板之后调用监听器的回调函数,也可以在 watchEffect 的第二个参数中添加 flush: 'post' 属性设置)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值