当前位置: 首页 > article >正文

vue——v-model,computed,watch(内含项目实战)

目录

双向数据绑定指令v-model

文本输入框的双向数据绑定

 单选框的双向数据绑定

 复选框的双向数据绑定

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

下拉单选按钮的双向数据绑定

 计算属性 computed

方法调用

 调用计算属性

响应式数据更新

数据变动侦听器watch

 侦听 基本类型 的数据变动

 侦听对象类型的数据变动

 知识扩展

解决方法

实战项目:购物车

 v-for 遍历商品项

 增加商品数

  减少商品数

计算商品总数

 计算商品总价

完整代码参考


双向数据绑定指令v-model

表单元素的值与Vue实例中的数据进行绑定,当vue内的数据发生改变时, 网页视图会自动更新;当用户手动更改 input 的值, 数据也会自动更新。 (v-model只能用于表单元素)

文本输入框的双向数据绑定

对于单个文本输入框, v-model 绑定的是 input 输入框元素的 value 属性(即默认填充的文本值)

         <p>文本框:{{ test.word }}</p>
       <input type="text" v-model="test.word">


         const test=reactive({
                    word:"",
                });

 单选框的双向数据绑定

对于多个单选框, v-model 绑定的是 input 单选元素组的选中的值value

       你的爱好是: <ins>{{ test.radio }}</ins><br>
       <input type="radio" value="健身" v-model="test.radio" name="hobby">健身
       <input type="radio" value="阅读" v-model="test.radio" name="hobby">阅读
       <input type="radio" value="旅游" v-model="test.radio" name="hobby">旅游


        const test=reactive({
                    radio:"",
                });

 网页勾选选项后 的内部数据更新

 复选框的双向数据绑定

对于多个复选框, v-model 绑定的是 input 复选元素组的选中的值value

你的爱好是: <ins>{{ test.checkbox }}</ins><br>
           <input type="checkbox" value="健身" v-model="test.checkbox">健身
           <input type="checkbox" value="阅读" v-model="test.checkbox">阅读
           <input type="checkbox" value="旅游" v-model="test.checkbox">旅游

            const test=reactive({
                    checkbox:[],
                });

网页勾选选项后 的内部数据更新 

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

对于单个复选框, v-model 绑定的是单个 input 复选按钮的是否被选中的布尔值

            需要记住密码? <ins>{{ test.checkbox }}</ins><br>
            <input type="checkbox"v-model="test.checkbox">需要记住密码


            const test=reactive({
                    checkbox:false,
                });

下拉单选按钮的双向数据绑定

对于 <select>, v-model 绑定的是 select 元素众多选项中被选中的值value

    <p>您的专业是:{{test.select}}</p>
        <select v-model=" test.select">
            <option value="计算机">计算机</option>
            <option value="数字媒体">数字媒体</option>
            <option value="护理">护理</option>
            <option value="中医">中医</option>
        </select>

        const test=reactive({
                    select:[],
                });

网页勾选选项后 的内部数据更新  

 计算属性 computed

计算属性通过对已有属性进行计算而得到新的属性值,其基于它的响应式依赖进行缓存的,只有当计算属性的相关响应式依赖发生改变时才会更新值

方法调用

使用方法调用时,程序会不厌其烦的重复执行 getter 函数来获取计算结果,这就意味着需要不止一次这样的计算,随着次数的增多非常耗性能,网页的负担也越大。

解析:此代码中执行了两次 getter 函数

        <p>{{ updateTest() }}</p>
        <p>{{ updateTest() }}</p>

            const test=reactive({
                    x:20,
                    y:30
                })
                
                const updateTest=()=>{
                    console.log("调用普通的方法updateTest,不做缓存")
                    return test.x+test.y
                }

方法调用

 调用计算属性

使用计算属性可以避免像调用方法调用一样的问题,两种方式在结果上确实是相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存(也就是说程序会帮你将这个被调用函数的计算结果进行缓存,当你需要时,程序会立即返回先前缓存的计算结果,就不用 重复执行 getter 函数)只有当响应式依赖的值更新时才重新计算

解析:此代码中执行了一次 getter 函数,之后计算结果被缓存,直到响应式数据被更新,否则不会执行getter 函数

       //计算属性为属性,所以后面无需像调用方法那样跟上括弧
         <p>{{ updateTest_1}}</p>
        <p>{{ updateTest_1 }}</p>

         const test=reactive({
                    x:20,
                    y:30
                })
            const updateTest_1=computed(()=>{
                    console.log("调用带计算属性的方法updateTest_1,并缓存起来")
                    return test.x-test.y
                })

 调用计算属性 结果 

响应式数据更新

不同于方法调用的是,计算属性是基于它的依赖来进行缓存的,只有依赖的属性发生变化时才会重新计算,否则会直接返回缓存的值

方法调用——响应式数据更新

解析:由于响应式被数据更新,所以再次执行两次getter 函数;此代码中共执行了四次getter 函数,

        <p>{{ updateTest() }}</p>
        <p>{{ updateTest() }}</p>

        x <input type="text" v-model.number="test.x"> <br>
        <p>您输入的x值为 {{test.x}}, 类型为 {{typeof test.x}}</p>

        const test=reactive({
                    x:20,
                    y:30
                })
                
                const updateTest=()=>{
                    console.log("调用普通的方法updateTest,不做缓存")
                    return test.x+test.y
                }

 方法调用- 响应式数据发生变动

 计算属性——响应式数据更新

由于响应式数据被更新,计算属性也随之更新,这里执行了一次getter 函数;此代码中执行了两次getter 函数,其余为缓存

        <p>{{ updateTest_1}}</p>
        <p>{{ updateTest_1 }}</p>

        y <input type="text" v-model.number="test.y"> <br>
        <p>您输入的y值为 {{test.y}}, 类型为 {{typeof test.y}}</p>

        const test=reactive({
                    x:20,
                    y:30
                })

        const updateTest_1=computed(()=>{
                    console.log("调用带计算属性的方法updateTest_1,并缓存起来")
                    return test.x-test.y
                })

 

计算属性 数据发生变动 

数据变动侦听器watch

用于监测页面上的数据变化,当数据发生改变时,可以更新页面内容、触发其他操作或发送请求等

 侦听 基本类型 的数据变动

watch侦听hobby属性的变化。当hobby发生改变时,会执行回调函数(newValue,oldValue),打印出变化的内容;这里嵌套了 if条件,如果条件满足则往下执行,反之则停止。

        爱好
        <br>
        <select v-model="hobby">
            <option value="请选择">请选择</option>
            <option value="健身">健身</option>
            <option value="旅游">旅游</option>
            <option value="阅读">阅读</option>
        </select>

         const hobby = ref("请选择") 


                watch(hobby,function(newValue,oldValue){

                    console.log("hobby的旧值:", oldValue, " --> hobby的新值:", newValue);

                        if(newValue=="健身"){
                            console.log("你的爱好是健身");
                        }
                    }
                )

 打印出 watch监听  变化的内容

 侦听对象类型的数据变动

        血型信息收集:
        <br>
        <select v-model="test.blood">
            <option value="请选择">请选择</option>
            <option value="A型">A型</option>
            <option value="B型">B型</option>
            <option value="AB型">AB型</option>
        </select>

        <select v-model="test.sex">
            <option value="请选择">请选择</option>
            <option value="男">男</option>
            <option value="女">女</option>
        </select>

        const test= reactive({
                    blood:"请选择",
                    sex:"请选择",
                });

                watch(test,
                function(newValue,oldValue){

                    console.log("test的旧值:", oldValue, " --> test的新值:", newValue);
                    }
                )

侦听对象类型的数据变动 

 注意:在这里`newValue` 此处和 `oldValue` 是相同的,因为它们是同一个对象`test`

以上实验失败原因:在JS中,对象及数组内的值是靠引用来获取的,当修改对象或数组的值时, 实际上修改的并不是值而是对象或数组的引用;如果修改了对象或数组的值,那么打印出来的结果则是修改后的值。

 知识扩展

watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:

单个ref

                const x=ref("0")
                
                //单个ref
                watch (x,(newValue,oldValue)=>{
                    console.log("新值:"+newValue,"旧值:"+oldValue)
                })

响应式对象

注:不能直接侦听响应式对象的属性值(如下)

        血型
        <br>
        <input type="text" v-model="test.blood">

         const test= reactive({
                    blood:"A型",
                
                });

                watch(test.blood,
                function(newValue,oldValue){
                    console.log("test的旧值:", oldValue, " --> test的新值:", newValue);
                    }
                )

直接侦听响应式对象的属性值 结果

那么如何 侦听响应式对象

需要用一个返回该属性的 getter 函数(    ()=>test.blood)

         watch( ()=>test.blood,
                function(newValue,oldValue){
                    console.log("test的旧值:", oldValue, " --> test的新值:", newValue);
                    }
                )

添加getter 函数 侦听响应式对象 

解决方法

我们可以添加一个返回该属性的 getter 函数来获取 对象“test” 内  “blood”  的值,这样就可以监听到对象类型的数据变动


                watch( ()=>test.blood,
                function(newValue,oldValue){
                    console.log("test的旧值:", oldValue, " --> test的新值:", newValue);
                    }
                )

实战项目:购物车

用以上知识做一个购物车(如下)

屏幕录制 2024-11-26 184424

 以下为项目初始代码,快来做做看吧!

<!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">
        <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>

    <script type="module">

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

        createApp({
            setup() {

                // 1.定义属性:存储商品的(响应式)数组 
                const cartItems = reactive([
                    { name: '苹果', quantity: 1, unit_price: 1 },
                    { name: '香蕉', quantity: 1, unit_price: 1 },
                    { name: '菠萝', quantity: 1, unit_price: 1 },
                ]);

                // 2.定义方法:增加商品数
                

                // 3.定义方法:减少商品数
               

                // 4.定义方法:计算商品总数
               

                // 5.定义方法:计算商品总价
               


                // 6.暴露属性和方法
              
            },
        }).mount('#app');
    </script>
</body>
</html>

 v-for 遍历商品项

 利用v-for 来获取对象内的值,即使对象内的值更新,v-for也会随之更新。这里用v-for 遍历数组cartItems 内的值和索引。

<div class="cart-item" v-for="(value,index) in cartItems">

        <div class="buttons">
//此代码中根据索引将数组内的商品名单拎出来,根据索引对应到各自的商品项,
//即当v-for遍历到索引为0的数组值时,此代码内的索引也为0,
//那么单拎出来的商品名为“苹果”,以此类推
            <span>{{ cartItems[index].name }}&nbsp;&nbsp;</span>

            <button>-</button>
            <span class="quantity">1 &nbsp;&nbsp;</span>
            <button>+</button>
            <p>
            请输入价格:
            <input type="text"/> 元/斤 <br> 
            单价:
            1 元/斤
            </p>
        </div>


//遍历属性内的值
const cartItems = reactive([
                    { name: '苹果', quantity: 1, unit_price: 1 },
                    { name: '香蕉', quantity: 1, unit_price: 1 },
                    { name: '菠萝', quantity: 1, unit_price: 1 },
                ]);

 当属性内的值更新时

const cartItems = reactive([
                    { name: '苹果', quantity: 1, unit_price: 1 },
                    { name: '香蕉', quantity: 1, unit_price: 1 },
                    { name: '菠萝', quantity: 1, unit_price: 1 },
                    { name: '榴莲', quantity: 1, unit_price: 1 },
                ]);

 增加商品数

先定义add来实现商品数的增加,然后用v-on监听鼠标事件,事件被触发时,会执行指定的方法。

由于有多个商品项增加按钮,所以我们可以通过调用v-for 的索引来区分,增加的商品数为哪个商品。

(即当用户点击增加苹果商品数时,此时鼠标监听到的索引(index)为“ 0 ”,那么对应的增加的商品数量为 cartItems[0].quantity 的)

<button @click="add(index)">+</button>

// 2.定义方法:增加商品数
 const add=(index)=>{
                    cartItems[index].quantity+=1
                }

  减少商品数

和增加商品数一样,先定义nagtive来实现商品数的增加,然后用v-on监听鼠标事件,事件被触发时,会执行指定的方法。


<button @click="nagtive(index)">-</button>

// 3.定义方法:减少商品数
const nagtive=(index)=>{
                    cartItems[index].quantity-=1
                }

当商品数减到0,商品数无法再往下减,我们可以使其在一个停止不减的状态。当商品数<=0时,使商品数等于0,这样商品数的最低值为0(也可以弄一个弹窗,提示用户)

  // 3.定义方法:减少商品数                  
            const nagtive=(index)=>{
                    cartItems[index].quantity-=1
                    if(cartItems[index].quantity<=0){
                        cartItems[index].quantity=0 
                //alert("你的购物车已为空!!")                       
                    }
                }

计算商品总数

解析:用计算属性或数据变动侦听器,跟踪商品数的变化,进而求出总数

先定义count来实现计算商品总数,先命名一个常量total_count 为0,便于计算商品总数;计算前我们需要得到各商品的商品数变化,利用 for 将商品项(cartItems)遍历一遍,得到各个商品项的商品数,计入常量total_count,再返回total_count计算结果

//将计算结果渲染出来
<h3>商品总数:  <ins > {{count}} </ins> 件</h3>

// 4.定义方法:计算商品总数
               const count=computed(()=>{
新常量,用于承载 各个商品项数量
                let total_count=0;
item_count 为新常量,用于装for所遍历的值
                for(const item_count of cartItems){
每一次遍历的商品项数量都计入 常量total_count
                    total_count +=item_count.quantity;
                }
返回计算结果
                return total_count
               })

 计算商品总价

解析:用计算属性或数据变动侦听器,跟踪商品数和单价的变化,进而求出总价

先定义money来实现计算商品总数,先命名一个常量total_money为0,便于计算商品总价;计算前我们需要得到各商品的商品数变化以及商品单价变化,首先用v-model 来得到输入的商品单价;其次,利用 for 将商品项(cartItems)遍历一遍,得到每个商品项变化后的商品数量以及商品单价;最后将每个商品项的单价和商品数相乘,最后计入常量total_money,再返回total_money计算结果

v-mode 获取数据
请输入价格:
            <input type="text" v-model="cartItems[index].unit_price"/> 元/斤 <br> 
            单价:
            1 元/斤
渲染计算结果
<h3>商品总价:  <ins > {{money}} </ins> 元</h3>


// 5.定义方法:计算商品总价
               const money=computed(()=>{
                let total_money=0;
                for(const item_money of cartItems){
                    total_money+=item_money.quantity*item_money.unit_price;
                }
                return total_money
               })

完整代码参考

<!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 遍历商品项的 索引和值
    <div class="cart-item" v-for="(value,index) in cartItems">
        <div class="buttons">
渲染商品名
            <span>{{ cartItems[index].name }}&nbsp;&nbsp;</span>
v-on 鼠标监听触发 增加商品数
            <button @click="nagtive(index)">-</button>
渲染 商品项的数量
            <span class="quantity">{{ cartItems[index].quantity }} &nbsp;&nbsp;</span>
v-on 鼠标监听触发 减少商品数
            <button @click="add(index)">+</button>
            <p>
v-model 获取数据更改
            请输入价格:
            <input type="text" v-model="cartItems[index].unit_price"/> 元/斤 <br> 
            单价:
            1 元/斤
            </p>
        </div>
    </div>


渲染 计算属性计算结果
    <h3>商品总数:  <ins > {{count}} </ins> 件</h3>
    <h3>商品总价:  <ins > {{money}} </ins> 元</h3>


    </div>

    <script type="module">

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

        createApp({
            setup() {

                // 1.定义属性:存储商品的(响应式)数组 
                const cartItems = reactive([
                    { name: '苹果', quantity: 1, unit_price: 1 },
                    { name: '香蕉', quantity: 1, unit_price: 1 },
                    { name: '菠萝', quantity: 1, unit_price: 1 },
                ]);

                // 2.定义方法:增加商品数
                const add=(index)=>{
                    cartItems[index].quantity+=1
                }

                // 3.定义方法:减少商品数
                const nagtive=(index)=>{
                    cartItems[index].quantity-=1
                    if(cartItems[index].quantity<=0){
                        cartItems[index].quantity=0
                        
                    }
                }

                // 4.定义方法:计算商品总数
               const count=computed(()=>{
                let total_count=0;
                for(const item_count of cartItems){
                    total_count +=item_count.quantity;
                }
                return total_count
               })

                // 5.定义方法:计算商品总价
               const money=computed(()=>{
                let total_money=0;
                for(const item_money of cartItems){
                    total_money+=item_money.quantity*item_money.unit_price;
                }
                return total_money
               })


                // 6.暴露属性和方法
                return{
                    cartItems,
                    add,
                    nagtive,
                    count,
                    money,
                }
              
            },
        }).mount('#app');
    </script>
</body>
</html>


http://www.kler.cn/a/413659.html

相关文章:

  • 速度革命:esbuild如何改变前端构建游戏 (1)
  • 鸿蒙主流路由详解
  • Python网络爬虫基础
  • 【Linux】线程的互斥和同步
  • k8s 架构详解
  • TCP/IP协议攻击与防范
  • Bitcoin---P2SH;P2SH举例;P2SH的局限性
  • 走出“ICU”,小鹏汽车低价回血
  • 速盾:ddos防御手段哪种比较好?高防cdn怎么样?
  • spring声明式事务源码详解
  • springboot+redis+lua脚本实现滑动窗口限流
  • 追加docker已运行容器添加或修改端口映射方法
  • 可视化绘图技巧100篇基础篇(十)-堆叠条形图(stacked bar)(二)
  • Unity-Lightmap入门篇
  • Mongodb入门到放弃
  • vue3 开发利器——unplugin-auto-import
  • React Hooks中use的细节
  • 【探寻密码的奥秘】-001:解开密码的神秘面纱
  • Spring MVC:原理、配置与基础应用详解
  • 【人工智能】Python常用库-Scikit-learn常用方法教程
  • 泷羽sec学习打卡-shell命令2
  • 【webrtc】 mediasoup中m77的IntervalBudget及其在AlrDetector的应用
  • Day3 苍穹外卖项目 公共字段自动填充(AOP)、文件上传、新增菜品、菜品分页查询、删除菜品、修改菜品
  • 【linux】手搓线程池
  • 彻底理解微服务的作用和解决方案
  • JS听到了替罪的回响