<script>標(biāo)簽可以放在html文件中的任何位置畴椰,但一般放在head或body標(biāo)簽里面滑臊。上次在視頻中聽老師講到最后放入head標(biāo)簽中蚪拦,避免頁面出現(xiàn)抖屏現(xiàn)象牍疏,所以決定深入了解一下瀏覽器的渲染和加載機(jī)制蠢笋。
<script>標(biāo)簽位置
1. 放在<head>里
<head>標(biāo)簽僅次于<title>標(biāo)簽,放在<head>中則此js代碼會在整個網(wǎng)頁最開始解析時就加載執(zhí)行鳞陨,然后依次向下解析渲染昨寞。
注意:進(jìn)行頁面顯示初始化的js必須放在head里面
2. 放在<body>里
瀏覽器按照頁面標(biāo)簽的順序依次解析,在讀取到j(luò)s代碼時會執(zhí)行js語句。
注意:對于通過事件調(diào)用的JS函數(shù)援岩,具體放在頁面的哪個位置并不影響其發(fā)揮作用的時間歼狼,因此,考慮到前端性能方面的問題享怀,可以把不是最先執(zhí)行的和事件調(diào)用的JS代碼放在body的最下面羽峰。
瀏覽器加載和渲染的順序
- IE下載的順序從上到下,渲染順序也是從上到下凹蜈,下載和渲染同時進(jìn)行。
- 在渲染到頁面的某一部分時忍啸,其上面的所有部分都已經(jīng)下載完成(并不是說所有相關(guān)聯(lián)的元素都已經(jīng)下載完)仰坦。
- 如果遇到語義解釋性的標(biāo)簽嵌入文件(JS腳本,CSS樣式)计雌,那么此時IE的下載過程會啟用單獨(dú)連接進(jìn)行下載悄晃。
- 樣式表在下載完成后,將和以前下載的所有樣式表一起進(jìn)行解析凿滤,解析完成后妈橄,將對此前所有元素(含以前已經(jīng)渲染的)重新進(jìn)行渲染。(也就是上面說的可能出現(xiàn)抖屏現(xiàn)象)
- JS翁脆、CSS中如有重定義眷蚓,后定義函數(shù)將覆蓋前定義函數(shù)。
JS的加載
- 不能并行下載和解析(阻塞下載)反番。
- 當(dāng)引用了JS的時候沙热,瀏覽器發(fā)送1個js request就會一直等待該request的返回。因?yàn)闉g覽器需要1個穩(wěn)定的DOM樹結(jié)構(gòu)罢缸,而JS中很有可能有代碼直接改變了DOM樹結(jié)構(gòu)篙贸,比如使用document.write或apppendChild,甚至是直接使用location.href進(jìn)行跳轉(zhuǎn),瀏覽器為了防止出現(xiàn)JS修改DOM樹枫疆,需要重新構(gòu)建DOM樹的情況爵川,所以 就會阻塞其他的下載和呈現(xiàn)
加快HTML頁面加載速度
頁面減肥:
a. 頁面的肥瘦是影響加載速度最重要的因素。
b. 刪除不必要的空格息楔、注釋寝贡。
c. 將inline的script和css移到外部文件。
d. 可以使用HTML Tidy來給HTML減肥值依,還可以使用一些壓縮工具來給JavaScript減肥兔甘。減少文件數(shù)量:
a. 減少頁面上引用的文件數(shù)量可以減少HTTP連接數(shù)。
b. 許多JavaScript鳞滨、CSS文件可以合并最好合并洞焙,人家財幫子都把自己的JavaScript. functions和Prototype.js合并到一個base.js文件里去了。減少域名查詢:
a. DNS查詢和解析域名也是消耗時間的,所以要減少對外部JavaScript澡匪、CSS熔任、圖片等資源的引用,不同域名的使用越少越好唁情。緩存重用數(shù)據(jù):
a. 對重復(fù)使用的數(shù)據(jù)進(jìn)行緩存疑苔。優(yōu)化頁面元素加載順序:
a. 首先加載頁面最初顯示的內(nèi)容和與之相關(guān)的JavaScript和CSS,然后加載HTML相關(guān)的東西甸鸟,像什么不是最初顯示相關(guān)的圖片惦费、flash、視頻等很肥的資源就最后加載抢韭。減少inline JavaScript的數(shù)量:
a. 瀏覽器parser會假設(shè)inline JavaScript會改變頁面結(jié)構(gòu)薪贫,所以使用inline JavaScript開銷較大。
b. 不要使用document.write()這種輸出內(nèi)容的方法刻恭,使用現(xiàn)代W3C DOM方法來為現(xiàn)代瀏覽器處理頁面內(nèi)容瞧省。使用現(xiàn)代CSS和合法的標(biāo)簽:
a. 使用現(xiàn)代CSS來減少標(biāo)簽和圖像,例如使用現(xiàn)代CSS+文字完全可以替代一些只有文字的圖片鳍贾。
b. 使用合法的標(biāo)簽避免瀏覽器解析HTML時做“error correction”等操作鞍匾,還可以被HTML Tidy來給HTML減肥。Chunk your content:
a. 不要使用嵌套table骑科,而使用非嵌套table或者div橡淑。將基于大塊嵌套的table的layout分解成多個小table,這樣就不需要等到整個頁面(或大table)內(nèi)容全部加載完才顯示咆爽。指定圖像和table的大惺崧搿:
a. 如果瀏覽器可以立即決定圖像或table的大小,那么它就可以馬上顯示頁面而不要重新做一些布局安排的工作伍掀。
b. 這不僅加快了頁面的顯示掰茶,也預(yù)防了頁面完成加載后布局的一些不當(dāng)?shù)母淖儭?br> c. image使用height和width。
HTML頁面加載和解析流程
用戶輸入網(wǎng)址(假設(shè)是個html頁面蜜笤,并且是第一次訪問)濒蒋,瀏覽器向服務(wù)器發(fā)出請求,服務(wù)器返回html文件把兔。
瀏覽器開始載入html代碼沪伙,發(fā)現(xiàn)<head>標(biāo)簽內(nèi)有一個<link>標(biāo)簽引用外部CSS文件。
瀏覽器又發(fā)出CSS文件的請求县好,服務(wù)器返回這個CSS文件围橡。
瀏覽器繼續(xù)載入html中<body>部分的代碼,并且CSS文件已經(jīng)拿到手了缕贡,可以開始渲染頁面了翁授。
瀏覽器在代碼中發(fā)現(xiàn)一個<img>標(biāo)簽引用了一張圖片拣播,向服務(wù)器發(fā)出請求。此時瀏覽器不會等到圖片下載完收擦,而是繼續(xù)渲染后面的代碼贮配。
服務(wù)器返回圖片文件,由于圖片占用了一定面積塞赂,影響了后面段落的排布泪勒,因此瀏覽器需要回過頭來重新渲染這部分代碼。
瀏覽器發(fā)現(xiàn)了一個包含一行Javascript代碼的<script>標(biāo)簽宴猾,趕快運(yùn)行它圆存。
Javascript腳本執(zhí)行了這條語句,它命令瀏覽器隱藏掉代碼中的某個<style>(style.display=”none”)仇哆。杯具啊沦辙,突然就少了這么一個元素,瀏覽器不得不重新渲染這部分代碼税产。
終于等到了</html>的到來怕轿,瀏覽器淚流滿面……
等等偷崩,還沒完辟拷,用戶點(diǎn)了一下界面中的“換膚”按鈕,Javascript讓瀏覽器換了一下<link>標(biāo)簽的CSS路徑阐斜。
瀏覽器召集了在座的各位<div><span><ul><li>們衫冻,“大伙兒收拾收拾行李,咱得重新來過……”谒出,瀏覽器向服務(wù)器請求了新的CSS文件隅俘,重新渲染頁面。
- 問題1
加載過程中遇到外部css文件笤喳,瀏覽器另外發(fā)出一個請求为居,來獲取css文件。遇到圖片資源杀狡,瀏覽器也會另外發(fā)出一個請求蒙畴,來獲取圖片資源。這是異步請求呜象,并不會影響html文檔進(jìn)行加載膳凝,但是當(dāng)文檔加載過程中遇到j(luò)s文件,html文檔會掛起渲染(加載解析渲染同步)的線程恭陡,不僅要等待文檔中js文件加載完畢蹬音,還要等待解析執(zhí)行完畢,才可以恢復(fù)html文檔的渲染線程休玩。 - 原因:
JS有可能會修改DOM著淆,最為經(jīng)典的document.write劫狠,這意味著,在JS執(zhí)行完成前牧抽,后續(xù)所有資源的下載可能是沒有必要的嘉熊,這是js阻塞后續(xù)資源下載的根本原因。 - 辦法:
可以將外部引用的js文件放在</body>前扬舒。
- 問題:
雖然css文件的加載不影響js文件的加載阐肤,但是卻影響js文件的執(zhí)行,即使js文件內(nèi)只有一行代碼讲坎,也會造成阻塞孕惜。 - 原因:
可能會有 var width = $('#id').width(),這意味著晨炕,js代碼執(zhí)行前衫画,瀏覽器必須保證css文件已下載和解析完成。這也是css阻塞后續(xù)js的根本原因瓮栗。 - 辦法:
當(dāng)js文件不需要依賴css文件時削罩,可以將js文件放在頭部css的前面。
- 注意:
不要在外部調(diào)用的js文件中調(diào)用運(yùn)行時間較長的函數(shù)费奸,如果一定要用弥激,可以使用setTimeout函數(shù)。 - 原因:
瀏覽器有以上五個常駐線程
-- 瀏覽器GUI渲染線程
-- javascript引擎線程
-- 瀏覽器定時器觸發(fā)線程(setTimeout)
-- 瀏覽器事件觸發(fā)線程
-- 瀏覽器http異步請求線程(.jpg <link />這類請求)
由于 javascript引擎線程為單線程愿阐,當(dāng)js引擎線程(第二個)進(jìn)行時微服,會掛起其他一切線程,所以代碼都是先壓到隊(duì)列缨历,采用先進(jìn)先出的方式運(yùn)行以蕴,這個時候3、4辛孵、5這三類線線程也會產(chǎn)生不同的異步事件丛肮。
預(yù)加載
現(xiàn)代瀏覽器存在 prefetch 優(yōu)化,瀏覽器會另外開啟線程魄缚,提前下載js宝与、css文件,需要注意的是鲜滩,預(yù)加載js并不會改變dom結(jié)構(gòu)伴鳖,他將這個工作留給主加載。
預(yù)加載網(wǎng)頁徙硅,利用空余時間來提前加載該網(wǎng)頁的后續(xù)網(wǎng)頁
<link rel="prefetch" href="http://">
為js腳本添加defer屬性榜聂,使得瀏覽器不等js腳本加載執(zhí)行完,就加載后面的圖片
<script defer="true" src="JavaScript.js" type="text/javascript"/>
解析
- DOM樹構(gòu)建過程是深度遍歷過程嗓蘑,當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)都構(gòu)建好后才會去構(gòu)建當(dāng)前節(jié)點(diǎn)的下一個兄弟節(jié)點(diǎn)须肆。
2 . 將CSS解析成 CSS Rule Tree 匿乃。
根據(jù)DOM樹和CSSOM來構(gòu)造 Rendering Tree。注意:Rendering Tree 渲染樹并不等同于 DOM 樹豌汇,因?yàn)橐恍┫?Header 或 display:none 的東西就沒必要放在渲染樹中了幢炸。
有了Render Tree,瀏覽器已經(jīng)能知道網(wǎng)頁中有哪些節(jié)點(diǎn)拒贱、各個節(jié)點(diǎn)的CSS定義以及他們的從屬關(guān)系宛徊。下一步操作稱之為Layout,顧名思義就是計(jì)算出每個節(jié)點(diǎn)在屏幕中的位置逻澳。
-
再下一步就是繪制闸天,即遍歷render樹,并使用UI后端層繪制每個節(jié)點(diǎn)斜做。
image.png
上述這個過程是逐步完成的苞氮,為了更好的用戶體驗(yàn),渲染引擎將會盡可能早的將內(nèi)容呈現(xiàn)到屏幕上瓤逼,并不會等到所有的html都解析完成之后再去構(gòu)建和布局render樹笼吟。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時霸旗,可能還在通過網(wǎng)絡(luò)下載其余內(nèi)容贷帮。
Reflow(回流):
瀏覽器要花時間去渲染,當(dāng)它發(fā)現(xiàn)了某個部分發(fā)生了變化影響了布局定硝,那就需要倒回去重新渲染皿桑。
Repaint(重繪):
如果只是改變了某個元素的背景顏色毫目,文字顏色等蔬啡,不影響元素周圍或內(nèi)部布局的屬性,將只會引起瀏覽器的repaint镀虐,重畫某一部分箱蟆。
Reflow要比Repaint更花費(fèi)時間,也就更影響性能刮便。所以在寫代碼的時候空猜,要盡量避免過多的Reflow。
引起reflow的原因
- 頁面初始化
- 某些元素的尺寸變了
- 某些CSS的屬性發(fā)生變化了恨旱,如display:none
- 操作DOM時辈毯,如添加子節(jié)點(diǎn)
減少 reflow/repaint
- 不要一條一條地修改 DOM 的樣式。與其這樣搜贤,還不如預(yù)先定義好 css 的 class谆沃,然后修改 DOM 的 className。
- 要把 DOM 結(jié)點(diǎn)的屬性值放在一個循環(huán)里當(dāng)成循環(huán)里的變量仪芒。
- 為動畫的 HTML 元件使用 fixed 或 absoult 的 position唁影,那么修改他們的 CSS 是不會 reflow 的耕陷。
- 千萬不要使用 table 布局。因?yàn)榭赡芎苄〉囊粋€小改動會造成整個 table 的重新布局
CSS規(guī)范
CSS選擇符是從右到左進(jìn)行匹配的据沈。從右到左哟沫!所以,#nav li 我們以為這是一條很簡單的規(guī)則锌介,秒秒鐘就能匹配到想要的元素嗜诀,但是,但是孔祸,但是裹虫,是從右往左匹配啊,所以融击,會去找所有的li筑公,然后再去確定它的父元素是不是#nav。尊浪,因此匣屡,寫css的時候需要注意:
- dom深度盡量淺。
- 減少inline javascript拇涤、css的數(shù)量捣作。
- 使用現(xiàn)代合法的css屬性。
- 不要為id選擇器指定類名或是標(biāo)簽鹅士,因?yàn)閕d可以唯一確定一個元素券躁。
- 避免后代選擇符,盡量使用子選擇符掉盅。原因:子元素匹配符的概率要大于后代元素匹配符也拜。-后代選擇符;#tp p{} 子選擇符:#tp>p{}
- 避免使用通配符,舉一個例子趾痘,.mod .hd *{font-size:14px;} 根據(jù)匹配順序,將首先匹配通配符,也就是說先匹配出通配符,然后匹配.hd(就是要對dom樹上的所有節(jié)點(diǎn)進(jìn)行遍歷他的父級元素),然后匹配.mod,這樣的性能耗費(fèi)可想而知.
總結(jié)
- JS的下載和執(zhí)行會阻塞DOM樹的構(gòu)建以及其它資源的下載慢哈。原因是瀏覽器需要一個穩(wěn)定的DOM樹結(jié)構(gòu) ,來防止JS修改DOM樹永票,導(dǎo)致需要重新構(gòu)建DOM樹的情況
- JS載入后馬上執(zhí)行卵贱,如果JS代碼中有執(zhí)行時間長的函數(shù),用SetTimeOut()
- CSS文件的加載雖不影響JS文件的加載侣集,但卻可能影響到JS文件的執(zhí)行造成阻塞键俱。因此必須保證CSS文件已下載和解析完成。
4.關(guān)于順序的問題世分,可以將CSS文件在最前面加載编振,再載入JS。并且對于事件調(diào)用型js代碼可以放到<body>標(biāo)簽的最末端 罚攀。