關於Vuehtml
vue是一個興起的前端js庫,是一個精簡的MVVM。MVVM模式是由經典的軟件架構MVC衍生來的,當View(視圖層)變化時,會自動更新到ViewModel(視圖模型),反之亦然,View和ViewModel之間經過雙向綁定(data-binding)創建聯繫。前端
前言vue
花了一個月時間擼了vue2.0的源碼,最近空了一點,今天開始記錄一下學習心得,本人按照本身理解的代碼的結構,分爲三部分來說node
基礎篇 ;好比全局配置、模板渲染、data、method、computed、watch、生命週期、ref、組件原理、組件-props、組件自定義事件算法
指令篇 ;好比v-bind、v-on、v-if、v-else、v-else-if、v-for、v-html、v-text、v-once、v-pre、v-model、v-show瀏覽器
高級篇 ;過濾器、自定義指令、插槽、做用域插槽、異步組件、transition內置組件、transition-group內置組件、Keep-Alive內置組件前端框架
每個知識點都會講得比較詳細,理解源碼須要紮實的js基礎。最後若是有遺漏的,最後再作一下補充,若是有問題,歡迎留言指出,很是感謝!架構
從源碼的角度看,Vue和jQuery看是差很少,都只是一個前端框架, 區別是一個是mvc模式,一個是mvvc模式。Vue是把ES5的defineProperty函數用到了極致,而jQuery爲了兼容低版本IE是沒有封裝這個API的,就像搭積木(對應的瀏覽器接口和ECMASCRIPT接口),Vue用到了幾個新的積木,而jQuery沒有用到而已。從功能上,Vue應該和React、Angular來對比,它們和jQuery能夠配合使用的。mvc
vue實例化時會把模板(掛載到vue上的el元素對應的DOM節點,或者template屬性)轉換成一個虛擬的VNode,並在更新DOM時採用diff算法對DOM樹進行檢測,只更新須要更新的DOM節點,對於一些靜態節點則不進行變動,以達到最快的速度。框架
本人採用的是v2.5.16這個版本的vue.js,本節先講解Vue源碼的大體結構,
代碼結構
vue.js源碼就是一個當即執行匿名函數表達式,內部定義了一個vue函數對象,組後返回掛載到window的vue屬性上,先複習一下當即執行匿名函數表達式,以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> (function(global,msg){ global.alert(msg) //運行時會彈出一個窗口,內容爲:Hello World })(this,'Hello World') </script> </body> </html>
知道了匿名函數後,咱們來看看在Vue內部的樣式,以下:
(function (global, factory) { //在瀏覽器環境下global等於全局window typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); //執行factory()函數,將返回值Vue傳遞給window.Vue }(this, (function () { function Vue(options) { //內部定義的Vue函數 if ("development" !== 'production' && !(this instanceof Vue)) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); } return Vue; //返回Vue這個函數對象 })));
這樣執行完這個匿名函數後就能夠經過window.Vue訪問到匿名函數內的Vue函數對象了。
這個匿名函數內部會在Vue的原型對象上掛載不少方法和屬性以供咱們使用,大體的主線流程以下(爲了更好理解,去掉了全部分支)
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); //執行factory()函數,將返回值Vue傳遞給window.Vue }(this, (function () { 'use strict'; function Vue(options) { //VUE的構造函數 if ("development" !== 'production' && !(this instanceof Vue)) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); //1.實例化時,先執行到這裏進行初始化 } function initMixin(Vue) { Vue.prototype._init = function (options) { //2.再執行到這裏 /**/ if (vm.$options.el) { //若是有傳入了掛載點的el節點 vm.$mount(vm.$options.el); //經過$mount函數把組件掛載到DOM上面 } } } function lifecycleMixin(Vue) { Vue.prototype._update = function (vnode, hydrating) { //7.更新操做,把Vnode渲染成真實DOM /**/ if (!prevVnode) { //若是prevVnode爲空,即初始化時 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false, vm.$options._parentElm, vm.$options._refElm); //執行vm.__patch__方法渲染成真實的DOM節點 vm.$options._parentElm = vm.$options._refElm = null; } else { //若是prevVnode不爲空,即更新操做時 vm.$el = vm.__patch__(prevVnode, vnode); //調用vm.__patch__進行更新操做 } } } function renderMixin(Vue) { /**/ Vue.prototype._render = function () { //6.把rener渲染成Vnode } } function mountComponent(vm, el, hydrating) { //5.掛載組件 vm:Vue實例 el:真實的DOM節點對象 /**/ var updateComponent; if ("development" !== 'production' && config.performance && mark) { /**/ } else { updateComponent = function () {vm._update(vm._render(), hydrating);}; //渲染成Vnode並轉換爲真實DOM } new Watcher(vm, updateComponent, noop, null, true); //Watcher實例建立後會經過get()方法執行updateComponent()方法的 /**/ } Vue.prototype.__patch__ = inBrowser ? patch : noop; Vue.prototype.$mount = function (el, hydrating) { //4.原型上的掛載(runtime-only版本) el = el && inBrowser ? query(el) : undefined; //進行一些修正,由於runtime-only版本是從這裏開始執行的 return mountComponent(this, el, hydrating) ////再調用mountComponent方法 }; var mount = Vue.prototype.$mount; //保存原型上的$mount函數 Vue.prototype.$mount = function (el, hydrating) { //3.解析源碼(Runtime+Compiler版本用的) /*中間進行模板的解析*/ return mount.call(this, el, hydrating) //最後執行mount }; return Vue; //最後返回Vue函數對象,會做爲一個Vue屬性掛載到window上 })));
、後面源碼講解時會仔細講每一個細節的