Web 傳輸的內容固然是越少越好,最近一段時間的工做一直致力於 Web 性能優化,這是我近期使用過的一些縮減 Web 體積的手段javascript
這些手段主要是爲了減小 Web 傳輸的內容大小,只有乾貨css
在使用 UI 庫的時候,UI 庫提供的樣式並非全部的都會使用到html
例如一個 button 組件通常都會提供 default/primary/success/warning/danger 五光十色好幾款樣式java
但咱們實際一個項目中也許只會用到其中的一兩種,爲了減小樣式表的體積,須要將那些沒有使用的樣式挑選出來刪除掉git
使用 uncss 工具來刪除無用的樣式github
該工具提供有在線版,只須要複製本身的 HTML 以及 CSS,點擊按鈕就能夠生成精簡後的樣式web
另外也能夠經過瀏覽器工具 Coverage 挑選出未使用的樣式,以下圖npm
通過分析得出每一個文件未使用樣式的百分佔比,其中紅色標記的爲未使用到的樣式,從下圖中能夠看到具體未使用到的樣式有哪些api
⚠️ 上面兩種方法都是經過樣式規則的選擇器在頁面上查找元素,若是能找到對應的元素,則說明該樣式規則有被使用
隨着在頁面上進行各類操做,該百分比可能會下降,由於有些樣式會在某些操做執行以後纔會被使用到,好比:hover
僞類相關的樣式,在鼠標移入元素以前不會被標記爲已使用的
因此,這兩種方式都有必定的侷限性,並非挑選出的樣式就必定是沒有用的,也許某個樣式是在用戶執行至關複雜的操做後纔會起做用,須要嚴格測試瀏覽器
ℹ️ 許多框架和庫也提供自定義打包版本,從源頭捨去那些無用的代碼
CSS 全名 層疊樣式表(Cascading Style Sheets),對同一個元素屢次指定同一個樣式只會讓優先級高的覆蓋優先級低的
在樣式規則的選擇器徹底相同的狀況下(好比這裏 .selector-1 > .selector-2 和 .selector-1 > .selector-2 是徹底相同的),被覆蓋的樣式能夠安全地刪除,以下
/* Before */ .selector-1 > .selector-2 { display: none; width: 200px !important; } .selector-1 > .selector-2 { display: block; width: 100px; } /* After */ .selector-1 > .selector-2 { width: 200px !important; } .selector-1 > .selector-2 { display: block; }
經過瀏覽器的開發者工具能夠輕鬆看到哪些樣式被覆蓋了
⚠️ 在選擇器不相同的時候,也有可能會匹配到同一個元素,這個時候本條規則並不適用,須要注意
⚠️ 有時候同一個樣式屬性反覆出現只是爲了兼容一些舊瀏覽器,也須要注意
有些樣式屬性能夠合併爲一條,好比
/* Before */ .selector { flex-direction: column; flex-wrap: wrap; } /* After */ .selector { flex-flow: column wrap; }
合併以後字節數減小
有些樣式是爲了兼容一些老舊瀏覽器而提供的,當前已經不須要再兼容這些瀏覽器了,對應的樣式能夠刪除掉,好比以下這些
- header { - display: block; - }
ℹ️ 使用 autoprefixer 刪除過期的瀏覽器廠商前綴(好比 -moz-,-ms- 這些)
部分樣式會繼承給後代元素,後代元素沒有必要再寫一遍,除非是確實須要覆蓋的
之因此會有這條是由於以前在項目中看到隨處可見的 box-sizing: border-box
屬性其實能夠主動設置爲繼承
*, *:before, *:after { box-sizing: inherit; } html { box-sizing: border-box; }
這樣全部元素都會繼承這個屬性,不用反覆定義
將多個規則集中相同的樣式提取出來,並使用羣組選擇器放在一塊兒,好比
/* Before */ .badge { background-color: orange; border-raidus: 5px; color: #fff; font-size: 13px; } .label { background-color: orange; border-raidus: 5px; color: #fff; font-size: 12px; } /* After */ .badge, .label { background-color: orange; border-raidus: 5px; color: #fff; } .badge { font-size: 13px; } .label { font-size: 12px; }
csscss 能夠用來分析冗餘的 CSS 代碼
這是一個 Ruby 工具,使用前須要先安裝 ruby1.9 或以上版本
這個工具只是用來分析冗餘樣式的,並不會主動刪除樣式,須要本身手動調整
⚠️ 在 CSS 中,樣式的前後順序是有意義的,隨意移動樣式規則可能會讓樣式出現問題,須要通過嚴格測試
ℹ️ csso 能夠用來刪除冗餘,合併樣式規則
壓縮主要是刪除無用的空白和註釋,或用更簡短的寫法代替
推薦使用工具 cssnano 來壓縮 CSS
該工具還提供了 在線版
這是壓縮前的代碼
這是壓縮後
@charset "utf-8";h1:before{margin:10px 20px;color:red;font-weight:400;background-position:100% 100%;quotes:"«" "»";background:linear-gradient(180deg,#ffe500,#ffe500 50%,#121 0,#121);min-width:0}
體積減小了一半
ℹ️ cssnano 自帶 autoprefixer 工具幫助清理瀏覽器廠商前綴
瀏覽器的 Coverage 工具也能挑選出未使用的 JavaScript 代碼,再也不重複
⚠️ 一樣的,挑選出來的代碼也不必定全是無用的,須要通過仔細測試
同 CSS 同樣,JavaScript 也有一些代碼是爲了兼容舊瀏覽器而存在的
像 es5-shim.js 就是爲了給那些不支持 ES5 的瀏覽器準備的,如今已經能夠放心地從項目中去掉了,目前全球使用支持 ES5 的瀏覽器的用戶佔比高達98%
另一些框架或庫的新版本一般將不會包含那些兼容舊瀏覽器的代碼,須要時保持更新便可,好比用 jQuery3.0 替換 jQuery1.12
一個項目經手的人多了以後,會出現一些匪夷所思的膨脹,好比同一個項目中引入了好幾個功能類似的插件
找出相關代碼,根據需求肯定真正須要使用的插件,去掉其它多餘的
⚠️ 此條須要通過嚴格的測試
隨着 Web 標準的豐富以及瀏覽器的更新換代,愈來愈多的功能能夠經過設備/瀏覽器原生的 API 來實現
好比 IntersectionObserver 能夠用來探測 DOM 元素是否位於窗口可視區域內,這就不須要藉助插件來實現這些功能了
相應的插件代碼能夠從項目中安全地刪除,或者只爲那些老舊設備/瀏覽器提供
主要是刪除沒用的空白和註釋等等
使用 Terser 來壓縮 JavaScript,經過 NPM 安裝 npm install terser -g
執行命令 terser main.js -o main.min.js -c -m
經常使用的字體格式有以下這些
WOFF2/WOFF
Web 開放字體格式(Web Open Font Format),加載快,壓縮率高
WOFF2 是 WOFF 的升級版本,壓縮率更高
SVG/SVGZ
矢量圖形字體(Scalable Vector Graphics Font),僅有少部分瀏覽器支持(好比 iOS Safari 4.1-)
EOT
Embedded Open Type,IE 獨佔
TTF/OTF
OpenType Font 和 TrueType Font,瀏覽器支持範圍最廣的格式
根據目標設備選擇合適的字體格式,不一樣的字體格式兼容的瀏覽器也是不同的
下圖是圖一套字體的不一樣文件格式的大小對比
咱們應該優先選用壓縮率更高的 WOFF2 文件格式,若是瀏覽器不支持該格式,降級到 WOFF,甚至 OTF/TTF
下面是完整定義字體的方式,瀏覽器會根據優先順序下載自身能識別但體積相對更小的字體文件
@font-face { font-family: 'My Font'; src: url('path/my-font.eot'); src: url('path/my-font.eot?#iefix') format('embedded-opentype'), url('path/my-font.woff2') format('woff2'), url('path/my-font.woff') format('woff'), url('path/my-font.ttf') format('truetype'), url('path/my-font.svg#svgFontName') format('svg'); }
因此上面的字體定義也能夠精簡爲以下,基本能夠知足市面上的主流瀏覽器
@font-face { font-family: 'My Font'; src: url('path/my-font.woff2') format('woff2'), url('path/my-font.woff') format('woff'); }
在一個字體文件中不是全部字體都會使用到,特別是在使用圖標字體的時候
裏面有不少圖標是我在項目中沒有用到的,這種時候就須要編輯字體文件,刪除那些沒用上的字體
百度有個在線字體編輯工具 http://fontstore.baidu.com/static/editor/index.html 能夠打開並編輯字體以及保存爲其它格式
這是通過我編輯事後的文件大小對比,文件大小差距很大,確實用到的字體比較少
在 Web 網頁中,圖像的體積佔了大頭,減小圖像能夠大幅增長性能
不一樣文件格式的圖像其文件大小,圖像質量是不同的,根據具體狀況選擇合適的圖像格式
經常使用 Web 圖像格式
格式 | 透明 | 動畫 | 說明 | 瀏覽器支持 |
---|---|---|---|---|
GIF | ✔️ | ✔️ | 顏色較少 | 全 |
JPEG | ❌ | ❌ | 有損格式,經常使用於照片 | 全 |
PNG | ✔️ | ❌ | 無損 | 全 |
WebP | ✔️ | ✔️ | 支持無損/有損壓縮,比JPEG,PNG和GIF更好的壓縮效果 | 較新 |
AVIF | ✔️ | ✔️ | 比WebP,JPEG,PNG和GIF更好的壓縮效果 | 最新 |
JPEGXL | ✔️ | ✔️ | 無損壓縮,更快的解碼和其餘各類改進 | 暫無 |
一些新的圖像格式擁有較高的性能,好比 AVIF 和 WebP
不過這些新的圖像格式不是全部瀏覽器都支持,此時可使用一個 <picture>
元素來包裹 <img>
元素,再經過使用 <source>
元素來爲 <img>
元素提供多個備胎資源供其自行選擇
<source>
元素能夠有多個,srcset 屬性是必須的(注意是 srcset)
<picture> <source type="image/avif" srcset="logo.avif"> <source type="image/webp" srcset="logo.webp"> <img alt="logo" src="logo.png"> </picture>
瀏覽器會自行忽略不支持的格式,若是瀏覽器支持 AVIF 格式就使用 logo.avif,若是支持 WebP 格式就使用 logo.webp
若是上面倆都不支持,就會使用 logo.png,不支持 <picture>
元素的瀏覽器會直接顯示 <img>
元素
值得一提的是 <picture>
元素內部必須包含一個 <img>
元素,不然圖像不會顯示(由於 <picture>
元素並非一個獨立顯示的元素,而是爲 <img>
元素服務的)
還有 <img>
元素始終都不該該忘記的 alt 屬性,當任何圖像格式都沒法顯示或者圖像下載失敗的時候,至少還能顯示替代的文字說明
要在 CSS 中使用這些新的格式一般用 JavaScript 來判斷瀏覽器是否支持
建立一個 Image 對象,而後加載一張較小的須要判斷格式的圖像,若是加載成功則說明瀏覽器支持該格式,下面是 Google 提供的判斷瀏覽器是否支持 WebP 的方法
const img = new Image() img.onload = img.onerror = () => { document.body.classList.add(img.height > 0 ? 'webp' : 'no-webp') } img.src = 'data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='
若是該瀏覽器支持,則給 <body>
元素添加 webp
類,不然添加 no-webp
,在 CSS 中就能夠這樣寫
.webp .logo { background: url(./logo.webp); } .no-webp .logo { background: url(./logo.png); }
這樣就能根據該瀏覽器是否支持 webp 格式加載不一樣格式的圖像了
ℹ️ 轉換圖像格式可使用 Sqoosh,在圖像大小和質量之間手動調整權衡
在 CSS 中使用媒體查詢結合 image-set 能夠依據設備/瀏覽器的寬度以及像素比顯示不一樣分辨率的圖像
ℹ️ 爲了方便一眼看出來,圖像的名稱包含了圖像的真實寬度,好比 logo-240.png 表示這張圖像寬度爲 240 像素
.logo { background-image: url(./images/logo-120.png); background-image: -webkit-image-set(url(./images/logo-120.png) 1x, url(./images/logo-240.png) 2x); background-image: image-set(url(./images/logo-120.png) 1x, url(./images/logo-240.png) 2x); } @media (min-width: 600px) { .logo { background-image: url(./images/logo-240.png); background-image: -webkit-image-set(url(./images/logo-240.png) 1x, url(./images/logo-480.png) 2x); background-image: image-set(url(./images/logo-240.png) 1x, url(./images/logo-480.png) 2x); } } @media (min-width: 1200px) { .logo { background-image: url(./images/logo-480.png); background-image: -webkit-image-set(url(./images/logo-480.png) 1x, url(./images/logo-960.png) 2x); background-image: image-set(url(./images/logo-480.png) 1x, url(./images/logo-960.png) 2x); } }
根據 移動優先 的原則,使用媒體查詢應該從小往大
ℹ️ 不支持 image-set
的瀏覽器將會使用前面定義的傳統 url 路徑
⚠️ image-set
目前還在草案中,須要添加對應的瀏覽器廠商前綴,示例已添加
⚠️ Safari 只支持 url 路徑和 1x/2x 這樣的設備像素比
而 <img>
元素經過其新增的 srcset 和 sizes 屬性來實現響應式圖像
<img alt="avator" src="avator-120.jpg" srcset="avator-120.jpg 120w, avator-240.jpg 240w, avator-480.jpg 480w" sizes="(max-width: 600px) 120px, 240px">
srcset 屬性爲圖像提供多個源供設備/瀏覽器自行選擇,其中圖像路徑後面的 120w/240w/480w 用於告訴設備/瀏覽器每張圖像的實際寬度
sizes 屬性爲圖像提供渲染尺寸,能夠經過媒體查詢提供多個渲染尺寸以及一個默認尺寸(這裏 240px 就是默認的渲染尺寸)
設備/瀏覽器會根據這些信息選擇最合適的圖像加載顯示
當設備/瀏覽器寬度在 600 像素如下時圖像將佔據 120 像素的寬度,此時若是設備像素比爲 1 則顯示 avator-120.jpg,若是設備像素比爲 2 則顯示 avator-240.jpg,爲 4 則應該顯示 avator-480.jpg
當設備/瀏覽器寬度大於 600 像素的時候圖像將佔據 240 像素的寬度,此時若是設備像素比爲 1 則顯示 avator-240.jpg,若是設備像素比爲 2 則顯示 avator-480.jpg
瀏覽器寬度 | 設備像素比 | 顯示哪張圖像 |
---|---|---|
<= 600px | 1 | avator-120.jpg |
- | 2 | avator-240.jpg |
- | 4 | avator-480.jpg |
> 600px | 1 | avator-240.jpg |
- | 2 | avator-480.jpg |
ℹ️ 設備像素比也有多是小數,好比 1.5,設備/瀏覽器會選擇它本身認爲最合適的那張圖像來顯示
ℹ️ 其中 src 屬性是給不支持 srcset 和 sizes 屬性的瀏覽器提供的
有些格式的圖像每每還會包含一些沒有用的信息,清理掉這些信息有助於縮小圖像體積
這一般使用工具來進行
使用 imagemin 壓縮圖像
頁面上有不少圖像咱們一開始是看不到的,有的在咱們滾動頁面以後纔會出如今屏幕上,又有的在某個對話框彈出後才能看到
對於這類圖像,咱們能夠推遲它們的加載時機,等到它們須要真正展現在屏幕上的時候才加載,而不是在頁面一開始時就加載,這將大大節省頁面初始化時加載的資源大小
使用瀏覽器原生的懶加載方案,這很是簡單,只須要給圖像元素添加一個 loading="lazy"
屬性便可
<img alt="avator" loading="lazy" src="avator.jpg">
目前該屬性只獲得一部分瀏覽器的支持,不支持的瀏覽器會忽略
該屬性的 polyfill
還可使用 JavaScript 插件,市面上有很多這類型的插件
⚠️ 引入一個插件會增長 JavaScript 的代碼量,可是延遲了部分圖像的加載時機,具體須要權衡
減小圖像最好的辦法就是沒有圖像
使用 SVG 替換圖像
上面這張圖像格式爲 png 大小爲 1.46kb
下面是使用 SVG 來表示一樣的圖像的代碼,只有 300 多字節,體積大幅度減少
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <path d="M50 92.5H6.09a4.47 4.47 0 01-3.87-6.71l22-38 22-38a4.46 4.46 0 017.74 0l22 38 22 38a4.47 4.47 0 01-3.87 6.71z" fill="##ff7f00"></path> <path d="M57.41 78.1A7.41 7.41 0 1150 70.7a7.39 7.39 0 017.41 7.4zm-2.14-14.89H44.81l-1.72-36h13.82z" fill="#fff"></path> </svg>
另外 SVG 既能夠改變顏色,也能夠任意放大縮小
SVG 可使用 SVGO 來優化
使用純樣式替換圖像
好比下面這個 loading 效果就是純樣式寫的
相對於圖像來講,純代碼的字節數就少得多了
@keyframes spin { to { transform: rotate(1turn); } } .loading { animation: spin 1.2s infinite linear; border: 4px solid rgba(0, 0, 0, 0.1); border-left-color: #46aaff; border-radius: 50%; height: 30px; width: 30px; }
<div class="loading"></div>