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

前言

我的个人 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 中:

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

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

    <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 源码找到):

    <% 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 公式思路我不敢用,我也没法用懒加载(懒加载是判断有公式才加载,我不确定如何判断)。我做的改进还是只有文章页渲染公式+公式延迟一秒非阻塞渲染。

    <% 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 里加入了全局图片懒加载机制:

    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 | 灰海宽松的博客 所以也不能全局关掉。

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

    document.addEventListener('DOMContentLoaded', () => {
        const path = window.location.pathname;
        const isHomePage = path === '/' || path === '/index.html';
    
        if (isHomePage) return; // 首页不懒加载
    
        const imgs = document.querySelectorAll('img');
    
        // ...
    });
    
  • 以及,红色的部分是报错,可能也会耗费大量时间。我有一个定义但是没创建的 css 文件,加载时也花了一定时间去查找但是没找到。删掉引用就好了。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰海宽松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值