问题解决:个人博客加载速度优化

前言

我的个人 hexo 博客地址:https://jingqing3948.github.io . 之前感觉启动加载特别特别慢,得 10s 左右,但是看其他人的 hexo 博客加载都比较快。然后我就总感觉不知道是我部署在 github pages 上的问题,还是图床用 github 仓库的问题,还是主题性能比较差的问题……后来发现其实是自己的配置问题,哈哈。

现在基本能在 3s 内完成加载了,虽然我不完全记得做了哪些具体优化,但是整个问题解决的思路是有的,所以也能总结很多经验,希望可以帮助到开发个人博客的朋友们!

优化思路

首先怎么判断哪些项加载太耗时了?

在自己的网站页面(部署的网站地址 https://xxx.github.io,或者自己的网站域名,或者本地部署预览 localhost:4000),打开开发者模式(F12 或 Ctrl + Shift + C)查看 Network 网络页面的具体加载时间,点两下时间按时间倒序排序:

如图,这里(我已经优化完成的结果)按时间倒序排序,最长的一个是 busuanzi 什么的一个东西,占了半秒钟多。这个其实是一个计算网站点击浏览量的插件。(我发现插件基本都会占用很多很多加载时间)

busuanzi 显示效果

这个东西我还是想保留的。而且比较难懒加载优化,我就不改了。

之前加载花费时间比较多的有:

  • twikoo 评论系统(这个我发现其实我压根没用到,我用的是 gitalk 评论系统,但是还是把 twikoo enable 了,浪费很多时间)直接 enable: false 关了。

  • gitalk 评论系统:这个加载非常费时间。所以我决定,首先只有部分页面开启,比如首页我就不开启了。我只在 post 也就是文章发布界面开启,判断方式是当前页面 layout 样式是否为 post。

    全局搜索后,找到加载 gitalk 的代码在 gitalk.ejs 中:

    1
    2
    3
    <% if (theme.gitalk.enable && post.comments && page.layout == 'post') { %>
    <div class="gitalk" id="gitalk-container"></div>
    <!-- 引入 gitalk 的代码 -->

    第二步,不是直接一句 <script src="xxx"></script> 就引入了,这样是阻塞加载。而是 DOM 创建后才加载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <script>
    window.addEventListener('DOMContentLoaded', function () {
    const gitalkScript = document.createElement('script');
    gitalkScript.src = 'https://cdn.staticfile.org/gitalk/1.7.2/gitalk.min.js';
    gitalkScript.onload = function () {
    const md5Script = document.createElement('script');
    md5Script.src = 'https://cdn.staticfile.org/blueimp-md5/2.19.0/js/md5.min.js';
    md5Script.onload = function () {
    const gitalk = new Gitalk({ ... });
    gitalk.render('gitalk-container');
    };
    document.body.appendChild(md5Script);
    };
    document.body.appendChild(gitalkScript);
    });
    </script>

    也就是说,gitalk 没加载完,就可以展示页面了。不过我的文章长度一般都不会只有一页,所以第一眼就出现底部 gitalk 评论模块的页面没有。这样优化完全没问题。

  • mermaid 和 mathjax:一样,尝试判断页面中是否有这两个元素才加载。下面是 mermaid 部分加载且懒加载的机制,部分加载是判断渲染后有无 pre class = “mermaid” 的类(这个对应元素名可以去开发者模式看 html 源码找到):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    <% if (page.content && page.content.includes('<pre class="mermaid"')) { %>
    <script>
    // 动态加载 Mermaid 脚本并初始化
    function loadMermaid(callback) {
    const script = document.createElement('script');
    script.src = '<%= theme.mermaid.cdn %>';
    script.defer = true; // 非阻塞加载
    script.onload = callback;
    document.body.appendChild(script);
    }

    // Mermaid 渲染逻辑
    const rerenderMermaid = () => {
    const isDark = document.body.classList.contains('darkmode');
    const theme = isDark ? 'dark' : 'default';

    if (!window.mermaid) return;

    mermaid.initialize({
    startOnLoad: false,
    theme,
    themeVariables: {
    background: 'transparent',
    primaryColor: isDark ? '#1e1e1e' : '#ffffff',
    primaryTextColor: isDark ? '#d6e4f0' : '#1e1e1e',
    noteBkgColor: isDark ? '#2a3b4d' : '#f4f4f4',
    noteTextColor: isDark ? '#cddbf5' : '#333',
    lineColor: isDark ? '#ffffff' : '#333333',
    textColor: isDark ? '#ffffff' : '#000000'
    }
    });

    document.querySelectorAll('.mermaid').forEach((el) => {
    if (!el.dataset.code) el.dataset.code = el.textContent;
    el.removeAttribute('data-processed');
    el.innerHTML = el.dataset.code;
    });

    mermaid.init(undefined, document.querySelectorAll('.mermaid'));
    };

    // 页面加载后执行懒加载
    document.addEventListener('DOMContentLoaded', () => {
    loadMermaid(rerenderMermaid);
    });

    // 主题切换时重新渲染(注意也需确保 Mermaid 已加载)
    document.getElementById('todark')?.addEventListener('click', () => {
    setTimeout(() => {
    if (window.mermaid && document.querySelector('.mermaid')) {
    rerenderMermaid();
    }
    }, 200);
    });
    </script>
    <% } %>

    而且我还可以在其中自行修改样式!我的主页有一个切换浅色深色模式按钮,flag 变量是 darkmode,我可以用这个去改具体的 mermaid 的颜色。

  • 公式的判别比较难,GPT 给我一种思路是说判断有无形如 $$ ... $$ 的公式块。但是我的行内公式是单美元符号包裹,而且我不确定有没有文章是只有行内公式没有公式块的,所以这个选择性渲染 mathjax 公式思路我不敢用,我也没法用懒加载(懒加载是判断有公式才加载,我不确定如何判断)。我做的改进还是只有文章页渲染公式+公式延迟一秒非阻塞渲染。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <% if (page.layout === 'post') { %> 
    <script>
    setTimeout(function () {
    window.MathJax = {
    tex2jax: {
    inlineMath: [['$', '$'], ['\\(', '\\)']],
    processEscapes: true,
    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
    },
    showProcessingMessages: false,
    messageStyle: 'none',
    skipStartupTypeset: false
    };
    var script = document.createElement('script');
    script.src = "https://cdn.staticfile.org/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML";
    script.async = true;
    document.head.appendChild(script);
    }, 1000); // 延迟 1 秒加载
    </script>
    <% } %>

    这里的懒加载和公式判别,如果文章顶端有公式或者流程图,可以看到一瞬间的未渲染源代码,过 1s 后才渲染成流程图和公式形式。不过我觉得问题不大,比阻塞整个页面加载不出来好。

  • 图片懒加载:我在 main.js 里加入了全局图片懒加载机制:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    document.addEventListener('DOMContentLoaded', () => {
    const imgs = document.querySelectorAll('img');

    imgs.forEach((img) => {
    // 若已是 lazyload 或外链/emoji/logo 则跳过
    const src = img.getAttribute('src') || '';
    const isAlreadyLazy = img.classList.contains('lazyload');
    const isCDN = src.startsWith('http') || src.startsWith('//');
    const isSVG = src.endsWith('.svg');
    const isLogo = img.closest('header') || img.id === 'logo';

    if (!isAlreadyLazy && !isCDN && !isSVG && !isLogo) {
    img.setAttribute('data-src', src);
    img.removeAttribute('src');
    img.classList.add('lazyload');
    }
    });
    });

    但是!然后引发了一些问题。我的首页封面是一个轮播图,每次要随机刷新出一个封面。然后懒加载机制会将这个图片的 src 移除,导致首页图片随机刷新出来后突然又消失了。

    于是我决定放弃图片懒加载,然后我发现我有一个摄影界面,这个是必须要用懒加载的。Gallery | 灰海宽松的博客 所以也不能全局关掉。

    所以,只能局部开启了,至少首页取消图片懒加载机制。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    document.addEventListener('DOMContentLoaded', () => {
    const path = window.location.pathname;
    const isHomePage = path === '/' || path === '/index.html';

    if (isHomePage) return; // 首页不懒加载

    const imgs = document.querySelectorAll('img');

    // ...
    });
  • 以及,红色的部分是报错,可能也会耗费大量时间。我有一个定义但是没创建的 css 文件,加载时也花了一定时间去查找但是没找到。删掉引用就好了。

期待看到大家优化的个人博客!

Contact Me
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2022-2025 Jingqing3948
  • Visitors: | Views:

星光不问,梦终有回

LinkedIn
公众号