We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vue中有一个非常好用的功能:计算属性(computed)
在模板中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板过重且难以维护。这就是为什么 Vue.js 将绑定表达式限制为一个表达式。如果需要多于一个表达式的逻辑,应当使用计算属性。 你可以像绑定普通属性一样在模板中绑定计算属性。Vue 知道 vm.b 依赖于 vm.a,因此当 vm.a 发生改变时,依赖于 vm.b 的绑定也会更新。
来源:https://vuejs.org.cn/guide/computed.html
我们先来具象化一下问题。
// html <div id="app"> <p>姓名:{{user.name}}</p> <p>年龄: {{user.age}}</p> <p>{{info}}</p> </div>
// js const app = new Bue({ el: '#app', data: { user: { name: 'youngwind', age: 24 } }, computed: { info: function () { return `计算出来的属性-> 姓名: ${this.user.name}, 年龄: ${this.user.age}`; } } });
问题是:如何让info跟着name和age动态改变呢?
我们把这个问题拆解成两个更小的问题,然后逐个击破。
ok,我们先来解决第一个问题。(先把第二个问题放一边)
/** * 初始化所有计算属性 * 主要完成一个功能:将计算属性定义的function当成是该属性的getter函数 * @private */ exports._initComputed = function () { // 注意,这里的this指的是bue实例 let computed = this.$options.computed; if (!computed) return; for (let key in computed) { let def = computed[key]; if (typeof def === 'function') { def = { get: def }; def.enumerable = true; def.configurable = true; Object.defineProperty(this.$data, key, def); } } };
关键点说明:
实现效果如下图所示。
从图中我们可以看到,$data里面的info已经有值,并且DOM模板里面的{{info}}也已经正确解析了。 第一个问题解决了,但是,我们同时也看到,当我们改变name和age的时候,info并不会跟着改变!! 下面我们来看看怎么解决这第二个问题。
动态计算难在什么地方? 难在:当name或者age改变的时候,程序如何知道要改变info? 你可能会说,这不明摆着吗,一眼就看出来。 然而,程序不知道啊!我们拆解一下问题。
第2个问题好办,因为我们在《如何实现动态数据绑定》 #87 的时候就已经建立起一套完整的Binding、Watcher和Directive的体系。我们只需要把info指令分别push到wathcer name和wathcer age的_subs里面不就可以了,在这儿就不细说了。 我们重点看第一个问题。 解决思路还是从getter入手:定义info的function被当成了getter,那么当我们访问this.$data.info的时候,就会调用这个function。这个function又会去访问this.user.name和this.user.age,这意味着什么呢? 这意味着会去执行name和age的getter函数啊!所以我们可以自定义name和age的getter函数,让它做一些特殊的事情。 那要做什么事情呢?我们触发(notify)一个get事件,然后这个get事件会传播到$data顶层。我们在$data顶层注册一个colletDep(收集依赖)函数,这样我们不就能知道info依赖于user.name和user.age了吗? 嗯,没错,大概思路就是这样。下面展示部分关键代码,完整的代码可以参考这里
function Watcher(vm, expression, cb, ctx) { this.id = ++uid; this.vm = vm; this.expression = expression; this.cb = cb; this.ctx = ctx || vm; this.deps = Object.create(null); // 这里的getter可以不去细究,其实就是根据expression(比如user.name) // 拼接出它对应的函数,当成getter // 你完全可以理解为调用this.getter()方法其实就是为了得到user.name的值 this.getter = expParser.compileGetter(expression); this.initDeps(expression); }
/** * 要注意,这里的getter.call是完成计算属性的核心, * 因为正是这里的getter.call, 执行了该计算属性的getter方法, * 从而执行该计算属性所依赖的其他属性的get方法 * 从而发出get事件,冒泡到底层, 触发collectDep事件 * @param path {String} 指令表达式对应的路径, 例如: "user.name" */ Watcher.prototype.initDeps = function (path) { this.addDep(path); Observer.emitGet = true; this.vm._activeWatcher = this; // 就是在这儿调用info的getter,进而调用name和age的getter // 进入触发和传播get事件 this.value = this.getter.call(this.vm, this.vm.$data); Observer.emitGet = false; this.vm._activeWatcher = null; };
/** * 收集依赖。 * 为什么需要这个东西呢? * 因为在实现computed计算属性功能的过程中, * 发现程序需要知晓计算出来的属性到底依赖于哪些原先就有的属性 * 这样才能做到在对应原有的属性的_subs数组中添加新属性指令的watcher事件 * @param path {String} get事件传播到顶层时的路径,比如"user.name" * @private */ exports._collectDep = function (event, path) { let watcher = this._activeWatcher; if (watcher) { watcher.addDep(path); } }; // 看,就是在这儿给$data顶层注册收集依赖的事件的 this.observer.on('set', this._updateBindingAt.bind(this)) .on('get', this._collectDep.bind(this));
有两个小细节务必要注意:
最后构造出来的_rootBinding数据结构如下图所示。
具体的实现效果如下图所示,完整的代码参考这里
---EOF---
The text was updated successfully, but these errors were encountered:
说得太复杂了,没有讲到关键点,其实计算属性就是defineproperty的一个延伸,如果我自己来写我会这样http://www.codesnippet.cn/detail/0510201615088.html
Sorry, something went wrong.
其实什么watcher,binding都是对defineproperty自然的抽象而已
额, 直接读源码都没那么晕。
No branches or pull requests
前言
vue中有一个非常好用的功能:计算属性(computed)
来源:https://vuejs.org.cn/guide/computed.html
问题
我们先来具象化一下问题。
问题是:如何让info跟着name和age动态改变呢?
我们把这个问题拆解成两个更小的问题,然后逐个击破。
静态计算属性
ok,我们先来解决第一个问题。(先把第二个问题放一边)
关键点说明:
实现效果如下图所示。
从图中我们可以看到,$data里面的info已经有值,并且DOM模板里面的{{info}}也已经正确解析了。
第一个问题解决了,但是,我们同时也看到,当我们改变name和age的时候,info并不会跟着改变!!
下面我们来看看怎么解决这第二个问题。
动态计算属性
动态计算难在什么地方?
难在:当name或者age改变的时候,程序如何知道要改变info?
你可能会说,这不明摆着吗,一眼就看出来。
然而,程序不知道啊!我们拆解一下问题。
第2个问题好办,因为我们在《如何实现动态数据绑定》 #87 的时候就已经建立起一套完整的Binding、Watcher和Directive的体系。我们只需要把info指令分别push到wathcer name和wathcer age的_subs里面不就可以了,在这儿就不细说了。
我们重点看第一个问题。
解决思路还是从getter入手:定义info的function被当成了getter,那么当我们访问this.$data.info的时候,就会调用这个function。这个function又会去访问this.user.name和this.user.age,这意味着什么呢?
这意味着会去执行name和age的getter函数啊!所以我们可以自定义name和age的getter函数,让它做一些特殊的事情。
那要做什么事情呢?我们触发(notify)一个get事件,然后这个get事件会传播到$data顶层。我们在$data顶层注册一个colletDep(收集依赖)函数,这样我们不就能知道info依赖于user.name和user.age了吗?
嗯,没错,大概思路就是这样。下面展示部分关键代码,完整的代码可以参考这里
有两个小细节务必要注意:
最后构造出来的_rootBinding数据结构如下图所示。
具体的实现效果如下图所示,完整的代码参考这里
---EOF---
The text was updated successfully, but these errors were encountered: