深入淺出MV*框架源碼(一):從一個(gè)高仿庫(kù)Moon看起

前言

徹底擺脫秋招之后组底,我也來到了公司實(shí)習(xí)缀皱,我們主要使用的框架是Vue.js,如果自然而然地碌补,我需要學(xué)習(xí)這個(gè)框架虏束。平心而論,vue的api數(shù)量可以說是angular的1/10不到厦章,雖然簡(jiǎn)潔镇匀,但內(nèi)部實(shí)現(xiàn)并不簡(jiǎn)單。

隨便下個(gè)斷點(diǎn)沖進(jìn)vue源碼之處袜啃,都能感受到里面結(jié)構(gòu)的復(fù)雜汗侵,對(duì)于我等菜鳥來說實(shí)在是無法多待一會(huì)兒的。

所以群发,本著面對(duì)復(fù)雜問題先將其簡(jiǎn)單化的思維方式晰韵,我和另一位朋友決定先從一個(gè)將Vue核心api實(shí)現(xiàn)的高仿庫(kù)摸透,之后再來啃Vue源碼熟妓。

為什么選擇Moon

市面上有很多MVVM庫(kù)雪猪,Vue也有早期版本在github上供人研究,為什么我們選擇了Moon呢起愈?

原因有三:

  1. Moon源碼只有2000多行抬虽,卻實(shí)現(xiàn)了實(shí)例屬性(data、computed阐污、methods休涤、template)、render笛辟、指令功氨、組件以及組件通信疑故、生命鉤子、slot等大部分Vue核心功能钦铁,在各大MVVM庫(kù)中算是研究性價(jià)比最高的(目前為止我能找到的)。
  2. Moon的作者異常活躍阅虫,我們一旦有啥問題就可以和他郵件交流窝革。
  3. Moon相比Vue的早期版本也有它獨(dú)到的思想,作者本身實(shí)力也很厲害侮攀。比如他針對(duì)靜態(tài)HTML元素渲染做的優(yōu)化

研究源碼一般而言有兩種方法:自頂向下和自底向上蚪腐。

自頂向下就是先從最抽象的層面上觀察源碼正林,從它的結(jié)構(gòu)涵但、設(shè)計(jì)著手,再一步步深入到各個(gè)代碼模塊->子程序->具體語(yǔ)句劫侧。

自底向上就是特意選擇一個(gè)地方打一個(gè)斷點(diǎn)拳球,然后運(yùn)行代碼,通過一步步調(diào)試觀察它所經(jīng)過的調(diào)用棧和所有的中間變量。

我選擇綜合兩種方法來研究源碼:先自頂向下把它的代碼結(jié)構(gòu)摸清楚,再自底向上打斷點(diǎn)一步步照亮黑暗區(qū)域。和我們平時(shí)打游戲需要一個(gè)大地圖提供全局觀,然后自己探索其中的黑暗處道理相似。

本系列文章于2017.12.1開始寫针贬,作者打包的最新版本是v0.11.0桦他。

Moon源碼整體結(jié)構(gòu)

為了方便起見,我繪制了一張圖(建議大家下載下來看):


moon源碼結(jié)構(gòu).png

這里我簡(jiǎn)單把各個(gè)文件夾(標(biāo)了顏色的方塊)看成是類谆棱,然后實(shí)現(xiàn)了看成接口的各個(gè)代碼文件的功能础锐。

每個(gè)代碼文件里有若干個(gè)子程序截粗,沒有寫成函數(shù)的我就假裝它們是一個(gè)函數(shù)的內(nèi)容菊值,并且在這個(gè)我造的函數(shù)名前面打了星號(hào),虛線所指向的方塊是它們實(shí)際做的事情磅崭。

打開源碼的package.json儿子,可以看出作者是使用gulp打包構(gòu)建他的代碼的。而整塊代碼被他分割成了六個(gè)文件夾:

  1. compiler---編譯模板到dom樹的各個(gè)函數(shù)割岛。
  2. util---通用工具愉适、dom工具、vdom工具函數(shù)癣漆。
  3. observer---觀察者實(shí)例和相關(guān)的函數(shù)维咸。
  4. directives---處理指令的函數(shù)。
  5. instance---Moon實(shí)例上存在的函數(shù)。
  6. global---Moon對(duì)象上的靜態(tài)屬性和方法腰湾。

最后通過在index.js里逐個(gè)引入雷恃,交給wrapper.js(還有g(shù)ulp)打包成最終版本。

//index.js
"use strict";

/* ======= Global Variables ======= */
let directives = {};
let specialDirectives = {};
let components = {};
let eventModifiersCode = {
  stop: 'event.stopPropagation();',
  prevent: 'event.preventDefault();',
  ctrl: 'if(event.ctrlKey === false) {return null;};',
  shift: 'if(event.shiftKey === false) {return null;};',
  alt: 'if(event.altKey === false) {return null;};',
  enter: 'if(event.keyCode !== 13) {return null;};'
};
let eventModifiers = {};

/* ======= Observer ======= */
//=require observer/methods.js
//=require observer/computed.js
//=require observer/observer.js

//=require util/util.js
//=require util/dom.js
//=require util/vdom.js

/* ======= Compiler ======= */
//=require compiler/template.js
//=require compiler/lexer.js
//=require compiler/parser.js
//=require compiler/generator.js
//=require compiler/compiler.js

function Moon(options) {
//省略,這里不是這篇文章的重點(diǎn)
}
//=require instance/methods.js

//=require global/api.js

//=require directives/default.js
//wrapper.js
(function(root, factory) {
  /* ======= Global Moon ======= */
  (typeof module === "object" && module.exports) ? module.exports = factory() : root.Moon = factory();
}(this, function() {
    //=require ../dist/moon.js
    return Moon;
}));

一個(gè)Moon實(shí)例的一生

有了全局觀费坊,我們就從探究一個(gè)new一個(gè)的Moon過程開始倒槐,看看它究竟經(jīng)歷了些什么?:

<div id="app">
    {{yf}}
</div>
<script src="./moon.js"></script>
<script>
  debugger
  const app = new Moon({
    el: "#app",
    data: {
        yf: "云峰"
    }
  })
</script>
  1. 很好附井,我們進(jìn)來了:


    preview-1.jpg
  2. 嗯讨越,不出所料,首先進(jìn)的是Moon構(gòu)造函數(shù)永毅,畢竟new了一下把跨。


    preview-2.jpg
  3. 我們沒有定義methods,所以跳過了initMethods沼死,不過沒關(guān)系着逐,以后會(huì)有機(jī)會(huì)進(jìn)去的~


    preview-3.jpg
  4. 嘿!我們new了一個(gè)Observer對(duì)象意蛀,它肯定是觀察當(dāng)前這個(gè)實(shí)例變化的耸别!


    preview-4.jpg
  5. initComputed同樣被我們跳過去了,不過沒關(guān)系县钥,我們要進(jìn)init了P憬恪!


    preview-5.jpg
  6. init里貌似做了一些事情若贮,不過緊接著就進(jìn)入了mount省有。


    preview-6.jpg
  7. mount貌似也做了一些事情,我感覺快要迷路了谴麦,不過這時(shí)候一個(gè)compile讓我好奇心大增蠢沿,它竟然把模板字符串轉(zhuǎn)成了一個(gè)匿名函數(shù)!做完這些它就進(jìn)了build细移。


    preview-7.jpg
  8. build一上來就把上一步生成的函數(shù)執(zhí)行了搏予!并且按作者的注釋來看的話還得到了virtual node,我們發(fā)現(xiàn)光得到還不行弧轧,后續(xù)還要和node進(jìn)行patch一下雪侥。


    preview-8.jpg
  9. 在patch里我們因?yàn)関irtual node不是dom node類型跳轉(zhuǎn)到了一個(gè)hydrate的子程序里


    preview-9.jpg
  1. hydrate的子程序在作者注釋中被解釋為Hydrates Node and a VNode,也就是把人為生成的vnode混入到實(shí)際存在于dom樹中的node里去精绎。

在混入完成后我們便一步步出棧速缨,回到了起點(diǎn)。

也就是說代乃,我們的Moon實(shí)例經(jīng)歷了從init初始化->mount掛載->build建立的過程旬牲,期間實(shí)例化了一個(gè)Observer觀察者仿粹,對(duì)模板字符串進(jìn)行了編譯,并執(zhí)行得到vnode原茅,最后作用于dom樹中的node來改變最終渲染結(jié)果吭历。(template->vnode->node)

那這些過程中具體細(xì)節(jié)是什么樣的呢?我準(zhǔn)備在之后的文章中從下面幾個(gè)角度分析:

  1. render函數(shù)從一段code->html的過程
  2. compiler從html->code的過程
  3. 數(shù)據(jù)變更檢測(cè)
  4. 指令
  5. 組件
    ...
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末擂橘,一起剝皮案震驚了整個(gè)濱河市晌区,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌通贞,老刑警劉巖朗若,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異昌罩,居然都是意外死亡哭懈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門茎用,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遣总,“玉大人,你說我怎么就攤上這事绘搞⊥埽” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵夯辖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我董饰,道長(zhǎng)蒿褂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任卒暂,我火速辦了婚禮啄栓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘也祠。我一直安慰自己昙楚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布诈嘿。 她就那樣靜靜地躺著堪旧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奖亚。 梳的紋絲不亂的頭發(fā)上淳梦,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音昔字,去河邊找鬼爆袍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陨囊。 我是一名探鬼主播弦疮,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蜘醋!你這毒婦竟也來了挂捅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤堂湖,失蹤者是張志新(化名)和其女友劉穎闲先,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體无蜂,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伺糠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斥季。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片训桶。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖酣倾,靈堂內(nèi)的尸體忽然破棺而出舵揭,到底是詐尸還是另有隱情,我是刑警寧澤躁锡,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布午绳,位于F島的核電站,受9級(jí)特大地震影響映之,放射性物質(zhì)發(fā)生泄漏拦焚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一杠输、第九天 我趴在偏房一處隱蔽的房頂上張望赎败。 院中可真熱鬧,春花似錦蠢甲、人聲如沸僵刮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)搞糕。三九已至,卻和暖如春能岩,著一層夾襖步出監(jiān)牢的瞬間寞宫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工拉鹃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辈赋,地道東北人鲫忍。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像钥屈,于是被迫代替她去往敵國(guó)和親悟民。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容