ref

在前面v-model双向绑定的介绍中,已经使用过了ref,本节详细讲解ref的用法

ref的用法

ref用于接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。

用一个简单的例子来演示,通过点击change按钮来修改msg的值

image-20220820210558423

可以看到控制台已经打印了多次change msg,但插值渲染的msg没有变动

这是因为这个msg不是响应式的,渲染到界面上就被固定了,无法被更新

这时就需要用到ref了,ref可以通过包装将其变成响应式

ref可以指定类型,直接使用泛型ref<>进行声明或可以引入Ref在声明变量时就声明类型

import { ref, Ref } from 'vue'
let msg1: Ref<string> = ref()
let msg2 = ref<string>()

image-20220820211026041

注意被ref包装之后需要.value 来进行赋值

从控制台可以看到ref返回的是一个对象

isRef

isRef用于判断是否是ref对象

image-20220820211955824

shallowRef

创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的

image-20220820212241571

可以看到点击change没有任何卵用,因为shallowRef中对象的属性不具有响应式的特性

简而言之,msg.value是响应式的,但msg.value.name不是

要修改对象的值,可以通过重新赋予一个对象实现

image-20220820212633784

triggerRef

强制更新页面DOM

image-20220820212840655

通过triggerRef,即使不是响应式的,也能通过刷新DOM来实现数据变动

customRef

用于自定义Ref

customRef 是个工厂函数要求我们返回一个对象 并且实现 get 和 set

image-20220820213953910

可以通过这个函数来自定义一些操作,比如调用接口后再进行触发

reactive

reactive用来绑定复杂的数据类型 例如 对象{} 数组[] 他是不可以绑定普通的数据类型的,如需绑定普通数据类型,可以使用ref

reactive从源码上约束了我们的类型

image-20220820220026501

image-20220820220136938

基本用法

image-20220820221948941

数组异步赋值问题

image-20220820222622306

可以看到在异步中,通过赋值的方式将arr赋值给了msg,控制台也打印出了新数组,但是并没有渲染到页面上

这是因为这种直接赋值的方式会破坏msg这种响应式的模式,所以需要使用push来解构这个arr

image-20220820222924134

如果想要直接赋值,可以使用以下方式

image-20220820223154283

readonly

拷贝一份proxy对象将其设置为只读

image-20220820223502733

shallowReactive

只能对浅层的数据 如果是深层的数据只会改变值 不会改变视图

image-20220820224026817

可以看到,两个都修改了,并不像上面描述的一样不改变深层的视图

这是因为在dom挂载之前的阶段,修改数据是会在视图生效的,但如果挂载之后操作,是不会被改变的

image-20220820224557337

可以看到,用事件触发的话,视图不会被修改,但是数据会被修改

toRef、toRefs和toRaw

toRef

如果原始对象是非响应式的就不会更新视图 数据是会变的

image-20220821003856260

可以看到每次点击都会改变数据,但是视图没有更新

image-20220821004049504

如果原始对象是响应式的是会更新视图并且改变数据

toRefs

toRefs可以帮我们批量创建ref对象 主要是方便我们解构使用

如果解构地去创建一个foo,bar并用reactive包裹,如下

image-20220821004451558

可以发现控制台打印出的是两个数字,并不是响应式的对象

toRefs可以帮我们解构,并返回一个响应式的对象

image-20220821005101532

可以看到返回了两个响应式对象

toRaw

toRaw 可以将响应式对象转化为普通对象

image-20220821010814946

computed计算属性

计算属性就是当依赖的属性的值发生变化的时候,才会触发他的更改。

如果依赖的值不发生变化的时候,使用的是缓存中的属性值

image-20220821103405491

可以看到,可以用computed来实现表单双向绑定的功能

也可以通过往computed传入一个包含get和set函数的对象来实现

image-20220821104644398

案例:实现一个简易购物车

<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>

image-20220821113055249

可以看到定义了一个total函数来对总价进行计算,但麻烦的是我们需要在很多地方有修改数据操作的地方调用这个total,这样就很麻烦

我们可以通过computed属性来解决这个问题

image-20220821113610264

<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>
最后修改:2022 年 08 月 31 日
如果觉得我的文章对你有用,能不能v我50参加疯狂星期四