ref
在前面v-model双向绑定的介绍中,已经使用过了ref,本节详细讲解ref的用法
ref的用法
ref用于接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value
property,指向该内部值。
用一个简单的例子来演示,通过点击change按钮来修改msg的值
可以看到控制台已经打印了多次change msg,但插值渲染的msg没有变动
这是因为这个msg不是响应式的,渲染到界面上就被固定了,无法被更新
这时就需要用到ref了,ref可以通过包装将其变成响应式
ref可以指定类型,直接使用泛型ref<>进行声明或可以引入Ref在声明变量时就声明类型
import { ref, Ref } from 'vue'
let msg1: Ref<string> = ref()
let msg2 = ref<string>()
注意被ref包装之后需要.value 来进行赋值
从控制台可以看到ref返回的是一个对象
isRef
isRef用于判断是否是ref对象
shallowRef
创建一个跟踪自身 .value
变化的 ref,但不会使其值也变成响应式的
可以看到点击change没有任何卵用,因为shallowRef中对象的属性不具有响应式的特性
简而言之,msg.value是响应式的,但msg.value.name不是
要修改对象的值,可以通过重新赋予一个对象实现
triggerRef
强制更新页面DOM
通过triggerRef,即使不是响应式的,也能通过刷新DOM来实现数据变动
customRef
用于自定义Ref
customRef 是个工厂函数要求我们返回一个对象 并且实现 get 和 set
可以通过这个函数来自定义一些操作,比如调用接口后再进行触发
reactive
reactive用来绑定复杂的数据类型 例如 对象{} 数组[] 他是不可以绑定普通的数据类型的,如需绑定普通数据类型,可以使用ref
reactive从源码上约束了我们的类型
基本用法
数组异步赋值问题
可以看到在异步中,通过赋值的方式将arr赋值给了msg,控制台也打印出了新数组,但是并没有渲染到页面上
这是因为这种直接赋值的方式会破坏msg这种响应式的模式,所以需要使用push来解构这个arr
如果想要直接赋值,可以使用以下方式
readonly
拷贝一份proxy对象将其设置为只读
shallowReactive
只能对浅层的数据 如果是深层的数据只会改变值 不会改变视图
可以看到,两个都修改了,并不像上面描述的一样不改变深层的视图
这是因为在dom挂载之前的阶段,修改数据是会在视图生效的,但如果挂载之后操作,是不会被改变的
可以看到,用事件触发的话,视图不会被修改,但是数据会被修改
toRef、toRefs和toRaw
toRef
如果原始对象是非响应式的就不会更新视图 数据是会变的
可以看到每次点击都会改变数据,但是视图没有更新
如果原始对象是响应式的是会更新视图并且改变数据
toRefs
toRefs可以帮我们批量创建ref对象 主要是方便我们解构使用
如果解构地去创建一个foo,bar并用reactive包裹,如下
可以发现控制台打印出的是两个数字,并不是响应式的对象
toRefs可以帮我们解构,并返回一个响应式的对象
可以看到返回了两个响应式对象
toRaw
toRaw 可以将响应式对象转化为普通对象
computed计算属性
计算属性就是当依赖的属性的值发生变化的时候,才会触发他的更改。
如果依赖的值不发生变化的时候,使用的是缓存中的属性值
可以看到,可以用computed来实现表单双向绑定的功能
也可以通过往computed传入一个包含get和set函数的对象来实现
案例:实现一个简易购物车
<template>
<div>
<table border style="width:500px">
<thead>
<tr>
<th>名称</th>
<th>数量</th>
<th>单价</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key="index" v-for="(item, index) in data">
<td align="center">{{item.name}}</td>
<td align="center"><button @click="addAndSub(item,false)">-</button>{{item.num}}<button @click="addAndSub(item,true)">+</button></td>
<td align="center">{{item.price * item.num}}</td>
<td align="center"><a href="#" @click="del(index)">删除</a></td>
</tr>
</tbody>
<tfoot>总价:{{$total}}</tfoot>
</table>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
type Shop = {
name:string,
num:number,
price:number
}
const data = reactive<Shop[]>([
{
name:"silver dick",
num:1,
price:100
},
{
name:"gold dick",
num:1,
price:200
},
{
name:"diamond dick",
num:1,
price:300
}
])
const addAndSub = (item:Shop, type:boolean):void => {
if(item.num > 1 && !type){
item.num --
total()
}
if (item.num < 99 && type){
item.num ++
total()
}
}
const del = (index:number):void => {
data.splice(index,1) //spilice(开始位置,删除个数)
total()
}
let $total = ref(0)
const total = () => {
$total.value = data.reduce(// reduce方法对数组中的每个元素执行一个由你提供的reduce函数
(prev,next) => {// prev表示上一次调用回调时的返回值,或者初始值 init; next表示当前正在处理的数组元素
return prev + (next.num * next.price)
},0)
}
total()
</script>
<style>
</style>
可以看到定义了一个total函数来对总价进行计算,但麻烦的是我们需要在很多地方有修改数据操作的地方调用这个total,这样就很麻烦
我们可以通过computed属性来解决这个问题
<template>
<div>
<table border style="width:500px">
<thead>
<tr>
<th>名称</th>
<th>数量</th>
<th>单价</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key="index" v-for="(item, index) in data">
<td align="center">{{item.name}}</td>
<td align="center"><button @click="addAndSub(item,false)">-</button>{{item.num}}<button @click="addAndSub(item,true)">+</button></td>
<td align="center">{{item.price * item.num}}</td>
<td align="center"><a href="#" @click="del(index)">删除</a></td>
</tr>
</tbody>
<tfoot>总价:{{$total}}</tfoot>
</table>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, computed } from 'vue'
type Shop = {
name:string,
num:number,
price:number
}
const data = reactive<Shop[]>([
{
name:"silver dick",
num:1,
price:100
},
{
name:"gold dick",
num:1,
price:200
},
{
name:"diamond dick",
num:1,
price:300
}
])
const addAndSub = (item:Shop, type:boolean):void => {
if(item.num > 1 && !type){
item.num --
}
if (item.num < 99 && type){
item.num ++
}
}
const del = (index:number):void => {
data.splice(index,1) //spilice(开始位置,删除个数)
}
let $total = ref(0)
$total = computed<number>(() => {
return data.reduce(// reduce方法对数组中的每个元素执行一个由你提供的reduce函数
(prev,next) => {// prev表示上一次调用回调时的返回值,或者初始值 init; next表示当前正在处理的数组元素
return prev + (next.num * next.price)
},0)
})
</script>
<style>
</style>