Vue.js 源码分析(一) 代码结构
關(guān)于Vue
vue是一個(gè)興起的前端js庫(kù),是一個(gè)精簡(jiǎn)的MVVM。MVVM模式是由經(jīng)典的軟件架構(gòu)MVC衍生來(lái)的,當(dāng)View(視圖層)變化時(shí),會(huì)自動(dòng)更新到ViewModel(視圖模型),反之亦然,View和ViewModel之間通過(guò)雙向綁定(data-binding)建立聯(lián)系。
前言
花了一個(gè)月時(shí)間擼了vue2.0的源碼,最近空了一點(diǎn),今天開(kāi)始記錄一下學(xué)習(xí)心得,按照自己理解的代碼的結(jié)構(gòu),分為三部分來(lái)講
基礎(chǔ)篇 ;比如全局配置、模板渲染、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
高級(jí)篇 ;過(guò)濾器、自定義指令、插槽、作用域插槽、異步組件、transition內(nèi)置組件、transition-group內(nèi)置組件、Keep-Alive內(nèi)置組件
每一個(gè)知識(shí)點(diǎn)都會(huì)講得比較詳細(xì),理解源碼需要扎實(shí)的js基礎(chǔ)。如果有遺漏的,最后再做一下補(bǔ)充,如果有問(wèn)題,歡迎留言指出,非常感謝!
從源碼的角度看,Vue和jQuery看是差不多,都只是一個(gè)前端框架, 區(qū)別是一個(gè)是mvc模式,一個(gè)是mvvc模式。Vue是把ES5的defineProperty函數(shù)用到了極致,而jQuery為了兼容低版本IE是沒(méi)有封裝這個(gè)API的,就像搭積木(對(duì)應(yīng)的瀏覽器接口和ECMASCRIPT接口),Vue用到了幾個(gè)新的積木,而jQuery沒(méi)有用到而已。從功能上,Vue應(yīng)該和React、Angular來(lái)對(duì)比,它們和jQuery可以配合使用的。
vue實(shí)例化時(shí)會(huì)把模板(掛載到vue上的el元素對(duì)應(yīng)的DOM節(jié)點(diǎn),或者template屬性)轉(zhuǎn)換成一個(gè)虛擬的VNode,并在更新DOM時(shí)采用diff算法對(duì)DOM樹(shù)進(jìn)行檢測(cè),只更新需要更新的DOM節(jié)點(diǎn),對(duì)于一些靜態(tài)節(jié)點(diǎn)則不進(jìn)行變更,以達(dá)到最快的速度。
采用的是v2.5.16這個(gè)版本的vue.js,本節(jié)先講解Vue源碼的大致結(jié)構(gòu),
代碼結(jié)構(gòu)
vue.js源碼就是一個(gè)立即執(zhí)行匿名函數(shù)表達(dá)式,內(nèi)部定義了一個(gè)vue函數(shù)對(duì)象,組后返回掛載到window的vue屬性上,先復(fù)習(xí)一下立即執(zhí)行匿名函數(shù)表達(dá)式,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
(function(global,msg){
global.alert(msg) //運(yùn)行時(shí)會(huì)彈出一個(gè)窗口,內(nèi)容為:Hello World
})(this,'Hello World')
</script>
</body>
</html>
知道了匿名函數(shù)后,我們來(lái)看看在Vue內(nèi)部的樣式,如下:
(function (global, factory) { //在瀏覽器環(huán)境下global等于全局window
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); //執(zhí)行factory()函數(shù),將返回值Vue傳遞給window.Vue
}(this, (function () {
function Vue(options) { //內(nèi)部定義的Vue函數(shù)
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這個(gè)函數(shù)對(duì)象
})));
這樣執(zhí)行完這個(gè)匿名函數(shù)后就可以通過(guò)window.Vue訪問(wèn)到匿名函數(shù)內(nèi)的Vue函數(shù)對(duì)象了。
writer by:大沙漠 QQ:22969969
這個(gè)匿名函數(shù)內(nèi)部會(huì)在Vue的原型對(duì)象上掛載很多方法和屬性以供我們使用,大致的主線流程如下(為了更好理解,去掉了所有分支)
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); //執(zhí)行factory()函數(shù),將返回值Vue傳遞給window.Vue
}(this, (function () {
'use strict';
function Vue(options) { //VUE的構(gòu)造函數(shù)
if ("development" !== 'production' && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options); //1.實(shí)例化時(shí),先執(zhí)行到這里進(jìn)行初始化
}
function initMixin(Vue) {
Vue.prototype._init = function (options) { //2.再執(zhí)行到這里
/**/
if (vm.$options.el) { //如果有傳入了掛載點(diǎn)的el節(jié)點(diǎn)
vm.$mount(vm.$options.el); //通過(guò)$mount函數(shù)把組件掛載到DOM上面
}
}
}
function lifecycleMixin(Vue) {
Vue.prototype._update = function (vnode, hydrating) { //7.更新操作,把Vnode渲染成真實(shí)DOM
/**/
if (!prevVnode) { //如果prevVnode為空,即初始化時(shí)
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false, vm.$options._parentElm, vm.$options._refElm); //執(zhí)行vm.__patch__方法渲染成真實(shí)的DOM節(jié)點(diǎn)
vm.$options._parentElm = vm.$options._refElm = null;
} else { //如果prevVnode不為空,即更新操作時(shí)
vm.$el = vm.__patch__(prevVnode, vnode); //調(diào)用vm.__patch__進(jìn)行更新操作
}
}
}
function renderMixin(Vue) {
/**/
Vue.prototype._render = function () { //6.把rener渲染成Vnode
}
}
function mountComponent(vm, el, hydrating) { //5.掛載組件 vm:Vue實(shí)例 el:真實(shí)的DOM節(jié)點(diǎn)對(duì)象
/**/
var updateComponent;
if ("development" !== 'production' && config.performance && mark) {
/**/
} else {
updateComponent = function () {vm._update(vm._render(), hydrating);}; //渲染成Vnode并轉(zhuǎn)換為真實(shí)DOM
}
new Watcher(vm, updateComponent, noop, null, true); //Watcher實(shí)例創(chuàng)建后會(huì)通過(guò)get()方法執(zhí)行updateComponent()方法的
/**/
}
Vue.prototype.__patch__ = inBrowser ? patch : noop;
Vue.prototype.$mount = function (el, hydrating) { //4.原型上的掛載(runtime-only版本)
el = el && inBrowser ? query(el) : undefined; //進(jìn)行一些修正,因?yàn)閞untime-only版本是從這里開(kāi)始執(zhí)行的
return mountComponent(this, el, hydrating) ////再調(diào)用mountComponent方法
};
var mount = Vue.prototype.$mount; //保存原型上的$mount函數(shù)
Vue.prototype.$mount = function (el, hydrating) { //3.解析源碼(Runtime+Compiler版本用的)
/*中間進(jìn)行模板的解析*/
return mount.call(this, el, hydrating) //最后執(zhí)行mount
};
return Vue; //最后返回Vue函數(shù)對(duì)象,會(huì)作為一個(gè)Vue屬性掛載到window上
})));
后面源碼講解時(shí)會(huì)仔細(xì)講每個(gè)細(xì)節(jié)的
總結(jié)
以上是生活随笔為你收集整理的Vue.js 源码分析(一) 代码结构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 坦克动画里的坦克怎么画
- 下一篇: 制定国家安全法体现了总体国家安全观的基本