Vue3-数据的计算

161 阅读4分钟

笔记整理-主要参考:vue3.chengpeiquan.com/

Vue3与Vue2的比较

vue2

  • computed和data在同级配置,不可同名重复定义

vue3

  • computed变量和ref变量用法一样,通过.value获取值
  • computed的value是只读的
// 在 Vue 2 的写法:
export default {
  data() {
    return {
      firstName: 'Bill',
      lastName: 'Gates',
    }
  },
  // 注意这里定义的变量,都要通过函数的形式来返回它的值
  computed: {
    // 普通函数可以直接通过熟悉的 this 来拿到 data 里的数据
    fullName() {
      return `${this.firstName} ${this.lastName}`
    },
    // 箭头函数则需要通过参数来拿到实例上的数据
    fullName2: (vm) => `${vm.firstName} ${vm.lastName}`,
  },
}

类型定义

  • 在defineComponent中会自动推导出Vue API类型,不需显式定义其变量类型
  • 显式定义方式:ComputedRef<数据类型>,这个包括原始数据类型、接口、类
// 在 Vue 3 的写法:
import { defineComponent, ref, computed } from 'vue'
//显式定义
import type { ComputedRef } from 'vue'

export default defineComponent({
  setup() {
    // 定义基本的数据
    const firstName = ref<string>('Bill')
    const lastName = ref<string>('Gates')

    // 定义需要计算拼接结果的数据
    const fullName = computed(() => `${firstName.value} ${lastName.value}`)
    // 注意这里添加了类型定义
    const fullName: ComputedRef<string> = computed(
      () => `${firstName.value} ${lastName.value}`
    )

    // 2s 后改变某个数据的值
    setTimeout(() => {
      firstName.value = 'Petter'
    }, 2000)

    // template 那边在 2s 后也会显示为 Petter Gates
    return {
      fullName,
    }
  },
})

优势对比&注意点

  • 性能优势

    • 原始数据未变化,多次访问computed,直接访问之前计算好的结果,不会执行函数
    • function,调用多少次就会执行多少次
    • computed,变化多少次才会执行多少次(不变化只是调用之前结果,不会执行函数)
    • ref变量、computed变量在template中直接写变量名,script中都需要.value
  • 注意点

    • computed只会更新响应式数据的计算

      • 若获取当前时间(不是响应式数据,则需要用普通函数获取返回值)
    • computed定义是数据,都是只读的

setter使用

  • 特点:

    • computed定义的变量默认是只读形式(只有一个getter),可添加set(setter)
    • 通过.value获取或更新数据

属性更新数据

  • 基本格式
// 注意这里computed接收的入参已经不再是函数
const foo = computed({
  // 这里需要明确的返回一个值
  get() {
    // ...
  },
  // 这里接收一个参数,代表修改 foo 时,赋值下来的新值
  set(newValue) {
    // ...
  },
})
  • 例子
// 还是这2个数据源
const firstName = ref<string>('Bill')
const lastName = ref<string>('Gates')

// 这里配合setter的需要,改成了另外一种写法
const fullName = computed({
  // getter还是返回一个拼接起来的全名
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  // setter这里改成只更新firstName,注意参数也定义TS类型
  set(newFirstName: string) {
    firstName.value = newFirstName
  },
})
console.log(fullName.value) // 输出 Bill Gates

// 2s后更新一下数据
setTimeout(() => {
  // 对fullName的赋值,其实更新的是firstName
  fullName.value = 'Petter'

  // 此时firstName已经得到了更新
  console.log(firstName.value) // 会输出 Petter

  // 当然,由于firstName变化了,所以fullName的getter也会得到更新
  console.log(fullName.value) // 会输出 Petter Gates
}, 2000)

应用场景

数据的拼接和计算
  • 购物车:有商品列表,同时要显示购物车内的商品总金额
复用组件的动态数据
  • 首页的文件列表&详情页的文件列表&列表中的文件列表(新闻网站)
  • 不需要重写UI,只需要改变数据

    •   通过路由名称动态获取要抵用哪个列表接口或在父组件通过props传递接口URL
const route = useRoute()

// 定义一个根据路由名称来获取接口URL的计算数据
const apiUrl = computed(() => {
  switch (route.name) {
    // 首页
    case 'home':
      return '/api/list1'
    // 列表页
    case 'list':
      return '/api/list2'
    // 作者页
    case 'author':
      return '/api/list3'
    // 默认是随机列表
    default:
      return '/api/random'
  }
})

// 请求列表
const getArticleList = async (): Promise<void> => {
  // ...
  articleList.value = await axios({
    method: 'get',
    url: apiUrl.value,
    // ...
  })
  // ...
}
获取多级对象的值
  • 场景特点:多级对象,又可能存在某级不存在数据
  • 方案:通过计算属性computed&try/catch,这样无需再template中做多层判断
// 例子比较极端,但在 Vuex 这种大型数据树上,也不是完全不可能存在
//完全不需要关心中间一级又一级的字段是否存在,只需要区分是不是默认值
const foo = computed(() => {
  // 正常情况下返回需要的数据
  try {
    return store.state.foo3.foo2.foo1.foo
  } catch (e) {
    // 处理失败则返回一个默认值
    return ''
  }
})
不同类型的数据转换
  • 场景特点:前端显示格式与最后保存的格式不一致。

    • 在输入框(UI界面),按照一定格式(数组),保存时需要用","隔开
  • 解决方案:通过computed是setter可代替input的change时间或watch监听
<template>
  <input
    type="text"
    v-model="tagsStr"
    placeholder="请输入标签,多个标签用英文逗号隔开"
  />
</template>

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

export default defineComponent({
  setup() {
    // 这个是最终要用到的数组
    const tags = ref<string[]>([])

    // 因为input必须绑定一个字符串
    const tagsStr = computed({
      // 所以通过getter来转成字符串
      get() {
        return tags.value.join(',')
      },
      // 然后在用户输入的时候,切割字符串转换回数组
      set(newValue: string) {
        tags.value = newValue.split(',')
      },
    })

    return {
      tagsStr,
    }
  },
})
</script>