如何提升前端性能和響應速度
下面大多是從前端工程化的角度給出的優化建議,如果需要了解語法上的優化,可以參考:
1. 原生 css 動畫代替 js 動畫
原生 css 動畫要比 js 實現的動畫要高效很多,所以在可能的情況下儘量用原生 css 動畫。
可以參考:
2. 原生 js 代替第三方庫
因爲第三方庫不可避免的會增大打包文件的體積,並且有很多我們用不着的代碼和性能的損失,所以在可能的情況下儘量用原生 js 的 api,代替第三方庫的 api,比如 jquery、lodash、underscore、moment 等。
可以參考:
3. 使用第三方庫時,用子模塊代替整個包
有些第三方庫會比較大,如果提供單個模塊的使用方式,就儘量使用子模塊代替使用整個包,比如 lodash、jquery-ui 等。
以 lodash
爲例:
// 不推薦
import _ from 'lodash';
_.forEach();
_.defaults();
// 推薦
import forEach from 'lodash/forEach';
import defaults from 'lodash/defaults';
forEach();
defaults();
4. 用高效的 api 代替低效的 api
如果相同的功能可以有多種選擇,應當儘量使用高效的一種方案。
比如:
- 用
document.getElementById, document.getElementsByClassName, document.getElementsByTagName
代替document.querySelector, document.querySelectorAll
- 用
el.innerHTML
代替document.createElement, el.appendChild
5. 扁平結構、避免無用嵌套
避免無用的閉包、無用的塊作用域,儘量是代碼結構扁平化。
比如:
// 低效的實現
const urlParams = (() => {
const params = {};
if (location.search) {
location.search.slice(1).split('&').forEach(item => {
const [key, value = ''] = item.split('=');
urlParams[key] = value;
});
}
return params;
})();
// 更高效的實現
const urlParams = {};
if (location.search) {
location.search.slice(1).split('&').forEach(item => {
const [key, value = ''] = item.split('=');
urlParams[key] = value;
});
}
6. 用 es6 的模塊化
現在 js
的模塊化主要是 commonjs
與 es6
模塊化規範,但是在開發的時候,建議是用 es6
的模塊化規範,因爲 es6
的模塊化可以使用 Tree Shaking
的功能。
這個功能能夠在構建工具打包代碼時,對代碼進行分析,只有真正用到的代碼會被打包,沒有用到的則不會。
// one.js
export const smile = {};
export const cry = [];
// two.js
import { smile } from './one';
export default smile;
上面的代碼以 two.js
爲入口進行打包,則 one.js
中只有 export const smile = {}
會被打包,而 export const cry = []
不會。
關於 Tree Shaking
,可以參考:
7. 合併、壓縮、分割
代碼的合併與壓縮是前端的必修課,如果使用 webpack
來打包,webpack
會自動幫我們完成,一般無需關心。
另外,在有些時候,代碼是需要做分割的,因爲 webpack
會把代碼都打包到一個文件中,當這個文件很大的時候,就需要分割成多個小文件。一般建議 bundle 文件最大不超過 350k
。
對於 webpack
,可以用 DllPlugin 或 SplitChunksPlugin 做文件分割。
8. 按需加載
對於很多應用來說,特別是 SPA 應用,有些資源是沒必要在首屏就加載出來的,而是等到要用的時候才加載,這就是按需加載。按需加載可以減小首屏加載文件的體積,達到提高響應速度的目的。
// about.js
export default render;
// main.js
document.getElementById('about').addEventListener('click', e => {
import('./about').then(({default: render}) => {
// 渲染頁面
render();
});
}, !1);
上面的代碼中只有當點擊了 #about
元素後,纔會加載 about.js
文件。
可以參考:
9. rollup
前端性能的優化除了從語法、http 協議、工程結構方向之外,構建工具也是一個可以優化的方向。
對於前端開發者來說,基本上都用 webpack
來打包項目,但 webpack
帶給我們強大功能的同時,也會有一些副作用產生,就是會產生很多冗餘的代碼(如果你有查看過 webpack 的 bundle 文件,便會發現)。
如果你的項目不需要處理靜態資源(如圖片),也不需要按需加載,並追求前端高性能的話,可以嘗試 rollup。
比如:
源代碼
# 目錄
|-- src/
|-- index.js
|-- prefix.js
|-- suffix.js
# prefix.js
const prefix = 'prefix';
export default str => `${prefix} | ${str}`;
# suffix.js
const suffix = 'suffix';
export default str => `${str} | ${suffix}`;
# index.js
import prefix from './prefix';
import suffix from './suffix';
export default str => suffix(prefix(str));
rollup
打包後的代碼:
'use strict';
const prefix = 'prefix';
var prefix$1 = str => `${prefix} | ${str}`;
const suffix = 'suffix';
var suffix$1 = str => `${str} | ${suffix}`;
var index = str => suffix$1(prefix$1(str));
module.exports = index;
webpack
打包後的代碼:
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/
/******/ 中間有 100 行代碼被省略
/******/
/******/ ]);
可以參考:
10. prepack
前端性能的優化還有一個方向,就是預編譯腳本,即把原本在運行階段才解析的代碼通過工具預執行,然後只留下結果。
prepack 便是這樣的一個工具,它的思路大致是這樣:
把不依賴外部環境的邏輯提前進行運算,並把運算結果替換到相應的源碼處,然後從源碼中移除這段邏輯。
源代碼
(() => {
const secondsOfOneDay = 24 * 60 * 60;
window.getSecondsOfDays = days => days * secondsOfOneDay;
})();
編譯後的代碼
(function () {
var _$0 = this;
var _1 = days => {
return days * 86400;
};
_$0.getSecondsOfDays = _1;
}).call(this);
可以參考:
11. css 選擇器不要嵌套太深
對於前端來說,css
對性能影響比較小,所以,這裏只提一點最常見、也是最有效果的建議:選擇器不要嵌套太深。
一般建議選擇器層級在 2 級以內,最多不超過 3 級。
// 下面是 less, scss, css 語法
// 不好
.one {
.two {
.three {
.four {}
}
}
}
// 不推薦
.one {
.two {
.three {}
}
}
// 好
.one .two {}
.one {}
後續
更多博客,查看 https://github.com/senntyou/blogs
版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證)