Vue核心特性解析(内含实践项目:设置购物车)

Vue 3 作为前端开发领域的热门框架,为构建用户界面提供了高效且灵活的解决方案。在上一篇博客:深度解析Vue3中,我们了解到了响应式数据、v-on、v-if、v-for、v-bind等Vue相关属性,这篇我们继续探讨Vue的其他属性,为你揭开Vue的魅力所在。

一、双向绑定指令

1、基本概念

在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦,而此时我们就可以使用v-model指令****。

v-model用于在表单元素(如<input>、<textarea>、<select>)和组件上创建双向绑定指令。这说明了该指令可以同时实现数据从数据层(JavaScript)到视图层(HTML)的渲染,可以从视图层获取用户输入并更新数据层

v-model 会忽略任何表单元素上初始的 valuecheckedselected attribute。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。所以应该在 JavaScript 中使用响应式系统的API来声明该初始值。

2、实际应用

(1)文本框(text)

①单向绑定

数据是单向流动的,通常是从数据层流向视图层。当数据发生改变时, 视图会自动更新. 但用户手动更改 input 的值, 数据不会自动更新。比如:

html 复制代码
<head>
    <style>
        .textColor{
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <h3>文本框 <ins>{{ data.text }}</ins></h3>
        input元素的value属性动态化(单向数据绑定): <input type="text" :value="data.text">    <br>
      
    </div>
    <script type="module">

        import { createApp, reactive } from './vue.esm-browser.js'
    
        createApp({
        setup() {
            const data = reactive({
                text: " ", 
            })

            return {
                data
            }
        }
    }).mount("#app")

    </script>
</body>

运行结果如下:

看代码,如果用户输入文本框中输入内容后,在"文本框"的后面没有实时更新相应内容,这便是单向绑定。

②双向绑定指令

数据不仅会从数据层流向视图,视图上的变化也会实时更新到数据层,二者相互影响。比如:

html 复制代码
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>双向数据绑定指令v-model (实时渲染)</title>

    <style>
        .textColor{
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <h3>文本框 <ins>{{ data.text }}</ins></h3>
        双向数据绑定: <input type="text" v-model="data.text">
      
    </div>
    <script type="module">

        import { createApp, reactive } from './vue.esm-browser.js'
    
        createApp({
        setup() {
            const data = reactive({
                text: "输入姓名", 
            })

            return {
                data
            }
        }
    }).mount("#app")
    </script>
</body>

运行结果如下:

双向绑定指令则会根据用户输入的内容实时更新到"文本框"后面。

(2)单选框(radio)

用于记录用户的单选选择,将用户选中的值绑定到数据中。比如:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>双向数据绑定指令v-model (实时渲染)</title>

    <style>
        .textColor{
            color: red;
        }
    </style>
</head>

<body>

    <div id="app">
        <h3>您的性别(单选):<ins> {{ data.radio }}</ins></h3> 
        <!-- 对于多个<input type="radio">, v-model 绑定的是 input 单选元素组的选中的值value -->
        <input type="radio" v-model="data.radio" value="男" name="radio">男
        <input type="radio" v-model="data.radio" value="女" name="radio">女
        <hr>
      
    </div>
    <script type="module">

        import { createApp, reactive } from './vue.esm-browser.js'
    
        createApp({
        setup() {
            const data = reactive({
                radio: "", 
            })

            return {
                data
            }
        }
    }).mount("#app")


    </script>



</body>
</html>

运行结果如下:

用户选择"男"或"女"后,data.radio的数据会实时更新为所选单选框的value值,并在页面上显示。

(3)多选框(checkbox)

用于记录用户的多选选择,将用户选中的值绑定到数据中。比如:

html 复制代码
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>双向数据绑定指令v-model (实时渲染)</title>

    <style>
        .textColor{
            color: red;
        }
    </style>
</head>

<body>

    <div id="app">
        <h3>您的爱好(多选): {{ data.checkbox }}</h3>
        <input type="checkbox" v-model="data.checkbox" value="足球">足球
        <input type="checkbox" v-model="data.checkbox" value="蓝球">蓝球
        <input type="checkbox" v-model="data.checkbox" value="羽毛球">羽毛球
        <hr>
      
    </div>
    <script type="module">

        import { createApp, reactive } from './vue.esm-browser.js'
    
        createApp({
        setup() {
            const data = reactive({
                checkbox: [], 
            })

            return {
                data
            }
        }
    }).mount("#app")
    </script>
</body>

运行结果如下:

当用户勾选或取消勾选选项时,checkbox数组会添加或删除相应选项的value值,页面上显示checkbox数组内容的部分也会更新。

(4)单个复选按钮的双向数据绑定

单个复选按钮的双向绑定把复选按钮的选中状态 (视图层面)和一个布尔类型的数据(模型层面)关联起来。用户点击复选按钮改变其选中状态,这个状态变化会被"传递"给绑定的数据,使该数据变为true(选中时)或false(未选中时)。常用于网页登录的"记住密码",如:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>双向数据绑定指令v-model (实时渲染)</title>

    <style>
        .textColor{
            color: red;
        }
    </style>
</head>

<body>

    <div id="app">
        <h3>需要记住密码?  {{ data.remember }} </h3>
        <input type="checkbox" v-model="data.remember">记住密码
      
    </div>
    <script type="module">

        import { createApp, reactive } from './vue.esm-browser.js'
    
        createApp({
        setup() {
            const data = reactive({
                remember: false, 
            })

            return {
                data
            }
        }
    }).mount("#app")


    </script>



</body>
</html>

运行结果如下:

当用户点击记住密码时,返回true的值

(5)下拉框(select)

绑定用户选择的下拉项的值。如:

html 复制代码
<head>
    <style>
        .textColor{
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <h3>下拉选择,您的专业是: {{ data.select }}</h3>  
        <select v-model="data.select">
            <option value="计算机">计算机</option>
            <option value="数字媒体">数字媒体</option>
            <option value="中医养生">中医养生</option>
            <option value="针灸推拿">针灸推拿</option>
        </select>
    </div>
    <script type="module">

        import { createApp, reactive } from './vue.esm-browser.js'
    
        createApp({
        setup() {
            const data = reactive({
                select: ""  
            })

            return {
                data
            }
        }
    }).mount("#app")
    </script>
</body>

运行结果如下:

当用户在下拉框中选择一个选项时,select数据会更新为所选选项的value值,页面上显示select的内容也会相应改变。

二、计算属性

1、基本概念

计算属性(computed)是基于响应式数据进行计算得到的值。计算属性会根据它所依赖的数据的变化而自动重新计算,并且计算结果会被缓存起来

2、实际应用

平时我们在网上购物时,选择多个商品或选多数量时,在购物车下方会计算商品的总数或价格,我们用其中的总数举例。

html 复制代码
<body>
    <div id="app">
        <h3>购买水果总数量为: {{ add() }}</h3>   
        苹果购买数量: <input type="text" v-model.number="data.x"> <br>
        香蕉购买数量:<input type="text" v-model.number="data.y"> <br>
        
    </div>

    <script type="module">

        import { createApp, ref, reactive, computed } from './vue.esm-browser.js'

        createApp({
        setup() {
            const data = reactive({
                x: "",
                y: "",
            })
            //方法-不做缓存
            const add = () => {
                console.log("调用普通的方法add,不做缓存") 
                return data.x + data.y
            }
            const sub = computed(() => {
                console.log("调用带计算属性的方法sub,并缓存起来") 
                return data.x + data.y
            })

            return {data,add,sub}
        }
    }).mount("#app")
    </script>
</body>

运行结果如下:

当用户输入购买数量时,sub函数会实时增加数量,计算购买总数量。

代码分析:

①add方法是一个普通的函数,当被调用时会在控制台输出相应信息,并返回data.x与data.y相加的结果。这里每次调用add方法都会重新执行该函数内的逻辑,不会对结果进行缓存。

②sub是通过computed函数创建的计算属性。当它所依赖的data.x和data.y发生变化时,会自动重新计算结果,并在控制台输出相应信息。而且计算属性会对结果进行缓存,只有在其依赖的数据发生改变时才会重新计算,相比于普通方法在性能上可能更优(当多次获取结果且依赖数据未变时)。

3、优势

  • 计算属性会缓存计算结果 。比如:当购物车内的商品只修改了名称,但其价格和数量没有被改变时,计算属性中计算总数量和总价便不会重新计算,因为他们所依赖的数据没有发生变化,这样也可以更好地避免进行过多的计算。
  • 当计算机属性所依赖的数据发生变化时,计算的数据也会实时更新,这也能确保数据的有效性。

三、侦听器

1、基本概念

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些"副作用":例如更改 DOM,或是根据异步操作的结果去修改另一处的状态,而此时我们可以利用侦听器。

侦听器(watch)用于观察和响应数据的变化。他允许数据在发生变化时执行特定的操作。比如:异步操作等。

2、实际应用

当用户选择选项后在后台弹出提示,比如:

html 复制代码
<body>

    <div id="app">
        <select v-model="date.year">
            <option value="">请选择</option>
            <option value="2023">2023</option>
            <option value="2024">2024</option>
            <option value="2025">2025</option>
        </select>
        年
       
        <select v-model="date.month">
            <option value="">请选择</option>
            <option value="10">10</option>
            <option value="11">11</option>
            <option value="12">12</option>
        </select>
        月
    </div>
    <script type="module">

        import { createApp, ref, reactive, watch } from './vue.esm-browser.js'
        createApp({  
            setup() {
                const date = reactive({       //日期
                    year: "2023",
                    month: "10"
                })
                watch( () => date.year,     
                    (newValue, oldValue) => {
                        console.log("date.year的旧值:", oldValue, " --> date.year的新值:", newValue)
        
                        if (date.year == "2024") {
                            console.log("您选中的年份是:2024年")
                        }
                    }
                )

                return {  date }
            }
        }).mount("#app")
    </script>
</body>

在这个例子中,watch函数监听data对象。当data对象发生变化时,回调函数就会被执行,并且可以获取到变化后的新值(newValue)和变化前的旧值(oldValue)。即用户选择了2024后,控制台则会输出上一个年份和用户当前选择的年份,如下图:

注:代码中的初始年份为2023

3、与计算机属性的区别

(1)计算属性:是基于依赖数据计算得到一个值,并且有缓存机制。只要依赖数据不变,计算属性的值就不会重新计算。它主要用于根据已有数据计算出一个新的值用于显示等场景。

(2)侦听器:重点在于监听数据的变化,然后执行副作用操作 (如发送网络请求、修改其他数据等)。它没有计算属性那样的缓存机制,每次数据变化都会执行回调函数

四、实践项目:实现购物车功能

通过Vue的学习,我们可以更好地利用他们的特性,制作一个购物车,实现以下从左图到右图的效果,用户通过点击"+"或"---"的按钮选择购买商品的数量,在文本框内输入商品单价后会自动计算所购买的商品总数和总价。

在实现该项目时,我们可以先制作一个简单的框架

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>实战小项目:购物车</title>

    <style>
      body {
          font-family: Arial, sans-serif;
      }
      .cart-item {
          width: 50%;
          margin-bottom: 15px;
          padding: 10px;
          border: 2px solid gray;
          border-radius: 10px;
          background-color: #ddd;
      }
      .buttons {
          margin-top: 5px;
      }
      .buttons button {
          padding: 5px 10px;
          margin-right: 5px;
          font-size: 16px;
          cursor: pointer;
          border: none;
          border-radius: 3px;
          background-color: pink;
      }
      .buttons input {
          width: 25px;
      }
      .buttons button:hover {
          background-color: yellow;
      }
      .quantity {
          font-size: 18px;
          font-weight: bold;
          margin-left: 10px;
      }
      h1, h2 {
          color: #333;
      }
  </style>
</head>
<body>
    <div id="app" >
      <h1>实战小项目:购物车</h1>
    <div class="cart-item" >
        <div class="buttons">
            <span>苹果&nbsp;&nbsp;</span>
            <button >-</button>
            <span class="quantity">1 &nbsp;&nbsp;</span>
            <button >+</button>
            <p>
            请输入价格:
            <input type="text" /> 元/斤 <br> 
            单价:
            1 元/斤
            </p>
        </div>
    </div>


    <h3>商品总数:  <ins> 1 </ins> 件</h3>
    <h3>商品总价:  <ins> 1 </ins> 元</h3>


    </div>
</body>
</html>

该代码包含了HTML和CSS样式, 这其中包含着之前的知识点,大家可以查看前面相关博客进行书写,也可以复制该代码,运用Vue的相关属性实现购物车的功能。

1、创建一个Vue应用程序

javascript 复制代码
<script>
createApp({
            setup() {

                }
            }).mount("#app")
</script>

接下来我们就在该代码内实现我们想要的效果

2、插入数组

使用reactive函数创建了一个名为cartItems的响应式数组,数组内包含了三个初始商品对象,每个对象有name(商品名称)、quantity(商品数量)、price(商品价格)三个属性,初始值分别设定了一些默认值(如数量和价格都为0),用于存储购物车中的商品信息。随着用户在页面上的操作(如调整数量、输入价格),这些数据会实时更新

javascript 复制代码
const cartItems = reactive([
          { name: '苹果', quantity: 0, price: 0 },
          { name: '香蕉', quantity: 0, price: 0 },
          { name: '菠萝', quantity: 0, price: 0 }
                ]);

增加数组后我们要将其遍历出来则可以使用v-for指令,假设有n个商品,则生成n个商品项(&nbsp;&nbsp;为空格

javascript 复制代码
  <div class="cart-item" v-for="(item, index) in cartItems"> 
                <span>{{item.name}}&nbsp;&nbsp;</span>
                <span class="quantity">{{cartItems[index].quantity}}&nbsp;&nbsp;</span>
            </div>
        </div>

3、增加或减少商品数量

首先在button元素内设置按键,用于用户增加或减少商品数量,通过v-on:click指令绑定按钮

javascript 复制代码
<button v-on:click="decreaseQuantity(index)">-</button>
                
<button v-on:click="increaseQuantity(index)">+</button>

两行代码分别位于商品名称的左右,代码如下:

javascript 复制代码
<button v-on:click="decreaseQuantity(index)">-</button>
<span class="quantity">{{cartItems[index].quantity}}&nbsp;&nbsp;</span>
<button v-on:click="increaseQuantity(index)">+</button>

接下来在script元素内接受一个index参数,用于指定要增加数量的商品在cartlItems数组中的索引位置,在内部将商品的数量加1,实现增加数量的功能。

javascript 复制代码
  const increaseQuantity = (index) => {
           cartItems[index].quantity++;
                };

与加法类似,在内部将商品的数量减1,实现减少数量的效果,但在减1之前应该先判断商品数量(quantity)是否大于0,是则减1,但要限制数量不能少于0

javascript 复制代码
 const decreaseQuantity = (index) => {
            if (cartItems[index].quantity > 0) {
                        cartItems[index].quantity--;
                    }
                };

其中decreaseQuantity(index)和increaseQuantity(index),其中index作为商品在cartItems数组内的索引

4、输入价格

通过v-model双向绑定指令,使其能够实时更新商品的价格,同时在下面展示输入单价。

html 复制代码
<p>
   请输入价格:
    <input type="text" v-model="cartItems[index].price" /> 元/斤 <br>
   单价:{cartItems[index].price}} 元/斤
</p >

5、计算总价和总数量

通过computed函数创建一个totalItems属性,这样当购物时商品数量增加或减少可以实时更新

①使用reduce方法,在该函数内有两个参数(total,item),其中total作为累加器,初始值为0(由reduce函数的第二个参数指定),它会累计每次回调函数执行后的结果。item是cartItems数组中的每个商品对象。在每次回调执行时,将当前商品对象的quantity属性值累加到total上,这样遍历完整个cartItems数组后,total就得到了所有商品数量的总和。

javascript 复制代码
const totalItems = computed(() => {
                    return cartItems.reduce((total, item) => total + item.quantity, 0);
                });

扩展

reduce函数:用于将数组中的元素通过某种方式累积为一个单一的值。它可以对数组中的每个元素执行一个由用户提供的回调函数,把这个函数的返回值不断累积,最后返回一个最终结果。

举个例子:

javascript 复制代码
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((a, b) => {
    return a + b;}, 0);

在该代码中初始值0提供给了a,b的值为1,返回0+1=1作为新的a的值;第二次则a为1,b为2,返回1+2=3作为a的值,以此类推。

②通过reduce函数我们也可以实现计算商品总价的功能

javascript 复制代码
const totalPrice = computed(() => {
            return cartItems.reduce((total, item) => 
        total + (item.quantity * item.price), 0);
                });

6、return语句

最后通过return语句将对象和方法暴露出来,使得它们可以在HTML模板中进行数据绑定和方法调用,让Vue实例与页面元素关联起来,从而实现整个购物车功能在页面上的呈现和交互。

javascript 复制代码
 return {
          cartItems,
          totalItems,
           totalPrice,
           increaseQuantity,
           decreaseQuantity
                };

该示例可以清晰地看到如何运行Vue的各种属性,通过事件绑定、计算属性等来构建一个简单而使用的购物车应用。大家也来试试看吧!

完整代码如下;

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>实战小项目:购物车</title>

    <style>
        body {
            font-family: Arial, sans-serif;
        }

       .cart-item {
            width: 50%;
            margin-bottom: 15px;
            padding: 10px;
            border: 2px solid gray;
            border-radius: 10px;
            background-color: #ddd;
        }

       .buttons {
            margin-top: 5px;
        }

       .buttons button {
            padding: 5px 10px;
            margin-right: 5px;
            font-size: 16px;
            cursor: pointer;
            border: none;
            border-radius: 3px;
            background-color: pink;
        }

       .buttons input {
            width: 25px;
        }

       .buttons button:hover {
            background-color: yellow;
        }

       .quantity {
            font-size: 18px;
            font-weight: bold;
            margin-left: 10px;
        }

        h1, h2 {
            color: #333;
        }
    </style>
</head>

<body>
    <div id="app">
        <h1>实战小项目:购物车</h1>

        <!-- 提示:可以使用v-for指令,假设有n个品类,则生成n个商品项-->
        <div class="cart-item" v-for="(item, index) in cartItems">
            <div class="buttons">
                <span>{{item.name}}&nbsp;&nbsp;</span>
                <button v-on:click="decreaseQuantity(index)">-</button>
                <span class="quantity">{{cartItems[index].quantity}}&nbsp;&nbsp;</span>
                <button v-on:click="increaseQuantity(index)">+</button>
                <p>
                    请输入价格:
                    <input type="text" v-model="cartItems[index].price" /> 元/斤 <br>
                    单价:
                    {{cartItems[index].price}} 元/斤
                </p >
            </div>
        </div>


        <!-- 提示:可以用计算属性或数据变动侦听器,跟踪商品数和单价的变化,进而求出总数和总价-->
        <h3>商品总数: <ins> {{totalItems}} </ins> 件</h3>
        <h3>商品总价: <ins> {{totalPrice}} </ins> 元</h3>


    </div>

    <script type="module">
        import { createApp, reactive, computed } from './vue.esm-browser.js';

        createApp({
            setup() {
                const cartItems = reactive([
                    { name: '苹果', quantity: 0, price: 0 },
                    { name: '香蕉', quantity: 0, price: 0 },
                    { name: '菠萝', quantity: 0, price: 0 }
                ]);

                // 计算商品总数
                const totalItems = computed(() => {
                    return cartItems.reduce((total, item) => total + item.quantity, 0);
                });

                // 计算商品总价
                const totalPrice = computed(() => {
                    return cartItems.reduce((total, item) => total + (item.quantity * item.price), 0);
                });

                const increaseQuantity = (index) => {
                    cartItems[index].quantity++;
                };

                const decreaseQuantity = (index) => {
                    if (cartItems[index].quantity > 0) {
                        cartItems[index].quantity--;
                    }
                };

                return {
                    cartItems,
                    totalItems,
                    totalPrice,
                    increaseQuantity,
                    decreaseQuantity
                };
            }
        }).mount('#app');
    </script>
</body>

</html>
相关推荐
leluckys3 分钟前
flutter 专题十七 Flutter Flar动画实战
前端·flutter
豆包MarsCode19 分钟前
我用豆包MarsCode IDE 做了一个 CSS 权重小组件
开发语言·前端·javascript·css·ide·html
22x艾克斯28 分钟前
Web Notifications API-让网页也能像QQ一样实现消息通知
前端
X 西安30 分钟前
第十章JavaScript的应用
开发语言·javascript·ecmascript
想你的风吹到了瑞士36 分钟前
变量提升&函数提升
前端·javascript·vue.js
生椰拿铁You1 小时前
12 —— Webpack中向前端注入环境变量
前端
Huazzi.1 小时前
免费好用的静态网页托管平台全面对比介绍
前端·网络·github·web
吃土少女古拉拉1 小时前
前端和后端
前端·学习笔记
夫琅禾费米线1 小时前
leetcode2650. 设计可取消函数 generator和Promise
开发语言·javascript·leetcode·ecmascript
寒雒2 小时前
【Python】实战:实现GUI登录界面
开发语言·前端·python