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,先前就有人介绍学习Vue的源码,提到旧版本的源码行数只不过一千多行,可以一个一个commit学习下去。前段时间为了找下家,一直在用Vue做作品,效率也较以前原生JavaScript要快上许多,后来工作上手了,不禁想看看Vue源码长什么样子的,只是从第一个commit开始读起来较为费时,而Github上面Vue项目能够找到的最早branch是Vue 0.10,然而在发布版本里面,可以发现最早一版本是Vue 0.6.0版本。本文介绍也是从该本版开始,该版本较0.10的要上少40%左右代码。
本文目的在于最短时间内,介绍Vue0.6.0的核心模块源码,数据双向绑定,computed计算属性等。 看完本文之后,能明白Vue框架到底做了些什么,并自行构建一个早期Vue框架的简化版本。
在介绍Vue 源码之前,需要了解Object.defineProperty,通过这个方式,在获取对象字段值时,调用getter方法,而设置对象字段值时候,调用setter方法,来实现数据劫。Object.defineProperty是Vue的基础内容,至今不熟悉的请一定要看Object.defineProperty MDN
对于
data: { a: 1, c: 2 }
如果你要设置data.a的值,并更新渲染DOM,会怎么做呢? 了解Object.defineProperty神奇的setter和getter数据劫持功能后,自然而然是有以下步骤:
看了源码不难发现,创建的实例new Vue({}),指的是ViewModel构造函数,而ViewModel函数如下:
function ViewModel (options) { // just compile. options are passed directly to compiler new Compiler(this, options) }
所以comilper.js才是生成Vue的核心所在,下面是简化了compiler构造函数
function Compiler(vm, options) { let compiler = this; options = compiler.options = options || makeHash() var el = compiler.setupElement(options) var scope = options.scope; if (scope) utils.extend(vm, scope, true) compiler.vm = vm var observables = compiler.observables = [], computed = compiler.computed = []; compiler.bindings = makeHash() compiler.setupObserver() let keyPrefix; for(let key in vm) { keyPrefix = key.charAt(0) if (keyPrefix !== '$' && keyPrefix !== '_') { compiler.createBinding(key) } } compiler.compile(el, true) while (i--) { binding = observables[i] Observer.observe(binding.value, binding.key, compiler.observer) } if (computed.length) DepsParser.parse(computed) }
上面过程分为三个阶段:
这里实现每个key的数据劫持和事件监听 4. compiler.setupObserver() 创建compiler.observer监听'get', 'set', 'mutate'动作 5. compiler.createBinding(key),将所有的scope里面的字段key都创建Binding实例,而compiler.bindings[key]则指向创建的binding,同时若字段不含有'.'则会进行define方法,该方法先是将刚刚创建的Binding{key].value设置为vm[key],实现每个key的Binding实例都保存下value值。如果该value是对象或则数组,如scope.a为对象,则push到compiler.observables里面;最后现实数据劫持Object.defineProperty 在define里面的Object.defineProperty,setter方法: 若新值不等于旧值,则重新对bindings[key].value设置为新值,同时进行Observer.unobserve旧值和Observer.observe新值,只有value是对象或则数组的时候,才会进入Oberver.oberver/unobserve方法,那对于普普通通的data.a等于字符串这种呢?前文说好的设置data.a的时候通知所有和data.a有关的instances,又发生在那里呢? 答案在在setter里面还通过compiler.observer触发了set事件,set事件里面就有bindings[key].update,而正如上文所说的bindings[key].instances就是directive实例,而directive实例又是什么时候添加进去的呢,且看下面
那对于scope.a为对象,如scope.a = {b: 1},这里的scope.a.b是无法在步骤5中实现的,只有scope.a会出现在步骤5,那scope.a.b要如何创建binding呢? 在步骤5中,进入define方法后,若检测到scope.a是对象,则将binding['a'] push到compiler.observables里面,到了步骤6,遍历到{{a.b}}的时候,会创建bindings['a.b'],但是不会进入define方法里面,无法对bindings['a.b']赋值,和Object.defineProperty数据劫持,没有赋值没有数据劫持,那又要如何实现{{a.b}}以及后面的重新设置a.b的值呢?
通过对于普通的socpe.a = 12,而言打印创建的Vue实例有如下图
compiler下创建了a:Binding构造函数,而这个实例的instances包含了一个Directive,里面存放的value 12,
类似于observables,在compiler里面也会创建compiler.computed数组,在define方法里除了在compiler.observables.push(binding)外,若是对象,并且有$get方法,则是computed,这个版本里面,computed是有$get方法的,well...当然还有$set;
scope: { a: 1, c: { $get: function(){ return this.a } } }
自此Compiler构造函数也就大体如此,具体细节还是要多看源码
在compiler.observables里面,在Observer.observe里面,可以看到当处理对象的typeOf值是对象或则数组都会进行特别处理,若是对象上文已经提过处理方法,若是数组的话又会如何呢? 这里简要概述一下:通过ExpParser.parse方法,将bindings['a[0]'].value设{$get: newFunction('this.a; return this.a[0]')}的形式,后面就和computed类似了,这里有一点需要注意,在observer.js里面,重写了['push','pop','shift','unshift','splice','sort','reverse']等数组方法,当a.push(1)的时候,会触发mutate动作,执行bindings['a'].pub(),从而通知bindings['a[0]']的instances更新,具体内容可以自己去看源码
参考资料
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
相信大家都或多或少接触过Vue,先前就有人介绍学习Vue的源码,提到旧版本的源码行数只不过一千多行,可以一个一个commit学习下去。前段时间为了找下家,一直在用Vue做作品,效率也较以前原生JavaScript要快上许多,后来工作上手了,不禁想看看Vue源码长什么样子的,只是从第一个commit开始读起来较为费时,而Github上面Vue项目能够找到的最早branch是Vue 0.10,然而在发布版本里面,可以发现最早一版本是Vue 0.6.0版本。本文介绍也是从该本版开始,该版本较0.10的要上少40%左右代码。
目标
本文目的在于最短时间内,介绍Vue0.6.0的核心模块源码,数据双向绑定,computed计算属性等。
看完本文之后,能明白Vue框架到底做了些什么,并自行构建一个早期Vue框架的简化版本。
准备
在介绍Vue 源码之前,需要了解Object.defineProperty,通过这个方式,在获取对象字段值时,调用getter方法,而设置对象字段值时候,调用setter方法,来实现数据劫。Object.defineProperty是Vue的基础内容,至今不熟悉的请一定要看Object.defineProperty MDN
思路
对于
如果你要设置data.a的值,并更新渲染DOM,会怎么做呢?
了解Object.defineProperty神奇的setter和getter数据劫持功能后,自然而然是有以下步骤:
实现
看了源码不难发现,创建的实例new Vue({}),指的是ViewModel构造函数,而ViewModel函数如下:
所以comilper.js才是生成Vue的核心所在,下面是简化了compiler构造函数
上面过程分为三个阶段:
准备设置
observer实现
这里实现每个key的数据劫持和事件监听
4. compiler.setupObserver() 创建compiler.observer监听'get', 'set', 'mutate'动作
5. compiler.createBinding(key),将所有的scope里面的字段key都创建Binding实例,而compiler.bindings[key]则指向创建的binding,同时若字段不含有'.'则会进行define方法,该方法先是将刚刚创建的Binding{key].value设置为vm[key],实现每个key的Binding实例都保存下value值。如果该value是对象或则数组,如scope.a为对象,则push到compiler.observables里面;最后现实数据劫持Object.defineProperty
在define里面的Object.defineProperty,setter方法: 若新值不等于旧值,则重新对bindings[key].value设置为新值,同时进行Observer.unobserve旧值和Observer.observe新值,只有value是对象或则数组的时候,才会进入Oberver.oberver/unobserve方法,那对于普普通通的data.a等于字符串这种呢?前文说好的设置data.a的时候通知所有和data.a有关的instances,又发生在那里呢?
答案在在setter里面还通过compiler.observer触发了set事件,set事件里面就有bindings[key].update,而正如上文所说的bindings[key].instances就是directive实例,而directive实例又是什么时候添加进去的呢,且看下面
compiler.compile的实现
那对于scope.a为对象,如scope.a = {b: 1},这里的scope.a.b是无法在步骤5中实现的,只有scope.a会出现在步骤5,那scope.a.b要如何创建binding呢?
在步骤5中,进入define方法后,若检测到scope.a是对象,则将binding['a'] push到compiler.observables里面,到了步骤6,遍历到{{a.b}}的时候,会创建bindings['a.b'],但是不会进入define方法里面,无法对bindings['a.b']赋值,和Object.defineProperty数据劫持,没有赋值没有数据劫持,那又要如何实现{{a.b}}以及后面的重新设置a.b的值呢?
通过对于普通的socpe.a = 12,而言打印创建的Vue实例有如下图
![](https://github.com/funfish/blog/raw/master/images/Vue.PNG)
compiler下创建了a:Binding构造函数,而这个实例的instances包含了一个Directive,里面存放的value 12,
computed原理
类似于observables,在compiler里面也会创建compiler.computed数组,在define方法里除了在compiler.observables.push(binding)外,若是对象,并且有$get方法,则是computed,这个版本里面,computed是有$get方法的,well...当然还有$set;
自此Compiler构造函数也就大体如此,具体细节还是要多看源码
监听数组
在compiler.observables里面,在Observer.observe里面,可以看到当处理对象的typeOf值是对象或则数组都会进行特别处理,若是对象上文已经提过处理方法,若是数组的话又会如何呢?
这里简要概述一下:通过ExpParser.parse方法,将bindings['a[0]'].value设{$get: newFunction('this.a; return this.a[0]')}的形式,后面就和computed类似了,这里有一点需要注意,在observer.js里面,重写了['push','pop','shift','unshift','splice','sort','reverse']等数组方法,当a.push(1)的时候,会触发mutate动作,执行bindings['a'].pub(),从而通知bindings['a[0]']的instances更新,具体内容可以自己去看源码
参考资料
The text was updated successfully, but these errors were encountered: