vue源码解读--计算属性

目录导航

这一节,我们的示例代码如下

默认情况下页面将渲染出"default",当我们第一次点击onChangeIndex函数后将显示"三岁就会写bug",同时打印出''update'',当再次点击则页面不会有变化,但是仍然打印出"update";当点击onChangeName后页面展示"三岁就会写bug哦",同时打印"update",当再次点击时,则页面无变化同时不会打印"update".

那么为什么会这样呢?

\bullet 几个小问题

我们之前在分析组件的createComponent和组件的init时候都跳过了部分关于computed的逻辑

    \star 在组件创建的过程中,调用extend,判断computed是否存在,然后进行计算属性的init,继而调用defineComputed

    \star 在组件的init过程中,调用initState方法,该方法除了对props、data执行了相关逻辑外,还判断了computed是否存在,并在存在时调用其init,继而调用defineComputed

        也就是说,vue在构建组件构造器时候便已经对compoted进行了处理,而处理函数即defineComputed,那么我现在有3个疑问:a-为什么要提前处理?b-两处处理有什么不同?c-只处理一次行不行?

        \circ 首先看extend中的处理

                     首先拿到组件的options对象,并对computed进行遍历,并对每一个key(name函数)调用defineComputed,入参为组件的原型、name函数

            \circ 再看init过程

            可以看到,两次都是拿到computed的key遍历调用defineComputed,不一样的是,一次传入的target是实例的原型,一次则是vm实例。我们都知道this会首先查找实例本身,若不存在则跟随原型链查找原型对象。且for...in循环具有查找原型链的能力。也就是说,同一个组件,只会在构建构造器时候执行一次。当对组件进行初始化时候,for...in将查找到原型上存在的key,故不会重复多次调用defineComputed。这可以认为是一种"数据共享",是一种优化手段

            所以,之前的疑问可以这么回答:为了避免当在同一个页面中多次使用同一个组件时每次都在组件中定义一遍key造成性能浪费,便利用原型链特性一劳永逸的在构建组件阶段放置到原型链上以避免走重复逻辑(疑问a、b);只在组件上定义会存在a、b提出的性能问题,而只在原型上定义则无法针对如mixin一类后植入的key进行处理(疑问c)

\bullet 创建过程

当执行组件初始化过程中会调用initState并判断computed存在执行initComputed,传入组件实例和在组件中定义的computed对象(我们这里即name函数)

        --向组件实例挂载_computedWatchers,默认是空对象,并通过Object.create赋予其原型链访问权力

        --isSSR在浏览器环境下为false,进入判断,进行watcher实例化。入参为:组件实例、getter函数(name函数)、noop空函数、{ lazy: true }。实例化watcher的关键信息如下

(在计算时作为判断条件)
(在计算时调用)
(可以看到,在创建过程中并没有调用get进行计算,而是返回undefined)

        --使用for...in循环拿到每一个key,即我们定义的每一个函数

        --向实例的_computedWatchers上绑定一个watcher,从之前文章的分析我们知道watcher将在一定的时机触发update进行patch最终渲染为dom

        --调用defineComputed,将组件实例、每一个计算属性的key及其对应的处理函数(name函数)传入

                --shouldCache为true    

                --sharedPropertyDefinition.get对应的是一个匿名函数

                --由于将sharedPropertyDefinition作为Object.defineProperty的属性描述符,故当访问this.name时,将会触发拦截从而调用sharedPropertyDefinition的get方法,而get方法指向上一步返回的computedGetter函数

\bullet 计算过程--default

        当vue将组件编译为render函数的过程中将对模板中的变量name进行访问,此时将触发sharedPropertyDefinition的get,即computedGetter函数

        --拿到我们在创建过程中保存的每一个computed.key对应的watcher

        --调用evalute函数

                \cdot 调用get函数,求值

                    --向dep.target保存this

                    --调用getter函数,即name函数

(计算得出'default'并返回)

        --调用depend函数

                这实际上调用的是

(经过之前的分析,我们知道这实际上是一次依赖收集。其中dep是发布者,watcher是订阅者,这里为了在发布者中保存订阅以便在适当的时机去发布广播触发更新)

因此,得出的值为"default"

\bullet 计算过程--三岁就会写bug

        当我们点击onChangeIndex函数将手动把saveIndex加加,由于saveIndex是在data中定义的响应式数据,故它将首先走向get方法进行依赖收集,此时dep中将有两个订阅者:计算属性name和saveIndex

        加加的操作则触发saveIndex的set方法,将触发dep的notify

            继而调用watcher的update,由于第一个dep是计算属性的,故只会执行到this.lazy中将this.dirty置为true后便会调用saveIndex对应watcher的update,此次将触发queueWatcher执行render,而在render的过程中将再次访问name,此时this.saveIndex>0成立,获取firstName和lastName并计算返回

因此,得出的值为"三岁就会写bug"

\bullet 最后关于"当点击onChangeName后页面展示"三岁就会写bug哦",同时打印"update",当再次点击时,则页面无变化同时不会打印"update".",主要是因为在set拦截中进行了值比较,当相等时则return,因此不会重新render,故而无法调用update

\star \star \star 可以看到,当其依赖项发生变化时总是会重新求值,但是如果求出的值相等时vue并没有做限制,而是每次都重新计算了一次。这样的不合理可能是由于我当前看的源码版本(2.5.2)较老导致的,毕竟尤大这样的神级人物不会考虑不到这一点\star \star \star

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

推荐阅读更多精彩内容