Webkit内核-节点渲染与JS执行原理

简介
在Webkit中,节点的渲染是由HTMLContructionSite类来完成的,该类包含一个DOM树的根节点HTMLDocument对象,其它的元素都是它的后代元素。
因为HTML文档的Tag标签是有开始结束标记的,所以构建过程使用栈结构是再合适不过的。HTMLContructionSite类中包含一个HTMLElementStack变量,它是一个保存元素节点的栈,在其中的元素就是当前有开始标记但还没有结束标记的的元素节点。
实例
一个HTML片段如:
解释器先做入栈操作,当解释到img元素的开始标记时,栈中的元素就是body、div和img,当遇到img的结束标记时,img出栈,img是div的子元素,当遇到div的结束标记时,div出栈,表明div和它的子女都已经处理完毕,以此类推。
根据DOM标准中的定义,节点有很多中类型、属性,所以他们有一个基础的父类 — Node类。
在Webkit中,DOM中的接口Interface对应于C++的类,Node类是其它类的基类,Node类实际上继承自EventTarget类,它表明Node类能够接受事件;同时,Node类还继承自另外一个关键的基类 — ScriptWrappable,这个与Javascript引擎密切相关。
Javascript的执行
在HTML解释器工作中,可能会有Javascript代码需要执行,它发生在将字符串解析成词语后创建节点时。这也是为什么全局执行的Javascript代码不能访问DOM树的原因,因为DOM在那个时间点还没有被创建出来。
Webkit将DOM树创建过程中需要执行的Javascript代码交给HTMLScriptRunner类来负责。原理就是利用Javascript引擎来执行Node节点中包含的代码。
因为JS代码可能会调用”document.write”等方法来修改文档结构,所以JS代码的执行会阻碍后面节点的创建,同时也会阻碍后边资源的下载,这也是JS阻塞加载的原理所在。
所以会有俩点建议:
1.将script标签加上”async”属性,说明这是一个可以异步执行的JS代码;
2.将script元素放在body的最后。(更推荐一些)
然而Webkit在这方面进行了一些优化,当遇到需要执行的JS代码时,Webkit会先暂停执行,使用预先扫描器HTMLPreloadScanner类扫描后面的词语。如果Webkit发现有其它资源需要加载,则使用资源加载器HTMLResourcePreloader类来请求资源,在这之后才执行JS代码。预先扫描并不会真实创建节点,所以速度较快。但是并不是所有渲染引擎都做了相关优化,所以第二种方式会更被推荐一些。
DOMContentLoaded
当Dom树创建完成后,Webkit触发了”DOMContentLoaded”事件,这就是我们熟悉的window.onload函数,此时一些代码才会在资源加载完成后执行,避免了DOM还没创建就出现操作的问题。
另外您可以关注我的公众号,更多技术文章将通过公众号进行分享