前言
最近在做一个项目文档网站,选择的是用 docsify 框架来实现,主要是看好其不用安装任何依赖包,不需要构建,纯静态页面。
虽然相比 VitePress 少了很多功能,但是我这边需求不多,用 docsify 足够满足了。
但是发现,docsify 竟然没有自带切换主题功能,不像 VitePress 那样开箱即用。然后网上找的已有插件问题也多,有的样式很丑,所以就想自己手写一个。
效果
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
样式元素,然后点击时,通过改变stylesheet
的disabled
属性,true
代表这个css文件不会起作用,false
或者没值则会起作用。
所以需要把light
和dark
的css主题文件同时引用到head里,然后通过js注册点击事件,动态改变stylesheet
的disabled
属性来实现切换主题效果。
记住切换主题
docsify
官方的页面里,切换主题后刷新页面就又恢复到light了,所以需要用到localStorage
来记住选择。
实现
思路理清了,就可以开干了,首先看下官方的自定义plugin文档:docsify.js.org/#/zh-cn/wri…
需要定义window.$docsify.plugins
,添加新的plugin,然后通过钩子(hook)处理异步逻辑,为了不影响其它plugin,需要push新的plugin:
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。
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:
- jsdelivr cdn: cdn.jsdelivr.net/npm/docsify…
- unpkg cdn: unpkg.com/browse/docs…
构建
首先先用工具构建出js和css,以及压缩版本。这里用到的是rollup
和postcss
构建工具。
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default'
})
]
}
// 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目录下了。
"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
{
"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…
上传后稍等一会儿,jsdelivr
和unpkg
上就能搜到对应的cdn:
使用
用法很简单,只需要引用对应的css``js
即可:
<!-- 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 的流程。