精品人妻互换一区二区三区免费,午夜精品久久久久久久9,欧美巨鞭大战丰满少妇,97精品人妻一区二区三区蜜桃

前端與seo有什么關(guān)聯(lián)嗎

  最近工作中一個(gè)項(xiàng)目在運(yùn)行時(shí)有一些性能問題,為此我看了很多與性能優(yōu)化相關(guān)的內(nèi)容,下面做個(gè)簡(jiǎn)單的分享。

  前端性能優(yōu)化,這包括 CSS/JS 性能優(yōu)化、網(wǎng)絡(luò)性能優(yōu)化等等內(nèi)容,這方面的內(nèi)容 《高性能網(wǎng)站建設(shè)指南》、《高性能網(wǎng)站建設(shè)進(jìn)階指南》、《高性能JavaScript》 等等書都做了很多講解,強(qiáng)烈推薦閱讀。(這些書單參見本文結(jié)尾)

  下面的內(nèi)容,上面提到的書中大都包含了,因此可以考慮轉(zhuǎn)而去讀這些書,做一個(gè)完完全全的了解,對(duì)于本文,也就不要再讀下去了。

  如果你堅(jiān)持看到了這里,那就來談?wù)勎矣龅降囊恍┣岸诵阅軉栴},并聊聊解決方案。

  1 優(yōu)先優(yōu)化對(duì)性能影響大的部分

  當(dāng)應(yīng)用有了性能問題后,不要一股腦扎到代碼中去,首先要想想那部分對(duì)性能影響最大。優(yōu)先優(yōu)化那些對(duì)性能影響大的部分,可以起到立桿見影的效果。

  使用 Chrome DevTools ,可以很快地找到導(dǎo)致性能變差的最主要因素,關(guān)于 Chrome DevTools 的使用強(qiáng)烈推薦閱讀 Google Developers 上面的系列教程 - Chrome DevTools。

  另外在對(duì)代碼進(jìn)行優(yōu)化的時(shí)候,也首先要關(guān)注那些存在循環(huán)或者高頻調(diào)用的地方。有的時(shí)候我們可能不知道某個(gè)地方是否會(huì)高頻執(zhí)行,比如某些事件的回調(diào)。這個(gè)時(shí)候可以使用 console.count 來對(duì)執(zhí)行次數(shù)進(jìn)行統(tǒng)計(jì)。當(dāng)這部分高頻執(zhí)行的代碼已經(jīng)足夠優(yōu)化的時(shí)候,就要考慮是否能夠減少執(zhí)行次數(shù)。比如一個(gè)時(shí)間復(fù)雜度為 O(n*n*n) 的算法,再怎么優(yōu)化也不如將其變?yōu)?O(n*n) 來的快。

  2 對(duì)高頻觸發(fā)的事件進(jìn)行節(jié)流或消抖

  對(duì)于 Scroll 和 Touchmove 這類事件,永遠(yuǎn)不要低估了它們的執(zhí)行頻率,處理這類事件的時(shí)候可以考慮是否要給它們添加一個(gè)節(jié)流或者消抖過的回調(diào)。節(jié)流和消抖,可能其他人不這么翻譯,其實(shí)也就是 debounce 和throttle 這兩個(gè)函數(shù)。

  debounce 和 throttle 是兩個(gè)相似(但不相同)的用于控制函數(shù)在某段事件內(nèi)的執(zhí)行頻率的技術(shù)。你可以在 underscore 或者 lodash 中找到這兩個(gè)函數(shù)。

  2.1 使用 debounce 進(jìn)行消抖

  多次連續(xù)的調(diào)用,最終實(shí)際上只會(huì)調(diào)用一次。想象自己在電梯里面,門將要關(guān)上,這個(gè)時(shí)候另外一個(gè)人來了,取消了關(guān)門的操作,過了一會(huì)兒門又要關(guān)上,又來了一個(gè)人,再次取消了關(guān)門的操作。電梯會(huì)一直延遲關(guān)門的操作,直到某段時(shí)間里沒人再來。

  所以 debounce 適合用在比如對(duì)用戶輸入內(nèi)容進(jìn)行校驗(yàn)的這種場(chǎng)景下,多次觸發(fā)只需要響應(yīng)最后一次觸發(fā)就好了。

  2.2 使用 throttle 進(jìn)行節(jié)流

  將頻繁調(diào)用的函數(shù)限定在一個(gè)給定的調(diào)用頻率內(nèi)。它保證某個(gè)函數(shù)頻率再高,也只能在給定的事件內(nèi)調(diào)用一次。比如在滾動(dòng)的時(shí)候要檢查當(dāng)前滾動(dòng)的位置,來顯示或隱藏回到頂部按鈕,這個(gè)時(shí)候可以使用 throttle 來將滾動(dòng)回調(diào)函數(shù)限定在每 300ms 執(zhí)行一次。

  需要提到的是,這兩個(gè)函數(shù)常常被誤用,且很多時(shí)候當(dāng)事人并沒有意識(shí)到自己誤用了。我曾經(jīng)用錯(cuò)過,也見過別人用錯(cuò)。這兩個(gè)函數(shù)都接受一個(gè)函數(shù)作為參數(shù),然后返回一個(gè)節(jié)流/去抖后的函數(shù),下面第二種用法才是正確的用法:

  // 錯(cuò)誤的用法,每次事件觸發(fā)都得到一個(gè)新的函數(shù)

  $(window).on('scroll', function() {

  _.throttle(doSomething, 300);

  });

  // 正確的用法,將節(jié)流后的函數(shù)作為回調(diào)

  $(window).on('scroll', _.throttle(doSomething, 200));

  3 JavaScript 很快,DOM 很慢

  JavaScript 如今已經(jīng)很快了,真正慢的是 DOM。因此避免使用一些不易讀但據(jù)說能提高速度的寫法。不久前,

  一位朋友對(duì)我說使用 ‘+’ 號(hào)將字符串轉(zhuǎn)為數(shù)字比使用 parseInt 快。對(duì)此我并沒有懷疑,因?yàn)橹庇X上 parseInt 進(jìn)行了函數(shù)調(diào)用,很可能會(huì)慢一些,我們一起在 node v6.3.0 上進(jìn)行了一些驗(yàn)證,結(jié)果的確如我們所預(yù)計(jì)的那樣,但是差別有多大呢,進(jìn)行了 5 億次迭代,使用 + 號(hào)的方法僅僅快了2秒。雖然快了兩秒,但實(shí)際中將字符轉(zhuǎn)為數(shù)字的操作可能只會(huì)進(jìn)行幾次,因此這樣的做法根本沒有意義,它只會(huì)讓代碼變得更難讀。

  plus: 1694.392ms

  parseInt: 3661.403ms

  真正慢的是 DOM,DOM 對(duì)外提供了 API,而 JavaScript 可以調(diào)用這些 API,它們兩者就像是使用一座橋梁相連,每次過橋都要被收取大量費(fèi)用,因此應(yīng)該盡量讓減少過橋的次數(shù)。

  3.1 為什么 DOM 很慢

  談到這里需要對(duì)瀏覽器利用 HTML/CSS/JavaScript 等資源呈現(xiàn)出精彩的頁(yè)面的過程進(jìn)行簡(jiǎn)單說明。瀏覽器在收到 HTML 文檔之后會(huì)對(duì)文檔進(jìn)行解析開始構(gòu)建 DOM (Document Object Model) 樹,進(jìn)而在文檔中發(fā)現(xiàn)樣式表,開始解析 CSS 來構(gòu)建 CSSOM(CSS Object Model)樹,這兩者都構(gòu)建完成后,開始構(gòu)建渲染樹。整個(gè)過程如下:

  渲染樹的構(gòu)建過程

  在每次修改了 DOM 或者其樣式之后都要進(jìn)行 DOM樹的構(gòu)建,CSSOM 的重新計(jì)算,進(jìn)而得到新的渲染樹。瀏覽器會(huì)利用新的渲染樹對(duì)頁(yè)面進(jìn)行重排和重繪,以及圖層的合并。通常瀏覽器會(huì)批量進(jìn)行重排和重繪,以提高性能。但當(dāng)我們?cè)噲D通過 JavaScript 獲取某個(gè)節(jié)點(diǎn)的尺寸信息的時(shí)候,為了獲得當(dāng)前真實(shí)的信息,瀏覽器會(huì)立刻進(jìn)行一次重排。

  3.2 避免強(qiáng)制性同步布局

  在 JavaScript 中讀取到的布局信息都是上一幀的信息,如果在 JavaScript 中修改了頁(yè)面的布局,比如給某個(gè)元素添加了一個(gè)類,然后再讀取布局信息。這個(gè)時(shí)候?yàn)榱双@得真實(shí)的布局信息,瀏覽器需要強(qiáng)制性對(duì)頁(yè)面進(jìn)行布局。因此應(yīng)該避免這樣做。

  3.3 批量操作 DOM

  在必須要進(jìn)行頻繁的 DOM 操作時(shí),可以使用 fastdom 這樣的工具,它的思路是將對(duì)頁(yè)面的讀取和改寫放進(jìn)隊(duì)列,在頁(yè)面重繪的時(shí)候批量執(zhí)行,先進(jìn)行讀取后改寫。因?yàn)槿绻麑⒆x取與改寫交織在一起可能引起多次頁(yè)面的重排。而利用 fastdom 就可以避免這樣的情況發(fā)生。

  雖然有了 fastdom 這樣的工具,但有的時(shí)候還是不能從根本上解決問題,比如我最近遇到的一個(gè)情況,與頁(yè)面簡(jiǎn)單的一次交互(輕輕滾動(dòng)頁(yè)面)就執(zhí)行了幾千次 DOM 操作,這個(gè)時(shí)候核心要解決的是減少 DOM 操作的次數(shù)。這個(gè)時(shí)候就要從代碼層面考慮,看看是否有不必要的讀取。

  另外一些關(guān)于高效操作 DOM 的方法,可以參見《高性能JavaScript》相關(guān)章節(jié),也可以先參考一下我的讀書筆記 《高性能JavaScript》 (https://github.com/wy-ei/notebook/issues/34 )

  4 優(yōu)化渲染性能

  瀏覽器通常每秒更新頁(yè)面 60 次,每一幀的時(shí)間就是 16.6ms,為了能讓瀏覽器保持 60幀 的幀率,為了讓動(dòng)畫看起來流暢,需要保證幀率達(dá)到 60fps,因此每一幀的邏輯需要在 16.6ms 內(nèi)完成。

  每一幀實(shí)際上都包含下列步驟:

  因此,通常 JavaScript 的執(zhí)行時(shí)間不能超過 10ms。

  JavaScript:改變?cè)貥邮剑砑釉氐?DOM 中等等

  Style:元素的類或者style改變了,這個(gè)時(shí)候需要重新計(jì)算元素的樣式

  Layout:需要重新計(jì)算元素的具體尺寸

  Paint:將元素的繪制的圖層上

  Composite:合并多個(gè)圖層

  當(dāng)然也不是說每一幀都會(huì)進(jìn)行這些操作。當(dāng)你的 JavaScript 改變了某個(gè) layout 屬性,比如元素的 width 和height 或者 top 等等,瀏覽器就會(huì)重新計(jì)算布局,并對(duì)整個(gè)頁(yè)面進(jìn)行重排。

  如果修改了 background、color 這樣的僅僅會(huì)讓頁(yè)面重繪的屬性,這不會(huì)影響頁(yè)面的布局,瀏覽器會(huì)跳過計(jì)算布局(layout)的過程,只進(jìn)行重繪(paint)。

  如果修改了一個(gè)不需要計(jì)算布局也不需要重繪的屬性,那就只會(huì)進(jìn)行圖層的合并,這是代價(jià)最小的修改。從https://csstriggers.com/ 上你可以知道修改那些樣式屬性會(huì)觸發(fā)(Layout,Paint,Composite)中的那些操作。

  4.1 將漸變或者會(huì)動(dòng)畫元素放到單獨(dú)的繪制層中

  繪制并非在一個(gè)單獨(dú)的畫布上進(jìn)行的,而是多層。因此將那些會(huì)變動(dòng)的元素提升至單獨(dú)的圖層,可以讓他的改變影響到的元素更少。

  可以使用 CSS 中的 will-change: transform; 或者 transform: translateZ(0); 這樣來將元素提升至單獨(dú)的圖層中。

  使用 Chrome DevTools 來審查圖層

  在調(diào)試的時(shí)候你可以在 Chrome DevTools 的 timeline 面板來觀察繪制圖層。當(dāng)然也不是說圖層越多越好,因?yàn)樾略黾右粋€(gè)圖層可能會(huì)耗費(fèi)額外的內(nèi)存。且新增加一個(gè)圖層的目的是為了避免某個(gè)元素的變動(dòng)影響其他元素。

  4.2 降低繪制復(fù)雜度

  某些屬性的重繪相對(duì)而言更加復(fù)雜,比如 filter、box-shadow 等濾鏡或漸變效果。因此不要濫用這類效果。

  5 優(yōu)化 JavaScript 的執(zhí)行

  下面提到的 JavaScript 優(yōu)化,并不是說如何讓 JavaScript 執(zhí)行的更快,而是如何讓 JavaScript 更高效地與 DOM 配合。

  5.1 使用 requestAnimationFrame 來更新頁(yè)面

  我們希望在每一幀剛開始的時(shí)候?qū)?yè)面進(jìn)行更改,目前只有使用 requestAnimationFrame 能夠保證這一點(diǎn)。使用setTimeout 或者 setInterval 來觸發(fā)更新頁(yè)面的函數(shù),該函數(shù)可能在一幀的中間或者結(jié)束的時(shí)間點(diǎn)上調(diào)用,進(jìn)而導(dǎo)致該幀后面需要進(jìn)行的事情沒有完成,引發(fā)丟幀。

  使用 setTimeout 可能導(dǎo)致丟幀

  requestAnimationFrame 會(huì)將任務(wù)安排在頁(yè)面重繪之前,這保證動(dòng)畫能有足夠的時(shí)間來執(zhí)行 JavaScript 。

  5.2 使用 Web Worker 來處理復(fù)雜的計(jì)算

  JavaScript 是在單線程的,并且可能會(huì)一直這樣,因此 JavaScript 在執(zhí)行復(fù)雜計(jì)算的時(shí)候很可能會(huì)阻塞線程,導(dǎo)致頁(yè)面假死。但 Web Worker 的出現(xiàn),以另外一種方式給了我們多線程的能力,可以將復(fù)雜計(jì)算放在 worker 中進(jìn)行,當(dāng)計(jì)算完成后,以 postMessage 的形式將結(jié)果傳回來。

  對(duì)于單個(gè)函數(shù),因?yàn)?Web Worker 接受一個(gè)腳本的 url 作為參數(shù),使用 URL.createObjectURL 方法,我們可以將一個(gè)函數(shù)的內(nèi)容轉(zhuǎn)換為 url,利用它創(chuàng)建一個(gè) worker。

  var workerContent=`

  self.onmessage=function(evt){

  // ...

  // 在這里進(jìn)行復(fù)雜計(jì)算

  var result=complexFunc();

  // 將結(jié)果傳回

  self.postMessage(result);

  };`

  // 得到 url

  var blob=new Blob([workerContent]);

  var url=window.URL.createObjectURL(blob);

  // 創(chuàng)建 worker

  var worker=new Worker(url);

  5.3 使用 transform 和 opacity 來完成動(dòng)畫

  如今只有對(duì)這兩個(gè)屬性的修改不需要經(jīng)歷 layout 和 paint 過程。

  6 優(yōu)化 CSS

  CSS 選擇器在匹配的時(shí)候是由右至左進(jìn)行的,因此最后一個(gè)選擇器常被稱為關(guān)鍵選擇器,因?yàn)樽詈笠粋€(gè)選擇越特殊,需要進(jìn)行匹配的次數(shù)越少。要千萬避免使用 *(通用選擇器)作為關(guān)鍵選擇器。因?yàn)樗芷ヅ涞剿性?,進(jìn)而倒數(shù)第二個(gè)選擇器還會(huì)和所有元素進(jìn)行一次匹配。這導(dǎo)致效率很低下。

  div p * {}

  另外 first-child 這類偽類選擇器也不夠特殊,也要避免將它們作為關(guān)鍵選擇器。關(guān)鍵選擇器越特殊,瀏覽器就能用較少的匹配次數(shù)找到待匹配元素,選擇器性能也就越好。

  還有一個(gè)老生常談的注意事項(xiàng),不要使用太多的選擇器。如果還有同學(xué)很悲劇地要兼容低版本 IE,要避免使用 CSS 表達(dá)式,它的性能很差,詳細(xì)內(nèi)容可參見我之前記錄的一篇筆記 《高性能網(wǎng)站建設(shè)指南》筆記(https://github.com/wy-ei/notebook/issues/15 )

  7 合理處理腳本和樣式表

  如今有了 requirejs,webpack 等工具,可能很少會(huì)在頁(yè)面中加載很多 JavaScript/CSS 代碼了。盡管如此,還是有必要談?wù)勅绾魏侠硖幚砟_本和樣式表。

  大多數(shù)人已經(jīng)知道通常要把 JavaScript 放在文檔底部,把 CSS 放在文檔頂部。為什么呢?因?yàn)?JavaScript 會(huì)阻塞頁(yè)面的解析,而外部樣式表會(huì)阻塞頁(yè)面的呈現(xiàn)和 JavaScript 的執(zhí)行。

  7.1 CSS阻塞渲染

  通常情況下 CSS 被認(rèn)為是阻塞渲染的資源,在CSSOM 構(gòu)建完成之前,頁(yè)面不會(huì)被渲染,放在頂部讓樣式表能夠盡早開始加載。但如果把引入樣式表的 link 放在文檔底部,頁(yè)面雖然能立刻呈現(xiàn)出來,但是頁(yè)面加載出來的時(shí)候會(huì)是沒有樣式的,是混亂的。當(dāng)后來樣式表加載進(jìn)來后,頁(yè)面會(huì)立即進(jìn)行重繪,這也就是通常所說的閃爍了。

  7.2 JavaScript 阻塞文檔解析

  當(dāng)在 HTML 文檔中遇到 script 標(biāo)簽后控制權(quán)將交給 JavaScript,在 JavaScript 下載并執(zhí)行完成之前,都不會(huì)解析 HTML。因此如果將 JavaScript 放在文檔頂部,恰好這個(gè)時(shí)候 JavaScript 腳本加載的特別慢,用戶將會(huì)等待很長(zhǎng)一段時(shí)間,這段個(gè)時(shí)候 HTML 文檔還沒有解析到 body 部分,頁(yè)面會(huì)是空白的。

  另外常常被忽略的事實(shí)是:在瀏覽器沒有下載并解析完成使用 link 引入的 CSS 文件之前,JavaScript 是不會(huì)執(zhí)行的,因?yàn)?JavaScript 中可能需要讀取樣式,而此時(shí)樣式表還沒有加載回來,因此瀏覽器不會(huì)執(zhí)行 JavaScript。可以給 JavaScript 加上 async 標(biāo)記,表示 JavaScript 的執(zhí)行不會(huì)讀取 DOM ,JavaScript 可以不被 CSS 阻塞,可以在空閑時(shí)間立刻執(zhí)行。

  綜上所述,你更要保證 CSS 文件加載的足夠快。

欧美在线观看一区二区| 一本色道久久综合狠狠躁篇的优点| 亚洲一区二区小说| 中文字幕乱码一区二区三区在线下载| 成人午夜av| 亚洲精品在线| 丝袜av一区| 亚洲精品国产精品国自产网站按摩| 丁香花在线电影小说观看| 久久精品熟女亚洲av麻豆| 韩国久久av| 日韩人妻一区二区| 国产黄片aaa| av影片在线看| 1000部毛片免费看| 日本精品一区二区三区在线视频一 | 亚洲成av人片一区二区| 可以看的毛片| 精品永久免费| 亚洲性色av| 丰满风韵老熟女| 成人区人妻精品一区二区 | 精品一区一区三区新区乱码| 国产精品内射| 色婷婷av一区二区三区影片| 午夜福利导航视频| 天天躁日日躁狠狠| 真人实女处被破www| 中文字幕在线观看视频网站| ass丰满老熟妇pics| 国产一区二区三区中文字幕| 夜夜嗨av一区二区三区懂色av| 狠狠躁天天躁夜夜躁婷婷老牛影视| 2023国产精品视频| 欧美jizzhd精品欧美巨大| av日韩一区二区| 日本老熟妇毛茸茸| 精品成人一区| 中文字幕人妻诱惑| 蜜臀av性久久久久av| 中文字幕一区二区三区不卡|