统一风格
文章的封面图我仔细研究之后终于找到了我自己觉得很舒服的风格!
整体呈现低对比度,饱和度,低亮度的蓝色,选图方面也相比以前更加讲究,每张图片都要丢进 ps 里面调个色,毕竟一个好的封面图决定了整个博客的颜值。
静态资源优化
这里我把静态资源分为四类
- 字体文件
- JS代码
- 封面图片
- 文章内部图片
这其中,请求次数最多的便是字体文件,因为在访问博客的过程必不可少的就是文字,其次是封面图,每个文章都必不可少的元素之一。这两样一直是请求次数最多的文件,那么对于他们的优化务必要十分重视!而JS代码和文章内部图片则是小体积文件,基本上也就是洒洒水的程度,可以忽略不计。
根据我的统计结果来看,字体文件所占用的流量是十分惊人的,其次是一些体积较大的高清封面图。因为一个访客不管阅不阅读文章,都要去请求封面图片资源和字体资源,由此集腋成裘,最后导致流量都浪费在这上面了
其次还有一个重要的原因,如果我们封面图是采用的外链形式的图片,那么在进入归档页面时会瞬间产生大量get请求导致页面卡顿(如果有css动画的话),这里的缩略图都是高清图片,非常占用网络资源。
而如果使用本地图片的话,hugo在生成网站时会根据展示的图片大小生成不同分辨率的缩略图以应对不同场合的需要,灵活利用资源,算是空间换时间。而生成的缩略图一般也只有几十k,不会太占硬盘。
那么思路有了,接下来的就是细节。
图片处理
肯定不能直接放原图上去,我的封面图基本上都是高分辨率图片,有的一个就是10MB左右,写十篇文章就是100MB了,这样肯定是不行的,所以我们要对原图进行压缩,最好是无损的,防止影响画质。
这里推荐两个我自用的压缩图片的网站,都是无广且清爽干净的网站。
根据测试5MB的图片可以压缩到1MB以下,并且画质都保持住了,效率还是很不错的。
这里我一次压缩了9张图片,单张图片在几十到几百k之间
💡这里说一下,不要直接压缩成Webp格式,因为只用使用本地Webp图片的话颜色会变得很奇怪,不知道是不是BUG🥲
字体处理
字体最好的处理办法就是换一个cdn,要求既免费,又迅速,且稳定。
那么可能有这样的cdn存在吗?还真有…那就是国内的饿了么cdn!(饿了么居然还有这种东西,也是挺离谱的😇但是确实很好用)
详细教程可以参考 🔗使用 npm 加速静态资源
这里说一些原文中没提到,网上大多答的乱七八糟的,但是非常重要的注意事项
- 换源的操作
npm config set registry https://registry.npmjs.org
- 查看是否登录成功,登录成功会输出用户名
npm whoami
- 包的名字不能与其他人重复!这一点很重要,我之前就是因为这个原因一直报403无权限。我建议就是用
user-packagename
的方式进行命名。user
是用户名,packagename
是包的名字。因为用户名肯定是唯一的,这样就避免了撞车(除非你的用户名是常用词) - 更新包的时候要修改版本号,要比之前的版本号高,不然也会报403.
- 一键更新并发布(可忽略第四步)
npm version patch && npm publish
其他版本管理方案:
# 1.0.1 表示小的bug修复
npm version patch
# 1.1.0 表示新增一些小功能
npm version minor
# 2.0.0 表示大的版本或大升级
npm version mmajor
# 1.1.0-0 后面多了个0,表示预发布
npm version preminor
💡提示
npm的仓库目录最好和你使用JSDelivr进行加速的仓库目录结构一样,这样方便后面可以进行资源的无缝迁移
完成上面的流程之后我们就可以愉快的白嫖饿了么cdn来加速我们的字体文件了~
代码块样式
Hugo自带的语法高亮是采用chroma进行渲染的,但是基本上好多亮了跟没亮一样,而且选取的monokai主题配色整体饱和度过高,颜色太鲜艳,和我风格整体不搭,所以我决定换成hightlight.js来进行渲染,配色则采用非常经典的 one dark 主题
语法高亮
8.25改进:将hightlight.js集成到hugo中,在生成网站时自动渲染代码块,而不是在浏览器中渲染,这样可以减少浏览器的压力,提高网站的加载速度
在 layouts/partials/head/custom.html
最后加入以下以下代码引入hightlight.js,atom-one-dark.min.css
就是主题名字,更多主题在Github仓库中可以找到
{{ if eq .Site.Params.syntaxHighlighter "highlight.js" }}
<link rel="stylesheet" href="https://npm.elemecdn.com/@highlightjs/cdn-assets@11.7.0/styles/atom-one-dark.min.css">
<script src="https://npm.elemecdn.com/@highlightjs/cdn-assets@11.7.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script>document.addEventListener('DOMContentLoaded', (event) => {document.querySelectorAll('pre code').forEach((el) => {hljs.highlightElement(el);});});</script>
{{ end }}
在 config.yaml
中的 params
字段中加入以下参数
syntaxHighlighter: "highlight.js"
由于hightlight.js渲染完会多出一圈空白,这里要在custom.scss
中进行调整
pre code.hljs {
display: block;
overflow-x: auto;
padding: 0 !important;
}
代码块样式
加入我最爱的 mac 红绿灯图标!可算是找到相关的文章了,加上之后可以丰富代码块配色,不至于纯黑色的显得有些死板
在custom.scss
中加入:
.article-content {
.highlight:before {
content: '';
display: block;
background: url(/code-header.svg);
height: 32px;
width: 100%;
background-size: 57px;
background-repeat: no-repeat;
margin-bottom: 5px;
background-position: -1px 2px;
}
}
在 static
文件夹下新建code-header.svg
,写入以下代码:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" width="450px" height="130px">
<ellipse cx="65" cy="65" rx="50" ry="52" stroke="rgb(220,60,54)" stroke-width="2" fill="rgb(237,108,96)"/>
<ellipse cx="225" cy="65" rx="50" ry="52" stroke="rgb(218,151,33)" stroke-width="2" fill="rgb(247,193,81)"/>
<ellipse cx="385" cy="65" rx="50" ry="52" stroke="rgb(27,161,37)" stroke-width="2" fill="rgb(100,200,86)"/>
</svg>
语言和复制按钮
原本的复制按钮要指针移动到代码块上才出现的,把它固定在右上角,并显示代码的语言种类。
在 themes/assets/ts/main.ts
中,第66行开始,改成以下代码
const highlights = document.querySelectorAll(".article-content div.highlight");
const copyText = `📄拷贝`,
copiedText = `已拷贝!`;
highlights.forEach((highlight) => {
const copyButton = document.createElement("button");
copyButton.innerHTML = copyText;
copyButton.classList.add("copyCodeButton");
highlight.appendChild(copyButton);
const codeBlock = highlight.querySelector("code[data-lang]");
// 获取语言
const lang = codeBlock.getAttribute("data-lang");
if (!codeBlock) return;
copyButton.addEventListener("click", () => {
navigator.clipboard
.writeText(codeBlock.textContent)
.then(() => {
copyButton.textContent = copiedText;
setTimeout(() => {
copyButton.textContent = copyText;
}, 1000);
})
.catch((err) => {
alert(err);
console.log("Something went wrong", err);
});
});
// Add language code button
const languageButton = document.createElement("button");
languageButton.innerHTML = lang.toUpperCase() + " ";
languageButton.classList.add("languageCodeButton");
highlight.appendChild(languageButton);
});
new StackColorScheme(document.getElementById("dark-mode-toggle"));
在custom.scss
中加入以下代码调整按钮位置(需要自己调整以下距离,因为每个人的大小不一样)
//代码复制按钮
.article-content .copyCodeButton {
position: absolute;
top: 10px;
right: 18px;
border-radius: 12px;
opacity: 1;
color: #ffffffad;
background: none;
border: none;
padding: 0;
font-weight: 500;
}
.article-content .languageCodeButton {
position: absolute;
border: none;
top: 9px;
right: 69px;
border-radius: 12px;
opacity: 1;
padding: 0 5px;
background: 0;
color: #ffffffad;
font-family: lato;
font-size: 1.5rem;
}
修复卡顿
手机模式的顶部导航栏在引入自定义字体后会卡顿掉帧,安卓和windows上倒是没有这种现象,但是ios上尤为明显。
这里可以把手机系统分为两类,apple system单独调用系统字体,其他系统优先调用自定义字体,优化不同用户的体验。
layouts/partials/head/custom.html
修改成以下代码:
<style>
:root {
/* 在style中优先调用-apple-system */
--sys-font-family: -apple-system, "PingFang SC", Georgia, 'Nimbus Roman No9 L', 'Hiragino Sans GB', 'Noto Serif SC', 'Microsoft Yahei', 'WenQuanYi Micro Hei', 'ST Heiti', sans-serif;
--code-font-family: "JetBrainsMono Regular", Menlo, Monaco, Consolas, "Courier New";
--article-font-family: -apple-system, "PingFang SC", var(--base-font-family);
}
</style>
归档页面
归档页面拆成两栏,防止大段空白。在custom.scss
中加入以下代码:
/* 归档页面两栏 */
@media (min-width: 1024px) {
.article-list--compact {
display: grid;
grid-template-columns: 1fr 1fr;
background: none;
box-shadow: none;
gap: 1rem;
article {
background: var(--card-background);
border: none;
box-shadow: var(--shadow-l2);
margin-bottom: 8px;
border-radius: 16px;
}
}
}
网站加速
如果是使用的Vercel或者GitHub pages托管博客的同学可以看一下,可以以最少的成本加快网站访问速度。(大厂cdn需要已经备案的域名)
CDN
经过一天研究,对于cdn加速这一块我算是彻底理解了。
先上测试图 (Vercel的表现居然还行,是因为早上没什么人导致速度较快吗)
实际表现加速后的网站仅次于在服务器上运行的,速度甩vercel一大截,所以效果还是很不错的!
那么到底怎么理解cdn呢?举个栗子,也许不太妥当,但是差不多是那个意思
我觉得你可以把自己想象成一个商店老板,现在你的生意做大了,有别的地方的顾客要买你的产品,但是你一个人已经忙不过来了,有的地区离你又特别远,给他们发货要很长时间,这时候你就需要开分店了!
那么第一部要考虑的问题,分店叫什么名字呢?当然,这个名字你完全可以自定义,只要别的地区的顾客知道这个名字,那么他们就是根据名字找到这家分店,在里面购买商品。不过有一点规定就是,分店不能和原店的名字相同,这样就歧义了,人家怎么知道哪家是分店呢?
分店首要的事情就是进货,那么必然是去原店进货,只要知道原店在地图上的哪里,那么就能顺着找到,从而完成进货。
而当分店缺货时,要进行紧急补货,那么去哪补呢?我们已经知道了分店肯定是去原店进货,那要是我们不告诉他去哪补货他肯定默认去原店补货。假如原店有多个地址怎么办?那么我们就需要指定其中一个地址来补货
那么总结一下:
- 加速域名为用户接入的CDN域名。
- 源站地址为用户的源站,当源站地址为域名时,此域名仅用于做DNS解析。
- 当源站拥有多个站点,回源时则选择回源host指定站点拉取资源。
- 当源站不存在回源host域名时,则到源站的默认站点拉取资源。
这样就很好理解cdn的概念了。下面进行实战!
这是我们托管在vercel上的网站,已经绑定好域名了,现在我们要给他套个cdn加速
💡在此之前我们要选一个cdn厂商,尽量选择带有免费https的,我用的是免费的又拍云(不是广告🙏)
加速域名我选择解析到www.lovir.cn
这时候我们还需要确定一下源站地址和回源host。
终端使用ping命令ping以下原域名来获得ip地址
ping blog.lovir.cn
如图所示,圈起来的地方就是服务器ip地址
填进源站设置里面
回源地址就填我们的源域名blog.lovir.cn
,如此就设置完成啦~等待几分钟后刷新以下就能看到我们的网站了。
图片文件迁移
上面我们提到了饿了么cdn,那么要是我们之前使用的是JSDelivr当图床的可以一并全部迁移过去
把GitHub上用来当图床的仓库全部下载下来,上传到npm,然后使用vscode的全局替换功能,将cdn.jsdelivr.net/gh/用户名/仓库名/IMAGE
全部替换成npm.elemecdn.com/包名@latest/IMAGE
,就可以实现资源的无缝迁移了!
JS 代码迁移
原主题的许多js都是用的Unpkg或者JSDelivr的cdn来加速的,而这些cdn在国内效果不是太好,所以我们需要将他们迁移到国内的cdn上,下面举个例子,其他的js代码都是一样的操作
在layouts/partials/comments/provider/waline.html
,waline的js和css样式一直引用的是unpkg的包,这里换成别的cdn
前两行换成:
<script src='//npm.elemecdn.com/@waline/client@v2/dist/waline.js'></script>
<link href='//npm.elemecdn.com/@waline/client@v2/dist/waline.css' rel='stylesheet'/>
💡这里修改的时候千万别直接换成饿了么的cdn来加速waline,不知道为什么两个文件显示出来的效果居然是不一样的,是版本问题吗😑
如果有些js代码下载下来之后发现不起作用,可能是因为它还会引用同目录下其他js文件,然而你只下载了一个放到了本地,导致引用路径错误,就没法调用其他文件了,所以js代码不起作用
设置 nginx 缓存
在nginx配置文件中写入以下代码设置缓存
在http模块内,server模块之前设置缓存地址为/var/cache/nginx
(会自动创建文件夹),并设置名称为my_cache
,最大容量100MB,过期时间60分钟
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:100m inactive=60m;
在location
模块中追加以下代码,如果返回200状态码(请求成功),缓存过期时间60分钟,如果返回404状态码(请求失败),缓存过期时间为一分钟
proxy_cache my_cache;
proxy_cache_valid 200 60m;
proxy_cache_valid 404 1m;
设置浏览器缓存
location / {
# 使用浏览器缓存
expires 3h;
}
宣传图
最后这是一个题外话😊
这是参考《给博客做一张宣传图》做的宣传图,效果我还挺喜欢的!