本文转载自微信公众号「神光的编程秘籍」,作者神说要有光。转载本文请联系神光的编程秘籍公众号。
其实有这个想法很久了,只是一直没开始写,正好赶上掘金的日更活动,就想着把它实现一遍吧,然后把实现过程记录下来。
想法的来源
前段时间在研究 babel,写了一本小册,里面有一个 基于 babel parser 实现 js 解释器的案例,写那个案例的时候我就在想,js 的解释执行都实现了,是不是再进一步,加上渲染的部分,写一个简易的浏览器呢。
最近在看 postcss 的源码,把 css parser 的实现也理清了,能够自己实现从 css 到 ast 的 parse。
之前写过 html parser,写了很多次类似的东西,比如 vue template parser、wxml parser 等,html 到 ast 的转换也能实现。
最近还写了一篇event loop 与 js 引擎、渲染引擎关系的文章,和一篇跨端引擎原理的文章,讲述了如何用 event loop 把 js 的解释执行与 css 的渲染综合起来,共用一个线程完成渲染和逻辑,以及如何基于容器的思路实现跨端引擎。
这些东西凑到一块,其实就是一个简易浏览器的实现思路,所以我想趁着这次更文活动,把它给实现了。一个是对这段时间输出的东西的一个综合,另一个也能加深对前端代码运行环境的理解。也希望给大家一些启发。
实现思路概述
这篇文章会讲一下实现思路,具体实现从下一篇开始。
html parser
我们会实现一个 html parser,把 html 字符串 parse 成 dom tree,使用一趟遍历的方式来实现,和 vue template compiler 的 parser 思路一致。
css parser
我们会实现一个 css parser,把 css 字符串 parse 成 css tree,实现思路和 postcss 的 parser 类似。
布局引擎
有了 html parser 和 css parser 之后把两者合并成 render tree,之后实现布局引擎,把样式计算为具体的坐标。
渲染引擎
有了具体的坐标,就可以渲染了,这里我们基于 canvas 来做渲染。到了这一步,就完成了 html、css 在界面上的显示。
js 解释器
js 字符串的解释执行,首先需要一个 js parser,这个就不自己实现了,用 acron 就行,之后自己做解释执行。会实现 scope 和函数的执行,注入几个绑定事件、操作 dom 的 api 的实现。
event loop
渲染和逻辑执行都实现了,但是两者要跑在同一个线程,那么就需要通过 event loop 来调度。js 按照 一个宏任务、所有微任务的方式的方式执行,每次 loop 结束 check 一下是否需要渲染,渲染按照每帧 16 ms的速率来刷新。
我们会通过不断的 setTimeout 来实现 event loop 不断取事件的逻辑,因为死循环会卡死主线程(在 c++ 里面可以用死循环实现 event loop,因为有其他线程来往队列里放事件对象)。
这样就能实现渲染的按帧率刷新和 js 逻辑的执行。
网络资源下载
上面实现了 html、css、js 的渲染和执行,而且实现了 event loop 的调度。这些资源是从网络上下载的,所以还要实现一个下载模块,这个模块要能从一个 url 下载 html,然后还会解析出其中的链接来下载。
静态服务器
html、css、js 等资源是从 web 服务器下载的,通过 http 协议,这个服务器也需要实现,可以是一个简单的静态服务器,也可以是有一些逻辑的动态服务器。
浏览器的界面
既然是浏览器,那么肯定要有一个输入 url 的地方和显示内容的地方,所以我们会做一个界面,上面是地址栏,用于输入 url,下面是内容区,用于显示渲染的内容。
整体架构图如下:
总结
本文是【手写一个简易浏览器】系列文章的第一篇,主要是讲述想法的来源、整体的实现思路和每一部分的大概思路。
首先是 html parser、css parser、js parser,除了 js 的会用 acorn 其余都会自己实现,然后会实现布局引擎和渲染引擎,实现 js ast 的解释执行,通过 event loop 综合两者,实现渲染和逻辑的调度。之后加上网络资源的下载、浏览器的界面,就是一个简易的浏览器了,思路是可行的。
后续每一部分会用一篇文章来讲清楚,并且会把源码放在 github,敬请期待。