3、vue中的watch监听器,filter过滤器以及computed计算属性

一、watch(监听器)

1、1浅监听

  • 作用:监听data数据的变化
  • 语法:
 new Vue({
   watch:{
      name(newValue,oldVaule){
       //oldVaule:旧值,改变之前的值
       //newValue:新值,改变之后的值
       //业务代码
     }
   }
})
  • 案例一:音乐列表
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
    <style>
        .box{
            margin: 20px;
            padding: 20px;
            border: 2px solid orangered;
        }
        .box span{
            display: inline-block;
            margin: 10px;
            padding: 10px;
            background: teal;
            color: #fff;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="app">
        <input type="text" v-model="kw">
     
        <div class="box" v-show="kw=='' ">
            <span v-for="(item,index) in hots" :key="index" @click=" kw=item ">{{item}}</span>
        </div>
    </div>
    <script>
        let vue = new Vue({
            el:"#app",
            data:{
                kw:"", //搜索关键字
                hots:["自由自在","最炫民族风","狼的诱惑","套马杆的汉子"]
            },
            methods:{},
            //监听数据的变化
            watch:{
                kw(newValue){
                    //数据变了,需要做一些开销比较大的操作,ajax
                    console.log("发起ajax请求,关键字:"+ newValue);
                }
            }
        })      
    </script>
</body>
</html>
  • 案例二----百度搜索案例
    实现原理:
    1.json的原理
    创建script标签:通过script标签的src发起请求:script标签添加到页面通过回调函数接收返回的结果
    2.布局,初始化
    3.监听输入框内容的变化,变化就发起请求 watch
    4.jsonp的方式发起请求
    5.获取到数据后,改变vue的 arr的值
    6.删除的过程中,如果没有数据发请求报错,加判断
    7.上下箭头
  • 代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
    <style>
        .active{
            background:#ccc
        }
    </style>
</head>
<body>
    <div id="app">
        <p>请输入搜索内容:<input type="text" v-model="wd" @keydown.down="downFun" @keydown.up="upFun" @keydown.enter="enterFun"></p>
        <ul>
            <li  v-for="(item,index) in arr" :key="item" :class="[index==n? 'active':'']">{{item}}</li>
        </ul>
    </div>
    <script>
       let vue = new Vue({
            el:"#app",
            //1.初始化数据
            data:{
                wd:"", //关键字
                arr:[],//搜索到的内容数组
                n:0, //样式的初始化下标
            },
            methods:{
                //6.下箭头
                downFun(){
                    this.n++;
                    if(this.n >= this.arr.length){
                        this.n = 0;
                    }
                },
                //7.上箭头
                upFun(){
                    this.n--;
                    if(this.n < 0){
                        this.n = this.arr.length -1 ;
                    }
                },
                //8.回车
                enterFun(){
                    window.open('https://www.baidu.com/s?wd='+this.arr[this.n])
                }
            },
            //2.监听数据的变化
            watch:{
                wd(){
                    //5.判断,有值,才发起请求
                    if(this.wd == ""){
                        this.arr = [];
                        return;
                    }

                    //3.发起请求,请求后端数据  jsonp
                    let os = document.createElement("script");
                    os.src = 'http://suggestion.baidu.com/su?cb=callback&wd='+this.wd;
                    document.body.appendChild(os);
                }
            }
        })

        //回调必须是全局函数
        function callback(res){
            //4.改变vue实例的arr的值,数据变了,视图也会跟着变化
            vue.arr = res.s;
        }
    </script>
</body>
</html>

1、2深度监听

  • 作用:监听data中引用数据的变化(数组,对象)
  • 语法
new Vue({
   watch:{
   //浅监听
    监听的属性名(newValue,oldVaule){
    //oldVaule:旧值,改变之前的值
    //newValue:新值,改变之后的值
    //业务代码
    },
    监听的属性名:{
    handler(new,old){
     //数据变化了调用的函数
    },
    deep:true/false //如果deep的值为true深度监听
   }
  }
})
  • 案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <div id="app">
        <div>引用数据类型(对象,数组):{{user}}</div>
        <button @click="change1">改变user</button>
        <div>基本数据类型(strng,number,boolean):{{name}}</div>
        <button @click="change2">改变name</button>
    </div>
    <script>
        new Vue({
            el:"#app",
            data:{
                name:"吃米饭",
                user:{
                    username:"xiaoming"
                }
            },
            methods:{
                //改变user
                change1(){
                    this.user.username = "daming";
                    //可以使用事件代替深度监听
                },
                //改变NAME
                change2(){
                    this.name = "吃面e"
                }
            },
            watch:{
                //浅监听
                // name(){}
                name:{
                    handler(){
                        console.log("name改变了");
                    },
                    deep:false
                },
                //如果监听对象、数组,需要使用深度监听
                //不建议使用深度监听,如果有过多的深度监听会造成页面的卡顿,建议使用事件代替深度监听
                user:{
                    handler(){
                        console.log("user改变了");
                    },
                    deep:true
                }
            }
        })
    </script>
</body>
</html>
watch:监听data属性的变化
浅监听:
    watch:{
        属性名(new,old){

        }
            
如果监听的是引用数据类型,浅监听不能监听到数据的变化
深度监听:
 watch:{
    监听的属性名:{
        handler(new,old){
            
        },
        deep:true
    }
 }
 不建议使用深度监听,过多的深度监听会造成页面的卡顿,建议使用事件代替

1、3总结

  • 不需要调用,自动调用
  • 监听数据的变化,可以异步,开销比较大的,watch都可以做
  • 如果监听的数据是数组或者对象,需要使用深度监听,不建议使用深度监听,过多的深度监听会造成页面的卡顿,建议使用事件代替深度监听

二、filter(过滤器)

2、1局部过滤器

  • 作用:转换数据
大部分情况,后端返回给我们的数据,都可以直接渲染,但是有些时候,返回的数据我们需要进行二次操作
例:
手机号:13112345678 --> 131****5678
时间:1696657349946 --> 2023/10/07
  • 语法
new Vue({
  filters:{
    过滤器名称(要过滤的内容,调用过滤器传递的参数){
        return 返回转换好的数据结果
    }
 }
})
  • 使用
<p>
     {{ 要转换的数据 | 过滤器名称 }}
     {{ 要转换的数据 | 过滤器名称(过滤器参数) }}
</p>
    |管道符
  • 案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
    <style>
        li{
            display: inline-block;
            padding: 20px;
            margin: 20px;
            border: 1px solid orangered;
        }
    </style>
</head>
<body>
    <div id="app">
        <!-- 过滤器使用   需要过滤的数据 | 过滤器名称 -->
        <ul>
            <li v-for="item in goods" :key="item.id">
                <p>商品名称:{{item.name}}</p>
                <!-- <p>商品价格:{{item.price | filterTwo(2)}}</p> -->
                <p>商品价格:{{item.price | filterTwo}}</p>
                <p>商品数量:{{item.num}}</p>
                <p>商品总价:{{item.price * item.num | filterTwo(2)}}</p>
                <p>用户电话:{{item.tel | filterTel}}</p>               
            </li>
        </ul>
    </div>
    <div id="app2">
        <h1>电话号码:{{tel | filterTel}}</h1>
    </div>
    <script>
        //全局过滤器--要定义在所有vue实例之前,一次只能定义一个/
        //语法:Vue.filter('过滤器名称',(需要过滤的数据, 传递的参数)=>{})
        Vue.filter("filterTel",(tel)=>{
            return tel.slice(0,3) + "****" + tel.slice(7)
        })
        new Vue({
            el:"#app",
            data:{
                goods:[
                    {id:1,name:"荔枝",num:5,price:49,tel:'13112345678'},
                    {id:2,name:"葡萄",num:4,price:5.5,tel:'13112345678'},
                    {id:3,name:"柚子",num:3,price:8.99,tel:'13112345678'},
                    {id:4,name:"香蕉",num:2,price:3.45,tel:'13112345678'}
                ],
    
            },
            methods:{},
            //1.局部过滤器:只能在当前vue实例中使用
            filters:{
                /* 
                 过滤器名称(要过滤的内容,调用过滤器传递的参数){
                    return 返回转换好的数据结果
                 }
                */
                filterTwo(p,n=2){ //p:需要过滤的数据,不需要传递的
                    console.log(p,n);
                    //返回处理好的数据
                    return p.toFixed(n);
                },
                filterTel(tel){
                    //'13112345678'
                    return tel.slice(0,3) + "****" + tel.slice(7)
                }
            }
        })
        new Vue({
            el:"#app2",
            data:{
                tel:"15167893245"
            }
        })
        
   </script>
</body>
</html>
  • 注意:局部过滤器只能在当前的vue实例中使用,其他的vue实例中不能使用

2、2全局过滤器

  • 局部过滤器:只能在当前的vue实例中使用,其他的vue实例不能使用
  • 全局过滤器:在vue实例定义之前,所有vue实例都可以使用
  • 语法
Vue.filter("过滤器名称",(需要过滤的数据,参数)=>{ return 处理好的数据 })
//全局过滤器--要定义在所有vue实例之前,一次只能定义一个/
  • 注意:全局过滤器要定义在所有vue实例之前
  • 案例
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   <script src="./vue.js"></script>
</head>
<body>
   <div id="app">
       <h3>新闻</h3>
       <ul>
           <li  v-for="item in news" :key="item.id">
               <p>城市:{{item.city}}</p>
               <p>新闻:{{item.news}}</p>
               <p>日期:{{item.time | filterTime}}</p>
           </li>
       </ul>
   </div>
   <script>
       //定义全局过滤器
       Vue.filter("filterTime",(time)=>{
           //转换某种时间格式 == 创建时间对象,获取对应的年月日,拼接
           let oDate = new Date(time*1); //new Date("2023-10-7") new Date(2023,10,7),new Date(时间戳)
           let y = oDate.getFullYear();
           let m = (oDate.getMonth()+1+"").padStart(2,'0');
           let d = (oDate.getDate()+"").padStart(2,'0');
           let h = (oDate.getHours()+"").padStart(2,'0');
           let mi = (oDate.getMinutes()+"").padStart(2,'0');
           let s = (oDate.getSeconds()+"").padStart(2,'0');
           return `${y}-${m}-${d} ${h}:${mi}:${s}`
       })

       new Vue({
           el:"#app",
           data:{
               news:[
                   {id:1,city:"杭州",news:"杭州亚运会",time:"1696657349946"},
                   {id:2,city:"北京",news:"北京哈哈哈",time:"1696659454316"},
                   {id:3,city:"上海",news:"上海呵呵呵",time:"1697657349946"}
               ]
           },
           methods:{},
       })
   </script>
</body>
</html>

三、computed(计算属性)

3、1 computed基础使用

  • 作用:经过计算得到的属性
  • 语法
  new Vue({
      computed:{
        属性名(){
          //计算逻辑return 结果
       }
     }
  })
  • 案例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <div id="app">
        <table width="500" border="1">
            <thead>
                <tr>
                    <th>编号</th>
                    <th>姓名</th>
                    <th>成绩</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="item in students" :key="item.id">
                    <td>{{item.id}}</td>
                    <td>{{item.name}}</td>
                    <td><input type="text" v-model.number="item.score"></td>
                </tr>
                <tr>
                    <td>平均成绩</td>
                    <td colspan="2">{{avagScore}}</td>
                </tr>
            </tbody>
        </table>

        <h4>平均成绩方法:{{getAvag()}}</h4>
        <h4>平均成绩方法:{{getAvag()}}</h4>
        <h4>平均成绩方法:{{getAvag()}}</h4>
        <h4>平均成绩方法:{{getAvag()}}</h4>
        <h4>方法-调用一次计算一次</h4>
        
        <h4>平均成绩计算属性:{{avagScore}}</h4>
        <h4>平均成绩计算属性:{{avagScore}}</h4>
        <h4>平均成绩计算属性:{{avagScore}}</h4>
        <h4>平均成绩计算属性:{{avagScore}}</h4>
        <h4>计算属性-有缓存,数据发生了变化才会重新调用计算</h4>
    </div>
    <script>
        new Vue({
            el: "#app",
            data: {
                students: [
                    { id: 1, name: "刘备", score: 99 },
                    { id: 2, name: "诸葛亮", score: 100 },
                    { id: 3, name: "孙策", score: 50 },
                    { id: 4, name: "司马懿", score: 85 },
                    { id: 5, name: "小乔", score: 70 }
                ],
            },
            methods: {
                getAvag() {
                    console.log("通过方法计算");
                    //计算平均值
                    let sum = 0;
                    this.students.forEach(value => {
                        sum += value.score;
                    });
                    //返回平均成绩
                    return (sum / this.students.length).toFixed(2)
                }
            },
            //mounted(){}  页面一渲染完成就需要去计算成绩
            //计算属性:需要计算后得到的值,就会使用计算属性
            //依赖数据:如果数据发生变化会重新计算
            computed: {
                //属性名(){  计算  return 计算结果 }
                avagScore() {
                    console.log("计算属性");
                    //计算平均值
                    let sum = 0;
                    this.students.forEach(value => {
                        sum += value.score;
                    });
                    //返回平均成绩
                    return (sum / this.students.length).toFixed(2)
                }
            }
        })
    </script>
</body>
</html>
  • 注意:计算属性有缓存,数据发生了变化才会重新调用计算

3、2 总结

  • 要调用,当作普通的属性使用{{name}},不加()
  • 依赖数据,依赖的数据发生变化会重新调用计算
  • 做同步操作
  • 计算属性和method的区别?
 相同点:它们的结果都是通过依赖的数据决定的,当依赖的数据发生变化都会重新计算
 不同点:
     计算属性有缓存效果,不管调用多少次,只要数据没变都会应用第一次的计算结果
     方法是调用几次计算几次
  • computed和watch的区别
  wacth一对一,监听一个属性的变化,去执行多个事件,异步操作
  computed多对一,即可以依赖多个数据,结果却为一个,同步操作

3、3 购物车案例

  • 效果图
    在这里插入图片描述
  • 代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
    <style>
        img {
            width: 100px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div id="app" class="container">
        <!-- 1.布局 -->
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th><input type="checkbox"  @change="changeAll" v-model="isAll"> 全选</th>
                    <th>商品信息</th>
                    <th>商品单价</th>
                    <th>商品数量</th>
                    <th>商品小计</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(item,index) in goods" :key="item.id">
                    <td><input type="checkbox" v-model="item.isChecked" @change="changeOne"></td>
                    <td>
                        <strong>{{item.name}}</strong>
                        <img :src="item.img" alt="">
                    </td>
                    <td>{{item.price}}</td>
                    <td>
                        <button type="button" class="btn btn-defalut" @click="sub(index)">-</button>
                        {{item.num}}
                        <button type="button" class="btn btn-primary" @click="add(index)">+</button>
                    </td>
                    <td>{{item.price*item.num}}</td>
                    <td><button type="button" class="btn btn-danger" @click="delItem(index)">删除</button></td>
                </tr>
                <tr>
                    <td>总计:</td>
                    <td colspan="5">{{total}}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <script>
        //10.全局过滤器,保留2位小数
        // 后端的数据
        let mock = [
            {
                id: 1,
                img: "https://img10.360buyimg.com/n7/jfs/t1/116325/12/33822/74207/642ec4caF5b53c8a7/8c548ca792f592fc.jpg",
                name: "手机",
                price: 2999.9,
                num: 3
            },
            {
                id: 2,
                img: "https://img12.360buyimg.com/n7/jfs/t1/72688/18/26431/128682/6433e218F83aa27a3/6e79bf181755bf44.jpg.avif",
                name: "电脑",
                price: 13145.9,
                num: 1
            },
            {
                id: 3,
                img: "https://img14.360buyimg.com/n7/jfs/t1/105370/22/32732/131023/646effdaFdff68a3f/ffeecdc7067016bd.jpg.avif",
                name: "ipad pro",
                price: 5999,
                num: 1
            }
        ]
        new Vue({
            el: "#app",
            data: {
                goods: [],    
                isAll:false           
            },
            methods: {
                //3.点击删除,删除对应的一项
                delItem(index){
                    this.goods.splice(index,1);
                },
                //4.点击+,数量+1
                add(index){                 
                    //判断,不能超过5
                    // this.goods[index].num < 5 ? ++this.goods[index].num : this.goods[index].num = 5
                    
                    //边界值判断 Math.min(4,5)
                    this.goods[index].num = Math.min(5,++this.goods[index].num);
                },
                //5.点击-,数量-1
                sub(index){    
                    //边界值判断 Math.min(4,5)
                    this.goods[index].num = Math.max(1,--this.goods[index].num);
                },
                //7.点击全选按钮,让所有的商品改变状态
                changeAll(){
                    // console.log("全选改变了",this.isAll);
                    //改变所有商品的选择状态,和全选一样
                    this.goods.forEach(value=>{
                        value.isChecked = this.isAll;
                    });
                    console.log(this.goods);
                },
                //8.点击商品的input,如果全选,全选按钮也需要选中
                changeOne(){
                    this.isAll = this.goods.every(value=>{
                        return value.isChecked == true
                    });
                }
            },
            //2.请求数据--挂载完成后开始请求数据
            mounted(){
                //发起ajax
                // this.goods = mock;
                // //6.给input标签一个checked属性,但是后端没有给,请求到数据后自己添加
                // this.goods.forEach(value=>{
                //     value.isChecked=false;
                // });
                // console.log(this.goods);

                //没有再data中定义,后期添加的属性没有响应式
                //先处理数据,然后再赋值给data中的属性,这样就是在data中定义的,就会有响应式
                mock.forEach(value=>{
                    value.isChecked=false;
                });
                this.goods = mock;
            },
            //9.计算总价
            computed:{
                total(){
                    //循环+
                    let sum = 0;
                    this.goods.forEach(value=>{
                        //选中的才+
                        if(value.isChecked){
                            sum += value.price * value.num;
                        }
                    });
                    return sum;
                }
            }
        })
    </script>
</body>
</html>
  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值