通过“书店购物系统”实战来介绍Vue中的响应式基础

零 Vue.js教程评论94字数 6228阅读20分45秒阅读模式

通过“书店购物系统”实战来介绍Vue中的响应式基础

前言

先介绍本文的主角:

  1. ref:
    • 用于包裹基本类型值或对象以创建响应式引用。当你需要对单个值(如数字、字符串、布尔值等)进行响应式处理时,可以使用ref。访问.value属性来获取或设置实际的值。
  2. reactive:
    • 用于使一个普通对象的所有属性变为响应式的。适用于处理复杂的数据结构,如对象和数组。使用reactive创建的代理对象可以直接通过属性访问和修改,而不需要.value
  3. 计算属性(computed :
    • 虽然计算属性本身不是响应式数据的创建方式,但它依赖于响应式数据并在依赖项变化时自动重新计算,因此是响应式系统的一部分。
  4. 侦听器(watch :
    • 同样,watch也不是直接创建响应式数据,但它用于监听响应式数据的变化并执行相应的操作,是响应式系统应用的一个方面。

正文

先给出页面要求:文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

1、要求有三个部分:书籍推荐、购物车、待办事项文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

  • 书籍推荐:用户可以根据自己的预算点击增加和减少按钮来改变预算,再根据预算推荐给用户适合的书。
  • 购物车:可以增加、减少或者删除购买的书籍,并给出需要购买的书籍的总价格。
  • 待办事项:可以供用户输入或删除自己需要做的事项。

主体部分

实现一个底部导航栏的功能。导航栏有三个选项,分别是“书籍推荐”、“购物车”和“待办事项”。点击不同的选项会切换不同的内容区域。文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

0ae17ff385e149f1c240b8bd89f707d.png文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

为了实现点击不同部分在不刷新页面的情况下,很容易会想到AJAX异步交互,但这里不需要那么高级,只需要将对象响应式,每点击不同对象自动更新内容文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

reactive更新页面内容文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

当你用reactive处理一个普通对象时,它会返回一个新的代理对象,这个代理对象的属性是有响应式的。这意味着,当这些属性的值发生变化时,依赖于这些值的计算属性或视图将会自动更新。文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

js

复制代码
<template>
  <nav>
    <span @click="changeTab(1)">书籍推荐</span>|
    <span @click="changeTab(2)">购物车</span>|
    <span @click="changeTab(3)">待办事项</span>
  </nav>
    <Base v-if="state.index === 1" />
    <shopping v-if="state.index === 2" />
    <todo v-if="state.index === 3" />
</template>

<script setup>
import Base from './components/base.vue'
import shopping from './components/shopping.vue'
import todo from './components/todo.vue'
import { reactive } from 'vue'

let state = reactive({
  index: 1
})
const changeTab = (i) => {
  console.log(i)
  state.index = i
}

</script>

<style lang="css">
*{
  text-align: center;
}
</style>
  • 在模板部分,使用<nav>标签创建了导航栏容器,其中包含三个<span>标签作为导航选项。通过@click事件绑定changeTab函数来实现选项的点击切换。根据state.index的值,使用上一篇文章介绍的v-if指令动态渲染不同的组件内容。
  • 在脚本部分,导入了三个组件文件Baseshoppingtodo,以及Vue的reactive函数。使用reactive创建了一个响应式对象state,其初始值为{ index: 1 }。定义了一个changeTab函数,用于接收点击事件的参数i,并通过设置state.index的值来切换选项卡。

书籍推荐

6b007ac5d828c6e7146e3c799619aa4.png edad1468988f65f6c35b2cd59580f23.png 14f082e35d213b54369969c29f2f38f.png be1d3583452c55201b8eb6a498aea26.png文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

ref实现

ref用于包裹基本类型值或对象以创建响应式引用。当你需要对单个值(如数字、字符串、布尔值等)进行响应式处理时,可以使用ref。访问.value属性来获取或设置实际的值。文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

js

复制代码
<template>
    <div>
        <h2>{{ temp }}</h2>
        <h5>{{ sugget }}</h5>
        <button @click="add">+5</button>
        <button @click="minus">-5</button>
    </div>
</template>

<script setup>
import { ref  } from 'vue'

let temp = ref(20)
 let sugget = ref('计算机组成原理')

const add = () => {
    temp.value += 5
    if (temp.value >= 30) {
        sugget.value = '操作系统'
    }
    else if(temp.value >= 20) {
        sugget.value = '计算机组成原理'
    }
    else if(temp.value >= 10) {
        sugget.value = '形势与政策'
    }
    else{
        sugget.value = '草稿本'
    }
}
const minus = () => {
    temp.value -= 5
    if (temp.value >= 30) {
        sugget.value = '操作系统'
    }
    else if(temp.value >= 20) {
        sugget.value = '计算机组成原理'
    }
    else if(temp.value >= 10) {
        sugget.value = '形势与政策'
    }
    else{
        sugget.value = '草稿本'
    }
}

  • <h2>{{ temp }}</h2>:显示由temp变量管理的温度值,初始值为20。
  • <h5>{{ sugget }}</h5>:显示建议或提示信息,初始值为“计算机组成原理”。
  • <button @click="add">+5</button>:点击按钮时调用add方法,使预算增加5。
  • <button @click="minus">-5</button>:点击按钮时调用minus方法,使预算减少5。
  • let temp = ref(20):声明了一个响应式变量temp,初始值为20。ref用于创建可变的响应式引用,当temp的值发生变化时,绑定到它的模板部分会自动更新。
  • let sugget = ref('计算机组成原理'):同样使用ref创建了一个响应式字符串变量sugget,初始值为“计算机组成原理”
  • 声明两个函数addminus,它们分别用于增加和减少temp变量的值,并根据temp的值来更新sugget的建议文本。

侦听器(watch)实现

watch不是直接创建响应式数据,但它用于监听响应式数据的变化并执行相应的操作,是响应式系统应用的一个方面。文章源自灵鲨社区-https://www.0s52.com/bcjc/vue-jsjc/16495.html

js

复制代码
<template>
    <div>
        <h2>{{ temp }}</h2>
        <h5>{{ sugget }}</h5>
        <button @click="add">+5</button>
        <button @click="minus">-5</button>
    </div>
</template>

<script setup>
import { ref,watch } from 'vue'

let temp = ref(20)
 let sugget = ref('计算机组成原理')

const add = () => {
    temp.value += 5
}
const minus = () => {
    temp.value -= 5
}

watch(temp,(newVal,oldVal)=>{
    console.log(newVal,oldVal);
    if (newVal >= 30) {
        sugget.value = '操作系统'
    }
    else if(newVal >= 20) {
        sugget.value = '计算机组成原理'
    }
    else if(newVal >= 10) {
        sugget.value = '形势与政策'
    }
    else{
        sugget.value = '草稿本'
    }
},{ immediate: true })

watch用于侦听temp响应式数据的变化,并在数据变化时执行特定的函数。

  • watch(temp, (newVal, oldVal) => { ... }):创建一个观察者,监视temp的值。当temp的值发生改变时,传入的箭头函数会被调用。这个函数接收两个参数:newVal是变化后的新值,oldVal是变化前的旧值。
  • 接下来的if...else if...else语句根据newVal(即新的temp值)来更新sugget.value,提供不同的购书建议:
  • { immediate: true }:这是一个可选参数,当设置为true时,表示在初始绑定时就会立即以当前的temp值执行一次回调函数,意思就是:在运行开始时就会执行watch函数对temp值进行判断,这避免了初始值temp与初始值sugget无法对应

计算属性(computed)实现

虽然计算属性本身不是响应式数据的创建方式,但它依赖于响应式数据并在依赖项变化时自动重新计算,因此是响应式系统的一部分。

js

复制代码
<template>
    <div>
        <h2>{{ temp }}</h2>
        <h5>{{ sugget }}</h5>
        <button @click="add">+5</button>
        <button @click="minus">-5</button>
    </div>
</template>

<script setup>
import { ref,watch,computed } from 'vue'

let temp = ref(20)
const sugget = computed(() =>{
    if (temp.value >= 30) {
        return '操作系统';
    }
    else if(temp.value >= 20) {
        return'计算机组成原理';
    }
    else if(temp.value >= 10) {
        return'形势与政策';
    }
    else{
        return'草稿本';
    }
})

const add = () => {
    temp.value += 5
}
const minus = () => {
    temp.value -= 5
}

computed用于定义依赖于其他数据的衍生数据。它是一个计算属性,只有当依赖的数据发生变化时才会重新计算,结果会被缓存起来,直到下一次依赖的数据变化。

sugget重新声明为一个计算属性,它基于一个函数来计算其返回值。和watch类似,这个函数内部的逻辑会根据temp.value的值来决定返回什么样的字符串

使用computed的优点在于:

  • 效率:只有当依赖的响应式属性(在这里是temp.value)变化时,才会重新计算sugget的值,否则直接返回缓存的值,这在性能上更为高效。
  • 声明式:使得代码更加清晰易读,关注于“是什么”而不是“怎么做”,提高了可维护性。

watch 和 computed 的区别

  1. watch默认不会主动执行,且watch是监听某个变量的变更,会执行内部的回调
  2. computed 默认会主动执行,当回调函数中任意变量值变更时,computed都会重新执行

购物车

理解完前一章讲的模板语法和上文讲的响应式基础,接下来的应用简直过关斩将

3b11b8c5aacf41839a8e9a5596f7d22.png

  • 利用v-for模板语法指令遍历books数组,为每一本书创建一行<tr>,显示每本书的序号、名称、出版日期、价格和购买数量。
  • 为每本书提供增加、减少数量和删除的按钮。
  • 定义方法:
    • add(i):根据索引i,增加对应书籍的购买数量。
    • minus(i):根据索引i,减少对应书籍的购买数量(并禁用减按钮当数量为1时)。
    • del(i):根据索引i,使用splice方法从books数组中删除对应的书籍条目。
  • 显示总价格<h2>总价格:{{ totalPrice }}</h2>,利用计算属性computed动态计算。
  • 使用reactive创建书籍响应式对象,使得对象的属性变化能够被Vue自动追踪

响应式更新: 通过将books数组设置为响应式的,当数组中的任意一项书籍的属性(如count)发生变化时(比如通过addminus方法增减数量),Vue会自动检测到这个变化,并相应地更新与这些数据相关的DOM元素,确保用户界面能够即时反映出数据的最新状态。

js

复制代码
<template>
    <table>
      <thead>
        <th>序号</th>
        <th>书籍名称</th>
        <th>出版日期</th>
        <th>价格</th>
        <th>购买数量</th>
        <th>操作</th>
      </thead>
      <tbody>
        <tr v-for="(item,index) in books" :key="item.id">
          <td>{{ index+1}}</td>
          <td>{{ item.name }}</td>
          <td>{{ item.date }}</td>
          <td>{{ item.price }}</td>
          <td>
            <button @click="minus(index)" :disabled="item.count <= 1">-</button>
            <span class="counter">{{item.count}}</span>
            <button @click="add(index)">+</button>
          </td>
          <td>
            <button @click="del(index)">删除</button>
          </td>
        </tr>
      </tbody>
    </table>
    <h2>总价格:{{ totalPrice }}</h2>
</template>

<script setup>
import { computed ,reactive} from 'vue';
const add = (i) => {
  books[i].count++
}
const minus = (i) => {
  books[i].count--
}
const del= (i) => {
  books.splice(i, 1)
}
const totalPrice = computed(() => {
  let sum = 0
  for (let item of books) {
    sum += item.price * item.count
  }
  return sum
})
const books =reactive([
        {
          id: 1,
          name: '《算法导论》',
          date: '2006-9',
          price: 85.00,
          count: 1
        },
         。。。。。
      ])
</script>

<style lang="css" scoped>
table{
    border: 1px solid #ccc;
    margin: 0 auto;
    border-collapse: collapse;
}
th, td{
  padding: 8px 16px;
  border: 1px solid #aaa;
}
.counter{
    margin: 0,5px;
}

</style>

结语

Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构

非原始值将通过 reactive() 转换为响应式代理,Vue 能够拦截对响应式对象所有属性的访问和修改,以便进行依赖追踪和触发更新。

reactive() API 有一些局限性:

  1. 有限的值类型:它只能用于对象类型 (对象、数组和如 MapSet 这样的集合类型它不能持有如 stringnumber 或 boolean 这样的原始类型
  2. 不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

watch 和 computed 的区别

  1. watch默认不会主动执行,且watch是监听某个变量的变更,会执行内部的回调
  2. computed 默认会主动执行,当回调函数中任意变量值变更时,computed都会重新执行

零
  • 转载请务必保留本文链接:https://www.0s52.com/bcjc/vue-jsjc/16495.html
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论