<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DOM | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/dom/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Wed, 05 Jun 2013 15:26:16 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>Javascript 装载和执行</title>
		<link>https://coolshell.cn/articles/9749.html</link>
					<comments>https://coolshell.cn/articles/9749.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 05 Jun 2013 00:31:06 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9749</guid>

					<description><![CDATA[<p>一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9749.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9749.html">Javascript 装载和执行</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright  wp-image-9778" alt="" src="https://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg" width="300" height="225" srcset="https://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg 500w, https://coolshell.cn/wp-content/uploads/2013/06/javascript-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/06/javascript-360x270.jpg 360w" sizes="(max-width: 300px) 100vw, 300px" />一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，但一直耽搁了。自上篇《<a title="浏览器的渲染原理简介" href="https://coolshell.cn/articles/9666.html" target="_blank">浏览器渲染原理简介</a>》，正好也可以承前启后。</p>
<p>首先，我想说一下Javascript的装载和执行。通常来说，浏览器对于Javascript的运行有两大特性：<strong>1）载入后马上执行，2）执行时会阻塞页面后续的内容（包括页面的渲染、其它资源的下载）</strong>。于是，如果有多个js文件被引入，那么对于浏览器来说，这些js文件被被串行地载入，并依次执行。</p>
<p>因为javascript可能会来操作HTML文档的DOM树，所以，浏览器一般都不会像并行下载css文件并行下载js文件，因为这是js文件的特殊性造成的。所以，如果你的javascript想操作后面的DOM元素，基本上来说，浏览器都会报错说对象找不到。因为Javascript执行时，后面的HTML被阻塞住了，DOM树时还没有后面的DOM结点。所以程序也就报错了。</p>
<h4>传统的方式</h4>
<p>所以，当你写在代码中写下如下的代码：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;script type=&quot;text/javascript&quot;
        src=&quot;https://coolshell.cn/asyncjs/alert.js&quot;&gt;&lt;/script&gt;</pre>
<p><span id="more-9749"></span></p>
<p>基本上来说，head里的 &lt;script&gt;标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看：<strong><a href="https://coolshell.cn/asyncjs/async_test01.html" target="_blank">示例一</a></strong>。 注意：我的alert.js中只有一句话：alert(&#8220;hello world&#8221;) ，这更容易让你看到javascript是怎么阻塞后面的东西的。</p>
<p>所以，你知道为什么有很多网站把javascript放在网页的最后面了，要么就是动用了window.onload或是docmuemt ready之类的事件。</p>
<p>另外，因为绝大多数的Javascript代码并不需要等页面，所以，我们异步载入的功能。那么我们怎么异步载入呢？</p>
<h4>document.write方式</h4>
<p>于是，你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得，document.write了的&lt;script&gt;标签后就可以执行后面的东西去了，这没错。对于在同一个script标签里的Javascript的代码来说，是这样的，但是对于整个页面来说，这个还是会阻塞。 下面是一段测试代码：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;
    function loadjs(script_filename) {
        document.write(&#039;&lt;&#039; + &#039;script language=&quot;javascript&quot; type=&quot;text/javascript&quot;&#039;);
        document.write(&#039; src=&quot;&#039; + script_filename + &#039;&quot;&gt;&#039;);
        document.write(&#039;&lt;&#039;+&#039;/script&#039;+&#039;&gt;&#039;);
        alert(&quot;loadjs() exit...&quot;);
    }

    var script = &#039;https://coolshell.cn/asyncjs/alert.js&#039;;

    loadjs(script);
    alert(&quot;loadjs() finished!&quot;);
&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;
   alert(&quot;another block&quot;);
&lt;/script&gt;</pre>
<p>你觉得alert的顺序是什么？你可以在不同的浏览器里试一试。这里的想关的测试页面：<strong><a href="https://coolshell.cn/asyncjs/async_test02.html" target="_blank">示例二</a></strong>。</p>
<h4>script的defer和async属性</h4>
<p>IE自从IE6就支持defer标签，如：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;script defer type=&quot;text/javascript&quot; src=&quot;./alert.js&quot; &gt;
&lt;/script&gt;</pre>
<p>对于IE来说，这个标签会让IE并行下载js文件，并且把其执行hold到了整个DOM装载完毕（DOMContentLoaded），多个defer的&lt;script&gt;在执行时也会按照其出现的顺序来运行。最重要的是&lt;script&gt;被加上defer后，其不会阻塞后续DOM的的渲染。但是因为这个defer只是IE专用，所以一般用得比较少。</p>
<p>而我们标准的的HTML5也加入了一个异步载入javascript的属性：async，无论你对它赋什么样的值，只要它出现，它就开始异步加载js文件。但是， async的异步加载会有一个比较严重的问题，那就是它忠实地践行着“载入后马上执行”这条军规，所以，虽然它并不阻塞页面的渲染，但是你也无法控制他执行的次序和时机。你可以<a href="https://coolshell.cn/asyncjs/async_test01.async.html" target="_blank">看看这个示例去感受一下</a>。</p>
<p>支持 async标签的浏览器是：Firefox3.6+，Chrome 8.0+，Safari 5.0+，IE 10+，Opera还不支持（<a href="http://caniuse.com/#feat=script-async" target="_blank">来自这里</a>）所以这个方法也不是太好。因为并不是所有的浏览器你都能行。</p>
<h4>动态创建DOM方式</h4>
<p>这种方式可能是用得最多的了。</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">
function loadjs(script_filename) {
    var script = document.createElement(&#039;script&#039;);
    script.setAttribute(&#039;type&#039;, &#039;text/javascript&#039;);
    script.setAttribute(&#039;src&#039;, script_filename);
    script.setAttribute(&#039;id&#039;, &#039;coolshell_script_id&#039;);

    script_id = document.getElementById(&#039;coolshell_script_id&#039;);
    if(script_id){
        document.getElementsByTagName(&#039;head&#039;)[0].removeChild(script_id);
    }
    document.getElementsByTagName(&#039;head&#039;)[0].appendChild(script);
}

var script = &#039;https://coolshell.cn/asyncjs/alert.js&#039;;
loadjs(script);
</pre>
<p>这个方式几乎成了标准的异步载入js文件的方式，这个方式的演示请参看：<strong><a href="https://coolshell.cn/asyncjs/async_test03.html" target="_blank">示例三</a></strong>。这方式还被玩出了JSONP的东东，也就是我可以为script的src指定某个后台的脚本（如PHP），而这个PHP返回一个javascript函数，其参数是一个json的字符串，返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例：<a href="https://coolshell.cn/t.js" target="_blank">t.js</a> （这个示例是我之前在微博征集的<a href="https://coolshell.cn/t.html" target="_blank">一个异步ajax调用的小例子</a>）<strong><br />
</strong></p>
<h4>按需异步载入js</h4>
<p>上面那个DOM方式的例子解决了异步载入Javascript的问题，但是没有解决我们想让他按我们指定的时机运行的问题。所以，我们只需要把上面那个DOM方式绑到某个事件上来就可以了。</p>
<p>比如：</p>
<p><strong>绑在window.load事件上</strong>——<strong><a href="https://coolshell.cn/asyncjs/async_test04.html" target="_blank">示例四</a> </strong></p>
<p><strong></strong>你一定要比较一下示例四和示例三在执行上有什么不同，我在这两个示例中都专门用了个代码高亮的javascript，看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况，你就知道不同了）</p>
<p><code data-enlighter-language="js" class="EnlighterJSRAW">window.load = loadjs(&quot;https://coolshell.cn/asyncjs/alert.js&quot;)</code></p>
<p><strong>绑在特定的事件上</strong>——<strong><a href="https://coolshell.cn/asyncjs/async_test05.html" target="_blank">示例五</a></strong></p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;p style=&quot;cursor: pointer&quot; onclick=&quot;LoadJS()&quot;&gt;Click to load alert.js &lt;/p&gt;</code></p>
<p>这个示例很简单了。当你点击某个DOM元素，才会真正载入我们的alert.js。</p>
<h4>更多</h4>
<p>但是，绑定在某个特定事件上这个事似乎又过了一点，因为只有在点击的时候才会去真正的下载js，这又会太慢了了。好了，到这里，要抛出我们的终极问题——<strong>我们想要异步地把js文件下载到用户的本地，但是不执行，仅当在我们想要执行的时候去执行</strong>。</p>
<p>要是我们有下面这样的方式就好了：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">var script = document.createElement(&quot;script&quot;);
script.noexecute = true;
script.src = &quot;alert.js&quot;;
document.body.appendChild(script);

//后面我们可以这么干
script.execute();</pre>
<p>可惜的是，这只是一个美丽的梦想，今天我们的Javascript还比较原始，这个“JS梦”还没有实现呢。</p>
<p>所以，我们的程序员只能使用hack的方式来搞。</p>
<p>有的程序员使用了非标准的script的type来cache javascript。如：</p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;script type=cache/script src=&quot;./alert.js&quot;&gt;&lt;/script&gt;</code></p>
<p>因为&#8221;cache/script&#8221;，这个东西根本就不能被浏览器解析，所以浏览器也就不能把alert.js当javascript去执行，但是他又要去下载js文件，所以就可以搞定了。可惜的是，webkit严格符从了HTML的标准——对于这种不认识的东西，直接删除，什么也不干。于是，我们的梦又破了。</p>
<p>所以，我们需要再hack一下，就像N多年前玩preload图片那样，我们可以动用object标签（也可以动用iframe标签），于是我们有下面这样的代码：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">    function cachejs(script_filename){
        var cache = document.createElement(&#039;object&#039;);
        cache.data = script_filename;
        cache.id = &quot;coolshell_script_cache_id&quot;;
        cache.width = 0;
        cache.height = 0;
        document.body.appendChild(cache);
    }</pre>
<p>然后，我们在的最后调用一下这个函数。请参看一下相关的示例：<strong><a href="https://coolshell.cn/asyncjs/async_test06.html" target="_blank">示例六</a></strong></p>
<p>在Chrome下按 Ctrl+Shit+I，切换到network页，你就可以看到下载了alert.js但是没有执行，然后我们再用示例五的方式，因为浏览器端有缓存了，不会再从服务器上下载alert.js了。所以，就能保证执行速度了。</p>
<p>关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式，如：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">var xhr = new XMLHttpRequest();
xhr.open(&#039;GET&#039;, &#039;new.js&#039;);
xhr.send(&#039;&#039;);</pre>
<p>到这里我就不再多说了，也不给示例了，大家可以自己试试去。</p>
<p>最后再提两个js，一个是<a href="http://stevesouders.com/controljs/" target="_blank">ControlJS</a>，一个叫<a href="http://headjs.com/" target="_blank">HeadJS</a>，专门用来做异步load javascript文件的。</p>
<p>好了，这是所有的内容了，希望大家看过后能对Javascript的载入和执行，以及相关的技术有个了解。<strong>同时，也希望各前端高手不吝赐教！</strong></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/8170.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg" alt="一次Ajax查错的经历" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8170.html" class="wp_rp_title">一次Ajax查错的经历</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png" alt="一些文章资源和趣闻" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_title">一些文章资源和趣闻</a></li><li ><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="开源中最好的Web开发的资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_title">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9749.html">Javascript 装载和执行</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9749.html/feed</wfw:commentRss>
			<slash:comments>104</slash:comments>
		
		
			</item>
		<item>
		<title>浏览器的渲染原理简介</title>
		<link>https://coolshell.cn/articles/9666.html</link>
					<comments>https://coolshell.cn/articles/9666.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 22 May 2013 00:17:47 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9666</guid>

					<description><![CDATA[<p>看到这个标题大家一定会想到这篇神文《How Browsers Work》，这篇文章把浏览器的很多细节讲得很细，而且也被翻译成了中文。为什么我还想写一篇呢？因为两...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9666.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9666.html">浏览器的渲染原理简介</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>看到这个标题大家一定会想到这篇神文《<a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" target="_blank">How Browsers Work</a>》，这篇文章把浏览器的很多细节讲得很细，而且也被<a href="http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff" target="_blank">翻译成了中文</a>。为什么我还想写一篇呢？因为两个原因，</p>
<p style="padding-left: 30px;">1）这篇文章太长了，阅读成本太大，不能一口气读完。</p>
<p style="padding-left: 30px;">2）花了大力气读了这篇文章后可以了解很多，但似乎对工作没什么帮助。</p>
<p>所以，我准备写下这篇文章来解决上述两个问题。希望你能在上班途中，或是坐马桶时就能读完，并能从中学会一些能用在工作上的东西。</p>
<h4>浏览器工作大流程</h4>
<p>废话少说，先来看个图：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9667" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg" width="712" height="231" srcset="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg 791w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-300x97.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-768x250.jpg 768w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-604x196.jpg 604w" sizes="(max-width: 712px) 100vw, 712px" /></p>
<p style="text-align: left;">从上面这个图中，我们可以看到那么几个事：</p>
<p><span id="more-9666"></span></p>
<p style="text-align: left;">1）浏览器会解析三个东西：</p>
<ul>
<li>一个是HTML/SVG/XHTML，事实上，Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。</li>
</ul>
<ul>
<li>CSS，解析CSS会产生CSS规则树。</li>
</ul>
<ul>
<li>Javascript，脚本，主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.</li>
</ul>
<p>2）解析完成后，浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意：</p>
<ul>
<li>Rendering Tree 渲染树并不等同于DOM树，因为一些像Header或display:none的东西就没必要放在渲染树中了。</li>
</ul>
<ul>
<li>CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。</li>
</ul>
<ul>
<li>然后，计算每个Frame（也就是每个Element）的位置，这又叫layout和reflow过程。</li>
</ul>
<p>3）最后通过调用操作系统Native GUI的API绘制。</p>
<h4>DOM解析</h4>
<p>HTML的DOM Tree解析如下：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Web page parsing&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;Web page parsing&lt;/h1&gt;
        &lt;p&gt;This is an example Web page.&lt;/p&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>上面这段HTML会解析成这样：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9669" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg" width="456" height="300" srcset="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg 651w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01-300x197.jpg 300w" sizes="(max-width: 456px) 100vw, 456px" /></p>
<p>下面是另一个有SVG标签的情况。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9670" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg" width="408" height="320" srcset="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg 680w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02-300x235.jpg 300w" sizes="(max-width: 408px) 100vw, 408px" /></p>
<h4>CSS解析</h4>
<p>CSS的解析大概是下面这个样子（下面主要说的是Gecko也就是Firefox的玩法），假设我们有下面的HTML文档：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;doc&gt;
&lt;title&gt;A few quotes&lt;/title&gt;
&lt;para&gt;
  Franklin said that &lt;quote&gt;&quot;A penny saved is a penny earned.&quot;&lt;/quote&gt;
&lt;/para&gt;
&lt;para&gt;
  FDR said &lt;quote&gt;&quot;We have nothing to fear but &lt;span&gt;fear itself.&lt;/span&gt;&quot;&lt;/quote&gt;
&lt;/para&gt;
&lt;/doc&gt;
</pre>
<p>于是DOM Tree是这个样子：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9672" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg" width="368" height="318" srcset="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg 368w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example-300x259.jpg 300w" sizes="(max-width: 368px) 100vw, 368px" /></p>
<p>然后我们的CSS文档是这样的：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">  /* rule 1 */ doc { display: block; text-indent: 1em; }
/* rule 2 */ title { display: block; font-size: 3em; }
/* rule 3 */ para { display: block; }
/* rule 4 */ [class=&quot;emph&quot;] { font-style: italic; }</pre>
<p>于是我们的CSS Rule Tree会是这个样子：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9673" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg" width="397" height="238" srcset="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg 397w, https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example-300x179.jpg 300w" sizes="(max-width: 397px) 100vw, 397px" /></p>
<p>注意，图中的第4条规则出现了两次，一次是独立的，一次是在规则3的子结点。所以，我们可以知道，建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector，好多人以为这个事会比较快，其实并不一定。关键还看我们的CSS的Selector怎么写了。</p>
<p><strong>注意：CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以，你就会在N多地方看到很多人都告诉你，DOM树要小，CSS尽量用id和class，千万不要过渡层叠下去，……</strong></p>
<p>通过这两个树，我们可以得到一个叫Style Context Tree，也就是下面这样（把CSS Rule结点Attach到DOM Tree上）：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9674" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg" width="405" height="318" srcset="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg 405w, https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example-300x235.jpg 300w" sizes="(max-width: 405px) 100vw, 405px" /></p>
<p>所以，Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree，然后，通过比对DOM生成Style Context Tree，然后Firefox通过把Style Context Tree和其Render Tree（Frame Tree）关联上，就完成了。注意：Render Tree会把一些不可见的结点去除掉。而<strong>Firefox中所谓的Frame就是一个DOM结点，不要被其名字所迷惑了</strong>。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9677" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png" width="328" height="366" srcset="https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png 364w, https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree-268x300.png 268w" sizes="(max-width: 328px) 100vw, 328px" /></p>
<p>注：Webkit不像Firefox要用两个树来干这个，Webkit也有Style对象，它直接把这个Style对象存在了相应的DOM结点上了。</p>
<h4>渲染</h4>
<p>渲染的流程基本上如下（黄色的四个步骤）：</p>
<ol>
<li>计算CSS样式</li>
<li>构建Render Tree</li>
<li>Layout &#8211; 定位坐标和大小，是否换行，各种position, overflow, z-index属性 ……</li>
<li>正式开画</li>
</ol>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9675" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg" width="712" height="196" srcset="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg 1318w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping-300x82.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping-1024x282.jpg 1024w" sizes="(max-width: 712px) 100vw, 712px" /></p>
<p style="text-align: left;">注意：上图流程中有很多连接线，这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout，有些改变不会，就是那些指到天上的箭头，比如，修改后的CSS rule没有被匹配到，等。</p>
<p style="text-align: left;">这里重要要说两个概念，一个是Reflow，另一个是Repaint。这两个不是一回事。</p>
<ul>
<li>Repaint——屏幕的一部分要重画，比如某个CSS的背景色变了。但是元素的几何尺寸没有变。</li>
</ul>
<ul>
<li>Reflow——意味着元件的几何尺寸变了，我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow，或是Layout。（<strong>HTML使用的是flow based layout，也就是流式布局，所以，如果某元件的几何尺寸发生了变化，需要重新布局，也就叫reflow</strong>）reflow 会从&lt;html&gt;这个root frame开始递归往下，依次计算所有的结点几何尺寸和位置，在reflow过程中，可能会增加一些frame，比如一个文本字符串必需被包装起来。</li>
</ul>
<p>下面是一个打开Wikipedia时的Layout/reflow的视频（注：HTML在初始化的时候也会做一次reflow，叫 <dfn>intial reflow</dfn>），你可以感受一下：</p>
<p><center><object width="480" height="400" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" align="middle"><param name="src" value="http://player.youku.com/player.php/sid/XMzI5MDg0OTA0/v.swf" /><param name="allowfullscreen" value="true" /><param name="quality" value="high" /><param name="allowscriptaccess" value="always" /><embed width="480" height="400" type="application/x-shockwave-flash" src="http://player.youku.com/player.php/sid/XMzI5MDg0OTA0/v.swf" allowfullscreen="true" quality="high" allowscriptaccess="always" align="middle" /></object></center>Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法，一个结点的reflow很有可能导致子结点，甚至父点以及同级结点的reflow。<strong>在一些高性能的电脑上也许还没什么，但是如果reflow发生在手机上，那么这个过程是非常痛苦和耗电的</strong>。</p>
<p>所以，下面这些动作有很大可能会是成本比较高的。</p>
<ul>
<li>当你增加、删除、修改DOM结点时，会导致Reflow或Repaint</li>
<li>当你移动DOM的位置，或是搞个动画的时候。</li>
<li>当你修改CSS样式的时候。</li>
<li>当你Resize窗口的时候（移动端没有这个问题），或是滚动的时候。</li>
<li>当你修改网页的默认字体时。</li>
</ul>
<p style="text-align: left;">注：display:none会触发reflow，而visibility:hidden只会触发repaint，因为没有发现位置变化。</p>
<p style="text-align: left;">多说两句关于滚屏的事，通常来说，如果在滚屏的时候，我们的页面上的所有的像素都会跟着滚动，那么性能上没什么问题，因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图，或是有些Element不跟着滚动，有些Elment是动画，那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。</p>
<p style="text-align: left;">基本上来说，reflow有如下的几个原因：</p>
<ul>
<li>Initial。网页初始化的时候。</li>
<li>Incremental。一些Javascript在操作DOM Tree时。</li>
<li>Resize。其些元件的尺寸变了。</li>
<li>StyleChange。如果CSS的属性发生变化了。</li>
<li>Dirty。几个Incremental的reflow发生在同一个frame的子树上。</li>
</ul>
<p style="text-align: left;">好了，我们来看一个示例吧：</p>
<p>[javascript]var bstyle = document.body.style; // cache</p>
<p>bstyle.padding = &quot;20px&quot;; // reflow, repaint<br />
bstyle.border = &quot;10px solid red&quot;; //  再一次的 reflow 和 repaint</p>
<p>bstyle.color = &quot;blue&quot;; // repaint<br />
bstyle.backgroundColor = &quot;#fad&quot;; // repaint</p>
<p>bstyle.fontSize = &quot;2em&quot;; // reflow, repaint</p>
<p>// new DOM element &#8211; reflow, repaint<br />
document.body.appendChild(document.createTextNode(&#8216;dude!&#8217;));[/javascript]</p>
<p style="text-align: left;">当然，我们的浏览器是聪明的，它不会像上面那样，你每改一次样式，它就reflow或repaint一次。<strong>一般来说，浏览器会把这样的操作积攒一批，然后做一次reflow，这又叫异步reflow或增量异步reflow</strong>。但是有些情况浏览器是不会这么做的，比如：resize窗口，改变了页面默认的字体，等。对于这些操作，浏览器会马上进行reflow。</p>
<p style="text-align: left;">但是有些时候，我们的脚本会阻止浏览器这么干，比如：如果我们请求下面的一些DOM值：</p>
<ol>
<li>offsetTop, offsetLeft, offsetWidth, offsetHeight</li>
<li>scrollTop/Left/Width/Height</li>
<li>clientTop/Left/Width/Height</li>
<li>IE中的 getComputedStyle(), 或 currentStyle</li>
</ol>
<p style="text-align: left;">因为，如果我们的程序需要这些值，那么浏览器需要返回最新的值，而这样一样会flush出去一些样式的改变，从而造成频繁的reflow/repaint。</p>
<h4 style="text-align: left;">减少reflow/repaint</h4>
<p>下面是一些Best Practices：</p>
<p><strong>1）不要一条一条地修改DOM的样式。与其这样，还不如预先定义好css的class，然后修改DOM的className。</strong></p>
<p>[javascript]// bad<br />
var left = 10,<br />
top = 10;<br />
el.style.left = left + &quot;px&quot;;<br />
el.style.top  = top  + &quot;px&quot;;</p>
<p>// Good<br />
el.className += &quot; theclassname&quot;;</p>
<p>// Good<br />
el.style.cssText += &quot;; left: &quot; + left + &quot;px; top: &quot; + top + &quot;px;&quot;;[/javascript]</p>
<p><strong>2）把DOM离线后修改。如：</strong></p>
<ul>
<li>使用documentFragment 对象在内存里操作DOM</li>
<li>先把DOM给display:none(有一次reflow)，然后你想怎么改就怎么改。比如修改100次，然后再把他显示出来。</li>
<li>clone一个DOM结点到内存里，然后想怎么改就怎么改，改完后，和在线的那个的交换一下。</li>
</ul>
<p>3）<strong>不要把DOM结点的属性值放在一个循环里当成循环里的变量。</strong>不然这会导致大量地读写这个结点的属性。</p>
<p>4）<strong>尽可能的修改层级比较低的DOM</strong>。当然，改变层级比较底的DOM有可能会造成大面积的reflow，但是也可能影响范围很小。</p>
<p>5）<strong>为动画的HTML元件使用fixed或absoult的position</strong>，那么修改他们的CSS是不会reflow的。</p>
<p>6）<strong>千万不要使用table布局</strong>。因为可能很小的一个小改动会造成整个table的重新布局。</p>
<blockquote><p>In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.</p>
<p><cite><a href="http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout">Fixed layout, CSS 2.1 Specification</a></cite></p></blockquote>
<blockquote><p>This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.</p>
<p><cite><a href="http://www.w3.org/TR/CSS21/tables.html#auto-table-layout">Automatic layout, CSS 2.1 Specification</a></cite></p></blockquote>
<h4>几个工具和几篇文章</h4>
<p>有时候，你会也许会发现在IE下，你不知道你修改了什么东西，结果CPU一下子就上去了到100%，然后过了好几秒钟repaint/reflow才完成，这种事情以IE的年代时经常发生。所以，我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。</p>
<ul>
<li>Chrome下，Google的<a href="http://code.google.com/webtoolkit/speedtracer/">SpeedTracer</a>是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。</li>
</ul>
<ul>
<li>Firefox下这个基于Firebug的叫<a href="https://addons.mozilla.org/en-US/firefox/addon/firebug-paint-events/" target="_blank">Firebug Paint Events</a>的插件也不错。</li>
</ul>
<ul>
<li>IE下你可以用一个叫<a href="http://ajax.dynatrace.com/pages/">dynaTrace</a>的IE扩展。</li>
</ul>
<p>最后，别忘了下面这几篇提高浏览器性能的文章：</p>
<ul>
<li><a href="http://code.google.com/speed/page-speed/docs/rules_intro.html">Google &#8211; Web Performance Best Practices</a></li>
<li><a href="http://developer.yahoo.com/performance/rules.html">Yahoo &#8211; Best Practices for Speeding Up Your Web Site</a></li>
<li><a href="http://stevesouders.com/hpws/rules.php">Steve Souders &#8211; 14 Rules for Faster-Loading Web Sites</a></li>
</ul>
<h4>参考</h4>
<ul>
<li>David Baron的演讲：Fast CSS: How Browsers Lay Out Web Pages：<a href="http://dbaron.org/talks/2012-03-11-sxsw/slide-1.xhtml" target="_blank">slideshow</a>, <a href="http://dbaron.org/talks/2012-03-11-sxsw/master.xhtml">all slides</a>, <a href="http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3">audio (MP3)</a>, <a href="http://schedule.sxsw.com/2012/events/event_IAP12909">Session page</a>, <a href="http://lanyrd.com/2012/sxsw-interactive/spmbt/">Lanyrd page</a></li>
</ul>
<ul>
<li>How Browsers Work: <a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" target="_blank">http://taligarsiel.com/Projects/howbrowserswork1.htm</a></li>
</ul>
<ul>
<li>Mozilla 的 Style System Overview：<a href="https://developer.mozilla.org/en-US/docs/Style_System_Overview" target="_blank">https://developer.mozilla.org/en-US/docs/Style_System_Overview</a></li>
</ul>
<ul>
<li>Mozilla 的 Note of reflow： <a href="http://www-archive.mozilla.org/newlayout/doc/reflow.html" target="_blank">http://www-archive.mozilla.org/newlayout/doc/reflow.html</a></li>
</ul>
<ul>
<li>Rendering: repaint, reflow/relayout, restyle：<a href="http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/" target="_blank">http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/</a></li>
</ul>
<ul>
<li>Effective Rendering CSS：<a href="http://css-tricks.com/efficiently-rendering-css/" target="_blank">http://css-tricks.com/efficiently-rendering-css/</a></li>
</ul>
<ul>
<li><strong></strong>Webkit Rendering文档：<a href="http://trac.webkit.org/wiki/WebCoreRendering" target="_blank">http://trac.webkit.org/wiki/WebCoreRendering</a></li>
</ul>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg" alt="Web开发人员速查卡" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_title">Web开发人员速查卡</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/9749.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg" alt="Javascript 装载和执行" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9749.html" class="wp_rp_title">Javascript 装载和执行</a></li><li ><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif" alt="CSS 布局:40个教程、技巧、例子和最佳实践" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_title">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png" alt="一些文章资源和趣闻" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_title">一些文章资源和趣闻</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9666.html">浏览器的渲染原理简介</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9666.html/feed</wfw:commentRss>
			<slash:comments>155</slash:comments>
		
		<enclosure url="http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3" length="20902392" type="audio/mpeg" />

			</item>
	</channel>
</rss>
