三、前端
1. 前端性能優化策略
- 減少 HTTP 請求
- CSS 放在頭部、避免使用 CSS 表達式
- JS 放在尾部、精簡 JS
- 使用外部 CSS 和 JS 文件
- 刪除重複腳本、壓縮組件
- 避免重定向
- 使用緩存
- 使用 CDN、減少 CDN 查找
2. 跨域解決方案及其原理
同源策略/SOP(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源是指"協議+域名+端口"三者相同,即便兩個不同的域名指向同一個IP地址,也非同源。
常見的跨域場景(不允許通信):
(1)不同協議,同一域名:http://www.domain.com/a.js 和 https://www.domain.com/b.js
(2)同一域名,不同端口:http://www.domain.com:8000/a.js 和 http://www.domain.com/b.js
(3)不同域名:http://www.domain1.com/a.js 和 http://www.domain2.com/b.js
(4)主域相同,子域不同:http://www.domain.com/a.js 和 http://x.domain.com/b.js 和 http://domain.com/c.js
(5)域名和域名對應相同IP:http://www.domain.com/a.js 和 http://192.168.4.12/b.js
跨域解決方案:
(1)通過 jsonp 跨域
(2)document.domain + iframe 跨域:僅限主域相同,子域不同的跨域場景。
(3)location.hash + iframe 跨域:a欲與b跨域相互通信,通過中間頁c來實現。
(4)window.name + iframe 跨域
(5)postMessage 跨域
(6)跨域資源共享(CORS):只需要服務端設置Access-Control-Allow-Origin即可。
(7)nginx 代理跨域
(8)nodejs 中間件代理跨域
(9)WebSocket 協議跨域
3. 防抖和節流
防抖函數的作用
防抖函數的作用就是控制函數在一定時間內的執行次數。防抖意味着 N 秒內函數只會被執行一次,如果 N 秒內再次被觸發,則 重新 計算延遲時間。
防抖函數的實現
- 事件第一次觸發時,timeout 是 null,調用 later(),若 immediate 爲true,那麼立即調用 func.apply(this, params);如果 immediate 爲 false,那麼過 wait 之後,調用 func.apply(this, params)。
- 事件第二次觸發時,如果 timeout 已經重置爲 null (即 setTimeout 的倒計時結束),那麼流程與第一次觸發時一樣,若 timeout 不爲 null (即 setTimeout 的倒計時未結束),那麼清空定時器,重新開始計時。
function debounce(func, wait, immediate = true) {
let timeout, result;
// 延遲執行函數
const later = (context, args) => setTimeout(() => {
timeout = null;// 倒計時結束
if (!immediate) {
// 執行回調
result = func.apply(context, args);
context = args = null;
}
}, wait);
let debounced = function (...params) {
if (!timeout) {
timeout = later(this, params);
if (immediate) {
// 立即執行
result = func.apply(this, params);
}
} else {
clearTimeout(timeout);
// 函數在每個等待時延的結束被調用
timeout = later(this, params);
}
return result;
}
// 提供在外部清空定時器的方法
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
immediate 爲 true 時,表示函數在每個等待時延的開始被調用。immediate 爲 false 時,表示函數在每個等待時延的結束被調用。
防抖的應用場景
搜索框輸入查詢,如果用戶一直在輸入中,沒有必要不停地調用去請求服務端接口,等用戶停止輸入的時候,再調用,設置一個合適的時間間隔,有效減輕服務端壓力。
表單驗證。
按鈕提交事件。
瀏覽器窗口縮放,resize 事件 (如窗口停止改變大小之後重新計算佈局) 等。
節流函數的作用
節流函數的作用是規定一個單位時間,在這個單位時間內最多隻能觸發一次函數執行,如果這個單位時間內多次觸發函數,只能有一次生效。
節流函數的實現
function throttle(func, wait, options = {}) {
var timeout, context, args, result;
var previous = 0;
var later = function () {
previous = options.leading === false ? 0 : (Date.now() || new Date().getTime());
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function () {
var now = Date.now() || new Date().getTime();
if (!previous && options.leading === false) previous = now;
//remaining 爲距離下次執行 func 的時間
//remaining > wait,表示客戶端系統時間被調整過
var remaining = wait - (now - previous);
context = this;
args = arguments;
//remaining 小於等於 0,表示事件觸發的間隔時間大於設置的 wait
if (remaining <= 0 || remaining > wait) {
if (timeout) {
// 清空定時器
clearTimeout(timeout);
timeout = null;
}
// 重置 previous
previous = now;
// 執行函數
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function () {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
}
禁用第一次首先執行,傳遞 {leading: false} ;想禁用最後一次執行,傳遞 {trailing: false}。
節流的應用場景
按鈕點擊事件。
拖拽事件。
onScoll。
計算鼠標移動的距離 (mousemove)。
4. link 和 import 的區別
link 屬於 XHTML 標籤,而 @import 是 CSS 提供的。
(1)引用的方式不同:
link(外部引用): <link rel="stylesheet" href="xxx.css" type="text/css" />
@import(導入式): @import url(xxx.css);
(2)放置的位置不同:link 一般放在 head 標籤中,而 @import 必須放在 <style type="text/css"> 標籤中。
(3)加載方式不同:link 會和 dom 結構一同加載渲染,而 @import 只能等 dom 結構加載完成以後才能加載渲染頁面。
(4)兼容性不同:@import 只能在 IE6 以上才能識別,而 link 是 XHTML 標籤,無兼容問題。
(5)樣式權重不同:link 方式的樣式的權重高於 @import。
(6)改變樣式:link 支持使用 JavaScript 改變樣式,而 @import 不可以。
(7)加載內容不同:link 除了可以加載 css 文件以外,還可以加載 MIME 類型的文件;而 @import 只能加載 css 文件。
5. 瀏覽器內核
- IE、傲遊、Avant、騰訊TT (trident內核)
- Mozilla Firefox (gecko內核)
- Safari (webkit內核)
- Chrome (Blink內核)
- Opera (原爲Presto內核,現爲Blink內核)
6. 瀏覽器兼容問題及其解決方案
問題一:在不加樣式控制的情況下,標籤默認的外補丁(margin)和內補丁(padding)不同
解決方案: CSS設置 *{ margin:0; padding:0; }
問題二:塊級屬性標籤 float 之後,如果有左右 margin,那麼在 IE6 中顯示的 margin 比設置的大
解決方案: 在float的標籤樣式中加入 { display: inline; }, 將其轉化爲行內屬性
問題三:行內屬性標籤,在設置 display:block 之後,如果採用 float 佈局,同時又有左右 margin 的情況下,那麼在 IE6 中顯示的間距有 bug
解決方案: 在 { display:block; } 後面加入 { display:inline; display:table; }
問題四:如果給標籤設置較小的高度(一般小於10px),那麼在IE6,IE7和遨遊中這個標籤的高度會超出你設置的高度
在IE6、IE7和遨遊中,這個標籤的高度不受你設置的高度的控制,它會達到默認的行高。這是因爲 IE8 之前的瀏覽器都會給標籤一個最小默認的行高的高度,即使標籤是空的,這個標籤的高度還是會達到默認的行高。
解決方案: 給超出高度的標籤設置 { overflow:hidden; } 或者設置行高 { line-height } 小於你設置的高度
問題五:如果給標籤設置最低高度 min-height,會出現不兼容的情況
解決方案: 如果我們要設置一個標籤的最小高度是200px,需要進行的設置爲:
{
min-height:200px;
height:auto !important;
height:200px;
overflow:visible;
}
問題六:圖片之間默認有間距
幾個 img 標籤放在一起的時候,有些瀏覽器會有默認的間距,加了問題一中提到的通配符也不起作用。
解決方案: 將 img 設置爲 float 屬性
問題七:格式爲PNG24的圖片在IE6瀏覽器上會出現背景
解決方案:把圖片改成 PNG8 格式
問題八:設置透明度(兼容所有瀏覽器)
.tran_class {
filter: alpha(opacity=50); // IE瀏覽器 (trident內核)
-moz-opacity: 0.5; // 老版Mozilla firefox (gecko內核)
-khtml-opacity: 0.5; // 老版Safari (webkit內核)
opacity: 0.5; // 支持opacity的所有瀏覽器,如:Chrome (Blink內核)、opera (以前是presto內核,現在改用google chrome的Blink內核)
}
問題九: IE6,IE7中 ol 的序號全爲1,不遞增
解決方案: li設置樣式 { display: list-item }
問題十: IE6,IE7不支持 display:inline-block
解決方案: 設置 inline, 並觸發 haslayout
{ display:inline-block; *display:inline; *zoom:1 }
問題十一: 使用 hack 解決瀏覽器兼容問題
不同的瀏覽器對CSS的解析不同,會導致生成的頁面效果不同。通過使用 hack 可以解決瀏覽器兼容問題(針對的更多是 老式/舊版本 瀏覽器),但不推薦使用,因爲當代碼量多時,編寫複雜度會變高,還可能會在瀏覽器更新之後產生更多的兼容問題。
hack 可以理解爲不同瀏覽器識別的標識符。
(1)五大瀏覽器的hack
div {
transform: rotate(90deg); /* 標準模式 */
-moz-transform: rotate(90deg); /* Firefox */
-webkit-transform: rotate(90deg); /* Safari and Chrome */
-o-transform: rotate(90deg); /* Opera */
-ms-transform: rotate(90deg); /* IE */
}
(2)CSS屬性hack
IE6能識別 下劃線_ 和 星號* ,但不能識別 !important
IE7能識別 星號* 和 !important ,但不識別 下劃線_
Firefox 能識別 !important ,但不識別 下劃線_ 和 星號*
書寫順序:FF IE7 IE6
比如這樣一個CSS設置:
{
height:300px;
*height:200px;
_height:100px;
}
在IE6中從上往下讀,直到_height,把高度設置成了100px;
在IE7中從上往下讀,直到*height,把高度設置成了200px;
在其他瀏覽器中,把高度設置成了300px。
(3)CSS選擇符hack
IE6能識別*html .class{}
IE7能識別*+html .class{} 或者 *:first-child+html .class{}
(4)條件註釋法:這種方式是 IE 瀏覽器專有的 hack 方式,微軟官方推薦使用。
問題十二:獲取自定義屬性
IE瀏覽器下,可以使用獲取常規屬性的方法來獲取自定義屬性,也可以使用getAttribute()獲取自定義屬性。
Firefox瀏覽器下,只能使用getAttribute()獲取自定義屬性。
解決方案:統一使用 getAttribute() 獲取自定義屬性
問題十三:Chrome 瀏覽器的中文界面下默認會將小於 12px 的文本強制按照 12px 顯示
解決方案:加入 CSS 屬性 {-webkit-text-size-adjust: none; }
問題十四:超鏈接訪問過後 hover 樣式就不出現了;被點擊訪問過的超鏈接樣式不再具有 hover 和 active 了
解決方案:改變 CSS 屬性的排列順序:
L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}
問題十五:even對象
IE瀏覽器下,even對象有x,y屬性,但是沒有pageX,pageY屬性。
Firefox瀏覽器下,event對象有pageX,pageY屬性,但是沒有x,y屬性。
解決方案:條件註釋
缺點:在IE瀏覽器下可能會增加額外的 HTTP 請求。
7. 從輸入網址到最後瀏覽器呈現頁面內容,中間發生了什麼?
(1)輸入網址,瀏覽器,發送UDP包給DNS服務器,通過DNS解析得到網址的IP地址(即服務器的IP),並將IP地址緩存。
(2)客戶端(瀏覽器)和服務器端之間建立TCP連接:由於TCP郵差需要知道4個東西(本機IP,本機端口,服務器IP,服務器端口),現在只知道了本機IP,服務器IP, 兩個端口怎麼辦?其中,本機端口很簡單,操作系統可以給瀏覽器隨機分配一個, 服務器端口更簡單,用的是一個“衆所周知”的端口,HTTP服務就是80(HTTPS服務就是443), 我們直接告訴TCP郵差就行。經過三次握手以後,客戶端和服務器端的TCP連接就建立起來了!
(3)瀏覽器發送HTTP請求。
(4)Web服務器處理請求,並將HTTP Response(一個HTML頁面)返回給瀏覽器。
(5)瀏覽器再次發起請求:由於這個HTML頁面中可能引用了大量其他資源,例如JS文件,CSS文件,圖片等,這些資源也位於服務器端,並且可能位於另外一個域名下面。所以瀏覽器只好一個個地下載,從使用DNS獲取IP開始,之前做過的事情還要再來一遍。
(6)當服務器把JS,CSS這些文件發送給瀏覽器時,會告訴瀏覽器這些文件什麼時候過期(使用Cache-Control或者Expire),瀏覽器可以把文件緩存到本地,當第二次請求同樣的文件時,如果不過期,直接從本地取就可以了。如果過期了,瀏覽器就可以詢問服務器端,文件有沒有修改過?(依據是上一次服務器發送的Last-Modified和ETag),如果沒有修改過(304 Not Modified),還可以使用緩存。否則的話服務器就會把最新的文件發回到瀏覽器。
(7)現在瀏覽器擁有了 HTML、CSS 和 JavaScript(可以修改 DOM Tree),開始進行渲染:
- HTML,瀏覽器把它解析成 DOM Tree;CSS,瀏覽器把它解析成 CSS Rule Tree
- DOM Tree 和 CSS Rule Tree 解析完成之後,被附加到一起,形成渲染樹 Render Tree
- (重排 - Reflow) 計算節點信息:根據渲染樹計算每個節點的幾何信息
- (重繪 - Repaint) 渲染繪製:根據計算好的節點信息,渲染繪製整個頁面
最後,我們就能看到呈現的頁面了。
8. 重排和重繪
重排:若渲染樹的部分節點更新,且尺寸變化,就會發生重排。
重繪:若渲染樹的部分節點更新,但不改變其他節點,就會發生重繪。
PS:重繪不一定導致重排,但重排一定會導致重繪。重排會產生比重繪更大的開銷。
觸發重排:
- (1)頁面第一次渲染:在頁面發生首次渲染的時候,所有組件都要進行首次佈局,這是開銷最大的一次重排
- (2)瀏覽器窗口尺寸改變
- (3)元素位置和尺寸(寬、高、內外邊距、邊框等)發生改變的時候
- (4)增加或刪除DOM節點
- (5)內容發生改變(文字數量或圖片大小等等)
- (6)元素字體大小變化
- (7)激活CSS僞類(例如::hover)
- (8)增加或修改樣式,設置style屬性
- (9)查詢某些屬性或調用某些方法,如:調用getComputedStyle方法,或者IE裏的currentStyle時,也會觸發重排
- (10)display:none —— 設置該屬性的元素將會消失,不佔據空間
觸發重繪:
- (1)visibility:hidden —— visibility 屬性規定元素是否可見。即使不可見的元素也會佔據頁面上的空間。
- (2)outline —— outline (輪廓)是繪製於元素周圍的一條線,位於邊框邊緣的外圍。輪廓線不會佔據空間,也不一定是矩形。
- (3)背景顏色:color、background-color
如何減少重排和重繪以提升頁面性能:
- (1)樣式集中改變。
- (2)DOM離線化。
- (3)批量添加DOM。
- (4)複製節點,在副本中修改,然後直接替換當前的節點。
- (5)降低受影響的節點:在頁面頂部插入節點將影響後續所有節點,而絕對定位元素的改變會影響較少的元素,將
position
屬性設置爲absolute或fixed。
- (6)分離讀寫操作。
- (7)緩存佈局信息。
- (8)使用 css spirit,也叫 css 精靈,它將所有的圖片放到一張圖片上,然後通過定位來實現圖片的使用。
參考:https://www.imooc.com/article/45936
END