vue.js是一个很是优秀的前端开发框架,不是我说的,你们都知道。本人也使用过vue.js开发过移动端SPA应用,仍是学习阶段,经验尚浅,能力有限。不过我也懂得只会使用轮子不知因此然是远远不够的,凭本身浅薄的见识,斗胆写一篇略微深刻的一点文章。html
首先我如今的能力,独立阅读源码仍是有很大压力的,所幸vue写的很规范,经过方法名基本能够略知一二,里面的原理不懂的地方多方面查找资料,本文中不规范不正确的地方欢迎指正,学生很是愿意接受各位前辈提出宝贵的建议和指导。前端
写这篇文章时GitHub上vue最新版是v2.5.13,采用了flow做为类型管理工具,关于flow相关内容选择性忽略了,不考虑类型系统,只考虑实现原理,写下这篇文章。vue
本文大概涉及到vue几个核心的地方:vue实例化,虚拟DOM,模板编译过程,数据绑定。node
下图为最新版本vue的生命周期react
首先从建立vue实例开始,vue的构造函数在src/core/instance/index.js文件中,不过在src/core/index.js中对其进行了一系列处理,其中关于服务器环境渲染等相关内容在此不作讨论。这里有initGlobalAPI方法在src/core/global-api/index.js中,此方法初始化了一些vue提供的的全局方法,set,delete,nextTick等等,并初始化了和处理mixins,extends等相关功能的方法。如今回过来从全局来看src/core/instance/index.js,在其中还包括几个方法,它们初始化了vue原型上面提供的一些方法,而vue的构造函数中调用的就是原型上面的_init方法。git
研究vue的实例化就要研究_init方法,此方法定义在src/core/instance/init.js下的initMixin中,里面是对vue实例即vm的处理。其中包括开发环境下的代理配置等一些列处理,并处理了传递给构造函数的参数等,重点在一系列方法github
initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created')
其实从名字就能看出这些方法都是作什么的:初始化生命周期,初始化事件,初始化渲染,触发执行beforeCreate生命周期方法,初始化data/props数据监听,触发执行created生命周期方法。web
此时,对应到生命周期示例图,created方法执行结束,接下来判断是否传入挂载的el节点,若是传入的话此时就会经过$mount
函数把组件挂载到DOM上面,整个vue构造函数就执行完成了。以上是vue对象建立的基本流程,其中有几个重要的关键点也是vue的核心所在,下面来重点探讨一下。算法
上面提到了挂载的$mount
函数,此函数的实现与运行环境有关,在此只看web中的实现。该方法在src/platforms/web/runtime/index.js中定义,挂载在vue的原型上。实现只有简单的两行,判断运行环境为浏览器,调用工具方法查找到el对应的DOM节点,再调用位于src/core/instance/lifecycle.js下的mountComponent方法来实现挂载,这里就涉及到了挂载以前的处理问题。对于拥有render(JSX)函数的状况,组件能够直接挂载,若是使用的是template,须要从中提取AST渲染方法(注意若是使用构建工具,最终会为咱们编译成render(JSX)形式,因此无需担忧性能问题),AST即抽象语法树,它是对真实DOM结构的映射,可执行,可编译,可以把每一个节点部分都编译成vnode,组成一个有对应层次结构的vnode对象。有了渲染方法,下一步就是更新DOM,注意并非直接更新,而是经过vnode,因而涉及到了一个很是重要的概念。api
虚拟DOM技术是一个很流行的东西,现代前端开发框架vue和react都是基于虚拟DOM来实现的。虚拟DOM技术是为了解决一个很重要的问题:浏览器进行DOM操做会带来较大的开销。
操做DOM是不可避免的,常规的操做也不会有任何问题,可是经验不足的开发者每每很容易写出大量的多余或重复的DOM操做,成为前端性能优化中重要的问题。想提高效率,咱们就要尽量减小DOM操做,只修改须要修改的地方。要知道js自己运行速度是很快的,而js对象又能够很准确地描述出相似DOM的树形结构,基于这一前提,人们研究出一种方式,经过使用js描述出一个假的DOM结构,每次数据变化时候,在假的DOM上分析数据变化先后结构差异,找出这个最小差异而且在真实DOM上只更新这个最小的变化内容,这样就极大程度上下降了对DOM的操做带来的性能开销。
上面的假的DOM结构就是虚拟DOM,比对的算法成为diff算法,这是实现虚拟DOM技术的关键,在vue初始化时,首先用JS对象描述出DOM树的结构,用这个描述树去构建真实DOM,并实际展示到页面中,一旦有数据状态变动,须要从新构建一个新的JS的DOM树,对比两棵树差异,找出最小更新内容,并将最小差别内容更新到真实DOM上。
有了虚拟DOM,下面一个问题就是,何时会触发更新,接下来要介绍的,就是vue中最具特点的功能--数据响应系统及实现。
记得vue.js的做者尤雨溪老师在知乎上一个回答中提到过本身创做vue的过程,最初就是尝试实现一个相似angular1的东西,发现里面对于数据处理很是不优雅,因而创造性的尝试利用ES5中的Object.defineProperty来实现数据绑定,因而就有了最初的vue。vue中响应式的数据处理方式是一项颇有价值的东西。
关于响应式的实现原理,vue官网上面其实有具体介绍,下面是一张官方图片:
vue会遍历此data中对象全部的属性,并使用Object.defineProperty把这些属性所有转为getter/setter,而每一个组件实例都有watcher对象,它会在组件渲染的过程当中把属性记录为依赖,以后当依赖项的 setter被调用时,会通知watcher从新计算,从而导致它关联的组件得以更新。这就是响应实现的基本原理,Object.defineProperty没法shim,因此vue不支持IE8及如下不支持ES5的浏览器。
一个简单的demo:
<input type="text" id="inputName"> <br> <span id="showName"></span>
// 传统方式处理数据 // document.getElementById('inputName').addEventListener('keyup', function (e) { // document.getElementById('showName').innerText = e.target.value; // }); // 利用Object.defineProperty自动响应数据 var obj = {}; Object.defineProperty(obj, 'name', { get: function () { }, set: function (val) { document.getElementById('showName').innerText = val; } }); document.getElementById('inputName').addEventListener('keyup', function (e) { obj.name = e.target.value; });
这个例子并非什么复杂的实现,可是却体现了vue最核心的东西,咱们能够发现,Object.defineProperty下的get和set是能够自动相应的,基于此vue实现了一套基于数据驱动视图的自动响应系统,使得开发模型获得了极大的简化。
至此,本文就暂时结束了,水平通常能力有限,后面随着理解的加深会更深刻去学习。更多文章欢迎访问我的网站。