首页 > IT职业 > IT图书 > Docsify 深入源码
背景本文作者:ivweb 高磊 原文出处:IVWEB社区 未经同意,禁止转载
当前互联网时代,技术门槛越来越低,人人都可以建立并生成各式各样,多元化、多样化的站点。
文档站点一般作为各行各业领域内的知识技术介绍及使用的资料站点,可提高资料的使用效率,保证资料的质量。
目前市面上有大致这么几款主流的文档生成站点,分别为docsify、gitbook、Phenomic等,可帮助用户快速搭建文档站点。
今天我们详细介绍下docsify的使用文档及实现原理。
介绍docsify是由现饿了么前端团队@elemeFE的cinwell.li编写的一套文档站点生成框架,github上已有3k+ star,这款框架和其他框架如gitbook等相比,最大的区别就在于docsify不是静态生成html,而通过动态请求markdown编译生成html。
docsify还具有轻应用、全文搜索功能、支持多个主题、兼容IE10+、支持SSR等特性。
docsify-cli快速入手docsify提供了一个名为docsify-cli脚手架工具,通过这个脚手架工具,可以快速生成docsify站点并部署到本地或远程服务器。
// 安装docsify-cli
npm install docsify-cli -g /* @desc: 初始化docsify,将模板文件拷贝至path下
* @param:local[boolean],local默认为false,若设置为true,则将js,css等资源文件拷贝到本地path下的vendor目录下.
* @param:theme[string], theme默认为'vue',主题可选'buble','dark','pure','vue'.
*/
-----------------------------------------------------
| docsify init [--local] [--theme] |
|---or--- |
| docsify i [-l] [-t] |
----------------------------------------------------- /* @desc: 初始化docsify后,才能部署docsify,否则会报出下方的errorMsg
* @errorMsg: No docs found, please run docsify init first.
* @param: open[boolean],默认为false,若为true,则部署后自动打开默认浏览器该站点地址.
* @param: port[number],默认为3000,可自定义其他未被占用的端口.
*/
-----------------------------------------------------
| docsify serve [--open] [--port] |
|---or--- |
| docsify s [-o] [-p] |
----------------------------------------------------- 经过这三步,就可以顺利的部署一个属于自己的文档站点了,不过要想一键部署一个站点,也未尝不可,下面让你快速掌握装逼技巧:
原理及特点:
/* @desc: 一键生成文档站点.
* @param: config[string],默认为false, 需要加载的配置文件,可自定义docsify配置.
* @param: port[number],默认为4000,可自定义其他未被占用的端口.
*/
-----------------------------------------------------
| docsify start [--config] [--port] |
|---or--- |
| docsify start [-c] [-p] |
----------------------------------------------------- Docsify源码解读 @ v4.3.1 docsify在初始化的流程如下:

initMixin、routerMixin、renderMixin、fetchMixin、eventMixin等方法向Docsify对象原型注入方法和属性接下来我们看下在docsify每个模块具体都干了些什么事情:
源码如下:
export function initMixin (proto) { // proto为Docsify原型对象
proto._init = function () {
const vm = this
vm.config = config || {}
initLifecycle(vm) // Init hooks
initPlugin(vm) // Install plugins
callHook(vm, 'init')
initRouter(vm) // Add router
initRender(vm) // Render base DOM
initEvent(vm) // Bind events
initFetch(vm) // Fetch data
callHook(vm, 'mounted')
}
} 
这里this._init方法依次由上至下调用了上图所示的方法,完成初始化工作。
这里定义了六个钩子函数,即init、beforeEach、afterEach、doneEach、ready、mounted,钩子函数初始化为空数组。下面介绍下钩子的生命周期:
引入用户自定义插件,即其设置的钩子函数,来替换默认钩子函数。
调用"init"钩子函数,vm为当前实例。
设置路由模式,默认为'hash',用户可设置为'history'或'hash'。


这里主要为编译层,初始化markdown编译器,重写了marked的一些标签编译方法,例如heading、code、link、paragraph、image等,最终生成app的DOM实例,并添加到DOM中(注意:此时Content、navbar和sidebar仅仅是模板实例,内容数据还未获取)。
为切换sidebar的按钮绑定事件。
通过xhr请求获取sidebar、navbar、content、coverpage的text数据,然后填充进DOM实例中。
调用"mounted"钩子函数,vm为当前实例。
源码如下:
export function routerMixin (proto) { // proto为Docsify原型对象
proto.route = {}
} 我们看到,router模块在运行周期中初始化仅仅定义了一个名为route的Docsify原型对象的对象属性。
源码如下:
export function renderMixin (proto) { // proto为Docsify原型对象,下面代码细节就不讲了,感兴趣的话可以down一份研究下
proto._renderTo = function (el, content, replace) {
// 如何插入content,replace = ['outerHTML'|'innerHTML']
...
}
proto._renderSidebar = function (text) {
// 读取并解析sidebar配置,渲染sidebar
...
}
proto._bindEventOnRendered = function (activeEl) {
// 滚动条滑动模块,以及对配置项autoHeader、auto2top的处理
...
}
proto._renderNav = function (text) {
// 渲染navbar内容,处理nav上活动标签样式
...
}
proto._renderMain = function (text, opt = {}) {
// 渲染content内容,首先调用beforeEach钩子,然后将markdown编译成html,最后调用afterEach处理后返回html,最后渲染content
// 然后读取excuteScript配置,若为true,则执行script内嵌脚本。若想执行外链脚本,需要引入external-script插件
...
}
proto._renderCover = function (text) {
// 渲染封面页
...
}
proto._updateRender = function () {
// 渲染文档站点标题
...
}
} 源码如下:
export function fetchMixin (proto) { // 同renderMixin的proto所述
let last // 记录上一次请求信息
proto._fetch = function (cb = noop) {
... // 强制结束上一次请求,加载content、nav以及sidebar数据
}
proto._fetchCover = function () {
... // 请求并渲染封面数据
}
proto.$fetch = function (cb = noop) {
... // 封装了一个方法用作路由切换的数据请求
}
} 源码如下:
export function eventMixin (proto) { // 同renderMixin的proto所述
proto.$resetEvents = function () { // 滚动到当前路由中id='query.id'的dom块,并使sidebar的活动标签实时响应,获取nav活动标签
scrollIntoView(this.route.query.id)
sidebar.getAndActive(this.router, 'nav')
}
} 源码如下:
export default function () {
window.Docsify = { util, dom, get, slugify }
window.DocsifyCompiler = Compiler
window.marked = marked
window.Prism = prism
} 这里导出一个方法,里面主要暴露了四个全局对象,方便提供使用。
工具类方法->util、dom操作方法->dom、请求markdown内容方法->get、缓存处理方法->slugify
Docsify好用之处就在于其提供了丰富的可扩展性,用户可以做更多想做的事情。除此之外,Docsify还提供了一些实用的功能(也可查阅Docsify官方文档):
docsify-server-renderer模块,官方示例Node直出-Docsify。若本文内容有不足或需要改正的地方,欢迎在评论区拍砖!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1409/viewspace-2799407/ ,如需转载,请注明出处,否则将追究法律责任。