加载中...

加载中...

自定义

本页讲解如何给博客加自己的东西:自定义 JS/CSS、注入点机制、自定义 tag 插件,以及主题配置的覆盖方式。改代码属于"开发级"操作,动手前先读「安装与主题配置」篇了解目录结构,改完按「部署」篇选择发布方式。

核心原则:能不改主题源码就不改。优先用 inject_point 注入和配置覆盖,主题升级(子模块 themes/matery/)时才不会被冲突拖累。

1. 自定义 JS/CSS

用途:给全站或单页追加脚本与样式,最常见的一类定制。

方式一:放 source/ 下,页面里引用(无需改主题源码):

<!-- 文章/页面正文里直接引用 -->
<link rel="stylesheet" href="/js/my-custom.css">
<script src="/js/my-custom.js" defer></script>
  • 文件放 source/js/source/css/ 等任意目录,构建时原样复制到 public/ 对应路径
  • 注意 _config.ymlskip_render 段:source/ 下的部分目录(如 nav/docs/live2d/)会跳过渲染,自定义 HTML 放这些目录时保持原样;普通 .js/.css 不受影响
  • 生产环境走 CDN 时,静态资源 URL 由主题配置 cdn 段决定(见「全局配置」篇),自定义文件直接 /js/xxx.js 引用即可,走站点自身域名

方式二:注入点全局注入(推荐,见下节)——脚本/样式会在所有页面生效,且不污染主题文件。

2. inject_point 注入机制

用途:在不修改主题模板的前提下,向页面的固定位置插入 HTML/JS/CSS。主题从 NexT 继承了一套注入系统,layout/layout.ejs 中实际使用了 3 个视图注入点:

<!-- layout.ejs -->
<%- inject_point('bodyBegin') %>   <!-- <body> 开头 -->
<%- inject_point('header') %>      <!-- 页头位置 -->
<%- inject_point('bodyEnd') %>     <!-- </body> 之前 -->

怎么用:注入内容不是从主题配置加载的 HTML 字符串,而是由 hexo 脚本通过 theme_inject filter 注册到运行时注册表——scripts/events/lib/injects.js 在生成前执行 hexo.execFilterSync('theme_inject', injects),把结果写入 theme.config.injectsinject_point helper(scripts/helpers/engine.js)渲染时读取该注册表并输出。

以主题真实写法(scripts/filters/default-injects.js)为模板,自定义注入放在主题 scripts/ 下任意新文件(如 scripts/filters/my-injects.js):

// themes/matery/scripts/filters/my-injects.js
'use strict';

const path = require('path');

hexo.extend.filter.register('theme_inject', function(injects) {
  // file(name, 文件路径):注册模板文件(name 无扩展名时取文件扩展名,路径相对 hexo 根目录)
  injects.bodyEnd.file('my-custom', path.join(hexo.theme_dir, 'layout/_partial/my-custom.ejs'));
  // raw(name, 内容字符串):直接注册 HTML/JS 内容
  injects.bodyEnd.raw('my-analytics', '<script src="/js/my-custom.js" defer></script>');
}, -99); // -99 = 最先执行,与 default-injects.js 保持一致

模板文件 layout/_partial/my-custom.ejs 的内容会被原样渲染进 <%- inject_point('bodyEnd') %> 所在位置。injects.<point>.file(name, path, locals, options, order) 第三个参数起依次为 locals/options/order,order 控制同一点多个注入的排序。

  • bodyEnd 是最常用的注入点:追加全局脚本、统计代码、悬浮组件
  • 主题定义了 12 个视图注入点headEnd/header/bodyBegin/bodyEnd/footer/postMetaTop/postMarkdownBegin/postMarkdownEnd/postCopyright/postComments/pageComments/linksComments),12 个点均已在模板中调用headEnd(head.ejs)、footer(footer.ejs)、postMetaTop/postMarkdownBegin/postMarkdownEnd/postCopyright/postComments(post-detail.ejs)、bodyBegin/header/bodyEnd(layout.ejs)、pageComments(bb/contact/msg)、linksComments(friends)。未在模板中调用的仅 postMetaBottom/postLeft/postRight(无注入时返回空字符串,评论页用 inject_point('pageComments') || partial('_partial/comments') 做回退)
  • 样式注入点variable/mixin/style)是另一套机制:注册的是 .styl 文件路径,由 Stylus 编译期注入,与视图注入点不同,不要混用
  • 注入内容原样输出,写 <script> 时建议带 defer(见「性能优化」篇 LCP 分层原则)

3. 自定义 tag 插件

用途:在 Markdown 里用短代码生成复杂 HTML。主题内置 12 个 tag 插件(note/tabs/timeline/mermaid/pdf/github-card 等,用法见「Tag 插件」篇),不够用时可自己写。

实现:在 themes/matery/scripts/tags/ 下新增文件,导出一个 Hexo tag 函数:

// themes/matery/scripts/tags/mybox.js
'use strict';

function mybox(args, content) {
  const title = args.join(' ') || '提示';
  return `<div class="my-box"><strong>${title}</strong><div class="my-box-body">${hexo.render.renderSync({ text: content, engine: 'markdown' })}</div></div>`;
}

hexo.extend.tag.register('mybox', mybox, { ends: true });

文章中使用:

{% mybox 注意事项 %}
这里写**任意 Markdown**,会被渲染为卡片内容。
{% endmybox %}
  • { ends: true } 表示需要 {% endmybox %} 闭合标签
  • content 默认是原始文本,需要渲染 Markdown 时用 hexo.render.renderSync(注意这是一个 Hexo 内部 API,仅在构建时执行)
  • 文件命名即插件名(mybox.js{% mybox %}),改完必须 hexo clean && hexo generate(见「常见问题」篇缓存不生效)
  • 想全局可用且不动主题源码:把文件放进主题 scripts/ 是唯一方式(主题是子模块,改动需双仓库提交,见「部署」篇)

4. 主题配置覆盖(模板注入机制)

用途:主题配置在 CI 构建时被动态生成——userConfig/_config.tmp.yml 是模板,构建时复制为 themes/matery/_config.yml 并替换占位符。改主题配置必须改模板,直接改 themes/matery/_config.yml 会被覆盖。

模板占位符tools/cicd.sh 用 sed 替换):

占位符替换为示例
{cdnPathVersion}jsDelivr 带版本路径https://cdn.jsdelivr.net/gh/appotry/hexo@1.1
{cdnPathLatest}latest CDN 路径https://cdn.jsdelivr.net/gh/appotry/hexo@latest
{urlVersion}文件版本号?v=12.19
{cdnUrl} / {mediaUrl} / {resUrl}各 CDN 域名https://cfblog.17lai.site

自定义配置项的完整链路(以加一个"我的开关"为例):

  1. 模板 userConfig/_config.tmp.yml 加键:
    myFeature:
      enable: true
      text: 你好
  2. Stylus 里读themes/matery/source/css/ 下):
    $my-feature-color = theme-config("myFeature.text", "默认值")
  3. 模板里读layout/ 下 EJS):
    <% if (theme.myFeature.enable) { %> <div><%= theme.myFeature.text %></div> <% } %>
  4. 发布:改配置后 tools/cicd.sh -r "特性" 递增版本号(配置属于代码侧改动,见「部署」篇发布流程选择)

注意userConfig/_config.tmp.yml 同时控制 CI 开关——all_minifier 压缩开关、图片 URL 处理(imgurl.sh --weserv2githubpage)、PWA Service Worker 版本号(userConfig/sw.tmp.js 生成 source/sw.js)都在构建流水线里处理,改模板前先 grep 确认键名没有被构建脚本占用。

附:自定义速查表

需求方式文件位置
单页脚本/样式Markdown 里 <link>/<script> 引用source/js/source/css/
全站脚本/样式inject_point 注入themes/matery/scripts/filters/
新短代码tag 插件themes/matery/scripts/tags/
主题配置项模板加键 + Stylus/EJS 读取userConfig/_config.tmp.yml
全局 HTML 片段注入点或 _partial/ 模板配置或 themes/matery/layout/_partial/
评论
数据加载中 ...