> 文章列表 > 20230415----重返学习-vue计算属性-watch监听器-vue过滤器-vue生命周期

20230415----重返学习-vue计算属性-watch监听器-vue过滤器-vue生命周期

20230415----重返学习-vue计算属性-watch监听器-vue过滤器-vue生命周期

day-050-fifty-20230415-vue计算属性-watch监听器-vue过滤器-vue生命周期

vue计算属性

  • computed 计算属性
    1. 可以计算的属性,写法是函数,使用时当做属性来使用,即类似于data中的值

    2. 函数运算后的结果(return),就是计算属性的值

    3. 多个值改变,影响一个结果的时候

      • 即多个data中的响应式数据变化,返回一个新的响应式数据的场景
    4. 逻辑简单

    5. 计算属性要求在data里面没有同名的属性

    6. 计算有缓存

      • 如果依赖的变量没有变化,那么它的值就会一直被缓存起来
        • this.数据变量名中this后面的值没有发生变动时,它就不会重复进行计算。
    7. 值与结果

      • 大多数情况:多个值改变,影响一个结果的时候
      • 极少数情况:1个值变化,影响到多个结果
        • 也就是用全写形式,用计算属性的set方法反向计算出来源值
    8. 全写形式:是一个对象,用get函数与set函数,用于在计算属性被修改时调用set函数

      computed:{fullName(){//简写---getreturn this.firstName+"--"+this.lastName;}
      }
      
      • 相当于
      computed:{fullName:{//全写get(){return this.firstName+"--"+this.lastName;},}
      }
      
      • 可扩展于在set中用于修改源头的data响应式属性
      computed:{fullName:{//全写get(){return this.firstName+"--"+this.lastName;},set(newvalue){let arr=newvalue.split("--");this.firstName=arr[0];this.lastName=arr[1];}}
      }
      
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><!-- computed 计算属性1.可以计算的属性,写法是函数,使用当做属性2. 函数运算后的结果(return),就是计算属性的值3.多个值改变,影响一个结果的时候4.逻辑简单5.计算属性data里面,没有6.计算有缓存7.大多数情况:多个值改变,影响一个结果的时候极少数情况:1个值变化,影响到多个结果 --><div id="app"><input type="text" v-model="firstName" />+<input type="text" v-model="lastName" />=<input type="text" v-model="fullName" /></div><script>var vm = new Vue({el: "#app",data: {firstName: "李",lastName: "丽丽",},computed: {// fullName(){//简写---get//    return this.firstName+"--"+this.lastName;// }fullName: {//全写get() {return this.firstName + "--" + this.lastName;},set(newvalue) {let arr = newvalue.split("--");this.firstName = arr[0];this.lastName = arr[1];},},},});</script></body>
</html>

购物车

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><style>* {margin: 0;padding: 0;}.box {box-sizing: border-box;margin: 0 auto;width: 480px;height: 590px;background-image: url(images/bg.png);}/* 按钮 */.minus,.add {display: inline-block;width: 52px;height: 44px;cursor: pointer;vertical-align: middle;background-image: url(images/sub.png);}.add {background-image: url(images/add.png);}/* 文本框 */.pronum,.info {box-sizing: border-box;width: 40px;height: 35px;line-height: 35px;background: white;border-radius: 3px;text-align: center;vertical-align: middle;}.pronum {display: inline-block;}/* 头部 */.top {padding: 50px 20px 0;height: 387px;}.hang {margin-bottom: 20px;display: flex;justify-content: space-between;align-items: flex-start;}.hang .info {box-sizing: border-box;padding: 0 10px;width: 260px;color: white;font-size: 14px;text-align: left;line-height: 35px;background: #171818;}.bottom {padding-top: 20px;color: #878787;padding-left: 50px;line-height: 40px;}</style></head><body><div id="app"><div class="box" id="computedBox"><div class="top"><div class="hang" v-for="item in list" :key="item.id"><i class="minus" @click="change(item.id,'minus')"></i><span class="pronum">{{item.count}}</span><i class="add" @click="change(item.id,'add')"></i><span class="info">单价:{{item.price}}元 &nbsp;&nbsp;小计:<em>{{(item.count*item.price).toFixed(2)}}</em></span></div></div><div class="bottom">商品合计:<span class="pronum">{{all.allCount}}</span> &nbsp;<br />共花费了:<span class="pronum">{{all.allCountPrice}}</span> &nbsp;<br />最贵商品单价:<span class="pronum">{{all.highPrice}}</span> &nbsp;</div></div></div><script>var vm = new Vue({el: "#app",data: {list: [{id: 0,count: 2,price: 20,},{id: 1,count: 4,price: 14,},{id: 3,count: 0,price: 10,},{id: 4,count: 0,price: 16,},{id: 5,count: 0,price: 22,},],},methods: {change(index, type) {//先找到,点击的那行数据let item = this.list.find((item) => item.id == index);//根据传递的参数,实现 加 减if (type == "minus") {//减if (item.count < 1) {item.count = 0;return;}item.count--;} else {//加item.count++;}},},computed: {// allCount(){//  // let result=this.list.reduce((res,item)=>{//     //     return res+item.count;//  // },0)//  let result=0;//  this.list.forEach((item)=>{//         result+=item.count;//  })//  return result;// },// allCountPrice(){//  let result=0;//  this.list.forEach((item)=>{//         result+=item.count*item.price;//  })//  return result;// },// highPrice(){//  let arr=[0];//初始值//  this.list.forEach((item)=>{//   if(item.count>0){//    arr.push(item.price);//   }//  })//  //如果是空数组,无法比大小,所以数组要设置初始值//  return Math.max(...arr);// },all() {let Cresult = 0;let Presult = 0;let arr = [0]; //初始值this.list.forEach((item) => {Cresult += item.count;Presult += item.count * item.price;if (item.count > 0) {arr.push(item.price);}});return {allCount: Cresult,allCountPrice: Presult,highPrice: Math.max(...arr),};},},});</script></body>
</html>

watch监听器

  • watch:监听器
    1. watch是一个对象,它内部具体要监听到的data响应式属性名的写法是函数

    2. 一个值变化,影响到多个结果

      • 也就是watch监听到的data响应式数据的值变化了,就会影响到其它值或方法的调用
    3. 逻辑介于 computed 和 methods 之间

      • 里面得出的结果一般比computed复杂,但比methods里的方法要来得简单
    4. 必须是data里面已有的

    5. 没有缓存

    6. 只有:一个值变化,影响到多个结果,无法反推

    7. 只有watch可以监控路由($route)

    8. 全写形式:是一个对象,用于watch监听器的具体属性

      • 之前的函数就写在handler属性里,可以用es的简写

        data: {fullName: "李-丽丽",
        },
        watch: {fullName(newValue,oldValue){//简写console.log(newValue,oldValue);}
        },
        
        watch: {fullName:{//全写,可以设置属性handler(newValue,oldValue){console.log(newValue,oldValue);},}
        },
        
      • immediate:true 是否打开页面就立刻监控1次

        data: {fullName: "李-丽丽",
        },
        watch: {fullName(newValue,oldValue){//简写console.log(newValue,oldValue);}
        },
        
        data: {fullName: "李-丽丽",
        },
        watch: {fullName:{//全写,可以设置属性handler(newValue,oldValue){console.log(newValue,oldValue);},immediate:true//打开页面,立刻监控1次}
        },
        
      • deep:true 是否深度监控,用于监控对象里面内容的变化

        data: {obj: {n: {a: 10,},},
        },
        watch: {obj(newValue, oldValue) {console.log(newValue, oldValue, "obj简写监控-不深层级");//只有obj.n变化时,不执行},"obj.n"(newValue, oldValue) {console.log(newValue, oldValue, "obj.n简写监控-不够深层级-只具体到对象");//只有obj.n变化时,不执行},"obj.n.a"(newValue, oldValue) {console.log(newValue, oldValue, "obj.n.a简写监控-深层级-具体到非对象值");//只有obj.n变化时,也执行},
        },
        
        data: {obj: {n: {a: 10,},},
        },
        watch: {obj: {handler(newValue, oldValue) {console.log(newValue, oldValue, "obj全写监控-深层级deep为true");//只有obj.n变化时,也执行},deep: true,//为true,就可以实现深度监听},
        },
        
        • 为true,就可以实现深度监听
    9. 如果要监听对象的某个具体属性,可以加引号

      watch: {"obj.n.a":{//全写,可以设置属性handler(newValue,oldValue){console.log(newValue,oldValue);}}},
      
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app"><input type="text" v-model="firstName" />+<input type="text" v-model="lastName" />=<input type="text" v-model="fullName" /><input type="text" v-model.number="obj.n.a" /></div><script>var vm = new Vue({el: "#app",data: {firstName: "李",lastName: "丽丽",fullName: "李-丽丽",obj: {n: {a: 10,},},},watch: {// fullName(newValue,oldValue){//简写//   //console.log(newValue,oldValue);//   let arr=newValue.split("-");//   this.firstName=arr[0];//   this.lastName=arr[1];// }// fullName:{//全写,可以设置属性//     handler(newValue,oldValue){//         console.log("111");//        let arr=newValue.split("-");//        this.firstName=arr[0];//        this.lastName=arr[1];//     },//     immediate:true//打开页面,立刻监控1次// }// obj:{//全写,可以设置属性//     handler(newValue,oldValue){//         console.log(newValue,oldValue);//     },//     deep:true//默认 无法深度监控,对象里面内容的变化//     //为true,就可以实现深度监听// }//复杂内容监控的时候,必须加引号// "obj.n.a": {//   //全写,可以设置属性//   handler(newValue, oldValue) {//     console.log(newValue, oldValue);//   },// },obj(newValue, oldValue) {console.log(newValue, oldValue, "obj简写监控-不深层级");//不执行},"obj.n"(newValue, oldValue) {console.log(newValue, oldValue, "obj.n简写监控-不够深层级-只具体到对象");//不执行},"obj.n.a"(newValue, oldValue) {console.log(newValue, oldValue, "obj.n.a简写监控-深层级-具体到非对象值");//执行},obj: {handler(newValue, oldValue) {console.log(newValue, oldValue, "obj全写监控-深层级deep为true");//执行},deep: true,//为true,就可以实现深度监听},},});</script></body>
</html>

watch与computed和methods之间的对比

  1. computed(多个值变化,影响到一个结果) 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。他虽然是函数但主要当作属性来使用,逻辑简单。
  2. methods方法表示一个具体的操作,主要书写业务逻辑,是函数并且以函数的方式来调用。
  3. watch(一个值变化,影响到多个结果改变)一个对象,键是需要观察的表达式,值是对应回调函数。
    • 主要用来监听某些特定数据的变化(watch监控路由对象‘$route’),从而进行某些具体的业务逻辑操作;
      • 以及在对应数据变化进而触发函数时,在内部调用异步函数。
    • 可以看作是computedmethods的结合体;
  • computed是属性调用,而methods是函数调用
  • computed带有缓存功能,而methods不是
  • computed可以监控对象,而methods不是
  • watch可以监控对象,而methods不是
    • watch和computed均可以监控程序员想要监控的对象,当这些对象发生改变之后,可以触发回调函数做一些逻辑处理。
      • watch监控自身属性变化(重新编译,性能差),只要调用就会调用回调。 vm.$watch()

      • watch监控路由对象(一个值变化,影响到多个结果改变),数据变化时来执行异步操作,这时watch是非常有用的。

      • 计算属性computed的特点(多个值变化,影响到一个结果)

        • 计算属性会依赖于他使用的data中的属性,
        • 只要是依赖的属性值有改变,则自动重新调用一下计算属性;
          • 如果他所依赖的这些属性值没有发生改变,那么计算属性的值是从缓存中来的,而不是重新编译,那么性能要高一些,所以vue中尽可能使用computed替代watch。
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app"><p>num1: {{num1}}</p><p>num2: {{num2}}</p><p>num3: {{num3}}</p><p>num1AddNum2Methods: {{num1AddNum2Methods()}}</p><p>num1AddNum2Computed: {{num1AddNum2Computed}}</p><p>num1AddNum2Watch: {{num1AddNum2Watch}}</p><button v-on:click="num1++">num1++</button><button v-on:click="num2++">num2++</button><button v-on:click="num3++">num3++</button></div><script>let vm = new Vue({el: "#app",data: {num1: 1,num2: 2,num3: 88,num1AddNum2Watch: 0,},computed: {num1AddNum2Computed() {console.log("num1AddNum2Computed-computed计算属性,依赖的值变动才调用一次");return this.num1 + this.num2;},},watch: {num1: {handler(value) {console.log("num1AddNum2Watch-num1-watch回调函数,监听的值变动就调用一次");this.num1AddNum2Watch = this.num1 + this.num2;},immediate: true, //打开页面,立刻监控1次},num2: {handler(value) {console.log("num1AddNum2Watch-num2-watch回调函数,监听的值变动就调用一次");this.num1AddNum2Watch = this.num1 + this.num2;},immediate: true, //打开页面,立刻监控1次},},methods: {num1AddNum2Methods() {//每次更新页面的时候就会执行console.log("num1AddNum2Methods-methods函数,页面刷新时就会触发");return this.num1 + this.num2;},},});</script></body>
</html>

vue过滤器

  • 过滤器使用场景:对数据进行统一处理,得到一个统一规范的结果

    • 第一个形参就是要进行过滤处理的数据

    • 第二及后面的形参,就是过滤器要传的参数

    • return 过滤器的新结果

      filters: {//局部过滤器currency(val, a = "¥", b = 2) {//val:过滤处理的数据//a:过滤器函数调用传递的第一个参数//b:过滤器函数调用传递的第二个参数return a + val.toFixed(b);},
      },
      
    • 可以使用过滤器的是 {{}} 和 v-bind,其余的指令基本都不可以

      {{msg|capitalize}}
      <h1 :title="msg|capitalize">111</h1>
      
  • 过滤器使用的方式为在值后面用|接上过滤器名称及传参

    {{n|currency}} 
    {{m|currency("$",3)}}
    
  • 可以给过滤器传参

    {{m|currency("$",3)}}
    
    <!DOCTYPE html>
    <html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app">{{n|currency}} {{m|currency("$",3)}} <script>var vm = new Vue({el: "#app",data: {n: 100,m: 200,msg: "hello",},filters: {//局部过滤器currency(val, a = "¥", b = 2) {//val:过滤处理的数据//a:过滤器函数调用传递的第一个参数//b:过滤器函数调用传递的第二个参数return a + val.toFixed(b);},},});</script></body>
    </html>
    
  • 全局过滤器与局部过滤器

    • 全局过滤器,项目中常用的方式。建议用全局函数代替。

      Vue.filter("capitalize",function(val){//全局过滤器let startNum=val.slice(0,1).toUpperCase();let endNum=val.slice(1);return startNum+endNum;
      })
      
    • 局部过滤器,只能在当前配置的vue实例中使用。

      var vm = new Vue({el: "#app",filters: {//局部过滤器currency(val, a = "¥", b = 2) {//val:过滤处理的数据//a:过滤器函数调用传递的第一个参数//b:过滤器函数调用传递的第二个参数return a + val.toFixed(b);},},
      });
      
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app">{{n|currency}} {{m|currency("$",3)}}{{msg|capitalize}}<h1 :title="msg|capitalize">111</h1></div><div class="appa">{{nn|currency}} {{mm|currency("$",3)}} {{msga|capitalize}}</div><script>Vue.filter("capitalize", function (val) {//全局过滤器let startNum = val.slice(0, 1).toUpperCase();let endNum = val.slice(1);return startNum + endNum;});var vm = new Vue({el: "#app",data: {n: 100,m: 200,msg: "hello",},filters: {//局部过滤器currency(val, a = "¥", b = 2) {//val:过滤处理的数据//a:过滤器函数调用传递的第一个参数//b:过滤器函数调用传递的第二个参数return a + val.toFixed(b);},},});var vma = new Vue({el: ".appa",data: {nn: 101,mm: 202,msga: "world",},});</script></body>
</html>

vue生命周期

  • 生命周期(钩子函数):
    1. 初始化阶段
      • beforeCreate:初始化之前,组件实例刚被创建,什么也没有,只有默认的生命周期和事件。
        • 默认的事件就是实例事件,如当前vue实例的
      • created:初始化之后,组件实例创建完成,初始化了 data 和 methods都赋值了, 有了el,但是没有赋值(一般用于发送ajax)
    2. 模板编译
      • beforeMount:模板编译之前,已经存在 虚拟DOM了,el这个时候赋值了,并没有渲染到页面上
      • mounted:模板编译之后, 虚拟DOM转化为真实DOM,并且渲染页面上
    3. 更新阶段(data 的数据修改的时候,会触发)
      • beforeUpdate:数据更新了,但是页面没有更新
      • updated:数据更新了,并且页面也更新了
    4. 销毁阶段(触发销毁 vm.$destory())
      • beforeDestroy :销毁之前,所有的功能和数据,methods 等,都可使用
      • destroyed:销毁之后,所有的都解除绑定,功能和属性,methods都无法使用,保持原来的样式,不在受vue的控制
    • 激活时
      • activated: keep-alive,组件被激活时
      • deactivated: keep-alive,组件被激活完成
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /></head><body><div id="app"><div>msg: {{msg}}</div><div>obj: {{obj}}</div><div>number: {{number}}</div><button @click="handleAdd">组件内number++</button></div></body><button id="addNumber">组件外number++</button><button id="handleDestroy">卸载vue组件</button>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>let vm = new Vue({name: "componentName",el: "#app",data: {msg: "data-msg-李",obj: {name: "lili",},number: 5,},methods: {handleAdd() {console.log(`vue实例内部this-->`, this.number, this);this.number++;},},beforeCreate() {console.log("初始化前beforeCreate: ", this, this.$attrs, [this.$nextTick,this.$emit,this.$slots,]); //已经可以访问this及它的一些实例方法如$nextTick及$attrs等了,但this里面的数据和方法还不能访问;// debugger;// console.log("beforeCreate---this.$el: ", this.$el); //还没生成,也没赋值;// console.log("beforeCreate---this.$data: ", this.$data); //还没生成,也没赋值;// console.log("beforeCreate---this.msg: ", this.msg); //还没生成,也没赋值;// 一般这个阶段用不着,一般也就不与模板有数据的ajax时用到,如统计类ajax、数据埋点、单纯地操作全局变量之类的。//this.obj["ageb"]=182;//报错},created() {console.log("初始化后created: ", this.$el); //已经生成,也没赋值;// console.log("created: ", this.$data); //已经生成,已经赋值;console.log("created: ", this.msg); //已经生成,已经赋值;// 一般用于发送与页面相关的ajax。this.obj["createdAge"] = 2;},beforeMount() {console.log("挂载前beforeMount: ", this.$el); //已经生成,就是el对应的模版变成了虚拟DOM对象了,已经赋值,但没把模版替换到页面上;this.obj["beforeMountAge"] = 3;},mounted() {console.log("挂载后mounted: ", this.$el);this.obj["mountedAge"] = 4;},beforeUpdate() {console.log("更新前beforeUpdate: ", this.number, this.$el.innerHTML);},updated() {console.log("更新后updated: ", this.number, this.$el.innerHTML);},beforeDestroy() {console.log("卸载前beforeDestroy: ", this.number);},destroyed() {console.log("卸载后destroyed: ", this.number, this.number);},});let addNumber = document.querySelector("#addNumber");addNumber.onclick = function () {// vm.number++;vm.handleAdd()console.log(`vue实例外部vm.number-->`, vm.number, vm);};let handleDestroy = document.querySelector("#handleDestroy");handleDestroy.onclick = function () {vm.$destroy();};
</script>

进阶参考

  1. 前端框架Vue中methods,computed,watch的调用时机的理解