背景

vue中,实现同一个性能需要,能够应用一般办法,也能够应用computed属性以及watch属性,对于它们的应用,刚开始时,存在着一些困惑

至于什么时候应用办法,什么时候应用计算computed属性,以及watch属性,往往令人很头疼

不同的形式适宜对应的场景,以下是本篇的内容

需要场景

输出A,B两数并求和,当和后果满足指定的条件时,指定该后果属于哪个年龄阶段

result <= 6 => 是个儿童result > 6 && result <= 17  => "岁是个少年"result > 17 && result  <=40 => "岁是个青年"result > 40 && result <=65 => "岁是个中年人"result > 65 && result <=100 =>"岁是个老年人"isNaN(result))=> "你输出的信息有误" result > 100 "岁,曾经超过了百岁,还是地球人么"

具体成果演示

<img src="https://static01.imgkr.com/temp/b07a314d7f9242feb3fbd1e61f0726ed.gif" class="medium-zoom lazy">

需要剖析

  1. 初始化值A,B两个数
  2. 计算拿到两数之和的后果,并且做绝对应的逻辑判断

办法1-应用模板形式实现

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>01-一般办法实现</title>    <style>        .box {           margin-left: 35%;           margin-top: 100px;        }    </style></head><body>        <div id="root">        <div class="box">            A: <input type="number"  v-model:value="A" />            <span>+</span>            B: <input  type="number" v-model:value="B" />            <button>=</button>            <span>{{ parseInt(A)+parseInt(B) }}</span><br /><br />            <div>                后果: {{ A}} +{{B}} = {{parseInt(A)+parseInt(B)}}                 <span v-if="(parseInt(A)+parseInt(B))<=6">岁是个儿童</span>                <span v-else-if="(parseInt(A)+parseInt(B)) > 6 && (parseInt(A)+parseInt(B)) <= 17">岁是个少年</span>                <span v-else-if="(parseInt(A)+parseInt(B)) > 17 && (parseInt(A)+parseInt(B)) <= 40">岁是个青年</span>                <span v-else-if="(parseInt(A)+parseInt(B)) > 40 && (parseInt(A)+parseInt(B)) <= 65">岁是个中年人</span>                <span v-else-if="(parseInt(A)+parseInt(B)) > 65 && (parseInt(A)+parseInt(B)) <= 100">岁是个老年人</span>                <span v-else-if="(parseInt(A)+parseInt(B)) > 100">岁,曾经超过了百岁,还是地球人么</span>                <span v-else="isNaN(parseInt(A)+parseInt(B))">你输出的信息有误</span>           </div>                                                        </div>    </div>    <script src="./js/vue.js"></script>    <script>        const vm = new Vue({            el: "#root",            data() {                return {                    A: "4",                    B: '5',                }            }        })    </script></body></html>

vue模板内应用表达式解决简略的逻辑,十分便当,然而它只适宜于简略的运算,如果逻辑很简单,那么保护模板就会变得很麻烦,不直观

倡议

对于简单的逻辑,都应该应用办法或者计算computed属性

额定拓展

为什么data的值写成一个函数,而非一个对象?

简要

当一个组件被定义,data必须申明为返回一个初始数据对象的函数,因为组件可能被用来创立多个实例

也就是说,在很多页面中,定义的组件能够复用在多个页面

如果data是一个纯碎的对象,则所有的实例将共享援用同一份data数据对象,无论在哪个组件实例中批改data,都会影响到所有的组件实例

如果data是函数,每次创立一个新实例后,调用data函数,从而返回初始数据的一个全新正本数据对象

这样每复用一次组件,会返回一份新的data数据,相似于给每个组件实例创立一个公有的数据空间,让各个组件的实例各自独立,互不影响

办法2-应用一般办法实现

示例代码如下所示,在methods中定义方法(性能),在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>01-应用一般办法实现</title>    <style>        .box {           margin-left: 35%;           margin-top: 100px;        }    </style></head><body>        <div id="root">        <div class="box">            A: <input type="number"  v-model:value="A" />            <span>+</span>            B: <input  type="number" v-model:value="B" />            <button>=</button>            <span>{{ addResult() }}</span><br /><br />            <div>                后果: {{ A}} +{{B}} = {{addResult()}}  {{outPut()}}            </div>          </div>    </div>    <script src="./js/vue.js"></script>    <script>        const vm = new Vue({            el: "#root",            data() {                return {                    A: "4",                    B: '5',                }            },            methods: {                addResult() {                    return parseInt(this.A)+parseInt(this.B)                },                outPut() {                    const result = parseInt(this.A)+parseInt(this.B);                    if(result <= 6) {                        return "岁是个儿童"                    }else if(result > 6 && result <= 17) {                        return "岁是个少年"                    } else if(result > 17 && result  <=40) {                        return "岁是个青年"                    }else if(result > 40 && result <=65) {                        return "岁是个中年人"                    }else if(result > 65 && result <=100) {                        return "岁是个老年人"                    }else if(isNaN(result)) {                        return "你输出的信息有误"                    }else {                        return "岁,曾经超过了百岁,还是地球人么"                    }                }            },        })    </script></body></html>

以上就是在methods中定义方法,去实现的

注意事项

应用一般办法,实现时,每当触发办法,都会引起页面从新渲染,执行办法函数,它是没有缓存的

如果有一个性能开销比拟大的计算属性,它须要遍历一个很大的数组,并做大量的计算,而这个计算属性又有其余依赖,如果没有缓存,不必计算属性,那么就会一直的执行收集属性的getter,如果不心愿有缓存,就用办法来代替

办法3-应用计算属性computed实现

vue实例配置选项中,增加computed属性,值是一个对象,并且增加与之绝对应的计算属性

计算属性失去的值是之前缓存的计算结果,不会屡次执行

实例代码如下所示

<!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>02-应用计算属性computed实现</title>    <style>        .box {           margin-left: 35%;           margin-top: 100px;        }    </style></head><body>        <div id="root">        <div class="box">            A: <input type="number"  v-model:value="A" />            <span>+</span>            B: <input  type="number" v-model:value="B" />            <button>=</button>            <span>{{ addResult }}</span><br /><br />            <div>                后果: {{ A}} +{{B}} = {{addResult}}  {{outPut}}            </div>          </div>    </div>    <script src="./js/vue.js"></script>    <script>        const vm = new Vue({            el: "#root",            data() {                return {                    A: "4",                    B: '5',                }            },            computed: {                addResult: {  // 原始书写的形式                    get() {                      return parseInt(this.A)+parseInt(this.B)                    }                },                outPut: {                    get() {                        const result = parseInt(this.A)+parseInt(this.B);                        if(result <= 6) {                            return "岁是个儿童"                        }else if(result > 6 && result <= 17) {                            return "岁是个少年"                        } else if(result > 17 && result  <=40) {                            return "岁是个青年"                        }else if(result > 40 && result <=65) {                            return "岁是个中年人"                        }else if(result > 65 && result <=100) {                            return "岁是个老年人"                        }else if(isNaN(result)) {                            return "你输出的信息有误"                        }else {                            return "岁,曾经超过了百岁,还是地球人么"                        }                    }                }            }        })    </script></body></html>

舒适提醒

当一旦确定计算属性只读取(get),而不去批改set,确定了只读不改,能够应用简写模式,如下所示等价

// 其余省略,如上所示   computed: {    // 一旦确定计算属性只读取(get),而不去批改set,能够应用简写模式    //确定了只读不改,那么就能够应用简写模式       addResult() {        return parseInt(this.A)+parseInt(this.B)    },    outPut() {        const result = parseInt(this.A)+parseInt(this.B);        if(result <= 6) {            return "岁是个儿童"        }else if(result > 6 && result <= 17) {            return "岁是个少年"        } else if(result > 17 && result  <=40) {            return "岁是个青年"        }else if(result > 40 && result <=65) {            return "岁是个中年人"        }else if(result > 65 && result <=100) {            return "岁是个老年人"        }else if(isNaN(result)) {            return "你输出的信息有误"        }else {            return "岁,曾经超过了百岁,还是地球人么"        }    }}})

注意事项

  1. 计算属性的后果,不必挂载在data上面进行数据的初始化,在vue模板中能够间接应用,不必加圆括号计算属性名(),这点有别于一般办法的调用
  2. 在模板中放入太多的逻辑会让模板过重且难以保护,也不直观(简略的逻辑能够放在模板中解决)
  3. 对于简单逻辑,能够应用计算属性(计算属性的 getter 函数是没有副作用, 但也能够应用办法,然而计算属性在计算数量量比拟大,具备缓存计算结果的作用,性能更高,频繁调用办法,解析模板,渲染页面,是比拟耗费性能的)
  4. 计算属性是基于它们的响应式依赖进行缓存的,只在相干响应式依赖产生扭转时它们才会从新求值,相比于一般办法的调用,每当触发从新渲染时,调用办法执行函数,会解析vue模板

办法4-应用watch监听属性来实现

  1. 通过vm对象的$watch()watch配置来监督指定的属性
  2. 当属性变动时,回调函数主动调用,在函数外部进行计算

具体实例代码

<!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>03-监听属性watch办法实现</title>    <style>        .box {           margin-left: 35%;           margin-top: 100px;        }    </style></head><body>        <div id="root">        <div class="box">            A: <input type="number"  v-model:value="A" />            <span>+</span>            B: <input  type="number" v-model:value="B" />            <button>=</button>            <span>{{ addResult }}</span><br /><br />            <div>                后果: {{ A}} +{{B}} = {{addResult}}  {{outPut}}            </div>          </div>    </div>    <script src="./js/vue.js"></script>    <script>        const vm = new Vue({            el: "#root",            data() {                return {                    A: "4",                    B: '5',                    addResult: '',                    outPut: ''                }            },            watch: {                A: {                    immediate: true,   // 初始化时让handler调用一下,默认是false                    handler(newVal,oldVal) {                        console.log("A数据扭转了","最新的:",newVal,"旧的:",oldVal);                        this.addResult = parseInt(newVal)+parseInt(this.B)                        const result = parseInt(this.addResult);                        if(result <= 6) {                            this.outPut = "岁是个儿童"                        }else if(result > 6 && result <= 17) {                            this.outPut = "岁是个少年"                        } else if(result > 17 && result  <=40) {                            this.outPut = "岁是个青年"                        }else if(result > 40 && result <=65) {                            this.outPut = "岁是个中年人"                        }else if(result > 65 && result <=100) {                            this.outPut = "岁是个老年人"                        }else if(isNaN(result)) {                            this.outPut= "你输出的信息有误"                        }else {                            this.outPut = "岁,曾经超过了百岁,还是地球人么"                        }                     }                },                B: {                    immediate: true,                    handler(newVal, oldVal) {                        console.log("B数据变了","最新的",newVal,"旧的",oldVal);                        this.addResult = parseInt(this.A)+parseInt(newVal);                        const result = parseInt(this.addResult);                        if(result <= 6) {                            this.outPut = "岁是个儿童"                        }else if(result > 6 && result <= 17) {                            this.outPut = "岁是个少年"                        } else if(result > 17 && result  <=40) {                            this.outPut = "岁是个青年"                        }else if(result > 40 && result <=65) {                            this.outPut = "岁是个中年人"                        }else if(result > 65 && result <=100) {                            this.outPut = "岁是个老年人"                        }else if(isNaN(result)) {                            this.outPut= "你输出的信息有误"                        }else {                            this.outPut = "岁,曾经超过了百岁,还是地球人么"                        }                     }                }            },        })    </script></body></html>

当响应的数据不须要immediate:true,deep: true,就能够简写,下面的watch,如下是等价的

留神

如果写成简写的形式,那么就无奈写配置选项

// 其余局部省略,如上所示    watch: {        // 等价于如下        A(newVal) {  // 这里的newVal参数,指的是以后监督属性,最新的值,能够写一个,也能够写两个(newVal,oldVal)            this.addResult = parseInt(newVal)+parseInt(this.B)            const result = parseInt(this.addResult);            if(result <= 6) {                this.outPut = "岁是个儿童"            }else if(result > 6 && result <= 17) {                this.outPut = "岁是个少年"            } else if(result > 17 && result  <=40) {                this.outPut = "岁是个青年"            }else if(result > 40 && result <=65) {                this.outPut = "岁是个中年人"            }else if(result > 65 && result <=100) {                this.outPut = "岁是个老年人"            }else if(isNaN(result)) {                this.outPut= "你输出的信息有误"            }else {                this.outPut = "岁,曾经超过了百岁,还是地球人么"            }         },        B(newVal) {            console.log("B数据变了","最新的",newVal,"旧的");            this.addResult = parseInt(this.A)+parseInt(newVal);            const result = parseInt(this.addResult);            if(result <= 6) {                this.outPut = "岁是个儿童"            }else if(result > 6 && result <= 17) {                this.outPut = "岁是个少年"            } else if(result > 17 && result  <=40) {                this.outPut = "岁是个青年"            }else if(result > 40 && result <=65) {                this.outPut = "岁是个中年人"            }else if(result > 65 && result <=100) {                this.outPut = "岁是个老年人"            }else if(isNaN(result)) {                this.outPut= "你输出的信息有误"            }else {                this.outPut = "岁,曾经超过了百岁,还是地球人么"            }         }    },})

当然,Vue提供了$watch实例办法,也能够这么写

<script>const vm = new Vue({    el: "#root",    data() {        return {            A: "4",            B: '5',            addResult: '',            outPut: ''        }    },        // 等价于如下形式vm.$watch('A',{    immediate: true,   // 初始化时让handler调用一下,默认是false,不写这个的话,第一次不会调用handler函数,会达不到本人的预期    handler(newVal,oldVal) {        console.log("A数据扭转了","最新的:",newVal,"旧的:",oldVal);        this.addResult = parseInt(newVal)+parseInt(this.B)        const result = parseInt(this.addResult);        if(result <= 6) {            this.outPut = "岁是个儿童"        }else if(result > 6 && result <= 17) {            this.outPut = "岁是个少年"        } else if(result > 17 && result  <=40) {            this.outPut = "岁是个青年"        }else if(result > 40 && result <=65) {            this.outPut = "岁是个中年人"        }else if(result > 65 && result <=100) {            this.outPut = "岁是个老年人"        }else if(isNaN(result)) {            this.outPut= "你输出的信息有误"        }else {            this.outPut = "岁,曾经超过了百岁,还是地球人么"        }     }})// 等价于如下所示,监督B属性vm.$watch('B', {    immediate: true,  // 初始化时,调用一些handler函数,不写这个的话,第一次不会调用handler函数    handler(newVal, oldVal) {        console.log("B数据变了","最新的",newVal,"旧的",oldVal);        this.addResult = parseInt(this.A)+parseInt(newVal);        const result = parseInt(this.addResult);        if(result <= 6) {            this.outPut = "岁是个儿童"        }else if(result > 6 && result <= 17) {            this.outPut = "岁是个少年"        } else if(result > 17 && result  <=40) {            this.outPut = "岁是个青年"        }else if(result > 40 && result <=65) {            this.outPut = "岁是个中年人"        }else if(result > 65 && result <=100) {            this.outPut = "岁是个老年人"        }else if(isNaN(result)) {            this.outPut= "你输出的信息有误"        }else {            this.outPut = "岁,曾经超过了百岁,还是地球人么"        }     }})</script>

对于watch属性,是一个十分有用的属性,如果须要对一些数据做一些监测,新旧数据的比照,变换,达到某些条件时,做一些逻辑操作,那么watch能够监听data上面的属性,还能够监听计算结果属性

对于watch与$watch书写的机会

  1. 如果很明确你要监督哪个数据,在传教概念实例的时候,就写watch
  2. 如果在创立实例的时候,你不晓得要监督哪个数据,后续会依据用户的一些行为,监测哪些数据,那么就能够应用$watch这个API
  3. 当被监督的属性变动时,回调函数主动调用,进行相干操作
  4. 监督的属性必须存在,能力进行监督
  5. 监督数据有两种形式一种实例化Vue对象时,传入watch配置选项,另一种是vm.$watch

watch中的深度监督

下面都是间接的监听data上面间接挂载的属性,当咱们想要监听某个对象下的单个属性时,那怎么办?如下所示

const vm = new Vue({    el: '#root',    data() {        return {            info: {              name: 'itclanCoder',  // 想要监听info对象下某单个属性              age: 4            }        }    },    // 监听多级构造中某个属性的变动    watch: {        'info.name': {            console.log("info上面的name属性扭转了");         }    }    })

Vue中,默认不监测对象外部值的扭转,如果想要监测对象下的每个属性的变动(也就是监测多层级构造),能够设置开启deep: true配置,如下所示

const vm = new Vue({    el: '#root',    data() {        return {            info: {              name: 'itclanCoder',               age: 4            }        }    },    // 监听多级构造中某个属性的变动    watch: {         info: {             immediate: true, // 初始化时,立刻调用handler函数             deep: true,  // 开启深度监测             handler() {                 console.log("name和age都扭转触发了");             }         }    }    })// 等价于上面这种写法vm.$watch('info',{    immediate: true, // 初始化时,立刻调用handler函数    deep: true,  // 开启深度监测    handler() {        console.log("name和age都扭转触发了");    }})

::: tip 留神

 vm.$watch('info',function(newVal,oldVal) {   // 此处不能写箭头函数,要写一般函数,否则this的绑定就会出问题            console.log("新值",newVal,"旧值",oldVal);        },{            immediate:true,            deep: true        })

在变更 (不是替换) 对象或数组时,旧值将与新值雷同,因为它们的援用指向同一个对象/数组。Vue 不会保留变更之前值的正本

正告

  1. 但凡vue治理的函数不要写箭头函数
  2. 计算属性外面的get,set不能写成箭头函数

watch反对异步工作维持数据

重点内容:

当须要在数据变动时执行异步或开销较大的操作时,应用watch这个形式是最有用的,而computed是没有方法做到的(靠的是返回值)

 watch: {        // 等价于如下        A(newVal) {  // 这里的newVal参数,指的是以后监督属性,最新的值,能够写一个,也能够写两个(newVal,oldVal)           setTimeout(() => {  // 这里的回调函数不能写成一般函数,否则this就会指向window,会出问题                this.addResult = parseInt(newVal)+parseInt(this.B)                const result = parseInt(this.addResult);                if(result <= 6) {                    this.outPut = "岁是个儿童"                }else if(result > 6 && result <= 17) {                    this.outPut = "岁是个少年"                } else if(result > 17 && result  <=40) {                    this.outPut = "岁是个青年"                }else if(result > 40 && result <=65) {                    this.outPut = "岁是个中年人"                }else if(result > 65 && result <=100) {                    this.outPut = "岁是个老年人"                }else if(isNaN(result)) {                    this.outPut= "你输出的信息有误"                }else {                    this.outPut = "岁,曾经超过了百岁,还是地球人么"                }             },2000)        },    },})

有时候,咱们想要提早多长时间在实现绝对应的逻辑,那么watch就能够无效的去开启一个异步工作

<img src="https://static01.imgkr.com/temp/f48c1fac362c4028a05fe6cb9b9a204d.png" class="medium-zoom lazy">

从下面的图中总结出

  1. computed:监测的是依赖值,当依赖值不变的状况下,会间接读取缓存进行复用,当依赖值有变动时,会从新计算
  2. watch: 监测的是属性值,只有属性发生变化,都会触发执行回调函数来执行一系列的操作

然而computed不行,computed靠的是返回值,watch是靠你本人亲手写代码去批改

计算属性外面是没有方法去开启异步工作,它必须同步执行,去保护数据的,然而watch却能够

当应用watchcomputed都能够实现时,那么举荐应用computed,然而当要解决实现一些异步工作时,那么就须要应用watch

computedwatch之间的区别

computed能实现的性能,watch都能够实现

watch能实现的性能,computed不肯定能实现,例如:watch能够进行一部操作

两个重要的小区别

  1. 所被vue治理的函数,最好写成一般函数,这样this的指向才是vm或组件实例对象
  2. 所有不被vue所治理的函数(定时器的回调函数,ajax的回调函数等Promise的回调函数)最好写成箭头函数,这样this的指向才是vm或组件实例对象

总结

vue中实现同一个性能,对于简略的逻辑性能,能够应用模板,其次是办法(但不具备数据缓存的能力),若逻辑很简单,须要缓存数据,则应用计算属性,而watch属性,同样也能实现

在平时的开发中,优先应用计算属性,能够看出它更简略,不便,然而想要执行异步工作,那么就得应用watch,computed能做的,watch也能做,但反过来,却不行

原文出处-vuejs中的一般办法/计算属性computed与监听属性watch四者的比拟