Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面

这篇具有很好参考价值的文章主要介绍了Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面

📋前言

这篇文章记录一下 Vue3 计算属性和侦听器 (computed、watch) 实战的内容,这篇文章我们在有计算属性和侦听器的基础上,我们来制作一个简易点餐页面,接下来我们一起来从零到一开始制作。

计算属性和侦听器相关文章推荐:
深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
浅谈在 Vue2 和 Vue3 中计算属性和侦听器的一些变化


🎯项目介绍

在创建项目之前,我们先简单看一下这次项目需要完成的页面内容,如下图。主页列表罗列着菜品名称、图片介绍,用户通过单机添加按钮,实现菜品添加的点餐功能。最后在页面的下方会显示用户点餐详情以及总数和总价,同时可以通过单机删除按钮,实现菜品的删除的取消点餐功能。
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面


🎯项目创建

要创建一个 Vite 项目,需要先安装 Vite。可以使用 npm 或者 yarn 进行安装。在命令行中输入:

npm install vite -g  # 全局安装 vite
或者
yarn global add vite  # 全局安装 vite

然后通过以下命令创建一个 Vite 项目,名称是 vite-demo

npm init vite@latest vite-demo

选择 Vue
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
然后选择 TypeScript
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
默认生成的项目结构如下,然后在控制台输入 npm install 安装相关依赖 (主要选择当前文件夹)
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
最后输入 npm run dev 启动项目,出现如下页面表示运行成功。
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
到此项目创建完成,接下来我们来看看具体代码。


🎯代码分析

我们根据上面项目介绍的图片展示,点餐页面分为三个部分,即菜品列表、点餐列表以及消费价格。可以先根据这个页面设计来实现代码的布局。在项目中的 App.vue 文件中,修改 template 模板部分的代码。

<template>
  <div class="food-container">
    <div class="food-wrap">
      <!-- 菜品列表 -->
      <ul class="food-main">
        <li v-for="(item, index) in foodList" :key="item.name">
          <img :src="item.url" class="food-image"/>
          <label>
            <span>{{item.name}}</span>
          </label>
          <button class="btn btn-add" @click="orderFood(index)">添加</button>
          <span class="food-price">价格 {{item.price}} 元/份</span>
        </li>
      </ul>
      <!-- 点餐列表 -->
      <div class="food-order">
        <ul class="order-main">
          <li class="order-item" v-for="(item, index) in orderList" :key="item.name">
            <label>{{item.name}}</label>
            <div>
              <span class="order-count"> X {{item.count}}</span>
              <button class="btn btn-delete" @click="removeFood(index)">删除</button>
            </div>
          </li>
        </ul>
      </div>
      <!-- 总消费价格 -->
      <div class="food-total-price">
        <span>
          <span class="total-count">已点 {{totalCount}} 份餐</span>
          <span>共计 <b>{{total}}</b></span>
        </span>
      </div>
    </div>
  </div>
</template>

在这段代码里,使用 v-for 指令分别渲染菜品列表和点餐列表。添加按钮和删除按钮分别绑定 orderFood()removeFood() 方法。最后通过 totalCount 的值是否为 0 来显示点餐份数。

接下来我们来实现点餐页面的业务逻辑,修改 App.vue 文件中的 script 部分代码。

<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue';
// 菜品接口类
interface Food {
  name: string;
  url: string;
  price: number;
}

// 订单接口类
interface Order {
  name: string;
  price: number;
  count: number;
}

// 菜品数据列表
const foodList = reactive<Food[]>([
  { name: '宫保鸡丁', url: '/src/assets/gbjd.png', price: 12.0 },
  { name: '鱼香肉丝', url: '/src/assets/yxrs.png', price: 17.0 },
  { name: '红烧排骨', url: '/src/assets/hspg.png', price: 20.0 },
]);

// 订单数据列表
const orderList = reactive<Order[]>([]);

// 总价
const total = ref(0);

// 总个数
const totalCount = computed((): number => {
  let count = 0;
  orderList.forEach((item: Order) => {
    count += item.count;
  })
  return count;
});

// 点餐函数
const orderFood = (index: number):void => {
  // 查看当前菜品是否已经被点
  const isOrdered = orderList.filter((item: Order): boolean => {
    return item.name === foodList[index].name;
  });
  if (isOrdered.length) {
    isOrdered[0].count += 1;
  } else {
    orderList.push({
      name: foodList[index].name,
      price: foodList[index].price,
      count: 1,
    })
  }
};

// 取消点餐操作
const removeFood = (index: number):void => {
  if (orderList[index].count > 0) {
    orderList[index].count -= 1;
  }
  if (orderList[index].count === 0) {
    orderList.splice(index, 1);
  }
};

// 监听订单列表变化
watch(
  () => orderList,
  () => {
    total.value = 0;
    orderList.forEach((order: Order) => {
      total.value += order.count * order.price;
    });
  },
  {deep:true}
);
</script>

这里首先分别定义了 FoodOrder 两个类型。然后初始化 foodListorderListtotal 变量,对应的菜品列表、点餐列表和消费总价。接下来使用一个 totalCount 计算属性统计总点餐份数。orderFood() 方法和 removeFood() 方法分别对应模板的添加和删除按钮。最后使用侦听器属性,检测 orderList 对象的变化。通过 orderList 数据变化来计算总点餐花费。这样,一个简单的点餐页面就完成了,运行效果如下。
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面


🎯完整代码(含 CSS 代码)

<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue';
// 菜品接口类
interface Food {
  name: string;
  url: string;
  price: number;
}

// 订单接口类
interface Order {
  name: string;
  price: number;
  count: number;
}

// 菜品数据列表
const foodList = reactive<Food[]>([
  { name: '宫保鸡丁', url: '/src/assets/gbjd.png', price: 12.0 },
  { name: '鱼香肉丝', url: '/src/assets/yxrs.png', price: 17.0 },
  { name: '红烧排骨', url: '/src/assets/hspg.png', price: 20.0 },
]);

// 订单数据列表
const orderList = reactive<Order[]>([]);

// 总价
const total = ref(0);

// 总个数
const totalCount = computed((): number => {
  let count = 0;
  orderList.forEach((item: Order) => {
    count += item.count;
  })
  return count;
});

// 点餐函数
const orderFood = (index: number):void => {
  // 查看当前菜品是否已经被点
  const isOrdered = orderList.filter((item: Order): boolean => {
    return item.name === foodList[index].name;
  });
  if (isOrdered.length) {
    isOrdered[0].count += 1;
  } else {
    orderList.push({
      name: foodList[index].name,
      price: foodList[index].price,
      count: 1,
    })
  }
};

// 取消点餐操作
const removeFood = (index: number):void => {
  if (orderList[index].count > 0) {
    orderList[index].count -= 1;
  }
  if (orderList[index].count === 0) {
    orderList.splice(index, 1);
  }
};

// 监听订单列表变化
watch(
  () => orderList,
  () => {
    total.value = 0;
    orderList.forEach((order: Order) => {
      total.value += order.count * order.price;
    });
  },
  {deep:true}
);

</script>

<template>
  <div class="food-container">
    <div class="food-wrap">
      <!-- 菜品列表 -->
      <ul class="food-main">
        <li v-for="(item, index) in foodList" :key="item.name">
          <img :src="item.url" class="food-image"/>
          <label>
            <span>{{item.name}}</span>
          </label>
          <button class="btn btn-add" @click="orderFood(index)">添加</button>
          <span class="food-price">价格 {{item.price}} 元/份</span>
        </li>
      </ul>
      <!-- 点餐列表 -->
      <div class="food-order">
        <ul class="order-main">
          <li class="order-item" v-for="(item, index) in orderList" :key="item.name">
            <label>{{item.name}}</label>
            <div>
              <span class="order-count"> X {{item.count}}</span>
              <button class="btn btn-delete" @click="removeFood(index)">删除</button>
            </div>
          </li>
        </ul>
      </div>
      <!-- 总消费价格 -->
      <div class="food-total-price">
        <span>
          <span class="total-count">已点 {{totalCount}} 份餐</span>
          <span>共计 <b>{{total}}</b></span>
        </span>
      </div>
    </div>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

.btn {
  display: inline-block;
  padding: 8px 10px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 14px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.2),
    0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}
.btn-add {
  color: #fff;
  background-color: #0d6efd;
  border: none;
}
.btn-delete {
  color: #fff;
  background-color: #dc3545;
  border: none;
}
.btn-delete:hover {
  color: #fff;
  background-color: #bb2d3b;
}
.btn-add:hover {
  color: #fff;
  background-color: #0b5ed7;
}

.food-container {
  width: 600px;
  margin: 40px auto;
}
.food-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

.food-price {
  float: right;
  margin-right: 10px;
}
.food-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 0px;
}

.food-image {
  float: left;
  height: 100%;
}
.order-main {
  margin-left: 0px;
  padding: 0px;
}

.order-main li {
  display: flex;
}
.order-item {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
}
.food-main li label {
  float: left;
  cursor: pointer;
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
  font-weight: bold;
  margin-left: 10px;
}

.food-main li button {
  float: right;
  margin-top: 3px;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}

.total-count {
  font-size: 0.8rem;
  color: #6c757d;
  margin-right: 21px;
}
.food-main li:hover {
  background-color: #ddd;
}
.order-count {
  margin-right: 30px;
}

.food-order {
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.food-order label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.food-total-price {
  display: flex;
  justify-content: end;
}

</style>

📝最后

到此文章结束,这就是 Vue3 计算属性和侦听器 (computed、watch) 实战的全部内容了,通过这篇文章我们从零到一制作了一个简易点餐页面,这样可以提高我们使用计算属性和侦听器的熟练程度。
Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面
文章来源地址https://www.toymoban.com/news/detail-493986.html

到了这里,关于Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)

    #五一技术创作马拉松# 计算属性 computed 和侦听器 watch 都是 Vue.js 框架中用来响应式更新视图的重要概念。在 Vue 项目开发中,这两个技术点是非常重要的,同时也是 Vue 基础中不可缺少的知识点。在面试中,计算属性 computed 和侦听器 watch 也是经常出现的考点,作为前端开发也

    2024年02月07日
    浏览(52)
  • Vue学习-计算属性和侦听器

    1、计算属性的定义和原理 1、定义:要用的属性不存在,要通过已有属性计算得来。 2、原理:底层借助了Objcet.defineproperty方法提供的getter和setter。 3、get函数什么时候执行?   (1) 初次读取时会执行一次。   (2)当依赖的数据发生改变时会被再次调用。 4、优势:与

    2023年04月18日
    浏览(35)
  • VUE教程-基础-计算属性和侦听器

    模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如: 在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量  message  的翻转字符串。当你想要在模板中的多

    2024年02月17日
    浏览(39)
  • 二、基础篇 vue计算属性和侦听器

    模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如: 在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量  message  的翻转字符串。当你想要在模板中的多

    2024年01月18日
    浏览(40)
  • 【源码系列#04】Vue3侦听器原理(Watch)

    专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核💪推荐🙌 欢迎各位ITer关注点赞收藏🌸🌸🌸 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数 第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性

    2024年02月04日
    浏览(34)
  • Vue3前端开发,watch数据侦听器的立即执行

    Vue3前端开发,watch数据侦听器的立即执行!实际上,我们可以通过回调函数的后面,再追加一个对象,来设置这个参数,immediate:true.来实现一种立即执行的效果。在页面记载完成后,马上就会执行一次watch. 如上所示,我们在回调函数的后面,追加了一个参数,是一个对象类型

    2024年01月18日
    浏览(38)
  • Vue3前端开发,watch侦听器的深度监听和精确监听

    Vue3前端开发,watch侦听器的深度监听和精确监听!今天和大家分享的内容是,关于watch的深度侦听和精确监听。 首先看一下,第一个案例,练习的是,深度监听的效果。默认是浅的侦听,是不会触发回调函数的。 如图,当我们点击按钮,修改num值的时候,触发了回调函数,在

    2024年01月23日
    浏览(30)
  • Web前端 ---- 【Vue3】computed计算属性和watch侦听属性(侦听被ref和reactive包裹的数据)

    目录 前言 computed watch watch侦听ref数据 ref简单数据类型 ref复杂数据类型 watch侦听reactive数据 本文介绍在vue3中的computed计算属性和watch侦听属性。介绍watch如何侦听被ref和reactive包裹的数据 在vue3中,计算属性computed也是组合式api,也就是说要先引入,再在setup中使用 语法 完整:

    2024年01月18日
    浏览(45)
  • Vue——侦听器

    目录 基本示例​ 深层侦听器​ 即时回调的侦听器​ 回调的触发时机​ this.$watch()​ 停止侦听器​        计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态

    2023年04月13日
    浏览(36)
  • Vue中watch侦听器用法

    watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用 watch第一个参数监听源 watch第二个参数回调函数cb(newVal,oldVal) watch第三个参数一个options配置项是一个对象{ immediate :true //是否立即调用一次 deep :true //是否开启深度监听 flush :“pre” // 更新时机 } flush配置项 p

    2024年02月06日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包