前言
一直觉得自己的博客站点页面加载很慢, 就想着去优化一下. 呐, 下图是一次文章页面的加载, 需要2.5s. 其中 js 文件就有18个. 众所周知, 浏览器对资源文件的并行下载数量是有限制的(不同浏览器限制不同). 也就是说, 这18个 js 文件是无法同时下载的, 再说了, 页面中还有其他资源文件, 因此. 减少资源文件的数量, 就可以提高页面的加载速度.
合并 js 文件
首先, 尝试将页面的 js 文件进行合并. 这里我使用了比较通用的方法, 正则匹配. 因此需要的话, 你大概率也可以使用:
<?php
// 开启页面缓冲
ob_start(function($content){
// 根据正则表达式, 将特定部分的 js 内容进行提取
$mergePatternJsData = function ($content, $pattern, $replaceStr){
// 没有找到匹配的内容
$noMatch = fn() => str_replace($replaceStr, "", $content);
// 将内容进行匹配提取
preg_match($pattern, $content, $matches);
if(empty($matches[0])) return $noMatch();
// 匹配其中的 js 文件
$pattern = "/<script\s*src='(.*)'.*><\/script>/U";
preg_match_all($pattern, $matches[0], $itemMatch);
// 这里对页面中的部分 js 文件进行过滤
// 因为一些 js 文件中使用相对位置引入了其他资源文件
// 因此若是将其链接挪走了, 会导致引入资源文件失败
$itemMatch = fileCantMergeFile($itemMatch);
if(empty($itemMatch[0])) return $noMatch();
// 将匹配到的 js 文件进行合并, 并返回合并后的链接
$replaceUrl = self::mergeResData($itemMatch[1]);
// 将原本的 js 内容去掉
$content = str_replace($itemMatch[0], '', $content);
// 替换新的 js 文件
return str_replace($replaceStr, "<script src='{$replaceUrl}'></script>", $content);
};
// JS_REPLACE_HEAD_STR/JS_REPLACE_FOOTER_STR 是提前在 header/footer 中放好的等待替换的字符串. 这里简单处理了, 其他处理方式也是可以的
// 这里说一下为什么 head 和 body 中的 js 资源不能进行合并
// 因为资源文件的加载时机不同, 因此不能进行合并
// 匹配 header 中的 js 文件
$headerPattern = '/<head>[\S\s]*<\/head>/U';
$content = $mergePatternJsData($content, $headerPattern, self::JS_REPLACE_HEAD_STR);
// 匹配 body 中的 js
$bodyPattern = '/<body[\s>][\S\s]*<\/body>/U';
return $mergePatternJsData($content, $bodyPattern, self::JS_REPLACE_FOOTER_STR);
});
function mergeResData($urlList){
if(empty($urlList)) return null;
$workPath = __DIR__; // 项目路径, 自行修改
// 保存缓存文件的路径, 相对于项目的相对路径
// 这一, 这个路径要 nginx 可访问哦
$relativePath = '';
// 获取合并后的本地缓存 js 文件
$filename = md5(implode('', $urlList)).'.js';
$filepath = "{$workPath}/{$relativePath}/{$filename}";
// 若缓存文件已经存在直接返回, 这里没有考虑 js 文件更新的情况. 若需要的话, 请自行处理
if(!file_exists($filepath)){ // 创建文件
// 提取所有文件的内容合集
$allContent = '';
foreach ($urlList as $itemUrl){
$allContent .= PHP_EOL.file_get_contents($workPath.parse_url($itemUrl, PHP_URL_PATH));
}
$dirname = dirname($filepath);
if(!is_dir($dirname)) mkdir($dirname, 0777, true);
file_put_contents($filepath, $allContent);
}
return "http://xxx.com/{$relativePath}/{$filename}";
}
如此一来, 就可以将页面中的 js 文件数量降低为2个了. 看一下效果, 效果还是十分显著的, js 数量减少了12个(多出来的是由 js 文件引入的 js), 时间减少了差不多0.9s
合并 css
既然 js 文件可以合并, 那么自然, css 也能够合并. 合并的代码拿上面的改改就出来了. 这里直接上效果
在合并之前, css 文件9个. 合并后为2个(因为其中一个使用了相对路径), 时间减少了差不多0.1s 左右. 这里是因为 css 文件数量不多, 且文件本身较小, 所以效果没有 js 那么明显.
js 和 css 都进行了合并, 难道就完了么? 不. 我们还可以对其进行压缩.
压缩 js/css
找到了这个库: mrclay/minify . 直接composer require mrclay/minify
引进来就行.
使用如下方法在将内容写入到缓存文件时, 对其进行压缩:
<?php
// 压缩 js 内容
$allContent = JSMin::minify($allContent)
// 压缩 css 内容
$allContent = Minify_CSSmin::minify($allContent)
这里, 因为加载的 js 和 css 本身大部分就已经是压缩过的了, 因此效果并不是那么明显. 时间仅减少了0.1s. 若是 资源文件本身没有压缩, 效果会更明显.
最终
最终效果, 页面的加载时间从原本的2.5s, 降低到了1.4s. 当然, 不同的网络环境, 时间也会不同, 但这也减少了差不多45%的加载时间, 而这, 还仅仅是通过合并资源文件来实现的. 想象平常我们究竟为了这玩意浪费了多少用户的时间啊.