响应式API以及计算属性的用法和小案例

14 篇文章 5 订阅

toRef的用法

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

当你要将 prop 的 ref 传递给复合函数时,toRef 很有用。

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  }
}

即使源 property 不存在,toRef 也会返回一个可用的 ref。这使得它在使用可选 prop 时特别有用,可选 prop 并不会被 toRefs 处理。
以下是示例代码:

<template>
    <!-- toRef:可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。-->
    <div>{{ obj.foo }} -- {{ obj.bar }}</div>
    <div>{{ foos }}</div>
    <button @click="change">修改</button>
</template>

<script setup lang="ts">
// 解构
import { reactive, toRef } from 'vue'

const obj = reactive({
    foo: 1,
    bar: 2,
})

// 点击事件
const change = () => {
    obj.foo += 10
}

// toRef

const foos = toRef(obj, 'foo')
</script>

<style>

</style>

toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 和原始 property 已经“链接”起来了
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

当从组合式函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行解构/展开。

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // 操作 state 的逻辑

  // 返回时转换为ref
  return toRefs(state)
}

export default {
  setup() {
    // 可以在不失去响应性的情况下解构
    const { foo, bar } = useFeatureX()

    return {
      foo,
      bar
    }
  }
}

toRefs 只会为源对象中包含的 property 生成 ref。如果要为特定的 property 创建 ref,则应当使用 toRef。
以下是示例代码:

<template>
    <!-- toRefs:它是用于把源响应式对象变为原始对象,但是这个原始对象中的属性还指向源响应式,简单的说就是这个普通对象中的属性具有响应式的功能。-->
    <div>{{ obj.foo }} -- {{ obj.bar }}</div>
    <div>{{ state }}</div>
    <button @click="change">修改</button>
</template>

<script setup lang="ts">
// 解构
import {isReactive, isRef, reactive, toRefs} from 'vue'

// 本身是一个响应式对象
const obj = reactive({
    foo: 1,
    bar: 2,
})
console.log(isReactive(obj))

// 点击事件
const change = () => {
    obj.foo += 10
    obj.bar += 5
}

// toRefs
const state = toRefs(obj)
// 判断这个对象是否为响应式对象
console.log(isReactive(state)) // 判断对象是否为 reactive 的响应式对象
console.log(isRef(state)) // 判断对象是否为 ref 的响应式对象
</script>

<style>

</style>

customRef

示例代码:

<template>
    <!-- customRef:自定义响应式 API,它要接收一个工厂函数,这个函数需要有 track 和 trigger 参数,同时还需要提供 setget 方法 -->
    <div>{{ message }}</div>
    <button @click="change">修改</button>
</template>

<script setup lang="ts">
// 解构
import {customRef} from 'vue'

// 定义的响应式对象,使用自定义的 API
const message = MyRef<string>('hello')

// 点击事件
const change = () => message.value = 'world'

// 1. 定义一个函数
function MyRef<T>(value: T) {
    // 2. 创建 customRef 对象,并返回。这个对象需要有两个参数:track 用于收集信息;trigger 用于触发更新操作
    // track 参数是一个函数,它用于 get 方法中。
    // trigger 参数也是一个函数,它用于 set 方法中。
    return customRef((track, trigger) => {
        // 3. 返回一个带有 set 和 get 方法的对象
        return {
            // 用于设置值
            set(newValue: T) {
                console.log('set()...')
                value = newValue
                trigger() // 触发更新
            },
            // 用于获取值
            get() {
                console.log('get()...')
                track() // 收集依赖
                return value
            }
        }
    })
}
</script>

<style>

</style>

计算属性Computed

接受回调函数

<template>
    <!--
    computed: 也叫计算属性,它是用于对某个变量进行计算
    使用语法为:
    const 要计算的变量(一般是template 模板中插值表达式中的一个变量名) = computed(()=>{
        计算逻辑
        return 计算后的结果
    })
    -->

    <div>count: {{ count1 }}</div>

    <button @click="add">+1</button>

</template>

<script setup lang="ts">
import {computed, ref} from "vue";

const count = ref<number>(1)

// 使用事件(方法)的方式
const add = () => {
    console.log('add........')
    return count.value++
}

// 计算属性
let count1 = computed(() => {
    console.log('computed.......')
    return count.value++
})

//count1.value = 50
</script>

<style>

</style>

接收对象实现可读写

<template>
    <!--
    computed: 还可以实现可写的计算属性,它需要接收一个对象,并且在这个对象中包含有 setget 方法。
    set() 方法需要接收一个参数,用于修改值,
    get() 方法是用于获取计算后的结果的值
    -->

    <div>count: {{ price }}  ---   {{ mul }}</div>

    <button @click="mul = 200">修改</button>

</template>

<script setup lang="ts">
import {computed, ref} from "vue";

const price = ref<number>(100)
// 可读写的计算属性
let mul = computed({
    set: (v: number) => {
        console.log('set.....')
        price.value = v
    },
    get: () => {
        console.log('get...')
        return price.value
    }
})
mul.value = 500 // 可以这样调用,因为这个计算属性是可写的
</script>

<style>

</style>

计算属性和方法的区别

<template>
    <!--
    方法也可以实例响应式对象的修改,而计算属性也可以实现响应式对象的修改,那么方法和计算属性有什么异同呢?
    相同点:都可以对某个对象进行计算
    不同点:
        1. 使用计算属性时不能带括号;而使用方法时需要带括号。
        2. 计算属性具有缓存功能,如果被被计算的对象没有发生变化,那么它只会被调用一次;
            而方法不具有缓存功能,每调用一次就会执行一次。
    -->
    <p>原字符串: {{ message }}</p>

    <p>计算属性: {{ reversedMessageByComputed }}</p>
    <p>计算属性: {{ reversedMessageByComputed }}</p>
    <p>计算属性: {{ reversedMessageByComputed }}</p>

    <p>方法: {{ reversedMessageByMethod() }}</p>
    <p>方法: {{ reversedMessageByMethod() }}</p>
    <p>方法: {{ reversedMessageByMethod() }}</p>
</template>

<script setup lang="ts">
import {computed, ref} from "vue";

// 响应式对象
const message = ref<string>('Hello')

// 计算属性
const reversedMessageByComputed = computed(() => {
    console.log('计算属性被调用........')
    return message.value.split('').reverse().join('')
})

// 方法
const reversedMessageByMethod = () => {
    console.log('方法被调用......')
    return message.value.split('').reverse().join('')
}
</script>

<style>

</style>

运用计算属性的表格案例

<template>
    <!-- 1. 定义视图 -->
    <table>
        <thead>
        <tr>
            <th>序号</th>
            <th>商品名称</th>
            <th>单价</th>
            <th>数量</th>
            <th>金额</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        <!-- 3. 数据渲染 -->
        <tr v-for="(item,index) in goodses" :key="item.id">
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.price }}</td>
            <!-- 6. 定义按钮事件 -->
            <td><button @click="decrement(index)">-</button>{{ item.count }}<button @click="increment(index)">+</button></td>
            <td>{{ item.price * item.count }}</td>
            <!-- 8. 定义删除事件 -->
            <td><button @click="del(index)">删除</button></td>
        </tr>
        </tbody>
    </table>
    <!-- 4. 定义总金额变量,用于计算属性使用 -->
    <div>总金额:{{ totalPrice }}</div>
</template>

<script setup lang="ts">
import {computed, reactive} from "vue";

// 2. 定义数据
type Goods = {
    id: number,
    name: string,
    price: number,
    count: number,
}
const goodses = reactive<Goods[]>([
        {
            id: 1,
            name: 'IPhone 13',
            price: 7988,
            count: 1,
        },
        {
            id: 2,
            name: '小米 11',
            price: 3688,
            count: 1,
        },
        {
            id: 3,
            name: '华为',
            price: 6800,
            count: 1,
        }
])

// 5. 计算属性
const totalPrice = computed(() => {
        return goodses.reduce((total, goods) => {
        return total += goods.count * goods.price
    }, 0)
})

// 7. 实现方法
const decrement = (i: number) => {
    // 减少
    if (goodses[i].count >= 2) {
        goodses[i].count--
    }
}
const increment = (i: number) => {
    // 增加
    goodses[i].count++
}

// 9. 实现删除
const del = (index: number) => {
    goodses.splice(index, 1)
}
</script>

<style>
table {
    width: 80%;
    border-left: 1px solid #999;
    border-top: 1px solid #999;
}

th, td {
    border-right: 1px solid #999;
    border-bottom: 1px solid #999;
    text-align: center;
}
</style>

案例最终的样子
这当中的reduce函数的用法我不会,去百度了一下了解到了他的用法:
reduce的用法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值