Vue3 —— computed 计算属性及源码学习

  • 该文章是在学习 小满vue3 课程的随堂记录
  • 示例均采用 <script setup>,且包含 typescript 的基础用法

前言

本篇文章主要学习 computed基本使用方式简单购物车实战源码理解

一、基本使用

computed 支持两种定义方式:选项式函数式

1、选项式

  • 传入 对象格式,对象内部有 getset 函数
  • 可读可写
<div class="">
  firstName: <input type="text" v-model="firstName" />
  <br />
  lastName: <input type="text" v-model="lastName" />
  <br />
  fullName: {{ fullName }}
  <br />
  <button @click="change">设置fullName</button>
</div>
import { ref, reactive, computed } from "vue";

const firstName = ref<string>("张");
const lastName = ref<string>("三");
const fullName = computed({
  // get 函数用于获取
  get() {
    return firstName.value + "-" + lastName.value;
  },
  // set 函数用于设置
  set(newVal) {
    // 数组的解构赋值
    [firstName.value, lastName.value] = newVal.split("-");
  },
});

const change = () => {
  fullName.value = "李-四";
};

2、 函数形式

  • 只读,只能获取 不能修改
<div class="">
  firstName: <input type="text" v-model="firstName" />
  <br />
  lastName: <input type="text" v-model="lastName" />
  <br />
  fullName2: {{ fullName2 }}
</div>
const fullName2 = computed(() => {
  return firstName.value + "***" + lastName.value;
});

// 这里修改会报错:Cannot assign to 'value' because it is a read-only property
// fullName2.value = "王-五";

二、简单购物车实战

设计一个购物车,可以实现简单的 商品数量增减单种商品总价总计价格购物车搜索 功能

效果如图:

在这里插入图片描述

<div>
  <input
    type="text"
    v-model="keyword"
    placeholder="computed购物车案例——搜索"
    style="width: 400px"
  />
  <br />
  <table
    border
    width="500"
    cellspacing="0"
    cellpadding="0"
    style="margin-top: 10px"
  >
    <thead>
      <tr>
        <th>物品名称</th>
        <th>物品单价</th>
        <th>物品数量</th>
        <th>物品总价</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in searchData" :key="index">
        <td>{{ item.name }}</td>
        <td>{{ item.price }}</td>
        <td>
          <button @click="item.num > 1 ? item.num-- : null">-</button>
          {{ item.num }}
          <button @click="item.num < 99 ? item.num++ : null">+</button>
        </td>
        <!-- 单个商品总价 -->
        <td>{{ item.num * item.price }}</td>
        <td>
          <button @click="del(index)">删除</button>
        </td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>总价:{{ total }}</td>
      </tr>
    </tfoot>
  </table>
</div>
const keyword = ref<string>("");

interface Data {
  name: string;
  price: number;
  num: number;
}

// 原始数组
const data = reactive<Data[]>([
  {
    name: "小满的绿帽子",
    price: 100,
    num: 1,
  },
  {
    name: "小满的红衣服",
    price: 200,
    num: 1,
  },
  {
    name: "小满的黑袜子",
    price: 300,
    num: 1,
  },
]);

// 总价
const total = computed(() => {
  return data.reduce((pre, cur) => {
    return pre + cur.num * cur.price;
  }, 0);
});

// 删除
const del = (index: number) => {
  data.splice(index, 1);
};

// 搜索数组
const searchData = computed(() => {
  return data.filter((item) => item.name.includes(keyword.value));
});

三、源码学习记录

  • reactivity.cjs.prod.js 中搜索 function computed 即可找到位置
  • computed 使用了 脏值检测原理
  • 以下截图了源码中比较关键的两个函数
    在这里插入图片描述
    在这里插入图片描述

源码学习记录:

/**
 *
 * computed()
 *
 * 1、如果 computed 的第一个参数是函数,说明是只读的传入形式,将参数赋值给 getter, setter为空或在开发环境抛出警告
 *
 * 2、否则则为选项式传参,直接去第一个参数里面读 get 和 set 并赋值
 *
 *
 * ComputedRefImpl
 *
 * 1、这个类是创建 computed 的核心函数
 *
 * 2、_dirty:是否是脏的,是否需要重新计算(脏值检测原理!!),默认是 true(如果值没有发生变化,就会使用缓存中的值)
 *
 * 3、get value() 的写法,可以看出,内部是劫持了 value 属性 (ref)
 *
 *    - toRaw 先将 this 脱离 proxy 代理
 *    - _dirty 若为 true,则需要重新计算
 *
 *          - _dirty 设置为 false(下次不需要再走进来)
 *          - self.effect.run() 读取 return出来值的操作(根据依赖变化后计算出的新值)
 *
 *
 *    - _cacheable 若为 false,即缓存不可用,那肯定也是要走进重新计算的
 *    - 否则直接返回现有的值
 *
 * 4、构造函数 中的 ReactiveEffect,源码中搜索 class ReactiveEffect
 *
 *    - ReactiveEffect(fn, scheduler, scope)
 *    - 跟上一章学习的 effect 是一样的,对其进行扩展,支持了调度(scheduler)
 *          - effect(() => { 依赖函数 })
 *
 * 5、调度函数又是在哪里调用的?  trigger ->  triggerEffect 函数中调用
 *    - 也就是说 当依赖发生变化时,才会调用 scheduler
 *
 * 6、new ReactiveEffect 传入的 scheduler 函数体中会将 _dirty 置为 true(即依赖发生变化时,_dirty置为true)
 *      然后在 get value() 时,走进的就是 run(),即重新获取不读缓存
 *
 * 7、依赖未改变时,_dirty 一直是 false,再次访问就会取老的值,不会重新获取
 *
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值