Hexo 主题开发之自定义模板

这篇具有很好参考价值的文章主要介绍了Hexo 主题开发之自定义模板。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关于 Hexo 如何开发主题包的教程已经是大把的存在了,这里就不再赘述了。这篇文章主要讲的是作为一个主题的开发者,如何让你的主题具有更好的扩展性,在用户自定义修改主题后,能够更加平易升级主题。

问题所在

Hexo 提供两种方式安装主题包:

  • 直接在 themes 目录下直接存放主题包文件,这种方式用户可以很方便的魔改主题,但是魔改后升级主题会变得比较困难
  • 通过 npm 安装主题包,这种方式更加方便用户升级主题,但是不易扩展现有的主题。

当用户想要自定义修改主题时,基本上只能通过第一种方式安装,且只能通过修改 源代码 形式去修改主题。这样带来的问题就是,当主题修复一些 bug 或者主题迭代 N 个版本后,用户想升级主题时就会变的比较麻烦。

有没有能让用户方便升级,又能提供一定个性化的能力的东西呢?答案是有的,那就是通过 npm 方式分发主题包,我们通过一些魔法,让其有一定的扩展能力,这篇文章就来讲解如何实现它。

模板

在 Hexo 中,主题的模板决定的网站页面程序的方式,当你不同页面结构很相似时候,可以通过布局(Layout)去复用相同的结构,而相似的部分可以抽离成通用局部模板,通过使用 Partial 去加载,以达到模板复用的效果。

这就是 Hexo 在开发主题处理模板复用的方式,可把一个个局部模板理解为一个个独立的组件,哪里需要是就在哪里加载它。如果说用户想替换某一个局部模板,我们可以让用户提供一个新的模板,然后去加载用户提供的模板,那是不是达到在用户不修改源代码情况下对主题进行个性话的扩展呢。

Partial

要想知道 Hexo 是如果加载局部模板的,翻看下 Hexo 源码里 Partial 的实现(/plugins/helper/partial.js),可以看到是通过调用 ctx.theme 获取到对应的 view,接着调用 render 渲染的。

const { dirname, join } = require("path");

module.exports = (ctx) =>
    function partial(name, locals, options = {}) {
        const viewDir = this.view_dir;
        const currentView = this.filename.substring(viewDir.length);
        const path = join(dirname(currentView), name); // 根据当前路径找到,局部模板路径
        const view = ctx.theme.getView(path) || ctx.theme.getView(name); // 根据路径去匹配 view
        const viewLocals = { layout: false };
        // Partial don't need layout
        viewLocals.layout = false;
        return view.renderSync(viewLocals);
    };

Hexo 对文件处理分为两种,一种是 source 目录文件处理,一种是对主题包里文件处理。在辅助函数注册里可以看 ctx 其实就是 hexo 运行时的实例,上面的 ctx.theme 就是主题文件处理的 Box。通过 Hexo 提供 api 可以看到,它不仅提供了 getView,还提供了 setViewremoveView 方法。

然后翻看 setView 代码,可以看到当你重新设置一个新的 view 时,它会覆盖掉已有的 view。也就是说我们可以直接覆盖主题里的 局部模板


  setView(path, data) {
    const ext = extname(path);
    const name = path.substring(0, path.length - ext.length);
    this.views[name] = this.views[name] || {};
    const views = this.views[name];

    views[ext] = new this.View(path, data);
  }

修改示例

以覆盖 hexo-theme-async 为示例,在生成前钩子 generateBefore 里,覆盖掉主题里默认的侧栏模板。

hexo.on("generateBefore", () => {
    hexo.theme.setView("_partial/sidebar/index.ejs", "<div>111</div>");
});

运行起来会发现侧栏模板已经替换成我们写的 111 了。

主题实现

通过上面方式确实可以达到覆盖主题默认模板能力,但是让用户直接修改会很不友好,需要自己去看主题中局部模板的路径信息,并且还需要自己编写加载文件内容,覆盖主题默认模板逻辑。

可以将这部分操作内置进入主题内处理,只需要让用户编写自己的模板,以及告诉我们需要替换对应模板即可。大致流程如下:

还可以提供默认配置,简化通过路径覆盖

通过在配置中配置好主题中使用的局部模板,类似这样,将主题中使用的局部模板以配置形式展示。

layout:
    path: layout
    # layout
    main: _partial/main
    header: _partial/header
    banner: _partial/banner
    sidebar: _partial/sidebar/index
    footer: _partial/footer

接着在加载局部模板时,直接读取配置的信息,当用户覆盖掉了 layout.header 时候,主题就会自动使用新的模板了。

<%- partial(theme.layout.header) %>

模板加载实现

根据上面配置,约定 layout.path 配置指向目录为用存在模板目录,以便可以自定义存放路径。

layout:
    path: layout

首先就是根据配置获取模板存在的绝对路径,可以根据 hexo 实例,获取到根目录,拼接出完整路径位置。

const { resolve } = require("path");
const layoutDir = resolve(hexo.base_dir, hexo.theme.config.layout.path);

然后是对文件目录的监听,这个可以直接使用 hexo-fs ,避免安装额外的依赖包,提供了新增、删除、修改、文件夹变动的监听,可以针对不同事件做出不同操作。

const { watch } = require("hexo-fs");

watch(layoutDir, {
    persistent: true,
    awaitWriteFinish: {
        stabilityThreshold: 200,
    },
}).then((watcher) => {
    watcher.on("add", (path) => /** 设置模板 */);
    watcher.on("change", (path) => /** 设置模板 */);
    watcher.on("unlink", (path) => /** 移除模板 */);
    watcher.on("addDir", (path) => /** 添加文件夹,递归遍历设置模板 */);
});

因为上面是通过配置去加载模板的,所有为了避免用户自定义的模板名称会与主题的模板名称冲突,导致覆盖了主题的模板,可以在使用时增加一个约定的前缀,避免重名,需要对设置模板进行简单封装。

const setView = (fullpath) => {
    const path = "async" + fullpath.replace(layoutDir, ""); // 约定固定前缀为 async
    hexo.theme.setView(path, readFileSync(fullpath, { encoding: "utf8" }));
};

上面处理方式,用户自定义模板,可以正常加载使用的,但是当自定义的模板又引入了其他模板时会存在一个问题,在有的模板引擎中会出现路径不正常。通过查看 view 实例信息,可以看到其指向目录是在 node_modules,而实际上是存在根目录的。

翻看 view 源码可以看到 source 是获取的 this._theme.base ,而 this._theme.base 往上找就 theme_dir,也就是主题存放的目录,最后又通过 renderer.compile 设置模板渲染到,导致传入 path 不正确。

知道了原因我对上面代码进行修正,设置后重新获取到 view,然后手动根据路径信息。

const setView = (fullpath) => {
    const path = "async" + fullpath.replace(layoutDir, ""); // 约定固定前缀为 async
    hexo.theme.setView(path, readFileSync(fullpath, { encoding: "utf8" }));

    const view = hexo.theme.getView(path);
    view.source = fullpath; // 修正原文件路径
    view._precompile(); // 重新调用渲染器的初始化
};

将上面操作,放置在在 Hexo 的 generateBefore 中:

const { resolve } = require("path");
const { watch, readdirSync, statSync } = require("hexo-fs");

hexo.on("generateBefore", () => {
    const layoutDir = resolve(hexo.base_dir, hexo.theme.config.layout.path);

    const setView = (fullpath) => {
        const path = "async" + fullpath.replace(layoutDir, ""); // 约定固定前缀为 async
        hexo.theme.setView(path, readFileSync(fullpath, { encoding: "utf8" }));
        const view = hexo.theme.getView(path);
        view.source = fullpath; // 修正原文件路径
        view._precompile(); // 重新调用渲染器的初始化
    };

    watch(layoutDir, {
        persistent: true,
        awaitWriteFinish: {
            stabilityThreshold: 200,
        },
    }).then((watcher) => {
        watcher.on("add", (path) => setView(path));
        watcher.on("change", (path) => setView(path));
        watcher.on("unlink", (path) => {
            const path = "async" + path.replace(layoutDir, "");
            hexo.theme.removeView(path);
        });
        watcher.on("addDir", (path) => loadDir(path));
    });

    const loadDir = (base) => {
        let dirs = readdirSync(base);
        dirs.forEach((path) => {
            const fullpath = resolve(base, path);
            const stats = statSync(fullpath);
            if (stats.isDirectory()) {
                loadDir(fullpath);
            } else if (stats.isFile()) {
                setView(fullpath);
            }
        });
    };

    loadDir(layoutDir);
});

到此主要功能以及实现了,其他待优化项这里就不描述了,可以看看完整实现源码。

使用示例

以为 hexo-theme-async 为例,在根目录新建 layout 目录,再目录下添加 sidebar.ejs 文件,结构如下:

┌── blog
│   └── layout
│          └── sidebar.ejs
│   └── scaffolds
│   └── source
│   └── themes

sidebar.ejs 添加一点内容

<div>111</div>

在 _config.async.yml 中修改 layout 配置,替换掉默认 sidebar 模板。

layout:
    sidebar: async/sidebar

运行起来后,可以看到效果和 修改示例 中的效果一样,但是简化了用户使用。

结语

通过上面方式,可以在使用 npm 安装主题时,也支持自定义替换部分区域,来个性化的目的,当主题版本迭代升级后,也更方便用户更新升级。

完整实现源码可以参考 hexo-theme-async 中源码。文章来源地址https://www.toymoban.com/news/detail-760295.html

到了这里,关于Hexo 主题开发之自定义模板的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android开发之自定义控件-组合控件的开发与实现

    最终实现的效果展示图:   类似支付宝微信,底部分隔线对齐标题效果:       完整渲染显示效果(包含三个条目右边不同颜色的文字): 立体效果:  隐藏资产总额条目右边更多箭头  隐藏中国历史条目右边的文字: 隐藏中国历史条目下边的分隔线: 隐藏条目2中国历史左

    2024年02月10日
    浏览(32)
  • 鸿蒙OS应用开发之自定义弹窗

    前面学习了文本输出,这是比较常用的输出方式,毕竟它是向人们展示内容的基本方式。在这里学习另外一个展示方式,就是弹窗的方式。说到弹窗,其实用户是非常害怕的,因为意味用户必须处理这个窗口,不处理就不能返回。特别是弹窗的广告,更是让用户痛苦不已,所

    2024年02月04日
    浏览(40)
  • Jmeter 压测实战:Jmeter 二次开发之自定义函数

    目录 1 前言 2 开发准备 3 自定义函数核心实现 3.1 新建项目 3.2 继承实现 AbstractFunction 类 3.3 最终项目结构 4 Jmeter 加载扩展包 4.1 maven 构建配置 4.2 项目打包 4.3 Jmeter 加载扩展包 5 自定义函数调用调试 5.1 打开 Jmeter 函数助手,选择自定义函数 5.2 京东宙斯接口验证 JMeter 是一个开

    2024年02月16日
    浏览(31)
  • Android开发控件形状之自定义圆角button(三种形态)

    第一步:在drawable文件下创建button的形状描述文件btn_shape.xml btn1.xml btn2.xml btn3.xml 第二步:在布局文件中layout.xml中对btn1.xml以上三种其中一种的引用语句,用来设置button形状: android:background=\\\"@drawable/btn1\\\" 效果图: 点击前 点击后     解析shape文件中的android:shape属性:  Android

    2024年02月13日
    浏览(33)
  • Jmeter压测实战:Jmeter二次开发之自定义函数

    Jmeter是Apache基金会下的一款应用场景非常广的压力测试工具,具备轻量、高扩展性、分布式等特性。Jmeter已支持实现随机数、计数器、时间戳、大小写转换、属性校验等多种函数,方便使用人员使用。如果在使用过程中存在和业务强耦合的常用功能函数,在Jmeter不支持的情况

    2024年02月11日
    浏览(24)
  • 【Hexo】Hexo搭建Butterfly主题并快速美化

    上篇文章《快速搭建Hexo博客网站并部署上线》讲述了如何快速搭建个人博客,部署到Github上线并且能够通过网址访问,但是它的样式是最初始的状态,不够美观,本篇文章讲述如何搭建Butterfly主题并快速美化我们的个人博客,好看的样式所产生的观感会让你在阅读时有一种心

    2024年02月03日
    浏览(31)
  • 微信小程序云开发之自定义顶部导航栏搜索框(非组件)

    提到微信小程序,就不得不提到它那磨人的顶部导航栏,真的有被丑到。身为强迫症患者,笔者实在是难以忍受,好在官方提供了解决方案,但是对于初学者还是有一丢丢的难度,为了初学者不在重蹈笔者的老路,这篇文章就给大家分享一下如何做一个好看的自定义顶部导航

    2024年02月10日
    浏览(32)
  • Hexo删除主题

    1、一般在入博客中的theme目录,这里以next主题为例。 在theme目录中,打开Git Bash Here; 2、另一种情况,以fluid主题为例;之前不知道是用那种方式把主题安装在了E:Blognode_moduleshexo-theme-fluid 找到相应的目录删除该主题即可。

    2024年02月21日
    浏览(26)
  • Devc++ 开发的 Easyx 瓦片地图编辑器之自定义贴图导入模块

     接上次开发  Editing While Playing 使用 Easyx 开发的 RPG 地图编辑器 tilemap eaitor-CSDN博客  由于还不能导入自己绘制的贴图,所以还要增加自定义贴图的导入导出模块 这里就单独搓了一个自定义导入导出部分,之后再缝合进瓦片地图编辑器里

    2024年02月21日
    浏览(35)
  • hexo博客更换主题的方法

    我用hexo搭建的自己的博客,但是感觉默认的主题不太好看,那么如何才能更换一个让自己满意的主题呢? hexo提供了很多主题可以参考,网址在这里:https://hexo.io/themes/ 。这个页面提供了主题的预览效果以及下载地址。点击主题的图片,即可预览该主题的效果,点击主题的名字

    2024年02月13日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包