代码、游戏、以及有趣的故事
2024-07-06
碎碎念:最近我的博客打算挪窝了,之前我一直都是使用语雀文档直接生成博客的,严格来说我觉得那其实也不算博客,更多的是像一种在线笔记;很早之前我一直听人说要自己搭建博客但一直没有行动,原因主要有两个吧,一个是觉得我自己产出不了啥高质量的技术博客,第二就是没想好要用什么技术栈来搭建自己的博客,使用自己熟悉的 「Java」 配合 「Vue」 呢?还是尝试尝试 「React」 + 「Golang」 或者 「Nodejs」,应该搭建成什么样式的是华丽的,还是技术新颖的呢?总之就这样一直在踌躇和胡思乱想中,拖拖拉拉了好久好久。
直到前几个月因为个人身上发生了一点事情,让我总想在这个世界记录下点什么,并且还在看了oldj大佬的博客之后
整个搭建过程其实我都是参考官方的教程,以及还参考了 astro-paper 这个开源项目。当然我也想分享分享一些我再搭建时遇到的问题以及其解决方案。
目前还只是暂时是先预定使用 Astro 作为常用博客网站了,不过后续如果它不能满足我了,可能还是我又会再重新捣鼓别的。
黑夜模式我只直接参考官方教程直接魔改的,代码其实没什么好说的,可能还有优化空间;不过有一个需要注意的主题切换的代码最好是放在布局代码中的 <head> 标签内,这样加载起来会比较快点,避免在切换页面时出现白屏
const primaryColorScheme = "" // 预设主题色
// 获取本地存储的 主题色
const currentTheme = localStorage.getItem("__cafe-blog_theme")
/**
* @description 获取用户设备的偏好主题色, 优先获取本地存储中的主题色,
* 其次获取预设的主题色, 最后获取系统的偏好主题色.
* @returns { String } 返回主题色
* */
function getPreferTheme() {
if (currentTheme) return currentTheme
if (primaryColorScheme) return primaryColorScheme
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
}
// 获取用户设备的偏好主题
let themeValue = getPreferTheme()
// 设置偏好
function setPreference() {
localStorage.setItem("__cafe-blog_theme", themeValue)
reflectPreference()
}
/**
* @description 将设置的偏好主题色响应到页面样式和结构中
* */
function reflectPreference() {
document.firstElementChild.setAttribute("data-theme", themeValue)
document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue)
if (themeValue === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
// 设置 meta 值
const body = document.body
if (body) {
const computedStyles = window.getComputedStyle(body)
const bgColor = computedStyles.backgroundColor
document.querySelector("meta[name='theme-color']")
?.setAttribute("content", bgColor)
}
}
reflectPreference()
// 设置主题切换按钮
window.onload = () => {
function setThemeFeature() {
// 设置初始值,以便屏幕阅读器能够获取最新的值
reflectPreference();
document.querySelector("#theme-btn")?.addEventListener("click", () => {
themeValue = themeValue === "light" ? "dark" : "light"
setPreference()
})
}
setThemeFeature()
// "astro:after-swap" 事件用于新页面替换旧页面时使用
document.addEventListener("astro:after-swap", setThemeFeature)
}
// 同步系统更改
window.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", ({ matches: isDark }) => {
themeValue = isDark ? "dark" : "light"
setPreference()
}
)
还有一个是关于主题图标切换的,原先的想法是用两张图片切换就可以了,但实际操作下来发现并不是很流畅,后来看B站上有视频说可以使用纯CSS去画出来,我觉得太麻烦了很花时间,且如果不想照搬自己实现效果可能也不太好;后来有看到有人是用AI(不是AIGC)画两套SVG图去进行切换,可惜我不会设计也不会AI;
但这个思路确实启发我了,我开始找有没有一种组件or插件可以「自由的切换两个SVG图,并且还有一定的过渡效果」,我找到了一个叫 svg-morpheus 的插件,可以完美达成我需要的效果,不过这个插件已经七八年没人维护更新了,不支持模块化,也不支持 NPM 依赖管理,索性我就自己拿源码简单改成 ES6 模块的了,这才能正常使用。关于他的教程可以看一下张鑫旭大佬的这篇博客:SVG-Morpheus实现SVG图标图形间的补形动画
评论系统我是参考这个文章:Astro 搭建博客系列:添加 giscus 评论系统的,使用的 Github 的 discussion 和 giscus;不过我并没有使用 Astro 原生的组件构建,我是借助 React 组件实现的,React 组件在 Astro 里还挺好的,虽然不能完全发挥性能,但是也搞好够用,而且 Astro 很多语法也是用 tsx 的所以组件转换起来挺简单的。
代码我就不展示了吧,反正就是一些配置相关的东西,唯独要注意的就是 giscus 的主题色转换需要配合上面主题转换,不然还蛮突兀的;注意,Giscus 是通过 iframe 引入的所以还需要自行处理主题切换同步问题,否则页面主题切换时 Giscus 是不会主动响应你的页面的,使用 iframe 的 postMessage API就可以简单做到响应了。
分页这块我是自己写的,从一开始的时候我就是打算允许使用 url 的 query 参数来访问不同分页,但是发现 Astro 其实并不推荐使用 query 参数(Astro 认为使用 query 参数是服务器级别的操作,Astro作为静态页面不应该使用 query 参数,如果要使用那就只能使用第三方框架组件了),之前有写过一些 Url 参数的工具代码,这里可以直接复用;
// 获取 url 中的指定参数
function getQueryString(name: string) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
var reg_rewrite = new RegExp('(^|/)' + name + '/([^/]*)(/|$)', 'i')
var r = window.location.search.substr(1).match(reg)
var q = window.location.pathname.substr(1).match(reg_rewrite)
if (r != null) {
return unescape(r[2])
} else if (q != null) {
return unescape(q[2])
} else {
return ''
}
}
// 修改 url 参数,返回一个全新 URL
function changeURLArg(url:string, arg: any, arg_val:any) {
var pattern = arg + '=([^&]*)';
var replaceText = arg + '=' + arg_val;
if (url.match(pattern)) {
var tmp = '/(' + arg + '=)([^&]*)/gi';
tmp = url.replace(eval(tmp), replaceText);
return tmp;
} else {
if (url.match('[\?]')) {
return url + '&' + replaceText;
} else {
return url + '?' + replaceText;
}
}
}
组件依旧是使用 React 写的,虽然可以完美运行的,但是总感觉性能挺一般的(个人写React少,也不知道怎么写才能完全发挥出性能),不过目前够用,所以就先放着吧,后面可能会再优化优化。
中间还把 一个 Astro 组件重构了,因为 React 不能使用 Astro 组件