碎碎念:最近我的博客打算挪窝了,之前我一直都是使用语雀文档直接生成博客的,严格来说我觉得那其实也不算博客,更多的是像一种在线笔记;很早之前我一直听人说要自己搭建博客但一直没有行动,原因主要有两个吧,一个是觉得我自己产出不了啥高质量的技术博客,第二就是没想好要用什么技术栈来搭建自己的博客,使用自己熟悉的 「Java」 配合 「Vue」 呢?还是尝试尝试 「React」 + 「Golang」 或者 「Nodejs」,应该搭建成什么样式的是华丽的,还是技术新颖的呢?总之就这样一直在踌躇和胡思乱想中,拖拖拉拉了好久好久。

直到前几个月因为个人身上发生了一点事情,让我总想在这个世界记录下点什么,并且还在看了oldj大佬的博客之后(这位大佬其实就是SwitchHosts的作者,他的博客也有时常分享些很有趣的东西,大家有空可以看一下),让我发现了其实一个博客最重要的是他的内容,这些内容不一定必须技术高深,也可以只是简单的分享,页面也不需要过于华丽。此后我尝试了使用 「博客园自定义」 和 「Hexo」 搭建但始终感觉不太满意 (Hexo 其实还可以功能也很强,但是他的语法对我来说太蛋疼了实在无法适应,其实还有一个选择时 VuePress,但是我Vue有点写腻了想尝试点别的,后续可能会试试用 VuePress 搭建个本人的笔记整合网站吧),直到上个月发现了这个 Astro,试用了一下发现它可以满足我现阶段的需求就选择使用它来搭建我的博客了。

整个搭建过程其实我都是参考官方的教程,以及还参考了 astro-paper 这个开源项目。当然我也想分享分享一些我再搭建时遇到的问题以及其解决方案。

目前还只是暂时是先预定使用 Astro 作为常用博客网站了,不过后续如果它不能满足我了,可能还是我又会再重新捣鼓别的。

黑夜模式

黑夜模式我只直接参考官方教程直接魔改的,代码其实没什么好说的,可能还有优化空间;不过有一个需要注意的主题切换的代码最好是放在布局代码中的 <head> 标签内,这样加载起来会比较快点,避免在切换页面时出现白屏 (但是还是不能完全避免,尤其是网速比较慢的时候,我部署是在 github action 上的,PC浏览器上直接看是没啥问题的,但试了一下手机端有时还是会白屏)

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;
        }
    }
}

(这些代码之前使用js的, 简单转换为ts的了)

组件依旧是使用 React 写的,虽然可以完美运行的,但是总感觉性能挺一般的(个人写React少,也不知道怎么写才能完全发挥出性能),不过目前够用,所以就先放着吧,后面可能会再优化优化。

中间还把 一个 Astro 组件重构了,因为 React 不能使用 Astro 组件

近期文章
title: setTimeout 原理
time: 2024-08-12
tag: #前端#JavaScript
title: V8 引擎的编译器和解释器
time: 2024-06-05
tag: #前端#JavaScript#V8引擎