Giter Site home page Giter Site logo

terribleness / vue-read Goto Github PK

View Code? Open in Web Editor NEW
17.0 1.0 5.0 106 KB

Vue.js源码阅读(2.4.4版本)共五章。从运行时、编译时、双向数据绑定、虚拟节点、异步队列等方面解读原理。

JavaScript 99.75% HTML 0.25%
vue vuejs javascript font-end

vue-read's Introduction

Vue.js 2.4.4版本,源码解读

提纲分为这五章:

1、Vue构造函数:实例方法和静态方法

2、Vue的响应原理:Observe、Watcher、Dep关系

3、Vue的编译过程:template、AST、Render过程

4、Vue的虚拟节点:VNode、patch算法

5、Vue的异步队列:nextTick调度




一、Vue构造函数:实例方法和静态方法

Vue运行时---第一步扩展实例方法(点击链接)

总结Vue扩展实例方法流程为:

1、 定义function Vue()

2、 initMixin扩展vm._init

3、 stateMixin扩展vm.$data、vm.$props、vm.$set、vm.$delete、vm.$watch

4、 eventsMixin扩展vm.$on、vm.$once、vm.$off、vm.$emit

5、 lifecycleMixin扩展vm._update、wm.$forceUpdate、vm.$destroy

6、 renderMixin扩展vm.$nextTick、vm._render和一些工具方法

Vue运行时---第二步扩展静态方法(点击链接)

总结Vue扩展静态方法流程为:

1、扩展了工具方法Vue.util.warn、Vue.util.extend、Vue.util.mergeOptions、Vue.util.defineReactive

2、扩展了Vue.set 、Vue.delete、Vue.nextTick

3、扩展了Vue.options.conponents、Vue.options.directives、Vue.options.filters

4、扩展了Vue.options._base 等于 Vue构造函数本身

5、扩展了Vue.options.components为默认{name、abstract、props、created、destroyed、watch、render}对象

6、initUse(Vue)扩展了Vue.use

7、initMixin(Vue)扩展了Vue.mixin

8、initExtend(Vue)扩展了Vue.cid属性和Vue.extentd方法

9、initAssetsRegisters(Vue)扩展了Vue.componet、Vue.directive、Vue.filter三个方法

10、‘core/index’中扩展了Vue.$isServer和Vue.$ssrContext的get方法

11、‘runtime/index中扩展了’Vue.config的配置方法mustUseProp、isReservedTag、isReservedAttr、getTagnamespace、isUnknownElement

12、‘runtime/index’中使用了平台相关的指令和组件覆盖之前生成的Vue.options.directives和Vue.options.components

13、‘runtime/index’中增加实例方法vm.patch

14、‘runtime/index’中扩展了实例方法vm.$mounted




二、Vue的响应原理:Observe、Watcher、Dep关系

Vue的响应原理:Observe、Watcher、Dep关系(点击链接)

1、 每个data中属性元素会有一个闭包Dep的实例,里面存放Watcher实例

2、 当template编译为render函数时,会调用属性的get方法,将每一次调用都生成一个watcher实例,并压入dep中,产生订阅效果。

3、 当设置data中属性值的时候,调用属性的set方法通知所有订阅该属性的元素更新

4、 而且props,computed,watch内同理是增加watcher产生订阅和发布的效果。




三、Vue的编译过程:template、AST、Render过程

Vue的编译过程:template、AST、Render过程(点击链接)

1、 template先判断是否存在render函数,如果不存在则判断template模板,最后判断el标签。

2、 const ast = parse(template.trim(),options)是生成抽象语法树。

3、 optimize(ast,options)优化AST树。

4、 const code = generate(ast, options)这一句之后,生成render函数。




四、Vue的虚拟节点:VNode、patch算法

①Vue的虚拟节点Vnode过程(点击链接)

清晰描述了“tempalte”转化为“ast抽象语法树”转化为“render函数”转化为“vnode虚拟树”的过程。

vue-read's People

Contributors

terribleness avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

vue-read's Issues

Vue的响应原理:Observe、Watcher、Dep关系

前面两篇讲述了Vue运行时---第一步扩展实例方法(点击链接)Vue运行时---第二步扩展静态方法(点击链接),今天第三节解读## Vue中双向数据绑定的原理。
我们先回到’instance/index’文件中,如下图

image

我们第一节已经说到 initMixin(Vue)只做了一件事,就是给Vue扩展了实例方法_init

image

接下来我们进入Vue比较重要的环节,比如我们举一个例子,
image

定义了data和template,我们这节就要搞清楚data值变化和template生成的html是如何相应变化的。如图一,new Vue之后,进入function Vue构造函数,执行this._init(options),这节其实也就是主要要搞清楚_init 方法,我们现在看到图二中_init 方法,

image

1、 我们先看到第一个红框处,mergeOptions是方法是用来合并options参数的,第一句resolveConstructorOptios(vm.constructor)将得到第二节中说道buildInComponents默认参数,并用传入的options取代默认参数。
2、 initLifecycle(vm)
我们找到’instance/lifeCycle’文件如下:

image

我们可以看到是初始化工作,第一句options.parent在buildInComponent中没有,如果自己传递了那么vm.$parent就是你传递额parent参数。如果没有传递那么vm.$parent就是null而且vm.$vm就是vm本身。接着又设置vm.$refs ={},vm._watch = null,vm._inactive = null,vm._directInactive = false,vm._isMounted = false,vm._isDestroyed = false,vm._isBeingDestroyed = false。
3、 initEvents(vm)、initRender(vm)、initInjections(vm)我就不一一写了,都是初始化,我们主要说initState(vm)
4、 initState(vm)这里面就是定义了Observe、Watcher、Dep的关系,进入看到initState(vm)函数内容

image

我主要讲解initData(vm)这个方法,其他initiativeProps、initMethods、iniComputed、initWatch后续再说。进入initData(vm)函数,如下图

image

看到observe(data,true)这一句,这里的data还是之前的的
image

进入observe方法后,如下图

image

这里出现了new observer(value)其实就是要把data传入了new Observer中。进入Observer中的构造函数,如下图

image

把data数据又传入了this.walk方法中,如下图

image

这里data对象{a:1,b:2}被取出a、b属性和各自值分别执行了

image

进入defineDeactive方法后,如下图出现最重要部分了。这也是为什么Vue不支持IE8的原因了,因为IE8中不支持Object.defineProperty

image

1、 先实例化 var dep = new Dep();这里使用了闭包,因为同一个data属性会对应到html中该data属性。
2、 取得data[‘a’]的属性描述,判断该属性是否能配置。这里先插入一句对象属性的描述规则,比如person对象的name属性是这样定义的。如下:

image

3、 接着取出原声getter和setter方法,我们先看第二个红框set方法当我们写{a:1}的时候,会自动自定元素的set方法,其中set方法的过程为:
一、 取出现有值,比较现有值和跟新值是否一样,
二、 先调用setter.call(obj.newVal)将obj的属性a修改为值1
三、 如果新值是一个负责类型递归执行observe(newVal)
四、 调用dep.notify方法,订阅-发布模型,通知所有元素更新操作
4、 进入dep.notify内容,看定于-发布模型的具体内容如下:

image

取出属性a对应的所有Watcher对象数组,循环所有对象挨个出发update函数,其实subs是一个Watcher实例数组。我们可以看Dep类的内容。

image

现在我们讲述了set方法的内容以及触发订阅者更新。那么subs的watcher监听又是哪里来的呢,这就要回到上上上图的get方法了,我们新截一张get方法图片如下,方便说明

image

说明get前,我们先要简单说明一下template编译为javascript函数的结果,我们回到举例
image

这里的template是一个字符串,会编译为javascript函数,过程我们后面会用一节来讲解,我们就只说这个例子中template编译后的结果。如下:

image

如果这就是将template字符串编译为javascript函数的过程,如果你对_c、_v、_s函数有疑问,你可以回忆第一节扩展实例方法的最后一个图renderMixin中添加的实例方法。
好了,现在tempate已经转换为javascript函数了,转换过程我以后章节再详细说明,第一句with(this)就是with(vm),那么其中的a、b、a出现就是wm.a和wm.b和wm.a了,这样就会触发data函数中的a、b属性的get方法了,当然这里是做了代理的,wm.a等同于wm.data.a,这样我们编译模版后,就会自动触发属性的get方法,我们再回到get方法中如下:
image

比如第一个wm.a进入get方法后,dep.depend方法执行,如下

image

这个this就是dep实例,而Dep.target是watcher,所以每次执行vm.a都会将一个dep实例压入watcher中,即订阅目的,而在修改wm.a值时,又会循环所有watcher通知所有元素更新,即发布目的。这就是Watcher、Dep的关系。

总结:

1、 每个data中属性元素会有一个闭包Dep的实例,里面存放Watcher实例
2、 当template编译为render函数时,会调用属性的get方法,将每一次调用都生成一个watcher实例,并压入dep中,产生订阅效果。
3、 当设置data中属性值的时候,调用属性的set方法通知所有订阅该属性的元素更新
4、 而且props,computed,watch内同理是增加watcher产生订阅和发布的效果。

Vue的编译过程:template、AST、Render过程

第三节Vue双向数据绑定:Observe、Watcher、Depd的关系”,关键是Object.defineProperty的set和get方法构造了订阅-发布模型。其中属性set方法的是在data、props、watch、computed等中赋值时发布更新,get方法是在编译template模板生成render函数后自动调用了get方法产生订阅watcher。

那么template模板是如何编译成render函数的呢?

这一节我们主要分析Vue中编译过程:template、AST(抽象语法树)、render过程。
我们看到源代码中’instance/init.js’文件中,如下:
image

这个_init函数已经出现很多次了,我们上一篇文章,主要是分析了其中initState(vm)方法中如何构建双向数据绑定的,而Vue中的编译过程我们主要是看到最后一句 wm.$mount(vm.$options.el),在第一篇文章中,Vue扩展实例方法我们提到过Vue.prototype.$mount,在’runtime/index.js’文件中,如下:
image

那么如果编译过的template就会调用这个$mount函数去渲染页面,但是如果没有编译过的template呢,我们需要先看到’web/entry-runtime-with-compiler.js’文件,这里面会先去做template编译过程再调用$mount函数,如下:
image

第一个红框是先保存之前的$mount函数,也就是在’runtime/index.js’中定义的Vue.prototype.$mount函数,第三个红框是使用这个保存下来的mount函数继续执行,那么第二个红框就是我们这节需要分析的重点了,这就是Vue的编译过程。
我们进入到compileToFunctions函数中,如下:
image

再进入createCompiler中,如下:
image

这个createCompiler是createCompilerCreator函数的返回值,但是参数是一个baseCompile函数。进入createCompilerCreator函数中。
image

我们看到createCompilerCreator返回了一个createCompiler函数。这里有点绕,但是思路清晰一点,实际上是上上图中 function baseCompile函数实际在做编译工作,参数是vue的实例化的参数options,而上图中createCompiler函数中前一部分实际上是对options参数做了一个替换工作。所以实际编译工作看到baseCompile(template,finalOptions)这句话,对了这里要插入一句template的来历,因为一些面试题中提到,Vue实例化过程中,el属性和template和render都有内容时,实际渲染的html是以哪个为准?这个问题我们看到’entry-runtime-with-compiler.js’这个文件,$mount函数部分如下:
image

这段话的目的就是去确认template实际内容时以el、template、render函数中哪一个为准?
1、首先判断render函数是否存在,如果存在则以render函数为准,直接渲染
2、如果render函数不存在,则检查template模板是否存在,如果存在template则编译template模板成为render函数
3、如果template不存在,则取得el标签中innerHTML作为template模板,再生成render函数
4、如果el不存在,则生成一个空的div标签。

言归正传,我们回到baseCompile函数中,看编译template成render函数过程。
image

函数第一句const ast = parse(template.trim(),options)是生成抽象语法书,比如例子:
image

生成的AST如下:
image

第二句是optimize(ast,options)优化AST树
第三句是const code = generate(ast, options)这一句之后,生成render函数了如下:
image

过程就是这样,生成的render函数继续执行后面的工作,我们具体来看看这三句代码的内容。
image

parse(template.trim(),options)

待续。。

optimize(ast,options)

待续。。

const code = generate(ast, options)

待续。。

Vue虚拟节点Vnode过程

上一节我们分析了“Vue的编译过程:template、AST、Render过程”,通过这一步之后,Vue把template模板编译成了render可移植性函数。
这一节我们分析“Vue的虚拟节点:VNode、patch算法”。这一部分的目的是把render函数生成vnode虚拟节点,在更新时找到最小变化的dom结点,生成新的dom树。
我们看到’web/runtime.js’文件中,如下
image

vm.options.render存储了执行生成的render函数生成,接下来就是执行$mount函数了,我们把这一节分为两部分“Vnode”和“patch”,Vnode部分是分析render如何生成虚拟dom树,patch部分是如何获得新的dom。
Vnode
如上图我们进入’lifecycle.js’中查看mountComponet函数如下:
image

红框这句就包含了vnode和path两部分,其中vm._render是生成vnode的,vm._update是包含patch的。我们先查看vm._render函数内容。如下:
image

我们看到红框这句vnode = render.call(vm._renderProxy,vm.$createElement),我们先来看看render函数,如下:

image

其实render生成vnode的过程就是执行render函数,其中_c,_v,_s都是第一节“Vue扩展实例方法”定义好的,我们现在分别看看_c,_v,_s函数的内容,在’instance/render.js’函数中。
image

_s函数内容就是转换字符串过程

在’shared/utils’中能找到工具方法,如下:
image

_v函数是生成一个vnode结点

在’core/vdom/vnode.js’能找到方法,如下:
image

Vnode的内容时什么嗯,我们继续看。Vnode结构就是实际上一个html中元素可能存在的属性集合。如下:
image

_c就是生成一个vnode树

在第一个参数是html标签内容,第二个参数是vnode数组。所有最终生成的vnode树结构如下:
image

这就生成vnode树,我们可以吧前后过程联系起来,从“tempalte”转化为“ast抽象语法树”转化为“render函数”转化为“vnode虚拟树”过程用实际数据结构表现出来,如下:

第一步元素template字符串:

image

第二步ast抽象语法树:

image

第三步render函数:

image

第四步vnode虚拟dom树:

image

总结:
清晰描述了“tempalte”转化为“ast抽象语法树”转化为“render函数”转化为“vnode虚拟树”的过程

Vue运行时--第二步扩展静态方法

我们上一节讲了### Vue运行时---第一步扩展实例方法(链接)。回到’core/index’文件中,接着往下面看initGlobalAPI(Vue)方法,该方法为Vue绑定静态方法。
image

我们进入’./global-api/index’找到initGlobalAPI定义如下:
image

1、先看第一部分扩展了Vue.util工具方法warn、extend、mergeOptions、defineReactive
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
2、接着扩展了Vue.set 、Vue.delete、Vue.nextTick这三个方法。
3、定义了Vue.options为空对象
4、ASSET_TYPES是一个数组,包含[conponent、directive、filter],于是forEach之后Vue扩展了Vue.options.conponents、Vue.options.directives、Vue.options.filters。
5、定义Vue.options._base 等于 Vue构造函数本身
6、接下来extentd(Vue.options.components,buildInComponents),这一句就是要去用默认的components对象去覆盖Vue.options.components对象,所以经过这一句之后,Vue.options.components的内容变为了buildInComponents的内容,如下:
image

上面这数据name、props、created、destroyed、watch、render就很熟悉了,再一般写Vue的时候会用到几个属性。
继续看第二张图扩展Vue的静态方法,最后initUse(Vue)、initMixin(Vue)、initExtend(Vue)、initAssetsRegisters(Vue)这四个方法。

initUse(Vue)

image

我们看到就一个方法Vue.use,具体里面做了哪些事情,我们后面具体讲。

initMixin(Vue)

image

同样只有一个方法Vue.mixin

initExtentd(Vue)

image

增加了一个静态属性 Vue.cid = 0
扩展了Vue.extend方法

initAssetsRegisters

image

这里是一个forEach循环,而且前面提到ASSET_TYPES是一个数组[component、directive、filter]三个常量字符串,于是循环之后,扩展了Vue.componet、Vue.directive、Vue.filter三个方法

这样initGlobalAPI(Vue)结束了,我们回到’core/index’文件中initGlobalAPI往下看:
image

红框位置扩展了Vue.$isServer和Vue.$ssrContext的get方法
再退回到’runtime/index’文件中
image

首先扩展了Vue.config的配置方法mustUseProp、isReservedTag、isReservedAttr、getTagnamespace、isUnknownElement
接着使用了平台相关的指令和组件覆盖之前生成的Vue.options.directives和Vue.options.components
接着增加了vue的事例属性patch或者noop方法,具体内容我们这里不讲解,用到的时候在具体说清楚。
最后最重要的是扩展了vue的事例vm.$mount方法

总结:

1、扩展了工具方法Vue.util.warn、Vue.util.extend、Vue.util.mergeOptions、Vue.util.defineReactive
2、扩展了Vue.set 、Vue.delete、Vue.nextTick
3、扩展了Vue.options.conponents、Vue.options.directives、Vue.options.filters
4、扩展了Vue.options._base 等于 Vue构造函数本身
5、扩展了Vue.options.components为默认{name、abstract、props、created、destroyed、watch、render}对象
6、initUse(Vue)扩展了Vue.use
7、initMixin(Vue)扩展了Vue.mixin
8、initExtend(Vue)扩展了Vue.cid属性和Vue.extentd方法
9、initAssetsRegisters(Vue)扩展了Vue.componet、Vue.directive、Vue.filter三个方法
10、‘core/index’中扩展了Vue.$isServer和Vue.$ssrContext的get方法
11、‘runtime/index中扩展了’Vue.config的配置方法mustUseProp、isReservedTag、isReservedAttr、getTagnamespace、isUnknownElement
12、‘runtime/index’中使用了平台相关的指令和组件覆盖之前生成的Vue.options.directives和Vue.options.components
13、‘runtime/index’中增加实例方法vm.patch
14、‘runtime/index’中扩展了实例方法vm.$mounted

Vue运行时---第一步扩展实例方法

Vue.js发布分为三个版本:
1、 完整版:同时包含编译器和运行时的版本。
2、 编译器:用来将模版字符串编译成为JavaScript渲染函数的代码。
3、 运行时:用来创建Vue实例、渲染并处理虚拟DOM等代码。基本上就是出去编译器的其他一切。
我们先阅读运行时版本,了解Vue的创建过程。之后再阅读编译器版本,看编译其如何将html模版编译成为javascript代码。其实我们在使用vue-cli开发时,每当我们使用npm run build打包时,vue-loader会自动将template标签内的html模版编译成javascript代码,用户访问网站的js文件内,只会执行运行时版本,因为已经编译完成了。
下载Vue.js 2.4.4版本,源码中,发布运行时版本的入口是entry-runtime.js,这个文件中直接导入runtime文件夹中index.js文件。

image

runtime/index.js是Vue针对Web平台添加一些特定方法,其中最重要的就是Vue.protorype.$mount方法了,我们看到runtime/index.js中第一句 import Vue from ‘core/index’,core文件夹就是Vue核心代码了(如下图)。

image

1、’core/index’中第一句import Vue from ‘./instance/index’,是扩展Vue的实例方法。
2、initGlobalApi(Vue),是扩展了Vue的静态方法。

最后返回了,扩展了实例方法和静态方法的Vue函数。

扩展实例方法

进入’./instance/index’,代码很简单(如下图),

image

首先定义了真正的function Vue(options),之后分别制定initMixin(Vue),stateMixin(Vue),eventsMixin(Vue),lifecycleMixin(Vue),renderMixin(Vue)这5个方法,为Vue函数添加实例不同用途的实例方法。

InitMixin(Vue)

image

这个函数里面做了一件事,就是给Vue扩展了vm._init实例方法。这个方法,也是在上图Vue构造函数中调用的this._init(options)

stateMixin(Vue)

这个函数里面主要就给Vue扩展了vm.$data、vm.$props、vm.$set、vm.$delete、vm.$watch这五个方法,具体每个函数里面干了什么,我们先不看。
image

EventsMixin(Vue)

image

这个函数里面主要给Vue扩展了vm.$on、vm.$once、vm.$off、vm.$emit四个方法。

lifecycleMixin(Vue)

image

这个函数里面给Vue扩展了vm._update、wm.$forceUpdate、vm.$destroy三个方法。

RenderMixin(Vue)

image

这个函数里面给Vue扩展了vm.$nextTick、vm._render、vm._o、vm._n、vm._s、vm._l、vm._t、vm._q、vm._i、vm._m、vm._f、vm._k、vm._b、vm._v、vm._e、vm._u、vm._g这些方法。

总结:
Vue扩展实例方法流程为:
1、 定义function Vue()
2、 initMixin扩展vm._init
3、 stateMixin扩展vm.$data、vm.$props、vm.$set、vm.$delete、vm.$watch
4、 eventsMixin扩展vm.$on、vm.$once、vm.$off、vm.$emit
5、 lifecycleMixin扩展vm._update、wm.$forceUpdate、vm.$destroy
6、 renderMixin扩展vm.$nextTick、vm._render和一些工具方法

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.