手写一个 docsify 切换主题 plugin

前言

最近在做一个项目文档网站,选择的是用 docsify 框架来实现,主要是看好其不用安装任何依赖包,不需要构建,纯静态页面。

虽然相比 VitePress 少了很多功能,但是我这边需求不多,用 docsify 足够满足了。

但是发现,docsify 竟然没有自带切换主题功能,不像 VitePress 那样开箱即用。然后网上找的已有插件问题也多,有的样式很丑,所以就想自己手写一个。

效果

源码:github.com/markz-demo/...

Demo:markz-demo.github.io/docsify-dar...

切换主题按钮:

思路

这里参考了另一个比较流行的快速搭建文档框架:VitePress,这个框架功能更强大,支持更多的文档站点相关功能,其中切换dark theme就是默认自带的功能。

样式

VitePress的样式已经很好看了,F12看下Elements,发现是svg实现的图片,直接照抄。

样式解决了,下一步就是怎么实现切换样式了。

切换样式

docsify官方提供了一些主题:docsify.js.org/#/zh-cn/the...

vue.css对应light theme;dark.css对应dark theme。然后点击这里可以切换主题。

看下是怎么实现的,点Edit Document源码

是用内嵌script实现的,先取到所有stylesheet样式元素,然后点击时,通过改变stylesheetdisabled属性,true代表这个css文件不会起作用,false或者没值则会起作用。

所以需要把lightdark的css主题文件同时引用到head里,然后通过js注册点击事件,动态改变stylesheetdisabled属性来实现切换主题效果。

记住切换主题

docsify官方的页面里,切换主题后刷新页面就又恢复到light了,所以需要用到localStorage来记住选择。

实现

思路理清了,就可以开干了,首先看下官方的自定义plugin文档:docsify.js.org/#/zh-cn/wri...

需要定义window.$docsify.plugins,添加新的plugin,然后通过钩子(hook)处理异步逻辑,为了不影响其它plugin,需要push新的plugin:

javascript 复制代码
function plugin(hook, vm) {
    hook.mounted(function () { // 这里只用到这一个hook
        // init();
    })
}

if (!window.$docsify) {
    window.$docsify = {}
}

window.$docsify.plugins = [].concat(plugin, window.$docsify.plugins)

定义初始化方法,创建switch button dom,然后insert到body里。window.Docsify.dom里提供了一些dom操作方法,方便操作dom。

javascript 复制代码
var className = 'docsify-dark-switch'; // button外层div class
var key = 'DOCSIFY_DARK_SWITCH'; // storage key
var buttonHtml = `...`; // button html
var isDark = localStorage.getItem(key) === 'true';
function init() {
    if (!window.Docsify.dom.find('.' + className)) {
        // 创建div,div内部创建switch button
        var content = window.Docsify.dom.create('div', buttonHtml);
        window.Docsify.dom.toggleClass(content, 'add', className);
        ...
        // 插入body
        window.Docsify.dom.before(document.body, content);

        // 注册click事件
        var button = content.querySelector('button');
        window.Docsify.dom.on(button, 'click', click);
    }
    toggle();
}

function click() {
    isDark = !isDark;
    localStorage.setItem(key, isDark); // click时改变storage value
    toggle();
}

function toggle() {
    var content = window.Docsify.dom.find('.' + className);
    var button = window.Docsify.dom.find('.' + className + '>button');
    if (content && button) {
        ... // 更改button样式
    }

    // dark模式下,会给html加个dark class,方便外部自定义dark样式
    window.Docsify.dom.toggleClass(document.documentElement, isDark ? 'add' : 'remove', 'dark');

    // 取到所有stylesheets
    var lightSheets = window.Docsify.dom.findAll('[rel="stylesheet"][title="light"]');
    var darkSheets = window.Docsify.dom.findAll('[rel="stylesheet"][title="dark"]');
    ...

    // 这里遇到个问题,需要先把所有stylesheets都改成disabled=true后,再set disabled=false才会让样式起作用。
    // 原因未知
    [...lightSheets, ...darkSheets].forEach(function (sheet) {
        sheet.disabled = true;
    });
    // 更改disabled属性值
    (isDark ? darkSheets : lightSheets).forEach(function (sheet) {
        sheet.disabled = false;
    });
}

主要逻辑代码都在上面了,具体参考源码:github.com/markz-demo/...

上传 CDN

目前上传了俩CDN:

构建

首先先用工具构建出js和css,以及压缩版本。这里用到的是rolluppostcss构建工具。

javascript 复制代码
// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer'),
    require('cssnano')({
      preset: 'default'
    })
  ]
}
javascript 复制代码
// rollup.config.js
const terser = require('@rollup/plugin-terser');

module.exports = {
  input: 'src/index.js',
  output: [
    {
      file: 'dist/docsify-dark-switch.js',
      format: 'cjs'
    },
    {
      file: 'dist/docsify-dark-switch.min.js',
      format: 'cjs',
      plugins: [terser()]
    }
  ]
};

添加npm scripts,然后直接运行npm run build就可以把文件打包到dist目录下了。

json 复制代码
"scripts": {
  "build": "npm run build:css && npm run build:js",
  "build:js": "rollup --c",
  "build:css": "postcss src/index.css -o dist/docsify-dark-switch.css --config ./postcss.config.js"
},

发布 npm 包

这里就需要优化下package.json了,这里列下必要属性:

  • name version description:这些就不说了。
  • main:指定包入口
  • unpkg:用于上传unpkg cdn
json 复制代码
{
  "name": "docsify-dark-switch",
  "version": "1.0.2",
  "description": "A docsify plugin to switch dark and light theme",
  "main": "dist/docsify-dark-switch.min.js",
  "unpkg": "dist/docsify-dark-switch.min.js",
  "homepage": "https://github.com/markz-demo/docsify-dark-switch",

npm地址:www.npmjs.com/package/doc...

上传后稍等一会儿,jsdelivrunpkg上就能搜到对应的cdn:

使用

用法很简单,只需要引用对应的css``js即可:

html 复制代码
<!-- head -->
<!-- 给 theme stylesheet 设置对应的 title,可以设置多个  -->
<link rel="stylesheet" title="light" href="//cdn.jsdelivr.net/npm/docsify/themes/vue.css">
<link rel="stylesheet" title="dark" href="//cdn.jsdelivr.net/npm/docsify/themes/dark.css">

<!-- 引用 docsify-dark-switch.css  -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify-dark-switch/dist/docsify-dark-switch.css">

<!-- Also insert you custom css -->

<!-- body -->
<!-- 引用 docsify-dark-switch.js 或 docsify-dark-switch.min.js  -->
<script src="//cdn.jsdelivr.net/npm/docsify-dark-switch/dist/docsify-dark-switch.min.js"></script>

也可以通过配置项 设置其它功能,具体可以参考下中文文档:markz-demo.github.io/docsify-dar...

总结

整理下知识点:

  • 自定义 docsify plugin 插件
  • 通过设置<link rel="stylesheet"节点的disabled属性,可以动态控制css样式文件是否起作用。
  • 发布 npm 包以及上传 CDN 的流程。

源码:github.com/markz-demo/...

Demo:markz-demo.github.io/docsify-dar...

相关推荐
长天一色2 分钟前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_23420 分钟前
npm、yarn、pnpm之间的区别
前端·npm·node.js
NiNg_1_23423 分钟前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦24 分钟前
如何在 Axios 中封装事件中心EventEmitter
javascript·http
神之王楠1 小时前
如何通过js加载css和html
javascript·css·html
余生H1 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
CXDNW1 小时前
【网络篇】计算机网络——应用层详述(笔记)
服务器·笔记·计算机网络·http·web·cdn·dns
流烟默1 小时前
Vue中watch监听属性的一些应用总结
前端·javascript·vue.js·watch
茶卡盐佑星_2 小时前
meta标签作用/SEO优化
前端·javascript·html
与衫2 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql