更快速將你的頁面展示給用戶[前端優化篇]


現在許多公司往往注重后端優化,而忽略了前端優化

想想如果辛苦優化了服務器,后台,排查了sql
卻在最后頁面加載展示的時候很慢,也得不償失

其實,前后台優化都是相輔相成的
后台優化好了,響應請求速度快,前台展示的更迅速
前台優化了不必要的請求,后台壓力也會更小

一直想靜下心來寫一篇博文,
無奈心浮氣躁,不得安定,斷斷續續一直寫到今天....
那么現在就來和大家分享一下我的前端優化經驗吧

 

大綱:

 

請求優化


首先我們來優化HTTP請求數

由於用戶瀏覽的,往往只是局部網頁,
所以只加載用戶可視范圍內的資源,就會減少一些不必要的請求,也會減少瀏覽器加載資源的消耗
考慮到移動端可視范圍,網絡流量,性能,延遲加載作用尤為明顯

圖片延遲加載  


適合延遲加載的東西很多,最需要的當然是圖片,這里推薦lazyload

https://github.com/tuupola/jquery_lazyload


圖片延遲加載的原理就首先將要延遲加載的圖片src替換為空白圖片或者參數指定的loading圖
然后根據當前元素的位置(offset)來判斷是否在頁面可視范圍(頁面寬/高度+滾動寬/高度)

如果在,就將真實圖片資源路徑替換回src讓瀏覽器加載


防止瀏覽器解析到HTML中<img>標簽的src屬性就開始下載資源,最好將原<img>的src屬性去掉
統一配置lazyload的參數去加載loading圖吧,如我們項目中這樣:

 

 $(".main_content img").lazyload({
                placeholder: "/images/loading.gif",
                threshold:200
            });

  

再來看到lazyload的源代碼,可視范圍判斷上下左右,寫的十分完善

  $.belowthefold = function(element, settings) {
        var fold;

        if (settings.container === undefined || settings.container === window) {
            fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
        } else {
            fold = $(settings.container).offset().top + $(settings.container).height();
        }

        return fold <= $(element).offset().top - settings.threshold;
    };

    $.rightoffold = function(element, settings) {
        var fold;

        if (settings.container === undefined || settings.container === window) {
            fold = $window.width() + $window.scrollLeft();
        } else {
            fold = $(settings.container).offset().left + $(settings.container).width();
        }

        return fold <= $(element).offset().left - settings.threshold;
    };

    $.abovethetop = function(element, settings) {
        var fold;

        if (settings.container === undefined || settings.container === window) {
            fold = $window.scrollTop();
        } else {
            fold = $(settings.container).offset().top;
        }

        return fold >= $(element).offset().top + settings.threshold  + $(element).height();
    };

    $.leftofbegin = function(element, settings) {
        var fold;

        if (settings.container === undefined || settings.container === window) {
            fold = $window.scrollLeft();
        } else {
            fold = $(settings.container).offset().left;
        }

        return fold >= $(element).offset().left + settings.threshold + $(element).width();
    };

    $.inviewport = function(element, settings) {
         return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) &&
                !$.belowthefold(element, settings) && !$.abovethetop(element, settings);
     };

 

關於圖片這里,除了延遲加載外,用戶上傳的圖片以及我們所用的資源圖片都應該進行壓縮處理
如需要進一步提高壓縮率,可以使用例如:google開發的webp圖片格式等..
不過不是所有瀏覽器都支持webp格式,需要針對瀏覽器響應

ajax局部加載數據  

根據上面這段代碼,其實我們就可以延遲加載其他內容了,
總之呢,這里我們的目的就是盡量減少不必要的請求

比如現在用的很多的下拉式翻頁,就是判斷到頁面底部之后再ajax獲取下頁內容


如果考慮到網頁只是局部更新的話,那使用ajax是很合適的
好處顯而易見,無需重新請求整頁,小巧快速,網頁展示也友好一些

善用ajax對前端性能,體驗都是有改善的

但是也要考慮到對搜索引擎的友好,
如果頁面整體功能改變了,或者頁面改動量大就要進行取舍了。

 

預加載  

延遲加載的目的就是減少不必要的請求,在用戶有需求時才請求資源
所以對於用戶來說,其實是有一點點“等待”的過程的

一般會用loading圖,等待文字來改善這里的用戶體驗


但是有一些需求是希望盡量少出現這種“等待”過程的

這里我們就可以預加載資源,如下,我們先在js中加載圖片

 

var img  = new Image();
img.src="test.png";

  

提前加載好了圖片,用戶進行下一步時,圖片則是從瀏覽器緩存中獲取

多頁數據也可以類似處理,頁面初始可以默認加載兩頁數據
翻到第二頁時,就ajax去獲取第三頁內容
翻到第三頁時,就去獲取第四頁內容......
總是提前預加載一頁數據

如此可一定程度上減少一些等待的過程


總的來說延遲加載是盡量少加載資源,預加載則是判斷可能要的資源,盡量去提前多加載,
都是為了優化用戶的體驗,適用於不同場景

資源優化

網頁中有些資源可以通過延遲加載等方式減少不必要的請求
而許多的javascript腳本,css樣式等資源卻是網頁中必須要加載的

既然不可避免需要加載,那接着,我們就可以去優化這些資源

 

資源壓縮(uglify-js,clean-css)  

首先我們可以優化資源文件的大小,資源文件變小了,傳輸速度當然也快了
這里我們壓縮分為如下兩種:

 

  • HTTP壓縮,我們可以在web服務器內設置壓縮輸出的內容

 

如下,在IIS7內啟用Gzip壓縮

可以看到,設置了之后資源的HTTP響應多了Content-Encoding:gzip

 

  • 對javascript代碼,css樣式代碼進行語法壓縮,減少文件大小

我們寫js代碼,css,都命名盡量取得有意義,多換行,tab把格式弄的優美
都是為了方便維護,再閱讀,而機器解析我們的代碼的時候可不管這些。

這里壓縮我推薦 uglify-js和 clean-css,都在nodejs環境下

這里可見壓縮效果還是很顯著的,按我目前代碼習慣,平均可以壓縮一半左右

 

但是,不可避免,我們在日常開發,發布的時候,會增加額外的工作量


這里我分享一下我的用法:

為了方便開發,我在項目中還是引用的原文件
只是在生成發布文件后,執行下面bat批處理腳本

把發布文件夾內的js代碼進行壓縮,同名覆蓋原文件,這樣就不用修改項目內js引用地址

 

@echo off
:: 設置壓縮JS文件的根目錄,腳本會自動按樹層次查找和壓縮所有的JS
SET JSFOLDER=D:\project\scripts
echo 正在查找JS文件
chdir /d %JSFOLDER%
for /r . %%a in (*.js) do (
    @echo 正在壓縮 %%~a ...
    uglifyjs %%~fa  -mangle -o %%~fa
)
echo 完成!
pause & exit

  

(*這個bat批處理腳本不是我寫的,是以前在網上搜來的)

 

自動化構建(gulp)  

當然,用這種bat批處理腳本只滿足一些小需求,我們還可以用自動化構建工具來生成
這里我推薦gulp,同樣在nodejs環境下,這里分享一個我自己寫的靜態資源壓縮生成后綴的gulpjs

裝好gulp之后,需要安裝gulp的插件gulp-clean-css,gulp-uglify,gulp-rev,gulp-rev-collector以及gulp-sync

還有這個gulp-bom,我之前處理完之后,老是有亂碼,發現生成的文件雖然是utf-8 卻不是utf-b+bom,所以需要再處理一下

 

var gulp = require('gulp'),
config = require('./config'),
cleancss = require("gulp-clean-css"),
uglifyjs = require("gulp-uglify"),
rename = require("gulp-rename"),
rev = require("gulp-rev"),
revCollector = require("gulp-rev-collector"),
del = require("del"),
sync = require("gulp-sync")(gulp),
bom=require("gulp-bom")
;


//生成后綴
gulp.task("addv_css", function () {

    return gulp.src(config.css.src)
    .pipe(rev())
    .pipe(gulp.dest(config.css.dest))
    .pipe(rev.manifest())
    .pipe(bom())
    .pipe(gulp.dest(config.css.rev));

});
gulp.task("addv_js", function () {

    return gulp.src(config.js.src)
    .pipe(rev())
    .pipe(gulp.dest(config.js.dest))
    .pipe(rev.manifest())
    .pipe(bom())
    .pipe(gulp.dest(config.js.rev));

});

//更改cshtml中的 style引用
gulp.task("changev_css", function () {

    return gulp.src(config.rev.csssrc)
    .pipe(revCollector({
        replaceReved: true
    }))
    .pipe(bom())
    .pipe(gulp.dest(config.rev.dest));
});

//更改cshtml中的 js引用
gulp.task("changev_js", function () {

    return gulp.src(config.rev.jssrc)
            .pipe(revCollector({
                replaceReved: true
            }))
            .pipe(bom())
            .pipe(gulp.dest(config.rev.dest));
});


//壓縮css
gulp.task("cleancss", function () {

    return gulp.src(config.css.cleansrc)
        .pipe(cleancss())
        .pipe(bom())
        .pipe(gulp.dest(config.css.dest));

});

//壓縮js
gulp.task("uglify", function () {

    return gulp.src(config.js.uglifysrc)
        .pipe(uglifyjs({
            mangle: true,
            define: true
        }))
        .pipe(bom())
        .pipe(gulp.dest(config.js.dest));

});

gulp.task("default", sync.sync([["addv_css", "addv_js"], "changev_css", "changev_js", ["cleancss", "uglify"]]));

 

  

 

資源合並  


我們項目內往往會引用多個javascript腳本,和多個css樣式文件
所以可以把多個腳本合並到一個js文件內,然后統一引用它就能減少http請求


這里uglify-js和 clean-css 都支持多個文件合並壓縮輸出

>uglifyjs js1.js js2.js -m -o merge.js

  

>cleancss -o megar.css style1.css style2.css

  

也可以在服務器內合並輸出,比如我們看淘寶的合並:

<script src="//g.alicdn.com/kissy/k/6.2.4/??node-min.js,node-base-min.js,dom-base-min.js,query-selector-base-min.js,dom-extra-min.js,node-event-min.js,event-dom-base-min.js,event-base-min.js,event-dom-extra-min.js,event-gesture-min.js,event-touch-min.js,node-anim-min.js,anim-transition-min.js,anim-base-min.js,promise-min.js,base-min.js,attribute-min.js,event-custom-min.js,json-base-min.js,event-min.js,io-min.js,io-extra-min.js,io-base-min.js,io-form-min.js,cookie-min.js"></script>

 

他們則是在web服務器內做了處理,請求多個文件,會自動合並
有條件的同學也可以這樣進行合並

圖片合並csssprite  


圖片合並目的也是減少http請求,將頁面中需要的相關圖片合並到一張大圖片里
然后用css的background-position定位到大圖的具體局部位置

比如這樣:


但是近年來好像用的越來越少了
想想原因,應該是。。。。維護起來[太麻煩]了
只要稍微改動一張圖片,尤其是改變了大小的話,整個大圖片都要改
並且還要把css樣式也改個遍
否則就是繼續往大圖片里拼,無效的老圖保留,導致圖片越來越臃腫


再有,如果只是一些這樣的小圖標的話,用fonticon要方便得多

iconfont  

iconfont就是圖標字體,將圖標輸出為矢量的字體

使用起來非常方便,對應字體的編碼,就和網頁中的普通文字一樣

<i class="icon iconfont">&#85;</i>

  

它對比合並的圖片來說
體積會更小,並且能用css控制大小,顏色
然后它還是矢量的,放大也不失真


雖然iconfont制作,維護成本也不低,引用起來為兼容瀏覽器得引用多種格式
但好在現在用的人多,網上也有許多免費圖標庫供使用

比如這里阿里巴巴圖標庫

http://iconfont.cn/

 

引用優化  

引用css放在<head>內,引用js放在</body>結束標簽前,現在很多朋友都會這么做了

css加載是異步的,更早的加載出樣式就能更早呈現出頁面
js放在尾部,防止瀏覽器加載js而阻塞頁面,造成頁面“白屏”現象

單獨域名存放資源  


如果有條件的話,我們還可以啟用額外的服務器,域名來存放資源

 

這樣能減少主域名的HTTP請求數,讓主服務器更快響應請求
還能減少主域名的cookie請求

 

緩存

說到緩存,首先想到的肯定是服務器后台的緩存
其實,打開我們的瀏覽器工具看一下,就會發現,網頁前台也存在着許多緩存
它無聲無息,悄悄優化着每一次訪問
 

Cache-Control緩存策略  

直接看響應頭,我們會發現這些字段:

 

 

Expires:Thu, 20 Oct 2016 06:43:43 GMT //告訴瀏覽器此日期以前可以使用緩存文件

Cache-Control:public, max-age=3600 //表示資源在3600毫秒之內可以使用緩存文件,如果和Expires同時存在則覆蓋Expires

Last-Modified:Wed, 13 Jul 2015 08:52:12 GMT//配合Cache-Controls使用,標示資源的最后修改日期

ETag:5384183131862232576//配合Cache-Controls使用,標示資源由服務器生成的唯一標識(某些資源最后修改日期不可靠,或者不希望展示最后修改日期,就可以使用ETag)

 

上面這些都是控制前端緩存的字段,然后再來看看下面的HTTP狀態碼

 

//200 OK 請求已成功,請求所希望的響應頭或數據體將隨此響應返回。

//304 Not Modified 請求資源沒有改變,可以使用緩存資源

 

所以這里當我們第一次訪問的時候,得到響應資源及緩存策略,

之后如果緩存有效,資源就會來自緩存
如果緩存無效了,就會判斷是否有Last-Modified或者ETag,有則發送If-Modified-Since或If-None-Match請求頭
服務器如果判斷資源無修改,就會返回304,有修改就會返回200以及新的緩存策略


當然,這里只是指用戶的普通操作,如:地址欄回車,頁面鏈接跳轉,新窗口,前進后退
如果用戶是進行 刷新 操作,則不會讀取緩存資源了,但是如果有Last-Modified或者ETag,依然會發送If-Modified-Since或If-None-Match請求頭
如果用戶是進行 強制刷新(ctrl+f5) 操作,那所有緩存策略都失效,會重新請求


可見,我們往往只有第一次請求的時候,才會有較多的資源加載
所以我們上面做了這么多優化,延遲加載,資源壓縮,合並等等,
都是為了用戶第一次訪問的時候盡量友好。


那么Cache-Control要如何在服務器設置呢,其實針對靜態資源來說
大部分服務器都會默認就將Cache-Control設置好了的,我們可以視情況修改一些時效參數等等


我們這里要注意的是用url去控制緩存的有效性

如下:

雖然它們請求的都是服務器上的同一個js文件,但是瀏覽器不會把它們當作同一個資源

可以看到v=1.01.js的請求是來源於cache
其他幾個請求都是新請求

所以,我們每次發布都可以修改資源url強制讓用戶頁面上的緩存失效
這樣用戶不需要刷新也能得到最新的資源

離線存儲  

離線存儲在我之前一篇文章里也提到過,在移動端應用的比較多
它和緩存不同,它設置好了之后,連離線也能訪問,無論用戶刷新或者新窗口,鏈接等等


使用manifest
<html manifest="/mobile.manifest">
在html上添加manifest,其中文件格式內容如:

 

CACHE MANIFEST
##需要離線的內容
CACHE:

Script/jquery.js
Script/gameconfig.js


Image/home.png
Image/logo.png

##總是訪問網絡的內容
NETWORK:
*
##訪問A失敗時訪問B
FALLBACK

  


瀏覽器將緩存chache內所有的內容,並且可以離線訪問,只要文件發生任何改變都將會重新讀取並刷新全部緩存,所以更改注釋是個更新緩存的好方法

這里要注意的是
1,添加了manifest的當前網頁也會被緩存 所以推薦的方式是頁面緩存,頁面動態內容全部用ajax獲取,所以在移動網站項目設計開始就要注意這個問題
2,頁面中添加iframe 然后子頁面引用manifest想達到緩存資源而不緩存當前頁面內容,是無效的。

 

本地存儲localStorage  


本地存儲數據一直是網頁端的弱項,在沒有HTML5的localStorage前,用cookie可以保存一點數據
但付出的代價很大,cookie能保存的數據很少,並且它會伴隨着每一次請求一起發送


localStorage就好多了,默認5MB的大小,除非用戶手動清除,否則一直不過期,就連IE8瀏覽器都支持


這里要注意,localStorage和cookie一樣受到跨域的限制
可以使用domain控制

document.domain="";

  

其它的

 

css3替換js動畫  

在js中,我們實現動畫,就是利用定時器循環改變dom元素的屬性來達到動畫效果
但是許多屬性更改之后會造成瀏覽器重繪,增加性能消耗


當然瀏覽器更新換代也做了許多優化,我們優化js,css減少重繪,也能改進動畫性能
但是想一想,究竟應不應該讓js去實現頁面動畫呢?

css3就是往這方面發展,讓js更純粹的去實現業務邏輯
頁面效果之類的事情就讓css去做吧

並且css3在動畫效率上面也有增強,瀏覽器會單獨處理css3動畫,不占用js主線程,還可以硬件加速
將來還有提升的可能,所以快把我們的js動畫替換為css3吧!

替換flash  

同樣更迭的還有flash,當初flash是為了彌補網頁展現的不足而出現的“插件”
而現在網頁標准一次次升級,html5的出現,再加上flash自身也有各種漏洞,性能問題
尤其是現在flash在移動端的支持很少,都加快了我們替換flash的步伐

結語  

寫到這里快寫完了,突然發現好像自己在啰啰嗦嗦了一整頁。。

卻又感覺哪里漏都了一些,好吧,希望大家不要嫌煩,如果都了解了,就當溫習吧

如有紕漏,歡迎提醒

 

哦,還有些想說的

優化一定要選擇適合自己項目,硬件條件的優化方式,千萬不要盲目硬搬
還有要持續不斷的關注新標准,方法,一直保持活力!




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM