手寫簡易瀏覽器之思路綜述篇
本文轉(zhuǎn)載自微信公眾號「神光的編程秘籍」,作者神說要有光。轉(zhuǎn)載本文請聯(lián)系神光的編程秘籍公眾號。
其實(shí)有這個(gè)想法很久了,只是一直沒開始寫,正好趕上掘金的日更活動,就想著把它實(shí)現(xiàn)一遍吧,然后把實(shí)現(xiàn)過程記錄下來。
想法的來源
前段時(shí)間在研究 babel,寫了一本小冊,里面有一個(gè) 基于 babel parser 實(shí)現(xiàn) js 解釋器的案例,寫那個(gè)案例的時(shí)候我就在想,js 的解釋執(zhí)行都實(shí)現(xiàn)了,是不是再進(jìn)一步,加上渲染的部分,寫一個(gè)簡易的瀏覽器呢。
最近在看 postcss 的源碼,把 css parser 的實(shí)現(xiàn)也理清了,能夠自己實(shí)現(xiàn)從 css 到 ast 的 parse。
之前寫過 html parser,寫了很多次類似的東西,比如 vue template parser、wxml parser 等,html 到 ast 的轉(zhuǎn)換也能實(shí)現(xiàn)。
最近還寫了一篇event loop 與 js 引擎、渲染引擎關(guān)系的文章,和一篇跨端引擎原理的文章,講述了如何用 event loop 把 js 的解釋執(zhí)行與 css 的渲染綜合起來,共用一個(gè)線程完成渲染和邏輯,以及如何基于容器的思路實(shí)現(xiàn)跨端引擎。
這些東西湊到一塊,其實(shí)就是一個(gè)簡易瀏覽器的實(shí)現(xiàn)思路,所以我想趁著這次更文活動,把它給實(shí)現(xiàn)了。一個(gè)是對這段時(shí)間輸出的東西的一個(gè)綜合,另一個(gè)也能加深對前端代碼運(yùn)行環(huán)境的理解。也希望給大家一些啟發(fā)。
實(shí)現(xiàn)思路概述
這篇文章會講一下實(shí)現(xiàn)思路,具體實(shí)現(xiàn)從下一篇開始。
html parser
我們會實(shí)現(xiàn)一個(gè) html parser,把 html 字符串 parse 成 dom tree,使用一趟遍歷的方式來實(shí)現(xiàn),和 vue template compiler 的 parser 思路一致。
css parser
我們會實(shí)現(xiàn)一個(gè) css parser,把 css 字符串 parse 成 css tree,實(shí)現(xiàn)思路和 postcss 的 parser 類似。
布局引擎
有了 html parser 和 css parser 之后把兩者合并成 render tree,之后實(shí)現(xiàn)布局引擎,把樣式計(jì)算為具體的坐標(biāo)。
渲染引擎
有了具體的坐標(biāo),就可以渲染了,這里我們基于 canvas 來做渲染。到了這一步,就完成了 html、css 在界面上的顯示。
js 解釋器
js 字符串的解釋執(zhí)行,首先需要一個(gè) js parser,這個(gè)就不自己實(shí)現(xiàn)了,用 acron 就行,之后自己做解釋執(zhí)行。會實(shí)現(xiàn) scope 和函數(shù)的執(zhí)行,注入幾個(gè)綁定事件、操作 dom 的 api 的實(shí)現(xiàn)。
event loop
渲染和邏輯執(zhí)行都實(shí)現(xiàn)了,但是兩者要跑在同一個(gè)線程,那么就需要通過 event loop 來調(diào)度。js 按照 一個(gè)宏任務(wù)、所有微任務(wù)的方式的方式執(zhí)行,每次 loop 結(jié)束 check 一下是否需要渲染,渲染按照每幀 16 ms的速率來刷新。
我們會通過不斷的 setTimeout 來實(shí)現(xiàn) event loop 不斷取事件的邏輯,因?yàn)樗姥h(huán)會卡死主線程(在 c++ 里面可以用死循環(huán)實(shí)現(xiàn) event loop,因?yàn)橛衅渌€程來往隊(duì)列里放事件對象)。
這樣就能實(shí)現(xiàn)渲染的按幀率刷新和 js 邏輯的執(zhí)行。
網(wǎng)絡(luò)資源下載
上面實(shí)現(xiàn)了 html、css、js 的渲染和執(zhí)行,而且實(shí)現(xiàn)了 event loop 的調(diào)度。這些資源是從網(wǎng)絡(luò)上下載的,所以還要實(shí)現(xiàn)一個(gè)下載模塊,這個(gè)模塊要能從一個(gè) url 下載 html,然后還會解析出其中的鏈接來下載。
靜態(tài)服務(wù)器
html、css、js 等資源是從 web 服務(wù)器下載的,通過 http 協(xié)議,這個(gè)服務(wù)器也需要實(shí)現(xiàn),可以是一個(gè)簡單的靜態(tài)服務(wù)器,也可以是有一些邏輯的動態(tài)服務(wù)器。
瀏覽器的界面
既然是瀏覽器,那么肯定要有一個(gè)輸入 url 的地方和顯示內(nèi)容的地方,所以我們會做一個(gè)界面,上面是地址欄,用于輸入 url,下面是內(nèi)容區(qū),用于顯示渲染的內(nèi)容。
整體架構(gòu)圖如下:
總結(jié)
本文是【手寫一個(gè)簡易瀏覽器】系列文章的第一篇,主要是講述想法的來源、整體的實(shí)現(xiàn)思路和每一部分的大概思路。
首先是 html parser、css parser、js parser,除了 js 的會用 acorn 其余都會自己實(shí)現(xiàn),然后會實(shí)現(xiàn)布局引擎和渲染引擎,實(shí)現(xiàn) js ast 的解釋執(zhí)行,通過 event loop 綜合兩者,實(shí)現(xiàn)渲染和邏輯的調(diào)度。之后加上網(wǎng)絡(luò)資源的下載、瀏覽器的界面,就是一個(gè)簡易的瀏覽器了,思路是可行的。
后續(xù)每一部分會用一篇文章來講清楚,并且會把源碼放在 github,敬請期待。