構建後的js代碼只包含被引用並被執行的模塊,而不被引用或不被執行的模塊會被刪除,以起到減包的做用。javascript
webpack 在 mode爲production下默認執行tree shaking,不設置mode默認爲productioncss
注意項:tree shaking只會檢測 ES2015 模塊語法(即 import 和 export)。更多細節html
webpack的tree shaking至今實現的功能並不完美,凡有反作用的模塊,tree-shaking並不起做用前端
例子🌰:vue
// index.js
import { func2, func3 } from './module-a'
func2()
複製代碼
// module-a.js
import lodash from 'lodash-es'
export * from './module-b'
export const func1 = function(value) {
return lodash.isArray(value)
}
export const func2 = function() {
console.log('這是func2')
return 123123
}
複製代碼
// module-b.js
export const func3 = () => {
console.log('B模塊的func3方法')
}
複製代碼
這個插件主要用於填充webpack自身Tree-shaking的不足,經過做用域分析來消除無用的代碼。原理請看java
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin')
.default
...
plugins: [
new WebpackDeepScopeAnalysisPlugin(),
],
複製代碼
對於css的tree shaking 則使用 purgecss-webpack-plugin 和 mini-css-extract-plugin 來實現react
在css中添加如下代碼:webpack
.red {
color: red
}
.blue {
color: blue
}
複製代碼
而後在js文件中引用:nginx
import './index.css'
var div = createElement('div')
div.styles.class = 'red'
console.log(div);
複製代碼
理想狀況下,咱們但願打包後的文件中只含**.red而不含未使用的.blue**,配置以下:git
const TerserJSPlugin = require("terser-webpack-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PurgecssPlugin = require('purgecss-webpack-plugin')
optimization: {
// 壓縮js、css
minimizer: [
new TerserJSPlugin({}),
new OptimizeCSSAssetsPlugin({})
],
},
module: {
...
use: [
MiniCssExtractPlugin.loader,
'css-loader',
]
}
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
// css tree shaking
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, '/src/**/*')}`, { nodir: true }),
}),
]
複製代碼
mini-css-extract-plugin會將css打包爲一個單獨的文件,這裏咱們使用插件purgecss-webpack-plugin幫助咱們分析過濾未引用的css,最終結果以下(壓縮有關的插件和配置參考第6小節):
Scope Hoisting 它可讓webpack打包出來的代碼文件更小,運行更快,它能夠被稱做爲 "做用域提高"。
啓用 Scope Hoisting的優勢以下:
代碼體積會變小,由於函數聲明語句會產生大量代碼。
代碼在運行時由於建立的函數做用域減小了,因此內存開銷就變小了。
plugins: [
// 開啓 Scope Hoisting 功能
new webpack.optimize.ModuleConcatenationPlugin()
]
複製代碼
在 mode production 狀況下默認開啓
咱們在開發環境進行驗證,這裏有兩個文件:
// index.js
import a from './module-a'
console.log(a);
// module-a.js
export default 'module-a'
複製代碼
當未啓動Scope Hoisting時,按照文件模塊打包,生成多個函數做用域,結果以下:
啓用時則會將兩個模塊預編譯到同一個模塊中:
e.g.
// index.js
import _ from 'lodash'
console.log(_.cloneDeep({a: 'index'}))
// module.js
import _ from 'lodash'
console.log(_.cloneDeep({a: 'module'}))
複製代碼
這裏有兩個文件中都引用了lodash這個第三方包,直接打包的話兩個文件中都會包含lodash的代碼:
可使用optimization.splitChunks來清除重複項:
optimization: {
splitChunks: {
chunks: 'all'
}
}
複製代碼
清除重複後會將lodash生成到一個單獨的chunk中:
當涉及到動態代碼拆分時,可使用**import()**動態加載,或者webpack特有的require.ensure
// index.js
function getLodash() {
return import(/* webpackChunkName: "lodash" */'lodash').then(({ default: _ }) => {
return _.cloneDeep({a: 'index'})
}).catch(e => 'error occurred')
}
getLodash().then(res => {
console.log(res);
})
複製代碼
這裏咱們import()動態加載lodash,而且利用註釋設置了chunkname,能夠看到將動態引入的lodash單獨拆分爲一個文件:
更多詳細介紹能夠參考Code Splitting和SplitChunksPlugin
將框架包 vue、react、ui框架、sdk等框架包抽離放入cdn
externals: {
vue: "Vue",
}
複製代碼
html 壓縮 HtmlWebpackPlugin
js 壓縮 UglifyJsPluginTerserWebpackPlugin
css 壓縮 css-loader 內置了壓縮,配合ExtractTextPluginMiniCssExtractPlugin
給css加上hash
e.g.
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true,
removeComments: true,
},
}),
]
複製代碼
能夠看到html文件實現了壓縮:
在 HtmlWebpackPlugin 3.x 中,minify爲true時默認爲{},此時須要手動配置,在4.x中則爲
{
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
複製代碼
詳細參見:github.com/jantimon/ht… 關於具體配置項能夠參考html-minifier
webpack 4.x 中默認使用TerserWebpackPlugin進行js壓縮,原有的UglifyjsWebpackPlugin不推薦使用
關於TerserWebpackPlugin和UglifyJsPlugin,你須要知道如下幾點:
在3.x中開發環境下配置以下:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
plugins: [
new UglifyJsPlugin(),
]
複製代碼
4.x開發環境中開啓,production下minimize默認爲true:
optimization: {
minimize: true,
}
複製代碼
壓縮前:
壓縮後:
還能夠在minimizer中新增TerserWebpackPlugin實例覆蓋默認壓縮:
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
test: /\.js(\?.*)?$/i,
}),
],
},
複製代碼
將來在webpack5中將會引入內置的css minimizer,在4.x中須要手動配置 optimize-css-assets-webpack-plugin
手動配置 minimizer 會覆蓋掉默認配置,所以須要補上TerserJSPlugin壓縮js
MiniCssExtractPlugin則會提取css到單獨的文件,配置以下:
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
optimization: {
minimizer: [
// 壓縮js
new TerserJSPlugin({}),
// 壓縮css
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
}
複製代碼
在import動態加載中配置preload和prefetch
預加載:
是一種 resource hint,用來指定頁面加載後很快會被用到的資源,因此在頁面加載的過程當中,咱們但願在瀏覽器開始主體渲染以前儘早 preload。import(/* webpackPreload: true */ 'Modal');
複製代碼
預取:
是一種 resource hint,用來告訴瀏覽器在頁面加載完成後,利用空閒時間提早獲取用戶將來可能會訪問的內容。import(/* webpackPrefetch: true */ 'Modal');
複製代碼
兩者的區別:
preload:
prefetch:
將hash替換爲chunkhash,這樣當chunk不變時,緩存依然有效
使用Name而不是id
每一個 module.id 會基於默認的解析順序(resolve order)進行增量。也就是說,當解析順序發生變化,ID 也會隨之改變
Brotli 壓縮算法具備多個特色,最典型的是如下 2 個:
brotil的支持狀況 可查看caniuse
動態壓縮即時發生。用戶發出請求,壓縮內容(當用戶等待時)而且提供壓縮內容。
靜態壓縮在用戶請求以前在磁盤上壓縮資產的時間。當用戶請求資產時,不會發生壓縮。預壓縮資產只是從磁盤提供。
webpack提供的壓縮方式compression-webpack-plugin ,brotli-webpack-plugin
gzip:
const CompressionWebpackPlugin = require('compression-webpack-plugin')
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
複製代碼
brotli:
const BrotliPlugin = require('brotli-webpack-plugin')
new BrotliPlugin({
asset: '[path].br[query]',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
複製代碼
1.5 nginx.conf
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_buffers 128 32k;
gzip_comp_level 9;
gzip_http_version 1.1;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli on;
brotli_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli_static off;
brotli_comp_level 11;
brotli_buffers 16 8k;
brotli_window 512k;
brotli_min_length 20;
複製代碼
壓縮工具:
也能夠經過webpack壓縮 imagemin-webpack-plugin
tinypng
imagemin
依賴:url-loader、file-loader、imagemin-webpack-plugin
const path = require('path')
const ImageminPlugin = require('imagemin-webpack-plugin').default
module.exports = {
...
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[hash:8].[ext]',
outputPath: 'imgs/',
},
},
],
},
],
},
plugins: [
new ImageminPlugin({ test: /\.(jpe?g|png|gif|svg)$/i }),
],
}
複製代碼
不推薦在打包的時候經過webpack壓縮,由於這樣會大量增長打包的時間,而且imagemin這個插件的壓縮能力遠不如tinypng
將過大的背景圖切分紅多張小圖,經過html的方式拼到一塊兒。
利用瀏覽器的併發加載(h2)
網絡請求 | 緩存 | 靈活性 | 其餘 | |
---|---|---|---|---|
雪碧圖 | 少 | 好 | 差 | 1.放大失真 2.同一圖標,不一樣顏色,會有重複部分 3.只用幾張也須要加載整張圖 |
iconfont | 少 | 好 | 好 | 1.矢量放大不失真 2.跟普通字體無異,能夠設置size,color,opacity, 等 3.文件小 |
base64 | 無 | 差 | 好 | 1.增長額外的css、js大小 2.增長圖片資源的大小 |
響應式圖片不只僅指圖片的排版和佈局,還包括根據設備大小加載不一樣的圖片。
image:
經過設置image標籤上的srcset告知瀏覽器在不一樣屏幕寬度下加載不一樣的圖片
經過設置image標籤上的sizes來設置圖片的尺寸臨界點,明肯定義了圖片在不一樣的media conditions下應該顯示的尺寸。
picture:
瀏覽器會遍歷 中的中
favicon.ico通常存放在網站根目錄下,不管是否在頁面中設置,瀏覽器都會嘗試請求這個文件。
因此確保這個圖標:
在不一樣地域的用戶請求資源(訪問網站)的響應速度具備很大的差別,爲了提升用戶體驗,咱們在用戶和服務器中間加了一層,就是CDN。CDN(Content Delivery Network),它的思想就是將源站的內容分發到最接近用戶的網絡邊緣節點,讓用戶可以就近取得所需的內容,提升用戶訪問的響應速度。
當用戶發起HTTP請求時,經過CDN向邊緣節點服務器發起請求,邊緣節點會檢測當前節點是否具備你想請求的數據,若是沒有就去源站,若是有請求數據就會進一步判斷,這個數據是否在有效期,根據是否過時來決定。
DNS解析(T1) -> 創建TCP鏈接(T2) -> 發送請求(T3) -> 等待服務器返回首字節(TTFB)(T4) ->> 接收數據(T5)
![]()
以chrome爲例,http1.1的併發請求數量最大是6(同一域名下),超出的併發請求必須等待
**對於大資源:**是否合併對於加載時間沒有明顯影響,但拆分資源能夠更好的利用瀏覽器緩存,不會由於某個資源的更新致使全部資源緩存失效,而資源合併後,任一資源的更新都會致使總體資源的緩存失效。另外還能夠利用域名分片技術,將資源拆分部署到不一樣域名下,既能夠分散服務器的壓力,又能夠下降網絡抖動帶來的影響。
**對於小資源:**合併資源每每具備更快的加載速度,但在網絡帶寬情況良好的狀況下,由於提高的時間單位以ms計量,收益能夠忽略。若是網絡延遲很大,服務器響應速度又慢,則能夠帶來必定收益,但在高延遲的網絡場景下,又要注意合併資源後可能帶來網絡往返次數的增長,進而影響到加載時間。
瀏覽器對網站第一次的域名DNS解析查找流程依次爲:瀏覽器緩存 -> 系統緩存 -> 本地hosts文件 -> 路由器緩存 -> ISP DNS緩存 -> 遞歸搜索
如今通常服務器都具有DNS緩存
在瀏覽器支持 DNS 預解析的特性時即便不使用該標籤瀏覽器依然會進行預解析。
<meta http-equiv="x-dns-prefetch-control" content="on"> // 關閉 off
複製代碼
<link rel="dns-prefetch" href="//domain.com">
複製代碼
淘寶的預解析
注:dns-prefetch需慎用,多頁面重複DNS預解析會增長重複DNS查詢次數。
須要注意的是,雖然使用 DNS Prefetch 可以加快頁面的解析速度,可是也不能濫用,由於有開發者指出 禁用DNS 預讀取能節省每個月100億的DNS查詢 。
每一次的http請求都會默認攜帶上cookie,cookie過大的話會致使傳輸變慢
對於一些靜態資源(圖片)的獲取,是不須要用到cookie,因此儘可能圖片資源放入無cookie服務器,減小與主域名之間的cookie混用
http2採用二進制的傳輸協議,而http1.1採用的是文本傳輸,二進制格式的傳輸效率要比文本快的多。
同一個TCP鏈接中能夠同時發送多個請求而不會阻塞
對消息頭採用Hpack進行壓縮傳輸,可以節省消息頭佔用的網絡流量,http1.1每次請求,都會攜帶大量冗餘的頭信息,浪費了不少寬帶資源。
它容許 Web 服務器在收到瀏覽器的請求以前提早發送一些資源給客戶端
// html
<img src="" />
// js
var img = new Image();
img.src = "";
複製代碼
雖然src屬性爲空字符串,但瀏覽器仍然會向服務器發起一個HTTP請求:
空src產生請求的後果不容小覷:
空的href屬性也存在相似問題。用戶點擊空連接時,瀏覽器也會向服務器發送HTTP請求,能夠經過JavaScript阻止空連接的默認的行爲。
每一次的重定向都是須要從新再發起一次http請求
HTTP請求很昂貴,返回無效的響應(如404未找到)徹底不必,下降用戶體驗並且毫無益處。 一些網站設計很酷炫、有提示信息的404頁面,有助於提升用戶體驗,但仍是浪費服務器資源。尤爲糟糕的是外部腳本返回404,不只阻塞其餘資源下載,瀏覽器還會嘗試把404頁面內容看成JavaScript解析,消耗更多資源。
由於IPv4即將用完以及主要的移動網絡正在迅速採用IPv6(美國已經達到50% 的 IPv6 使用閾值),將你的 DNS 更新到 IPv6 以應對將來是一個好的想法。只要確保在網絡上提供雙棧支持,就可讓 IPv6 和 IPv4 同時運行。畢竟,IPv6 不是向後兼容的。研究顯示,也是正由於 IPv6 自帶 NDP 以及路由優化,因此纔可以讓網站的載入速度提高10%到15%。
當客戶端向服務器請求資源時,會先抵達瀏覽器緩存,若是瀏覽器有「要請求資源」的副本而且沒有失效,就能夠直接從瀏覽器緩存中提取而不是從原始服務器中提取這個資源。
常見的http緩存只能緩存get請求響應的資源,對於其餘類型的響應則無能爲力,因此後續說的請求緩存都是指GET請求。
http緩存都是從第二次請求開始的。第一次請求資源時,服務器返回資源,並在respone header頭中回傳資源的緩存參數;第二次請求時,瀏覽器判斷這些請求參數,命中強緩存就直接200,不然就把請求參數加到request header頭中傳給服務器,看是否命中協商緩存,命中則返回304,不然服務器會返回新的資源。
強制緩存在緩存數據未失效的狀況下(即Cache-Control的max-age沒有過時或者Expires的緩存時間沒有過時),那麼就會直接使用瀏覽器的緩存數據,不會再向服務器發送任何請求。強制緩存生效時,http狀態碼爲200。這種方式頁面的加載速度是最快的,性能也是很好的,可是在這期間,若是服務器端的資源修改了,頁面上是拿不到的,由於它不會再向服務器發請求了。這種狀況就是咱們在開發種常常遇到的,好比你修改了頁面上的某個樣式,在頁面上刷新了但沒有生效,由於走的是強緩存,因此Ctrl + F5一頓操做以後就行了。 跟強制緩存相關的header頭屬性有(Pragma(http1.0)/Cache-Control(http1.1)/Expires)
當第一次請求時服務器返回的響應頭中沒有Cache-Control和Expires或者Cache-Control和Expires過時還或者它的屬性設置爲no-cache時(即不走強緩存),那麼瀏覽器第二次請求時就會與服務器進行協商,與服務器端對比判斷資源是否進行了修改更新。若是服務器端的資源沒有修改,那麼就會返回304狀態碼,告訴瀏覽器可使用緩存中的數據,這樣就減小了服務器的數據傳輸壓力。若是數據有更新就會返回200狀態碼,服務器就會返回更新後的資源而且將緩存信息一塊兒返回。跟協商緩存相關的header頭屬性有(ETag/If-Not-Match 、Last-Modified/If-Modified-Since)請求頭和響應頭須要成對出現
協商緩存的執行流程是這樣的:當瀏覽器第一次向服務器發送請求時,會在響應頭中返回協商緩存的頭屬性:ETag和Last-Modified,其中ETag返回的是一個hash值,Last-Modified返回的是GMT格式的最後修改時間。而後瀏覽器在第二次發送請求的時候,會在請求頭中帶上與ETag對應的If-Not-Match,其值就是響應頭中返回的ETag的值,Last-Modified對應的If-Modified-Since。服務器在接收到這兩個參數後會作比較,若是返回的是304狀態碼,則說明請求的資源沒有修改,瀏覽器能夠直接在緩存中取數據,不然,服務器會直接返回數據。
實際開發中不可必變產生重繪和迴流,咱們只能作到儘量的減小這種行爲的發生
next,nuxt,等
首頁骨架屏
css選擇器的效率排序:
1.id選擇器(#myid) 2.類選擇器(.myclassname) 3.標籤選擇器(div,h1,p) 4.相鄰選擇器(h1+p) 5.子選擇器(ul < li) 6.後代選擇器(li a) 7.通配符選擇器(*) 8.屬性選擇器(a[rel="external"]) 9.僞類選擇器(a:hover, li:nth-child)
Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]