1、相關知識講解php
看過雅虎的前端優化35條建議,都知道優化前端是有多麼重要。頁面的加載速度直接影響到用戶的體驗。80%的終端用戶響應時間都花在了前端上,其中大部分時間都在下載頁面上的各類組件:圖片,樣式表,腳本,Flash等等。css
減小組件數必然可以減小頁面提交的HTTP請求數。這是讓頁面更快的關鍵。減小頁面組件數的一種方式是簡化頁面設計。但有沒有一種方法能夠在構建複雜的頁面同時加快響應時間呢?嗯,確實有魚和熊掌兼得的辦法。html
這裏咱們就拿雅虎的第一條建議:儘可能減小HTTP請求數裏的減小圖片請求數量 進行講解。前端
咱們都知道,一個網站的一個頁面可能有不少小圖標,例如一些按鈕、箭頭等等。當加載html文檔時,只要遇到有圖片的,都會自動創建起HTTP請求下載,而後將圖片下載到頁面上,這些小圖片可能也就是十幾K大甚至1K都不到,假如咱們的一個頁面有一百個小圖標,咱們在加載頁面時,就要發送100個HTTP請求,若是你的網站訪問量很大併發量也很高,假如上百萬訪問量,那發起的請求就是千萬級別了,服務器是有必定的壓力的,而且一個用戶的一個頁面要發起那麼多請求,是很耗時的。web
因此,咱們優化的方案就是:將這些十幾K、幾K的小圖標合併在一張圖片裏,而後用CSS的background-image
和background-position
屬性來定位要顯示的部分。瀏覽器
二、代碼實現服務器
一、思路:數據結構
將一個文件夾裏的圖標,自動生成在一張圖片裏面,同時自動生成對應的css文件,咱們只要在HTML裏的標籤中添加相應的屬性值就能顯示圖片了。併發
二、實現過程:前端優化
1 <?php 2 //本身定義一個根目錄 3 define('ROOT', $_SERVER['DOCUMENT_ROOT'].'iconwww'); 4 //這個是圖片的目錄 5 define('RES_BASE_URL', 'http://localhost:8080/iconwww/img'); 6 7 /** 8 * 生成背景圖的函數 9 */ 10 function generateIcon() { 11 //網站根目錄 12 $webRoot = rtrim(ROOT, '/'); 13 //背景圖目錄 14 $root = "$webRoot/img/bg"; 15 //Php-SPL庫中 的 目錄文件遍歷器 16 $iterator = new DirectoryIterator($root); 17 //開始遍歷該背景圖目錄下的目錄,咱們是把想生成背景圖的目錄,放在bg目錄中以各個模塊的目錄分類存放 18 foreach ($iterator as $file) { 19 //遇到目錄遍歷 20 if (!$file->isDot() && $file->isDir()) { 21 //取得文件名 22 $fileName = $file->getFilename(); 23 generateIconCallback("$root/$fileName", "$webRoot/img/$fileName", "$webRoot/css/$fileName.css"); 24 } 25 } 26 } 27 28 /** 29 * 用戶生成合並的背景圖和css文件的函數 30 * @param string $dir 生成背景圖的圖標所在的目錄路徑 31 * @param string $bgSavePath 背景圖所保存的路徑 32 * @param string $cssSavePath css保存的路徑 33 */ 34 function generateIconCallback($dir, $bgSavePath, $cssSavePath) { 35 $shortDir = str_replace('\\', '/', substr($dir, strlen(ROOT-1))); 36 //返回文件路徑信息 37 $pathInfo = pathinfo($bgSavePath.'.png'); 38 39 $bgSaveDir = $pathInfo['dirname']; 40 //確保目錄可寫 41 ensure_writable_dir($bgSaveDir); 42 //背景圖名字 43 $bgName = $pathInfo['filename']; 44 //調用generateIconCallback_GetFileMap()函數生成每個圖標所須要的數據結構 45 $fileMap = array('a' => generateIconCallback_GetFileMap($dir)); 46 47 $iterator = new DirectoryIterator($dir); 48 foreach ($iterator as $file) { 49 if ($file->isDot()) continue; 50 if ($file->isDir()) { 51 //二級目錄也要處理 52 $fileMap['b-'.$file->getFilename()] = generateIconCallback_GetFileMap($file->getRealPath()); 53 } 54 } 55 ksort($fileMap); 56 57 //分析一邊fileMap,計算整個背景圖的大小和每個圖標的offset 58 //初始化偏移量和背景圖 59 $offsetX = $offsetY = $bgWidth = 0; 60 //設定每一個小圖標之間的距離 61 $spaceX =$spaceY = 5; 62 //圖片最大寬度 63 $maxWidth = 800; 64 $fileMd5List =array(); 65 //這裏須要打印下$fileMap就知道它的數據結構了 66 foreach ($fileMap as $k1 => $innerMap) { 67 foreach ($innerMap as $k2 => $itemList) { 68 //行高姐X軸偏移量初始化 69 $offsetX = $lineHeight = 0; 70 foreach ($itemList as $k3 => $item) { 71 //變量分別是:圖標的寬度,高度,類型,文件名,路徑,MD5加密字符串 72 list($imageWidth, $imageHeight, $imageType, $fileName, $filePathname, $fileMd5) = $item; 73 $fileMd5List []= $fileMd5; 74 //若是圖片的寬度+偏移量 > 最大寬度(800) 那就換行 75 if ($offsetX !== 0 && $imageWidth + $offsetX > $maxWidth) { 76 $offsetY += $spaceY + $lineHeight; 77 $offsetX = $lineHeight = 0; 78 } 79 //若是圖片高度 > 當前行高 那就講圖片高度付給行高咱們這的 80 if ($imageHeight > $lineHeight) $lineHeight = $imageHeight; 81 $fileMap[$k1][$k2][$k3] = array($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname); 82 //X軸偏移量的計算 83 $offsetX += $imageWidth + $spaceX; 84 if ($offsetX > $bgWidth) $bgWidth = $offsetX; 85 } 86 //Y軸偏移量的計算 87 $offsetY += $lineHeight + $spaceY; 88 } 89 } 90 //把右下兩邊多加了的空白距離給幹掉 91 $bgWidth -= $spaceX; 92 $bgHeight = $offsetY - $spaceY; 93 $fileMd5List = implode("\n", $fileMd5List); 94 95 //生成背景圖和 css文件 96 97 //資源路徑 98 $resBaseUrl = RES_BASE_URL; 99 $suffix = base_convert(abs(crc32($fileMd5List)), 10, 36); 100 $writeHandle = fopen($cssSavePath, 'w'); 101 fwrite($writeHandle, "/** bg in dir: $shortDir/ */\n[icon-$bgName]{background:url({$resBaseUrl}/$bgName.png?$suffix) no-repeat;display:inline-block;}"); 102 103 //作圖片,這些函數具體能夠查看PHP手冊 104 $destResource = imagecreatetruecolor($bgWidth, $bgHeight); 105 imagealphablending($destResource, false); 106 imagesavealpha($destResource, false); 107 $color = imagecolorallocatealpha($destResource, 255, 255, 255, 127); 108 109 imagefill($destResource, 0, 0, $color); 110 111 //對每一張小圖片進行處理,生成在大背景圖裏,並生成css文件 112 foreach ($fileMap as $innerMap) { 113 foreach ($innerMap as $itemList) { 114 foreach ($itemList as $item) { 115 list($imageWidth, $imageHeight, $offsetX, $offsetY, $imageType, $fileName, $filePathname) = $item; 116 if ($imageType === IMAGETYPE_PNG) { 117 $srcResource = imagecreatefrompng($filePathname); 118 } else if ($imageType === IMAGETYPE_JPEG) { 119 $srcResource = imagecreatefromjpeg($filePathname); 120 } 121 imagecopy($destResource, $srcResource, $offsetX, $offsetY, 0, 0, $imageWidth, $imageHeight); 122 imagedestroy($srcResource); 123 124 //寫入css 125 $posX = $offsetX === 0 ? 0 : "-{$offsetX}px"; 126 $posY = $offsetY === 0 ? 0 : "-{$offsetY}px"; 127 fwrite($writeHandle, "\n[icon-$bgName=\"$fileName\"]{width:{$imageWidth}px;height:{$imageHeight}px;background-position:$posX $posY;}"); 128 } 129 } 130 } 131 132 //壓縮級別 7 133 imagepng($destResource, "$bgSavePath.png", 7); 134 imagedestroy($destResource); 135 fclose($writeHandle); 136 137 $shortCssSavePath = substr($cssSavePath, strlen(ROOT)); 138 } 139 140 /** 141 * 將圖片的信息處理成咱們想要的數據結構 142 * @param [type] $dir [description] 143 * @return [type] [description] 144 */ 145 function generateIconCallback_GetFileMap($dir) { 146 $map = $sort = array(); 147 $iterator = new DirectoryIterator($dir); 148 foreach($iterator as $file) { 149 if(!$file->isFile()) continue; 150 $filePathname = str_replace("\\", '/', $file->getRealPath()); 151 //這些函數能夠查看PHP手冊 152 $imageInfo = getimagesize($filePathname); 153 $imageWidth = $imageInfo[0]; 154 $imageHeight = $imageInfo[1]; 155 $imageType = $imageInfo[2]; 156 157 if(!in_array($imageType, array(IMAGETYPE_JPEG, IMAGETYPE_PNG))) { 158 $fileShortName = substr($filePathname, strlen(ROOT) - 1); 159 echo "<p> $fileShortName 圖片被忽略: 由於圖片類型不是png|jpg.</p>"; 160 continue; 161 } 162 163 //這是咱們的圖片規格,行高分別有 16 32 64 128 256 99999 164 foreach(array(16, 32, 64, 128, 256, 99999) as $height) { 165 if($imageHeight <= $height) { 166 $mapKey = $height; 167 break; 168 } 169 } 170 if(!isset($map[$mapKey])) $map[$mapKey] = array(); 171 $filePathInfo = pathinfo($filePathname); 172 $map[$mapKey] []= array($imageWidth, $imageHeight, $imageType, $filePathInfo['filename'], $filePathname, md5_file($filePathname)); 173 $sort[$mapKey] []= str_pad($imageHeight, 4, '0', STR_PAD_LEFT) . $filePathInfo['filename']; 174 } 175 foreach($map as $k => $v) array_multisort($map[$k], SORT_ASC, SORT_NUMERIC, $sort[$k]); 176 ksort($map, SORT_NUMERIC); 177 return $map; 178 } 179 180 /** 181 * 判斷目錄是否可寫 182 * @param string $dir 目錄路徑 183 */ 184 function ensure_writable_dir($dir) { 185 if(!file_exists($dir)) { 186 mkdir($dir, 0766, true); 187 @chmod($dir, 0766); 188 @chmod($dir, 0777); 189 } 190 else if(!is_writable($dir)) { 191 @chmod($dir, 0766); 192 @chmod($dir, 0777); 193 if(!@is_writable($dir)) { 194 throw new BusinessLogicException("目錄不可寫", $dir); 195 } 196 } 197 } 198 199 generateIcon(); 200 ?> 201 <!DOCTYPE html> 202 <html> 203 <head> 204 <link rel="stylesheet" type="text/css" href="css/Pink.css"> 205 <title></title> 206 207 208 </head> 209 <body> 210 <div>咱們直接引入所生成的css文件,並測試一下是否成功</div> 211 <br> 212 <div>這裏在span標籤 添加屬性 icon-Pink ,值爲About-40,正常顯示圖片</div> 213 <span icon-Pink="About-40"></span> 214 </body> 215 </html>
調用以上代碼,咱們的瀏覽器是這樣顯示的:
而後css目錄生成了Pink.css文件:
img目錄下生成了Pink.png文件:
看看生成的背景圖是長啥樣子:
接下來咱們再看一下所生成的圖片大小與Pink文件夾裏全部小圖片總和的大小,對它們作個比較:
從上圖能夠看出,咱們生成的圖片的大小明顯小於文件夾全部圖片的大小,因此在將100個小圖標下載下來的速度 會明顯小於 將背景圖下載下來和將CSS下載下來的速度。
當訪問量大時,或者小圖片的量大時,會起到很明顯的優化效果!!!
代碼中的每個點都基本上有註釋,很方便你們去理解,只要你們用心去看,確定能將這一網站優化技術用到本身的項目中。
本次博文就寫到這!!!
若是此博文中有哪裏講得讓人難以理解,歡迎留言交流,如有講解錯的地方歡迎指出。
若是您以爲您能在此博文學到了新知識,請爲我頂一個,如文章中有解釋錯的地方,歡迎指出。
互相學習,共同進步!