20230415----重返学习-vue计算属性-watch监听器-vue过滤器-vue生命周期
day-050-fifty-20230415-vue计算属性-watch监听器-vue过滤器-vue生命周期
vue计算属性
- computed 计算属性
-
可以计算的属性,写法是函数,使用时当做属性来使用,即类似于data中的值
-
函数运算后的结果(return),就是计算属性的值
-
多个值改变,影响一个结果的时候
- 即多个data中的响应式数据变化,返回一个新的响应式数据的场景
-
逻辑简单
-
计算属性要求在data里面没有同名的属性
-
计算有缓存
- 如果依赖的变量没有变化,那么它的值就会一直被缓存起来
- 即
this.数据变量名
中this后面的值没有发生变动时,它就不会重复进行计算。
- 即
- 如果依赖的变量没有变化,那么它的值就会一直被缓存起来
-
值与结果
- 大多数情况:多个值改变,影响一个结果的时候
- 极少数情况:1个值变化,影响到多个结果
- 也就是用全写形式,用计算属性的set方法反向计算出来源值
-
全写形式:是一个对象,用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}}元 小计:<em>{{(item.count*item.price).toFixed(2)}}</em>元</span></div></div><div class="bottom">商品合计:<span class="pronum">{{all.allCount}}</span> 件<br />共花费了:<span class="pronum">{{all.allCountPrice}}</span> 元<br />最贵商品单价:<span class="pronum">{{all.highPrice}}</span> 元</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:监听器
-
watch是一个对象,它内部具体要监听到的data响应式属性名的写法是函数
-
一个值变化,影响到多个结果
- 也就是watch监听到的data响应式数据的值变化了,就会影响到其它值或方法的调用
-
逻辑介于 computed 和 methods 之间
- 里面得出的结果一般比computed复杂,但比methods里的方法要来得简单
-
必须是data里面已有的
-
没有缓存
-
只有:一个值变化,影响到多个结果,无法反推
-
只有watch可以监控路由($route)
-
全写形式:是一个对象,用于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,就可以实现深度监听
-
-
如果要监听对象的某个具体属性,可以加引号
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之间的对比
computed
(多个值变化,影响到一个结果) 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。他虽然是函数但主要当作属性来使用,逻辑简单。methods
方法表示一个具体的操作,主要书写业务逻辑,是函数并且以函数的方式来调用。watch
(一个值变化,影响到多个结果改变)一个对象,键是需要观察的表达式,值是对应回调函数。- 主要用来监听某些特定数据的变化(watch监控路由对象‘$route’),从而进行某些具体的业务逻辑操作;
- 以及在对应数据变化进而触发函数时,在内部调用异步函数。
- 可以看作是
computed
和methods
的结合体;
- 主要用来监听某些特定数据的变化(watch监控路由对象‘$route’),从而进行某些具体的业务逻辑操作;
- computed是属性调用,而methods是函数调用
- computed带有缓存功能,而methods不是
- computed可以监控对象,而methods不是
- watch可以监控对象,而methods不是
- watch和computed均可以监控程序员想要监控的对象,当这些对象发生改变之后,可以触发回调函数做一些逻辑处理。
-
watch监控自身属性变化(重新编译,性能差),只要调用就会调用回调。 vm.$watch()
-
watch监控路由对象(一个值变化,影响到多个结果改变),数据变化时来执行异步操作,这时watch是非常有用的。
-
计算属性computed的特点(多个值变化,影响到一个结果)
- 计算属性会依赖于他使用的data中的属性,
- 只要是依赖的属性值有改变,则自动重新调用一下计算属性;
- 如果他所依赖的这些属性值没有发生改变,那么计算属性的值是从缓存中来的,而不是重新编译,那么性能要高一些,所以vue中尽可能使用computed替代watch。
-
- watch和computed均可以监控程序员想要监控的对象,当这些对象发生改变之后,可以触发回调函数做一些逻辑处理。
<!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生命周期
- 生命周期(钩子函数):
- 初始化阶段
- beforeCreate:初始化之前,组件实例刚被创建,什么也没有,只有默认的生命周期和事件。
- 默认的事件就是实例事件,如当前vue实例的
- created:初始化之后,组件实例创建完成,初始化了 data 和 methods都赋值了, 有了el,但是没有赋值(一般用于发送ajax)
- beforeCreate:初始化之前,组件实例刚被创建,什么也没有,只有默认的生命周期和事件。
- 模板编译
- beforeMount:模板编译之前,已经存在 虚拟DOM了,el这个时候赋值了,并没有渲染到页面上
- mounted:模板编译之后, 虚拟DOM转化为真实DOM,并且渲染页面上
- 更新阶段(data 的数据修改的时候,会触发)
- beforeUpdate:数据更新了,但是页面没有更新
- updated:数据更新了,并且页面也更新了
- 销毁阶段(触发销毁 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>
进阶参考
- 前端框架Vue中methods,computed,watch的调用时机的理解