前言
我的个人 hexo 博客地址:https://jingqing3948.github.io . 之前感觉启动加载特别特别慢,得10s左右,但是看其他人的 hexo 博客加载都比较快。然后我就总感觉不知道是我部署在 github pages 上的问题,还是图床用 github 仓库的问题,还是主题性能比较差的问题……后来发现其实是自己的配置问题,哈哈。
现在基本能在 3s 内完成加载了,虽然我不完全记得做了哪些具体优化,但是整个问题解决的思路是有的,所以也能总结很多经验,希望可以帮助到开发个人博客的朋友们!
优化思路
首先怎么判断哪些项加载太耗时了?
在自己的网站页面(部署的网站地址 https://xxx.github.io
,或者自己的网站域名,或者本地部署预览 localhost:4000
),打开开发者模式(F12 或 Ctrl + Shift + C)查看 Network 网络页面的具体加载时间,点两下时间按时间倒序排序:
如图,这里(我已经优化完成的结果)按时间倒序排序,最长的一个是 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 文件,加载时也花了一定时间去查找但是没找到。删掉引用就好了。
期待看到大家优化的个人博客!