# Vue 组合式 API - watch 和 watchEffect、自定义指令
TIP
本章节我们将重点学习在单文件组件(SFC)中使用组合式 API。具体内容如下:
<script setup>
基本用法- 侦听器 watch
- watchEffect 方法
- 自定义指令
# 一、<script setup>
TIP
在单文件组件中使用组合式 API,需要在<script>
标签上添加setup
属性。 此时写在<script setup>
标签中的代码会被编译成组件setup()
函数的内容。
<script setup>
是在单文件组件(SFC)中使用组合式 API 的编译时语法糖
# 1、<script setup>
的基本用法
<script setup>
中的this
指向与setup()
方法中的一样,都指向undefined
<script setup>
const count = 0;
console.log(this); // undefined
</script>
在 <script setup>
声明的顶层的绑定(包括变量,函数声明,以及 import 导入的内容)都能在模板中直接使用
<script setup>
// 导入求和的方法 sum(2,3) 输出5
import sum from "./sum.js";
// 变量
const count = 0;
// 方法
function sayHello() {
return "Hello Vue~~";
}
</script>
<template>
<div>count:{{ count }}</div>
<div>{{ sayHello() }}</div>
<div>2+3={{ sum(2, 3) }}</div>
</template>
最终渲染效果如下:
注意事项
- 在
<script setup>
中创建的变量不会作为属性添加到组件实例中。 - 如果在某些情况下确实需要从选项式 API 中访问到
<script setup>
中的变量,建议切换到setup()
函数写法
# 2、响应式数据
TIP
- 在
<script setup>
中声明的顶层变量并不是响应式的,需要借助响应式 API(如ref()
或reactive()
)方法来创建响应式变量 - 如果变量为
ref
对象,在模板中直接使用时会自动解包,这一特点与setup()
函数中是一样的
<script setup>
import { ref } from "vue";
// 导入求和的方法 sum(2,3) 输出5
import sum from "./sum.js";
// 变量
const count = ref(0);
// 函数
function add() {
count.value++; // ref对象需要打点vaule属性访问值
}
</script>
<template>
<button @click="add">count+1</button>
<!--count会自动解包-->
<div>{{ count }} --{{ sum(2, 3) }}</div>
</template>
以上代码最终渲染效果如下:
# 3、使用组件
TIP
在<script setup>
中直接通过import
导入需要的组件,然后该组件就可以直接在模板中通过标签名使用
<script setup>
import { defineAsyncComponent } from "vue";
// 导入组件
import List from "./components/List.vue";
// 定义一个异步组件
const AsyncComp = defineAsyncComponent(() =>
import("./components/AsyncComp.vue")
);
</script>
<template>
<!--使用组件-->
<List />
<AsyncComp />
</template>
对于全局组件可以通过组件名直接在模板中使用
# 4、动态组件
TIP
在<script setup>
中组件是通过变量引用而不是基于字符串组件名注册。
所在模板中使用<component>
动态组件时,:is
的值可以直接是组定义。
<script setup>
import { shallowRef } from "vue";
import A from "./components/A.vue";
import B from "./components/B.vue";
/*
我们在更新currentComp的值时,只会用一个新的组件替换现有组件,并不会去更新组件中的内容,
即currentComp只需要做浅层响应性,并不需要做深层响应性,所以选择用shallowRef而不是ref,
*/
const currentComp = shallowRef(A);
</script>
<template>
<button @click="currentComp = A">A组件</button>
<button @click="currentComp = B">B组件</button>
<br />
<br />
<component :is="currentComp"></component>
</template>
以上代码最终渲染效果如下:
# 5、计算属性 computed
TIP
- 在组合式 API 中使用计算属性,需要借助 Vue 为我们提供的
computed()
方法 computed()
方法接受一个 getter 函数,返回值为一个计算属性 ref,计算属性在模板中使用时会自动解包
<script setup>
import { computed, ref } from "vue";
const price = ref(10.0);
// newPrice为计算属性,是一个ref对象
const newPrice = computed(() => {
return price.value + "元";
});
console.log(newPrice.value); // 10元
</script>
<template>
<!--newPrice 会自动解包,所以不用newPrice.value-->
<div>{{ newPrice }}</div>
</template>
- 如果计算属性需要可写,则
computed()
方法接受一个带有 getter 和 setter 的对象作为参数。 - 当读取计算属性时会调用 getter 方法,为计算属性赋值时,会调用 setter 方法
<script setup>
import { computed, ref } from "vue";
// tip为计算属性
const tip = computed({
get() {
console.log("读取了内容");
},
set(value) {
console.log("更新了内容");
},
});
</script>
<template>
<input type="text" v-model="tip" />
</template>
# 二、侦听器 watch
TIP
本小节将学习如何在<script setup>
中使用 watch 侦听器,主要内容涉及如下:
watch()
方法的基本使用- 侦听数据源
- 副作用清理
- 配置项
- 实战应用:单位千米与米的相互转换
- 停止侦听器
# 1、watch 方法的基本使用
TIP
在组合式 API 中,我们需要使用 Vue 提供的watch()
函数来侦听一个或多个响应式数据源的变化,并在数据源变化时调用所给的回调函数。
语法
watch(source, callback, options);
参数详解
source: 侦听的 “数据源”,他可以是以下几种形式
- 一个响应式对象(
reactive()
创建的对象) - 一个 ref 对象(
ref()
方法创建的对象) - 一个 getter 函数,返回一个值(可以用来监听一个对象属性)
- 或以上类型值组成的一个数组
callback:侦听的数据源发生变化时要调用的回调函数。这个回调函数接受三个参数
- 第一个参数 newValue,表示数据源的新值
- 第二个参数 oldValue,表示数据源的旧值
- 第三个参数 onCleanup ,一个用于注册副作用清理的回函数,该回调会在下一次数据源变化调用 callback 函数前调用
options: 是一个可选的配置参数对象,支持以下选项
immediate
当值为 true 时,在侦听器创建时立即触发回调 ,默认情况下是 falsedeep
如果数据源是对象,强制深度遍历,以便在深层级变更时触发回调flush
:调整回调函数的刷新时机,flush:'post'
表示在 Vue 更新之后 DOM 之后调用侦听器回调,可以侦听器回调中访问到 Vue 更新之后的 DOM
# 2、侦听数据源
TIP
侦听的 “数据源”,他可以是以下几种形式
- 一个响应式对象 (
reactive()
创建的对象) - 一个 getter 函数,返回一个值 (可以用来监听一个对象属性)
- 一个 ref 对象(
ref()
方法创建的对象) - 或以上类型值组成的一个数组
侦听响应式对象
数据源是一个响应式对象,默认是深层侦听器
<script setup>
import { reactive, watch } from "vue";
const state = reactive({
count: {
a: {
c: 0,
},
},
});
setTimeout(() => {
// 只要是该对象的属性(或嵌套属性)的值变化了,侦听器都能侦听到
state.count.a.c = 100;
}, 3000);
// 侦听state响应式对象 默认是深层侦听
watch(state, (newValue, oldValue) => {
console.log("count的值更新为", newValue);
});
</script>
<template>
<div>{{ state.count.a.c }}</div>
</template>
侦听 ref 对象
数据源是一个 ref 对象,默认是浅层侦听,添加
deep:true
配置可以开启深侦听
<script setup>
import { reactive, watch, ref } from "vue";
const state = ref({
count: {
a: {
c: 0,
},
},
});
// 如果watch中不添加deep:true配置,则以下修改没有办法被侦听到
setTimeout(() => {
state.value.count.a.c = 100;
}, 3000);
// 以下修改可以被正常侦听到
setTimeout(() => {
state.value = 100;
}, 4000);
watch(
state,
(newValue, oldValue) => {
console.log("c的值更新为", newValue);
},
{ deep: true }
);
</script>
侦听响应式对象的属性
你不能直接侦听响应式对象的属性值,需要通过 getter 函数返回该响应式对象的属性才可以。
import { reactive, watch } from "vue";
const state = reactive({ count: { a: 1 } });
// 下面为错误写法,无法侦听到state的count属性
watch(state.count, (newValue, oldValue) => {
// .....
});
// 以下为正确写法 侦听state对象的count属性
watch(
() => state.count,
(newValue, oldValue) => {
// .....
}
);
以上方式默认是浅侦听,侦听的回调函数只在对象的该属性值发生变化时才会触发。添加
deep:true
配置可以开启深侦听
<script setup>
import { reactive, watch } from "vue";
const state = reactive({ count: { a: 1 } });
// 添加 deep: true 配置时,以下修改可以被侦听器侦听到,否则侦听不到
setTimeout(() => {
state.count.a = 100;
}, 3000);
// 侦听state对象的count属性
watch(
() => state.count,
(newValue, oldValue) => {
console.log("count的值更新为", newValue);
},
{
deep: true, // 开启深层侦听
}
);
</script>
<template>
<div>{{ state.count.a }}</div>
</template>
以上代码,如果不添加
deep:true
配置项,当通过state.count.a=100
来修改 a 的值时,侦听器是没有办法侦听到的。
侦听一个数组
- 数据源是由 getter 函数、ref 对象、reactive 对象 类型值组成的一个数组,会侦听该数组中的每个成员的变化。
- 如果成员是
getter
函数和ref
对象,默认是浅侦听 - 如果是响应式对象,默认是深层侦听
侦听器回调函数的参数 newValue 和 oldValue 值为一个数组,数组中的每个成员对应数据源中每个成员的当前值和变化前值。
<script setup>
import { reactive, watch, ref } from "vue";
const msg = ref("Hello Vue");
const state = reactive({ count: { a: 1 } });
let num = ref({ a: 1 });
const obj = reactive({ a: { b: 2 } });
setTimeout(() => {
num.value.a = 100; // 侦听器侦听不到
num.value = { a: 100 }; // 侦听器能侦听到
}, 2000);
setTimeout(() => {
msg.value = "Hello watch"; // 侦听器能侦听到
}, 3000);
setTimeout(() => {
state.count.a = 100; // 侦听器能侦听到
}, 4000);
setTimeout(() => {
obj.a = { b: 100 }; // 侦听器能侦听到
obj.a.b = 200; // 侦听器侦听不到
}, 4000);
/**
* num.value 是浅侦听 只有num.value的值变化才能侦听到
* msg 是浅侦听,只有msg.value的值变化才能侦听到
* state 深侦听,state的属性或嵌套属性都能侦听到
* obj.a是浅侦听,只有obj.a的值变化才能侦听到,其内部属性变化不能侦听
*/
watch([() => num.value, msg, state, () => obj.a], (newValue, oldValue) => {
console.log("数组的值更新为", newValue);
});
</script>
<template></template>
以上代码最终执行结果如下,侦听器回调函数被调用了 4 次
总结:
侦听的数据源是
- 一个 getter 函数,用来侦听对象的某个属性,默认是 浅侦听
- 一个 ref 对象,默认是 浅侦听
- 一个响应式对象,默认是 深层侦听
- 或以上类型值组成的一个数组,数组中成员的类型与上面侦听的方式一样。
可以通过添加
deep:true
配置开启深侦听
# 3、副作用清理
TIP
在某些场景下,当监听的数据源发生变化时,就会向后台发起请求,如果数据在请求还没有回来时,数据源变化了 5 次,那就会向后台发送 5 次请求,完全是没有必要的。
所以我们需要在上一次请求没有回来前,如果数据又发生了变化,则把上一次的请求取消,再重新发送一次请求。
<script setup>
import { watch, ref } from "vue";
const count = ref(0);
// 侦听器,侦听属性count
watch(count, (newValue, oldValue, onCleanup) => {
console.log(`数据变化了从${oldValue} ==> ${newValue}`);
// 模拟发送请求
let timer = setTimeout(() => {
console.log("发起请求......");
}, 3000);
// 取消请求的方法
function cancle() {
console.log("取消请求");
clearTimeout(timer);
}
// 清理副作用的回调函数
onCleanup(cancle);
});
</script>
<template>
<button @click="count++">更新</button>
</template>
# 4、options 配置项
TIP
关于options
配置项中每一项配置的含义,与前面讲的项选项式 API 中watch
是一样的。
immediate
当值为 true 时,在侦听器创建时立即触发回调 ,默认情况下是 falsedeep
如果数据源是对象,强制深度遍历,以便在深层级变更时触发回调flush
:调整回调函数的刷新时机,flush:'post'
表示在 Vue 更新之后 DOM 之后调用侦听器回调,可以侦听器回调中访问到 Vue 更新之后的 DOM
具体用法可以参考选项式 API:侦听器配置选项 (opens new window)
# 5、实战应用:单位千米与米的相互转换
实现单位千米与米的转换
<script setup>
import { ref, watch } from "vue";
const kilometers = ref(0); // 千米
const meters = ref(0); // 米
watch(kilometers, (newValue, oldValue) => {
console.log("kilometers变化了");
meters.value = newValue * 1000;
});
watch(meters, (newValue, oldValue) => {
console.log("meters变化了");
kilometers.value = newValue / 1000;
});
</script>
<template>
<div>千米: <input type="text" v-model="kilometers" /></div>
<div>米:<input type="text" v-model="meters" /></div>
</template>
# 6、停止侦听器
TIP
在 setup()
或 <script setup>
中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,你无需关心怎么停止一个侦听器。
如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏
- 同步语句中创建的侦听器,在组件实例被销毁时自动停止
watch(count, (newValue, oldValue) => {
// ...
});
- 异步回调创建一个侦听器,在组件实例即将销毁前,手动停止
let unwatch = null;
setTimeout(() => {
unwatch = watch(count, (newValue, oldValue) => {
// ....
});
});
// 在组件实例即将销毁前,停止候听器
onBeforeUnmount(() => {
console.log("组件即将销毁");
// 停止侦听器
unwatch();
});
代码演示
App.vue
<script setup>
import List from "./components/List.vue";
import { ref } from "vue";
const isShow = ref(true);
</script>
<template>
<button @click="isShow = false">卸载List组件</button>
<List v-if="isShow"></List>
</template>
List.vue
<script setup>
import { watch, onBeforeUnmount, onUnmounted, ref } from "vue";
const count = ref(0);
let unwatch = null;
/* 同步侦听器,在组件销毁时,自动销毁
watch(count, (newValue, oldValue) => {
console.log(`count的值从${newValue} ===>${oldValue}`)
})
*/
setTimeout(() => {
unwatch = watch(count, (newValue, oldValue) => {
console.log(`count的值从${newValue} ===>${oldValue}`);
});
});
// 生命周期函数
onBeforeUnmount(() => {
console.log("即将销毁");
// 停止侦听器
unwatch();
count.value++;
});
// 生命周期函数
onUnmounted(() => {
console.log("实例销毁");
count.value++;
});
</script>
<template>
<button @click="count++">更新</button>
<div>{{ count }}</div>
</template>
以上代码,如果不在生命周期函数
onBeforeUnmount
中调用unwatch()
停止侦听器,组件被卸载后,侦听器依赖没有停止,最终渲染效果如下:
如果在生命周期函数
onBeforeUnmount
中调用unwatch()
停止侦听器,则最终渲染效果如下:
# 三、watchEffect() 方法
TIP
深入浅出 watchEffect()
方法的基本使用,侦听器调试,副作用清除,停止侦听器,watch()
与 watchEffect()
对比,axios 取消请求,实战应用 等。
# 1、watchEffect() 的基本使用
TIP
watchEffect()
方法一旦被调用,会立即运行其回调函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
function watchEffect(
effect: (onCleanup: OnCleanup) => void, // 运行副作用函数
options?: WatchEffectOptions // 可选的配置选项
): StopHandle;
interface WatchEffectOptions {
flush?: "pre" | "post" | "sync"; // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void;
onTrigger?: (event: DebuggerEvent) => void;
}
参数详解
- 第一个参数是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求
- 第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
flush:'post'
默认情况下,侦听器将在组件渲染之前执行。设置flush: 'post'
将会使侦听器延迟到组件渲染之后再执行onTrack
将在响应属性或引用作为依赖项被跟踪时被调用。onTrigger
将在侦听器回调被依赖项的变更触发时被调用。
代码示例
<script setup>
import { ref, watchEffect } from "vue";
const a = ref(2);
const b = ref(3);
watchEffect(() => {
console.log(`a+b=${a.value}+${b.value}`);
});
</script>
<template>
<div>a:<input v-model.number="a" /></div>
<div>b:<input v-model.number="b" /></div>
</template>
以上代码,最终渲染效果如下:
执行结果分析
watchEffect()
方法在数据第一次初始化时,就会调用回调函数执行副作用。- 不管 a 的值发生变化,还是 b 的值发生变化,侦听器都能侦听到。他并不需要我们指定侦听的数据源,会自动感知代码的依赖。
注意:
watch()
方法是惰执行的,只在数据更新时才会执行,即第一次初始化时并不会侦听,除非添加了immediate: true
配置。
# 2、侦听器调试
TIP
如果我们想要对侦听器做调试,可以向 watchEffect()
传入第二个参数,是一个包含了 onTrack
和 onTrigger
两个回调函数的对象。
onTrack
将在响应属性或引用作为依赖项被跟踪时被调用。onTrigger
将在侦听器回调被依赖项的变更触发时被调用。
侦听器的
onTrack
和onTrigger
选项仅会在开发模式下工作。
<script setup>
import { ref, watchEffect } from "vue";
const a = ref(2);
const b = ref(3);
watchEffect(
() => {
console.log(`${a.value}+${b.value}=${a.value + b.value}`);
},
{
onTrack(e) {
// 当读取a和b的值时触发,即: a.value b.value
console.log("track");
debugger;
},
onTrigger(e) {
// 当a和b的值发生变化时触发 即 a.value=20 b.value=20
console.log("trigger");
debugger;
},
}
);
</script>
<template>
<div>a: <input type="text" v-model.number="a" /></div>
<div>b: <input type="text" v-model.number="b" /></div>
</template>
更多响应性调试,查阅 Vue 官方文档:响应性调试 (opens new window)
# 3、副作用清除
TIP
watchEffect
回调函数的第一个参数onCleanup
为副作用清除函数,onCleanup
函数接收一个函数作为参数,该参数会在下一次回调函数调用前执行。
watchEffect((onCleanup) => {
// 取消定时器或取消请求等操作
function cancle() {}
// cancle 函数会在下一次回调函数调用前执行
onCleanup(cancle);
});
# 4、停止侦听器
TIP
停止侦听器的方式和watch
方法一样。
具体如下:
const stop = watchEffect(() => {});
// 停止侦听器
stop();
# 5、watch() 与 watchEffect() 对比
TIP
watch()
侦听器特点
- 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数,设置
immediate: true
时可以变为非惰性,页面首次加载就会执行 - 需要指定侦听的数据源,并且只在该数据源发生变化时,才会执行
- 可以获取数据变化前后的值
watchEffect()
侦听器特点
watchEffect()
立即执行,没有惰性,页面首次加载就会执行watchEffect()
不需要指定侦听的数据源,会自动收集依赖数据,依赖数据更新时重新执行自身- 无法获取到变化前的原始值,只能得到变化后的值
# 6、axios 取消请求
TIP
副作用清理函数常用来取消上一次请求,所以接下来,我们先来学习下如何在 axios 中取消上一次请求。
在 axios 中取消上一次请求相关代码,具体参考:axios 取消请求 (opens new window) 与 AbortController - Web API 接口参考 | MDN (mozilla.org) (opens new window)
从 v0.22.0
开始,Axios 支持以 fetch API 方式 —— AbortController
(opens new window) 取消请求,该请求一旦被取消将不能被再发起。
// controller表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。
const controller = new AbortController();
axios
.get("/foo/bar", {
// controller的只读属性signal 返回一个 AbortSignal 实例对象,该对象可以根据需要处理 DOM 请求通信,既可以建立通信,也可以终止通信。
signal: controller.signal,
})
.then(function (response) {
//...
})
.catch(() => {
console.log("请求失败");
});
// 中止一个尚未完成的 Web(网络)请求
controller.abort();
代码示例
当点击发送请求按扭后,会开始发起两个请求,如果请求还没有回来时,点击了取消请求按扭,则会同时把两个请求都取消。
<script setup>
import axios from "axios";
import { ref } from "vue";
// 请求地址
const url =
"https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833/icode/menu/";
// 请求ID
const paramId = ref(1001);
let controller = null;
// 发送请求
function onSend() {
controller = new AbortController();
axios
.get(`${url}+${paramId.value}`, {
signal: controller.signal,
})
.then((res) => {
console.log("请求成功内容:");
// 处理请求回来的内容
console.log(res.data);
})
.catch(() => {
console.log("请求失败");
});
axios
.get(`${url}+${paramId.value}`, {
signal: controller.signal,
})
.then((res) => {
console.log("请求成功内容:");
// 处理请求回来的内容
console.log(res.data);
})
.catch(() => {
console.log("请求失败");
});
}
function onAbort() {
// 取消请求 (多个请求)
controller.abort();
}
</script>
<template>
<button @click="onSend">发起请求</button>
<button @click="onAbort">取消请求</button>
</template>
# 7、实战应用
TIP
以下示例展示了 paramId
变量的数据发生变化时,会发送 Ajax 请求。
但是在请求数据还没有回来前 paramId
变值的值再次发生变化时,就会把上一次请求取消,然后重新发送一次请求。
<script setup>
import axios from "axios";
import { reactive, ref, watch, watchEffect } from "vue";
// 请求地址
const url =
"https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833/icode/menu/";
// 请求参数
const paramId = ref(1001);
// 接受请求回来的数据 (数组)
const list = ref(null);
let controller = null;
// 一上来,就发一个请求获取数据
watchEffect((onCleanup) => {
// AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。
controller = new AbortController();
// 可以发请求
axios
.get(`${url}${paramId.value}`, {
// 配置取消请求
signal: controller.signal,
})
.then((res) => {
list.value = res.data.data;
console.log(list.value);
})
.catch((error) => {
console.log("请求失败");
});
// 取消请求
function cancel() {
console.log("用户取消请求");
controller.abort();
}
// 在下一次执行前,会调用传入的cancle取消上一次请求
onCleanup(cancel);
});
</script>
<template>
<!-- 菜单 -->
<button @click="paramId = 1001">菜单1</button> |
<button @click="paramId = 1002">菜单2</button> |
<button @click="paramId = 1003">菜单3</button>
<ul>
<li v-for="item in list" :key="item.id">{{ item.productName }}</li>
</ul>
</template>
以上代码最终渲染效果如下:
注:
初始渲染时,就会发请求加菜单 1 中的内容,然后渲染成列表显示在页面。
当我们点击菜单 2 时,在数据没有回来前再点击菜单 3,则菜单 2 的请求被取消,然后发请求获取菜单 3 的内容最终返回的新内容替换原来的内容,渲染成列表显示在页面。
# 四、自定义指令
TIP
- 在全局注册的自定义指令,在任意组件的模板中可以直接使用。
- 在
<script setup>
中不需要显式注册局部指令,如果一个变量的命名遵循vNameOfDirective
这样的规范,Vue 会自动把他当作一条指令来执行。
指令的值和用法与选项式 API 中一模一样,具体可参考选项式 API
<script setup>
// 定义一条指令 v-my-directive ,以下是对象的完整写法
const vMyDirective = {
mounted(el, binding) {},
updated(el, binding) {},
};
// 定义一条指令v-focus,以下是函数简写形式
const vFocus = (el, binding) => {};
</script>
<template>
<!--使用指令-->
<div v-my-directive></div>
<input type="text" v-focus />
</template>
如果指令是从别处导入的,可以通过重命名来使期符合命名规范
import { focus as vFocus } from "./focus.js";
// focus.js内容如下
const focus = (el, binding) => {
/* ...... */
};
export { focus };
代码示例
自定义
v-focus
指令,单行文本框在添加该指令后,会自获取焦点同时背景色变蓝色。
<script setup>
import { ref } from "vue";
const msg = ref("Hello");
const vFocus = {
mounted(el, binding) {
el.focus();
el.style.border = "none";
el.style.backgroundColor = "skyblue";
},
};
// 以下是函数简写形式,当 mounted 与 updated 效果一样时,可采用函数简写形式
/*
const vFocus = (el, binding) => {
el.focus();
el.style.border = "none";
el.style.backgroundColor = "skyblue"
}
*/
</script>
<template>
<input type="text" v-focus v-model="msg" />
</template>
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X