【Vue3实践】(二)Vue3开发基础:响应式变量、双向绑定、计算属性、监听器
本篇主要是讲了VUE开发中经常会使用到的响应式变量、双向绑定的使用方式,并举例说明了计算属性和监听器之间的使用差别及各自的作用,有了本篇的基础,已经可以使用VUE做一些简单的开发了。需要注意这么几个细节:- 响应式与双向绑定的联系与区别,`v-model`的作用- 计算属性的使用限制,生成的快照属性不能别其他功能修改- 监听器`watch`与`watchEffect`的区别,`watchEffec
文章目录
1.前言
由于在日常开发中会有一部分前端的开发任务,会涉及到Vue的项目的搭建、迭代、构建发布等操作,所以想系统的学习一下Vue相关的知识点,本专题会依照Vue的搭建、开发基础实践、进阶用法、打包部署的顺序进行记录。
主要内容与历史文章链接如下:
- Vue3搭建、路由配置与基本语法
- 响应式变量、双向绑定、计算属性、侦听器
- 工具与组件的封装、父子组件的使用、生命周期
- 环境变量、axios配置与打包部署
本篇会在上一篇的基础上进行拓展,讲解VUE开发中需要使用到的核心功能,即:响应式变量、双向绑定、属性计算、监听器,本篇完成之后就可以做一些简单的功能开发了。
本篇所有的代码都会以组合式API进行编写。
2.响应式变量(对象)
所谓的响应式的变量,可以在变量发生变化的同时,让依赖于这个变量进行渲染的视图也同步发生变化,简单的说,就是以数据驱动视图。
同时,在响应式变量中有一种特殊的使用方式v-model
,可以在视图发生变化时,也同步的修改变量的值,我们把这种特殊的方式称为:双向绑定。
2.1.响应式的使用
响应式变量就是在原有的变量外面包了一层代理对象,我们知道代理的作用就是用来做增强,数据驱动视图、双向绑定等操作都是由代理对象来执行的。
创建这个代理对象的方式有两种:reactive
与 ref
,它们的区别在于reactive
只能代理对象类型的变量。而对于基础数据类型string
、number
等,就需要使用 ref
来进行代理了,同时 ref
还包含了代理对象类型的变量功能。
也就是说,我们直接使用ref
就可以了。
假设我们现在有一个任务清单ul
,通过 v-for
循环某个数组变量arr
来进行渲染,此时有一个按钮,每点击一次就往arr
中添加一个元素,在不使用ref
的情况下,我们查看点击的效果。
<template>
<div class="hello">
<ul>
<li v-for="(value,key) in arr" :key="key">{{ value }}</li>
</ul>
<button @click="addArr">添加</button>
</div>
</template>
<script setup>
let arr = ["item1", "item2", "item3"];
const addArr = function() {
arr.push("item" + (arr.length + 1));
console.log(arr);
};
</script>
从图中可以看到,数组中已经添加到了7个元素,但视图中并没有增加。
引入ref
之后,再查看点击的效果。
<template>
<div class="hello">
<ul>
<li v-for="(value,key) in arr" :key="key">{{ value }}</li>
</ul>
<button @click="addArr">添加</button>
</div>
</template>
<script setup>
import { ref } from "vue";
let arr = ref(["item1", "item2", "item3"]);
const addArr = function() {
arr.value.push("item" + (arr.value.length + 1));
console.log(arr);
};
</script>
此时图中有两个变化:
- 视图中的清单随数组的变化而发送变化
- 通过
console.log(arr)
打印出的数据变成了一个代理对象
在代理对象中有一个value
值保存了原有的数组数据,我们想获取到数组数据,就需要使用.value
进行解包,这也是为什么在第二段代码中使用的是arr.value.push
。
同时,我们可以注意到的是,在v-for="(value,key) in arr"></li>
标签中,我们并没有使用 arr.value
来获取数组,这是因为在模板、条件、循环这样的语法中,ref
代理对象会自动解包。
2.2.双向绑定(v-model)
双向绑定一般是用在可交互的表单元素上,例如:input
, select
, radio
, checkbox
等等,下面以 input
为例来使用一下双向绑定。
创建一个输入框,绑定inputValue
这个响应式对象,在<h1>
标签中通过模板展示inputValue
的值,同时提供一个重置按钮,点击后通过函数将输入的变量值重置为空字符串。
其中第一步是验证视图对数据的影响,第二步是验证数据对视图的影响。
<template>
<div class="hello">
输入框:<input v-model="inputValue" placeholder="请输入" />
<button @click="reset">重置</button>
<br />
<h1>{{ inputValue }}</h1>
</div>
</template>
<script setup>
import { ref } from "vue";
let inputValue = ref("");
const reset = function() {
inputValue.value = "";
};
</script>
这就是VUE中双向绑定的使用,非常简单。v-model
除了可以绑定上述的这类表单元素外,还可以在组件之间传递参数,这个在后面的组件相关的文章中会讲到。
3.计算属性
3.1.金额单位转换案例
属性计算,就是绑定一个属性,当这个属性的值发生变化时,可以做一些自定义的计算获得一个新的属性。
举一个简单的例子,我们在做金额相关的需求时,在后台数据库中存入的金额单位是分,但在前端视图中显示的是元,所有这里会有单位转换的操作。
<template>
<div class="hello">
请输入金额:<input v-model.number="amount" placeholder="请输入" /> 分
<br />
<h1>{{ amountYuan }} 元</h1>
</div>
</template>
<script setup>
import { computed, ref } from "vue";
let amount = ref(0);
// 此处暂不考虑除法的精度丢失问题
const amountYuan = computed(() => amount.value / 100);
</script>
computed
中的 getter 方法(() => amount.value / 100
)中会自动找到需要计算的对象amount
,当amount
的值发生变化时,就会触发 getter 方法,并更新amountYuan
这个属性的值。
需要注意的是,computed
中的 getter 方法(()=> xxx
)只用于计算并获取一个新的属性,不要在这个方法中做一些诸如请求服务器、修改DOM元素、修改其他属性的操作。
同时,对于生成的amountYuan
这个属性,应该将其视为一个只读快照,除了 getter 方法以外,不要有其他地方修改它,以免造成一些不可预知的错误。
如果一定要做上面的操作的话,建议使用监听器而不是计算属性
4.监听器
监听器又可以叫做侦听器、监视器,都是一个意思,监听器可以对某个数据源建立监听关系,当这个数据源发生变化时,就会触发一个回调函数,在这个回调函数中可以写自己想触发的功能逻辑。
VUE中的定义监听器有两种 watch
和 watchEffect
,它们的区别在于:
watch
:需要显式的指定被监听的数据源。watchEffect
:可以根据回调函数中使用的变量来推断被监听的对象(类似于computed
)。
监听器与计算属性的区别在于,监听器不会生成一个新的快照对象,而是基于现有的变量来做操作。
4.1.watch的语法
watch
是一个函数,可以传入3个参数:监听数据源、回调函数、监听选项。其中,数据源与回调函数可以通过数组传入多个,下面是简化版的函数定义。
// 侦听单个来源
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle
下面通过几个例子还尝试一下各个参数的使用方式。
4.2.单行文本参数校验
现在只有这么一个需求,有一个input
框用于输入手机号,在输入时校验输入的手机号是否正确,要实现这个需求,我们只需要在文本框中绑定一个属性phoneNumber
,然后通过watch
监听就可以了,这是一个最简单的监听,监听数据源类型为ref
,实现方法如下:
<template>
<div class="hello">
手机号:<input v-model="phoneNumber" placeholder="请输入手机号" />
<br />
<p v-if="showTip">请输入正确的手机号</p>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
const phoneNumber = ref("");
const showTip = ref(false);
const reg = /^(?:0|86|\+86)?1[3-9]\d{9}$|^$/;
watch(phoneNumber, (newValue) => {
if (!reg.test(newValue)) {
showTip.value = true;
return;
}
showTip.value = false;
});
</script>
此处也可以对原有的数据源做一点处理,即使用 getter方法,例如在每个手机号前面都加上一个+86
:
watch(
() => {
return "+86" + phoneNumber.value;
},
// 此处的参数phone即上面的getter方法返回值
(phone) => {
const reg = /^(?:0|86|\+86)?1[3-9]\d{9}$|^$/;
if (!reg.test(phone)) {
showTip.value = true;
return;
}
showTip.value = false;
});
4.3.对象监听(select联动)
当监听的数据源类型为对象时,需要注意两个点:
- 只有对象整体被修改才会触发回调函数,如果想要对象中的某个元素发生变化也能触发回调,则需要配置深层监听,即:
options
参数配置为{deep:true}
- 回调函数上的参数
newValue
与oldValue
值是一样的,因为是同一个对象。
接下来用一个稍微复杂一点点的例子 select的联动来验证一下对象的监听,同时也是对上一篇的基本语法做一次复习。
有这么一个需求:
- 页面表单上有3个
select
,编号分别为1,2,3
,其待选项都为A,B,C
。 - 其中
select1
是显示的,另外两个是隐藏的。 - 当
select1
值为A
时显示select2
,不为A
时将selelct2
值置为空并隐藏。 - 当
selelct2
值为A
时显示select3
,不为A
时将selelct3
值置为空并隐藏。
<template>
<div class="hello">
<div v-for="n in 3" v-show="form['select' + n].visible">
选项 {{ n }}:
<select v-model="form['select' + n].optionValue" class="select">
<option v-for="(item,index) in selectSource" :key="index" :value="item">
{{ item }}
</option>
</select>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
const selectSource = ["A", "B", "C"];
// 表单数据
const form = ref({
select1: { optionValue: "", visible: true, visibleTargetKey: "select2" },
select2: { optionValue: "", visible: false, visibleTargetKey: "select3" },
select3: { optionValue: "", visible: false, visibleTargetKey: "" }
});
watch(
// 通过两次JSON转换获取一个新的对象,此时newValue与oldValue才会不一样
() => JSON.parse(JSON.stringify(form.value)),
(newValue, oldValue) => {
// 找出是哪个值发生了变化
let changedKey;
for (let key in newValue) {
if (newValue[key].optionValue !== oldValue[key].optionValue) {
changedKey = key;
break;
}
}
// 都没有变化则return
if (!changedKey) {
return;
}
// 显示与隐藏的处理
disposeVisible(changedKey, newValue[changedKey].optionValue, newValue[changedKey].visibleTargetKey);
},
{ deep: true }
);
const disposeVisible = (changedKey, changedOptionValue, targetKey) => {
if (!targetKey) {
return;
}
for (let key in form.value) {
let isVisible = changedOptionValue === "A";
form.value[targetKey].visible = isVisible;
if (!isVisible) {
form.value[targetKey].optionValue = "";
}
}
};
</script>
联动效果的操作步骤比较多,这里就不一一截图说明了,感兴趣的同学可以复制上面的代码体验。
需要注意的是,深层监听{deep:true}
会迭代检查对象中的元素变化,对象越大,嵌套越深,消耗的性能就越高,所以我们在日常开发的时候尽可能的减少深层监听的使用。
4.4.watchEffect的使用
我们已经提到了,watchEffect
不需要显示的指定参数,而是通过回调函数中使用到的变量来进行推断,将4.1中的watch
方法改写一下,将newValue
去掉,在函数中直接使用phoneNumber.value
,最终的效果是一致的:
watchEffect(() => {
if (!reg.test(phoneNumber.value)) {
showTip.value = true;
return;
}
showTip.value = false;
});
5.总结
本篇主要是讲了VUE开发中经常会使用到的响应式变量、双向绑定的使用方式,并举例说明了计算属性和监听器之间的使用差别及各自的作用,有了本篇的基础,已经可以使用VUE做一些简单的开发了。
需要注意这么几个细节:
- 响应式与双向绑定的联系与区别,
v-model
的作用 - 计算属性的使用限制,生成的快照属性不能别其他功能修改
- 监听器
watch
与watchEffect
的区别,watchEffect
与computed
的区别 - 监听器监听数据源为对象时需要注意两个问题:深层监听、
newValue
和oldValue
的值相等
如果想将VUE使用的更加优雅,可以关注下一篇博客,在下一篇中将会讲到组件的封装、组合式函数的封装、父子组件的交互等功能的使用。
更多推荐
所有评论(0)