web頁面內容優化管理與性能技巧
回 想一下,以前我們不得不花費大量時間去優化頁面內容(圖片、CSS等等),如今用戶有更快速的互聯網鏈接,我們似乎能夠使用更大的圖像或更大的閃 存文件,里面包含的有視頻或者圖片。然而,隨著移動開發的興起,我們又回到了過去的窘狀。網站優化是十分重要的,需要下載的內容少,反應速度快,就能使我 們加載應用程序更快速。
圖片:控制在合適的尺寸大小
很多時候我們在不同的網站使用同樣的圖像,例如一個網上商店,所有產品 都 有一個概覽圖片。打個比方,有三個頁面描述產品,第一個頁面顯示產品清單,第二個頁面顯示產品細節圖,第三個頁面顯示產品原始大小圖。因此,我們需要三種 不同大小的圖片。如果使用一個文件放到不同的三個頁面上,那么瀏覽器會自動加載完整大小的圖片,就連清單頁也是,實際上清單頁只需要200×200尺寸的 圖片。如果原始文件大小在1MB左右,每頁上有十個產品介紹,那么用戶就要下載10MB大小的資源。這樣做效果不好。如果可以的話,盡量為你的網站不同位 置分配不同的圖像文件,那么就可以讓用戶少下載資源。把屏幕分辨率因素也考慮進去也是很好的。如果有人用iPhone打開你的網站頁面,手機上不需要顯示 電腦上那么大尺寸的圖片,只需適應手機屏幕的大小就可以了。通過CSS Media Queries,你就能將圖像壓縮到較小尺寸發送出去了:
@media only screen
and (min-device-width : 320px)
and (max-device-width : 480px) {
.header {
background-image: url(../images/background_400x200.jpg);
}
} 壓縮
傳送圖像的時候單單控制適當的尺寸往往是不夠的。不少文件格式在不失真的前提下可以被壓縮很多。有一類應用程序可以達到這個效果。比如Photoshop有個很好的功能叫做Save for Web and Devices:
在此對話框中有多個選項,其中最重要的是質量,將其設計為80%左右,就能顯著減少文件大小了。當然,你還可以使用代碼來壓縮文件,但我個人偏向于使用PS。下面是用PHP編寫的一個簡單的例子:
function compressImage($source, $destination, $quality) {
$info = getimagesize($source);
switch($info['mime']) {
case "image/jpeg":
$image = imagecreatefromjpeg($source);
imagejpeg($image, $destination, $quality);
break;
case "image/gif":
$image = imagecreatefromgif($source);
imagegif($image, $destination, $quality);
break;
case "image/png":
$image = imagecreatefrompng($source);
imagepng($image, $destination, $quality);
break;
}
}
compressImage('source.png', 'destination.png', 85); Sprite
增加應用程序性能的方法之一,是減少到服務器的請求數。每一個新圖像代表一個請求數。有一個辦法是將幾個圖片合并成一個,合并之后的圖像叫做一個sprite,在CSS中改變背景層的位置,就能準確的把特定部分的圖像顯示出來。比如推ter Bootstrap利用sprites引導內部圖標:

在CSS中,你可以參照以下方式,顯示你喜歡的sprite部分:
.icon-edit {
background-image: url("../img/glyphicons-halflings-white.png");
background-position: -96px -72px;
} 超高速緩存
瀏覽器超高速緩存十分好用。盡管有時在開發過程中會導致一些非常有趣的情況,但它確實有助于提高你的網站的性能。所有瀏覽器的超高速緩存下來的內容包括圖片、JavaScript或者CSS。有幾種方法可以控制緩存,建議你閱讀相關文章。一般情況下,你可以通過設置標題,達到控制效果:
$expire = 60 * 60 * 24 * 1;// seconds, minutes, hours, days
header('Cache-Control: maxage='.$expire);
header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expire).' GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 預讀取
HTML 5每天都在進步,有一個很好的功能叫做預讀取,它讓瀏覽器提前下載你馬上需要用到的資源:
<link rel="prefetch" href="/images/background.jpg">
數據URI方案/內聯圖像
幾年前我曾開發了一個簡單的網頁,只包含一個HTML文件夾,但當然里面應該還包括一些我需要的圖像。數據URI方案幫助我解決了問題。我們的想法是將圖像轉換成一個base64編碼的字符串,并將其放置在src屬性中的img標簽里,例如:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot">
通過這種方法,你的圖像實際上在HTML中并保存了一個HTTP請求。你的圖像越大的話,字符串就越長。下面是一個簡單的PHP腳本圖像轉換為base64字符串的實例:
$picture = fread($fp,filesize($file));
fclose($fp);
// base64 encode the binary data, then break it
// into chunks according to RFC 2045 semantics
$base64 = base64_encode($picture);
$tag = '<img src="data:image/jpg;base64,'.$base64.'" alt="" />';
$css = 'url(data:image/jpg;base64,'.str_replace("\n", "", $base64).'); '; 有些情況下這種方法挺好用的,但請注意,在IE瀏覽器中無法很好的兼容。
CSS
我 覺得編寫CSS就如同寫代碼。你同樣需要組織模式,定義不同板塊和關系。所以我認為CSS管理非常重要。應用程序的每一部分應該有對應的模式,并很好的獨 立。不同的內容存儲在不同的文件夾可以有效的管理,但同樣也存在問題。使用@import狀態這種方法不好用,因為每用一個@import都意味著一個新 的請求發送到服務器。如果你有20個不同的.css文件,就相當于瀏覽器要發送20個請求。瀏覽器在渲染/下載所有內容之前不會顯示頁面。如果你 的.css文件丟失了或者太大,瀏覽器加載頁面的時間就會大大延長。
使用CSS預處理器
CSS預處理器可以解決上述問題。你 同樣可以保存不同的文件夾,預處理器可以將這些散文件夾最終生成一個.css文件。實際上提供了一系列非常幫的功能比如變量、嵌套塊、混入和繼承。代碼看 上去類似CSS,但實際上被很好的統一格式與結構了。有幾種好用的預處理器值得體驗——Sass、LESS、Stylus。下面是用LESS編寫的例子:
.position(@top: 0, @left: 0) {
position: absolute;
top: @top;
left: @left;
text-align: left;
font-size: 24px;
}
.header {
.position(20px, 30px);
.tips {
.position(10px, -20px);
}
.logo {
.position(10px, 20px);
}
} 運行生成:
.header {
position: absolute;
top: 20px;
left: 30px;
text-align: left;
font-size: 24px;
}
.header .tips {
position: absolute;
top: 10px;
left: -20px;
text-align: left;
font-size: 24px;
}
.header .logo {
position: absolute;
top: 10px;
left: 20px;
text-align: left;
font-size: 24px;
} 又如你想再創建一個同樣樣式的按鈕,但是顏色不同,你可以這樣做:
.button {
border: solid 1px #000;
padding: 10px;
background: #9f0;
color: #0029FF;
}
.active-button {
.button();
color: #FFF;
} 高效的CSS
通常情況下,大多數開發人員沒有考慮過CSS效率問題。CSS的效率反映在頁面渲染上,如果模式低效,應用程序在瀏覽器上運行就會很慢。有趣的是瀏覽器解析CSS選擇器是從右到左的。所以以下代碼更不一點效率都沒有:
body ul li a {
color: #F000;
text-decoration: none;
} 這 是因為該引擎首先識別所有的<a>標簽,評估每個母元素,最終收集到所需模式。你要知道,為了提高效率,選擇器有個 先后排序:ID、類、標簽及其一般。這意味著一個帶有id的元素集比只帶有標簽選擇器的元素更快的被渲染。當然,在所有DOM樹元素都加上id是沒有意義 的,但你應該特定檢查代碼,把可能加id的地方都加上。比如你可以按照以下方式做:
ul #navigation li {
background: #ff0232;
} .content元素是body tag的子集,實際上所有元素都是body tag的子集。關于這個話題有兩個有用的鏈接:developers.google.com和css-tricks.com。
文件大小
正如我們上面提到的,代碼越少越好,因為瀏覽器在加載CSS之前不渲染頁面。下面幾個技巧可供縮小文件大小:
把類似的行
.header {
font-size: 24px;
}
.content {
font-size: 24px;
} 轉換成
.header, .content {
font-size: 24px;
} 用速記,而不是以下
.header {
background-color: #999999;
background-image: url(../images/header.jpg);
background-position: top right;
} 用下面的風格編寫
.header {
background: #999 url(../images/header.jpg) top right;
} 縮減代碼,也就是使用一個工具除去所有空間和線,可以使用CSSOptimiser或Minifycss。常見做法是在應用程序服務器端使用這種工具。也就是在后端寫的語言。通常情況相愛這些組件可以縮減你的代碼。
將你的CSS文件放在<head>標簽下
將你的CSS文件放在head標簽下是很好的方法,瀏覽器會首先下載它們。
JavaScript
減少HTTP請求數量
與CSS情況一樣,減少服務器請求是有利的。大多數情況下,加載JavaScript文件的同時不會停止渲染頁面,但會造成頁面某些部分失去作用。
縮減代碼
有些小工具可以縮減JavaScript,使文件大小減小了,但要記住在開發環境中,保持代碼整潔是十分必要的。這些工具幾乎都會改變變量名稱,并轉換成一個單行的字符串,這個過程不可能調試。
JavaScript本身并沒有一個機制來管理模數,因此,這些工具是為了解決這個問題的。他們提供一個應用程序接口,你可以定義和使用模數。例如http://requirejs.org/:
<!DOCTYPE html>
<html>
<head>
<title>My Sample Project</title>
<!-- data-main attribute tells require.js to load
scripts/main.js after require.js loads. -->
<script data-main="scripts/main" src="scripts/require.js"></script>
</head>
<body>
<h1>My Sample Project</h1>
</body>
</html> 運行腳本,你可以用require()代替main.js
require(["helper/util"], function(util) {
//This function is called when scripts/helper/util.js is loaded.
//If util.js calls define(), then this function is not fired until
//util's dependencies have loaded, and the util argument will hold
//the module value for "helper/util".
}); 使用名字空間
提到代碼組織,必然要提到命名空間的部分。原本在JavaScript中是沒有這個功能的,但你可以通過幾行代碼來實現這個功能。比如,你想達一個MVC框架,就可以用以下方式:
var model = function() { ... };
var view = function() { ... };
var controller = function() { ... }; 光有以上代碼是不夠的,很容易與其他行的代碼發生沖突。所以需要按照以下方式將它們作為獨立的對象(命名空間)分組,以保護整體框架:
var MyAwesomeFramework = {
model: function() { ... },
view: function() { ... },
controller: function() { ... }
} 遵循設計模式
JavasScript之所以很受歡迎是因為里面包含了大量例子。可重復使用的設計模式是編程中常見問題的解決方案。遵循某些設計模式可以幫助你更好的設計應用程序。如果我全都寫下來,估計都可以出書了,所以這里只寫出一些例子:
構造函數模式
用這個模式構建具體對象實例:
var Class = function(param1, param2) {
this.var1 = param1;
this.var2 = param2;
}
Class.prototype = {
method:function() {
alert(this.var1 + "/" + this.var2);
}
}; 或者:
function Class(param1, param2) {
this.var1 = param1;
this.var2 = param2;
this.method = function() {
alert(param1 + "/" + param2);
};
};
var instance = new Class("value1", "value2"); 模塊模式
模塊模式可以讓我們創建私有和公共方法。比如下面的代碼中,變量_index和方法privateMethod是私有的,increment和getIndex是公開的。
var Module = (function() {
var _index = 0;
var privateMethod = function() {
return _index * 10;
}
return {
increment: function() {
_index += 1;
},
getIndex: function() {
return _index;
}
};
})(); 觀察者模式
事件的訂閱和分派發生的時候就能看到這種模式。觀察者對特定對象相關的東西有興趣,一旦發生動作,就會通知觀察者。下面的例子顯示我們如何才能增加用戶對象的觀察者:
var Users = {
list: [],
listeners: {},
add: function(name) {
this.list.push({name: name});
this.dispatch("user-added");
},
on: function(eventName, listener) {
if(!this.listeners[eventName]) this.listeners[eventName] = [];
this.listeners[eventName].push(listener);
},
dispatch: function(eventName) {
if(this.listeners[eventName]) {
for(var i=0; i<this.listeners[eventName].length; i++) {
this.listeners[eventName][i](this);
}
}
},
numOfAddedUsers: function() {
return this.list.length;
}
}
Users.on("user-added", function() {
alert(Users.numOfAddedUsers());
});
Users.add("Krasimir");
Users.add("Tsonev"); 函數鏈接模式
這種模式可以很好的組織模塊的公共接口。節省時間,提高可讀性:
var User = {
profile: {},
name: function(value) {
this.profile.name = value;
return this;
},
job: function(value) {
this.profile.job = value;
return this;
},
getProfile: function() {
return this.profile;
}
}; var profile = User.name("Krasimir Tsonev").job("web developer").getProfile();
console.log(profile); 我強烈推薦Addy Osmani出的書,它涵蓋了JavaScript中設計模式所有最棒的資源。
Assets-Pack
在 本文結尾的時候,我想分享一些關于服務器上CSS和JavaScript代碼管理方面的想法。這是一個常用手段來添加合并、縮小、編譯成應用程序的邏輯。 時常有種緩存機制,但在程序運行的時候所有事情都在同時發生。就是說你或許有代碼的邏輯,同時處理.js或.css文件請求,然后提供適當的內容。這個過 程的背后是匯編、壓縮,以及其他。在我最新一個項目中我用到一種叫做Assets-Pack的工具。它非常有用,我可以詳盡解釋它能做什么,但更有趣的是 我是怎樣使用這個工具的。只能用在開發模式中,不是停留在基于代碼形式的,也不是在服務器上調配的。
我的想法是運用這個工具只當你在處理 CSS和JS的時候,它可以監視特定目錄中的變化,然后把代碼編譯/打包成為一個單一的文件。通過這個步驟,你不需要再去考慮壓縮或者匯編。所有你所要做 的僅僅是將編譯后的靜態文件發送給用戶。這增加了應用程序的性能,因為它只能提供靜態文件,這當然讓事情變得更簡單。你不需要設置任何服務器或實施不必要 的邏輯。
下面是你如何安裝和使用Assets-Pack:
npm install -g assetspack
用法
該模塊可與JSON配置,當它通過命令行被調用的時候,你應該把設置放到.json文件中。
通過命令行
創建assets.json文件夾,在同一個目錄下執行以下代碼:
assetspack
如果你想使用另一種名稱或者換一個目錄:
assetspack --config [path to json file]
代碼形式的
var AssetsPack = require("assetspack");
var config = [
{
type: "css",
watch: ["css/src"],
output: "tests/packed/styles.css",
minify: true,
exclude: ["custom.css"]
}
];
var pack = new AssetsPack(config, function() {
console.log("AssetsPack is watching");
});
pack.onPack(function() {
console.log("AssetsPack did the job");
}); 配置
配置應該是一個有效的JSON文件/對象,下面只是一個對象數組:
[
(asset object),
(asset object),
(asset object),
...
] Asset Object
Asset Object的基本結構如下:
{
type: (file type /string, could be css, js or less for example),
watch: (directory or directories for watching /string or array of strings/),
pack: (directory or directories for packing /string or array of strings/. ),
output: (path to output file /string/),
minify: /boolean/,
exclude: (array of file names)
} pack屬性不是強制的,如果丟失了,它的值還是相等的,默認情況下的縮減是假屬性。
下面是一些例子:
Packing CSS
{
type: "css",
watch: ["tests/data/css", "tests/data/css2"],
pack: ["tests/data/css", "tests/data/css2"],
output: "tests/packed/styles.css",
minify: true,
exclude: ["header.css"]
} Packing JavaScript
{
type: "js",
watch: "tests/data/js",
pack: ["tests/data/js"],
output: "tests/packed/scripts.js",
minify: true,
exclude: ["A.js"]
} Packing .less Files
Packing .less Files有點不同,pack屬性是強制性的,基于你的切入點。你應當導入所有其他的.less文件。排除屬性在這里無效。
{
type: "less",
watch: ["tests/data/less"],
pack: "tests/data/less/index.less",
output: "tests/packed/styles-less.css",
minify: true
} 如果有其他問題,可以在源代碼(GitHub)庫中查看tests/packing-less.spec.js
壓縮其他格式文件
assets-pack適用于所有文件格式。比如你可以結合HTML模板和一個簡單文件,用以下方式:
{
type: "html",
watch: ["tests/data/tpl"],
output: "tests/packed/template.html",
exclude: ["admin.html"]
} 有一點需要注意的是這里沒有縮小倍率。
結論
作為前端Web開發人員,我們應該盡量為的用戶提供最佳的性能。上面的提示不應該涵蓋所有資產的組織和性能方面的技巧,但它們是常用的幾種。
via 極客標簽