Java程序猿的Vue爬坑之路(一)计算属性&侦听属性

Vue 的模板内的表达式非常便利,但是万物应有度。在模板中放入太多的逻辑会使代码过于繁重和难以维护。例如:

<h1>
  {{title.split('').reverse().join('')}}
</h1>

在这个地方 这个表达式可能就不再是简单的声明式逻辑,我们必须要看一段时间,才能意识到这里是想要显示title值得翻转字符串,如果在代码中大量的引用title的翻转字符串时就会更为难以处理。换言之,如果我们更改下这个表达式,翻转后再次去掉某个字符,页面所有引用该翻转字符串的地方都要重复的进行表达式的更改

所以,对于任何的对对象原属性(值)的重复或复杂逻辑,二次/多次 处理后进行引用的属性 都应该统一封装,统一声明引用 而在VUE内 我们可以视情况而定 选择 计算属性、侦听器、自定义函数 三种方式进行处理封装。下面具体介绍这三种方式处理数据的方式和引用方式

计算属性

计算属性处理数据定义在computed内 ,在模板内使用和使用data内的属性方式一致,可以视为 data的一个包装属性吧

代码示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body >
        <div id="person">
            <label>原名称:</label>
            <input v-model="person.name" />
            <label>翻转名称:</label>
            <input v-model="reversePersonName" />
            <label>性别:</label>
            <input v-model="person.sex" />
            <label>年龄:</label>
            <input v-model="person.age" />
        </div>
    </body>
    <script>
        new Vue({
            el:"#person",
            data:{
                person:{
                    name:'Tom',
                    sex:'男',
                    age:18
                }
            },
            computed:{
                reversePersonName:function(){
                    return this.person.name.split('').reverse().join('')
                }
            }
        })
    </script>
</html>

以计算属性的方式对数据进行处理,即第一次渲染该计算属性时,对该数据进行处理后会基于它们的响应式依赖进行缓存,只在相关响应式依赖发生改变后才会进行重新求值。这就意味着 如果name的值没有发生改变 ,多次对reversePersonName进行访问都会立即返回之前的计算结果,而不会进行重复的进行计算。

而当我们对 原名称 person.name属性的值进行改变时reversePersonName这个计算属性也会重新执行函数 对数据进行修改并更新缓存 重新渲染数据

但是 官方文档也有提到 计算属性并不支持异步操作比如 调用一个异步的API(AJAX)

下面我们模仿下异步操作 代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body >
        <div id="person">
            <label>原名称:</label>
            <input v-model="person.name" />
            <label>翻转名称:</label>
            <input v-model="reversePersonName" />
            <label>性别:</label>
            <input v-model="person.sex" />
            <label>年龄:</label>
            <input v-model="person.age" />
        </div>
    </body>
    <script>
        new Vue({
            el:"#person",
            data:{
                person:{
                    name:'Tom',
                    sex:'男',
                    age:18
                }
            },
            computed:{
                reversePersonName:function(){
                    var value=this.person.name.split('').reverse().join('')
                    console.log('计算属性监听到Name发生变化:'+value)
                     // 模仿异步请求接口调用 假设该请求需要1000毫秒才能返回数据 
                    setTimeout(function () {
                        return value
                    }, 1000);
                    
                }
            }
        })
    </script>
</html>

代码执行渲染后发现 计算属性已经无法正常渲染数据 甚至初始化渲染时 都无法正常渲染数据

image-20190531232246827.png

侦听器(侦听属性)

侦听器/侦听属性 顾名思义 watch : 这个东西就是一个类似html里的onchange事件的东西,不同的是onchange是用来监听html元素/值的变化 ,watch是用来监听vue实例里的属性的变化的

不同的是侦听器,只是一个用来监听数据变化的并不能直接在模板直接引用或调用

使用侦听器想要达到和计算属性同样的效果,必须要增加一个属性来存储进行计算后的结果 随后进行渲染

代码示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body >
        <div id="person">
            <label>原名称:</label>
            <input v-model="name" />
            <label>翻转名称:</label>
            <input v-model="reversePersonName" />
            <label>性别:</label>
            <input v-model="sex" />
            <label>年龄:</label>
            <input v-model="age" />
        </div>
    </body>
    <script>
        var alt = new Vue({
            el:"#person",
            data:{
                name:'Tom',
                sex:'男',
                age:18,
                reversePersonName:'moT'
            },
            watch:{
                name:function(val){
                    this.reversePersonName=val.split('').reverse().join('')
                }
            }
        })
    </script>
</html>

运行结果:


image-20190531225105857.png

这段代码运行渲染后,结果看起来很正常并且对原名称进行修改 翻转名称也会随之改变 对不对!但是! 还没完!

当我们把这些属性包装到一个对象内:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body >
        <div id="person">
            <label>原名称:</label>
            <input v-model="person.name" />
            <label>翻转名称:</label>
            <input v-model="person.reversePersonName"/>
            <label>性别:</label>a
            <input v-model="person.sex" />
            <label>年龄:</label>
            <input v-model="person.age" />
        </div>
    </body>
    <script>
        var alt = new Vue({
            el:"#person",
            data:{
                person:{
                    name:'Tom',
                    sex:'男',
                    age:18,
                    reversePersonName:'moT'
                }
            },
            watch:{
                person:function(val){
                    console.log(val)
                    this.person.reversePersonName=val.name.split('').reverse().join('')
                }
                
            }
        })
    </script>
</html>

这个时候渲染结果看起来还是很正常,但是!当你对person的name进行重新赋值或者直接改变input值时,你会发现。 翻转名称的值竟然没有同步更新!!!


image-20190531225357661.png

后来查了官方给的文档和部分网上资料,发现了原因

侦听器默认情况下必须要对侦听的属性 进行更改时才会正常更新缓存 重新渲染数据,因为它默认是没开启深度监听的,这种情况下 不会监听对象内部的属性变化,数组是可以的

侦听器可以定义三个参数

  • 第一个 handler:其值是一个回调函数。即监听到变化时应该执行的函数。
  • 第二个 deep:其值是true或false;确认是否深入监听。(一般监听时是不能监听到对象属性值的变化的,数组的值变化可以听到。)
  • 第三个 immediate:其值是true或false;确认是否以当前的初始值执行handler的函数

所以我们侦听器可以换成这种写法:

watch:{
        person:{
                handler:function(newVal,oldVal){
                        this.person.reversePersonName=val.name.split('').reverse().join('')
                },
                deep:true,
                immediate:true
        }

}

并且侦听器是支持异步操作的(执行一个异步API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

模仿异步API访问 代码示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body >
        <div id="person">
            <label>原名称:</label>
            <input v-model="person.name" />
            <label>翻转名称:</label>
            <input v-model="person.reversePersonName"/>
            <label>性别:</label>a
            <input v-model="person.sex" />
            <label>年龄:</label>
            <input v-model="person.age" />
        </div>
    </body>
    <script>
        var alt = new Vue({
            el:"#person",
            data:{
                person:{
                    name:'Tom',
                    sex:'男',
                    age:18,
                    reversePersonName:'moT'
                }
            },
            watch:{
                person:{
                    handler:function(newval,oldval){
                        this.person.reversePersonName='计算中:模拟API访问...'
                        setTimeout(function () {
                            this.reversePersonName=newval.name.split('').reverse().join('')
                        }, 0)
                    },
                    deep:true,
                    immediate:true
                }
            }
        })
    </script>
</html>

自定义函数

自定义函数处理数据方式如下,我们在模板内 调用函数来获取并渲染数据

使用自定义函数来处理并渲染数据是不会使用缓存的,即:无论原属性的值 有没有发生变化 每次在模板内引用该函数,都会重新计算并渲染数据,这样在大量的数据操作时对网页渲染性能造成的影响降是灾难级的,慎用。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body >
        <div id="person">
            <label>名称:{{reversePersonName()}}</label>
            
            <label>性别:</label>
            <input v-model="person.sex" />
            <label>年龄:</label>
            <input v-model="person.age" />
        </div>
    </body>
    <script>
        new Vue({
            el:"#person",
            data:{
                person:{
                    name:'Tom',
                    sex:'男',
                    age:18
                }
            },
            methods:{
                reversePersonName:function(){
                    return this.person.name.split('').reverse().join('');
                }
            }
        })
    </script>
</html>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354