Docsify 創建文檔網站


Docsify 創建文檔網站

效果預覽

1 引言

在軟件開發過程中,編程人員經常需要寫文檔,如開發文檔、接口 API 文檔、軟件使用手冊等,也會編寫 Blog 記錄開發過程,技術感悟(比如我的博客:EnjoyToShare )。對於這些文檔,一般情況下編寫人員有以下幾種需求:編寫簡單、對外發布、格式友好、形式專業。而編寫的工具則有好多,包括以下幾類:

文檔編寫工具

  • word工具類:如 office word,wps,txt 等

  • 平臺博客類:csdn,簡書,oschina 等

  • 自建網站類:github,hexo,gitbook,markdown 等

  • 知識工具類:confluence,語雀,看雲等

當然,各種工具有各自的優缺點,簡單一點的話,使用語雀、看雲來寫長系列文章或者書籍也比較適合,但作爲一個開發人員,希望找一個能屬於自己的,簡單的,有點逼格的文檔工具,特別是針對開源軟件文檔編寫,放個 pdf 或者 doc 文檔,不便於維護,最好能跟 github 關聯,即時可看,又方便維護,如此,則非 docsify 莫屬了(當然 gitbook 也行)。如下可以截圖看一下基於 docsify 構建的文檔。本文針對如何使用 docsify 實現文檔構建進行講解,希望能幫助到想構建自己的文檔網站的同仁。

2 Docsify 簡介

Docsify 官網的介紹,一句話:一個神奇的文檔網站生成工具,使用它,可以使用簡單的方式,構建一個專業的文檔網站。如果使用過 GitBookHexo 的同仁,可以繼續使用 markdown 編寫文檔,然後轉爲 html 進行顯示。而 docsify 是一個動態生成文檔網站的工具。不同於 GitBookHexo 的地方是它不會生成將 .md 轉成 .html 文件,所有轉換工作都是在運行時進行。只需要創建一個 index.html ,就可以開始寫文檔而且直接部署在 GitHub Pages 進行發佈,方便、快捷、格式友好,樣式不錯。

基於 Docsify 設計文檔預覽鏈接:EnjoyToShare 項目筆記

3 使用 docsify 構建文檔

本章節將對如何使用 docsify 構建文檔進行詳細描述。

3.1 構建 docsify 目錄結構

(1) 安裝 npm

(2) 安裝 nodejs

(3) 安裝 docsify

  • 安裝 docsify-cli 工具,方便創建及本地預覽文檔網站。
npm i docsify-cli -g

(4) 初始化項目

  • 進入指定文件目錄,進行初始化操作
docsify init ./docs

docsify 有其規範的目錄結構,初始化成功後,可以看到 ./docs 目錄下最基本的結構如下:

  • index.html # 入口文件
  • README.md # 會做爲主頁內容渲染
  • .nojekyll # 用於阻止 GitHub Pages 會忽略掉下劃線開頭的文件

目錄結構

(5) 本地預覽網站

  • 運行一個本地服務器通過 docsify serve 可以方便的預覽效果,而且提供 LiveReload 功能,可以讓實時的預覽。默認訪問 http://localhost:3000/#/ 和 http://127.0.0.1:3000/#/
docsify serve docs
  • 預覽圖:(由於 README.md 文件被我增加了內容,故顯示修改後的內容)

本地預覽

一個基本的文檔網站就搭建好了,docsify 還可以自定義導航欄,自定義側邊欄以及背景圖和一些開發插件等等。更多配置請參考官方文檔 https://docsify.js.org

期待繼續優化,,,go on

3.2 添加文檔標題名

  • 在頁面左上角添加文檔標題名(自定義),顯示如下圖所示:

添加文檔標題名

  • 操作如下:在 index.html 文件裏添加 name 字段:
<script>
    window.$docsify = {
      name: 'EnjoyToShare',
    }
  </script>

若想在點擊文檔標題的時候鏈接到想要的地址,可進行如下操作:

  • 操作如下:在 index.html 文件裏添加 nameLink 字段:
<script>
    window.$docsify = {
      nameLink: 'https://wugenqiang.gitee.io',
    }
  </script>

3.3 添加 GitHub 圖標

  • 在頁面右上角添加 GitHub 圖標,顯示如下圖所示:

添加 GitHub 圖標

  • 操作如下:在 index.html 文件裏添加 repo 字段:
<script>
    window.$docsify = {
      repo: 'wugenqiang/CS-Notes',
    }
  </script>

3.4 添加編輯文檔按鈕

  • 操作如下:在 index.html 文件裏添加:
<script>
    window.$docsify = {
      formatUpdated: '{YYYY}/{MM}/{DD} {HH}:{mm}',
      plugins: [
        function(hook, vm) {
          hook.beforeEach(function (html) {
            var url = 'https://github.com/wugenqiang/CS-Notes/tree/master/' + vm.route.file
              var editHtml = '[📝 EDIT DOCUMENT](' + url + ')\n'
              var editHtml_end = '[🖊 Edit Document](' + url + ')\n'
              return editHtml
                   + html
                   + '\n----\n'
                   + '> Last Modified {docsify-updated} '
                   + editHtml_end
          })
        }
      ],
    }
  </script>
  • 注意:記得將代碼中的 '> Last Modified {docsify-updated}'中{docsify-updated}改成 { docsify-updated },括號和字母之間沒有空格!!

  • formatUpdated 字段爲更新時間格式,若不加這一字段,則{ docsify-updated }字段內容不顯示

4 定製功能

4.1 支持 DOT 語言作圖

DOT 語言是貝爾實驗室開發的用於作圖的腳本語言,最初在桌面端程序 Graphviz 中支持。後來有人開發了 Viz.js 使得瀏覽器端也能支持 DOT 語言作圖的渲染。我們的目的如下:當 Markdown 渲染器識別到一處語言名爲 dot 代碼塊時,就調用 Viz.js 渲染代碼塊中的語句,使它們成爲 DOT 語言定義的矢量圖。

具體操作如下:(以下所有操作都在 docsify 項目的 index.html 文件中進行)

  • (1)首先,引入 Viz.js 文件,只要在 head 中添加一條語句就行:
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/viz.js"></script>
  • (2)添加如下部分:
<script>
    window.$docsify = {
      markdown: {
        renderer: {
          code: function(code, lang) {
            if (lang === "dot") {
              return (
                      '<div class="viz">'+ Viz(code, "SVG")+'</div>'
              );
            }
            return this.origin.code.apply(this, arguments);
          }
        }
      }
    }
  </script>

下面看看具體實現:

  • 操作:
​```dot
digraph demo{
    A->B[dir=both];
    B->C[dir=none];
    C->D[dir=back];
    D->A[dir=forward];
}
​```
  • 效果圖:

在這裏插入圖片描述

4.2 支持 LaTex 數學公式

LaTeX 是大門鼎鼎的文檔排版軟件,它對於數學公式的支持非常好。和 DOT 語言類似,一開始也是隻有桌面端程序支持,但是後來同樣有人開發了各種各樣的 .js 來在瀏覽器端進行支持。

具體操作如下:(以下所有操作都在 docsify 項目的 index.html 文件中進行)

  • (1)引入 docsify-katex.js,head 中添加:
<!-- CDN files for docsify-katex -->
<script src="//cdn.jsdelivr.net/npm/docsify-katex@latest/dist/docsify-katex.js"></script>
<!-- or <script src="//cdn.jsdelivr.net/gh/upupming/docsify-katex@latest/dist/docsify-katex.js"></script> -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css"/>

下面看看具體實現:

  • 操作:
$$
\left[
\begin{matrix}
 1      & 2      & \cdots & 4      \\
 7      & 6      & \cdots & 5      \\
 \vdots & \vdots & \ddots & \vdots \\
 8      & 9      & \cdots & 0      \\
\end{matrix}
\right]
$$
  • 效果圖:

[124765890] \left[ \begin{matrix} 1 & 2 & \cdots & 4 \\ 7 & 6 & \cdots & 5 \\ \vdots & \vdots & \ddots & \vdots \\ 8 & 9 & \cdots & 0 \\ \end{matrix} \right]

更多 Latex 矩陣樣式請參考 使用 Latex 寫矩陣

4.3 支持 PDF 頁面展示

  • (1)在 index.html 中添加插件:
<!-- PDFObject.js is a required dependency of this plugin -->
<script src="//cdnjs.cloudflare.com/ajax/libs/pdfobject/2.1.1/pdfobject.min.js"></script> 
<!-- docsify-pdf-embed.js  -->
<script src="//unpkg.com/docsify-pdf-embed-plugin/src/docsify-pdf-embed.js"></script>
  • (2)在 index.html 中添加代碼:
markdown: {
        renderer: {
          code: function(code, lang, base=null) {

            /* if (lang === "dot") {
              return (
                      '<div class="viz">'+ Viz(code, "SVG")+'</div>'
              );
            } */

            var pdf_renderer = function(code, lang, verify) {
              function unique_id_generator(){
                function rand_gen(){
                  return Math.floor((Math.random()+1) * 65536).toString(16).substring(1);
                }
                return rand_gen() + rand_gen() + '-' + rand_gen() + '-' + rand_gen() + '-' + rand_gen() + '-' + rand_gen() + rand_gen() + rand_gen();
              }
              if(lang && !lang.localeCompare('pdf', 'en', {sensitivity: 'base'})){
                if(verify){
                  return true;
                }else{
                  var divId = "markdown_code_pdf_container_" + unique_id_generator().toString();
                  var container_list = new Array();
                  if(localStorage.getItem('pdf_container_list')){
                    container_list = JSON.parse(localStorage.getItem('pdf_container_list'));
                  }
                  container_list.push({"pdf_location": code, "div_id": divId});
                  localStorage.setItem('pdf_container_list', JSON.stringify(container_list));
                  return (
                          '<div style="margin-top:'+ PDF_MARGIN_TOP +'; margin-bottom:'+ PDF_MARGIN_BOTTOM +';" id="'+ divId +'">'
                          + '<a href="'+ code + '"> Link </a> to ' + code +
                          '</div>'
                  );
                }
              }
              return false;
            }
            if(pdf_renderer(code, lang, true)){
              return pdf_renderer(code, lang, false);
            }
            //return this.origin.code.apply(this, arguments);
            return (base ? base : this.origin.code.apply(this, arguments));
          }
        }
      }
  • (3)使用命令:
​```pdf
path-to-the-pdf-file,,,example: https://wugenqiang.github.io/CS-Books/pdf.js/web/viewer.html?file=../../pdf-book/leetcode-cpp.pdf
​```

結果展示:

https://wugenqiang.github.io/CS-Books/pdf.js/web/viewer.html?file=../../pdf-book/leetcode-cpp.pdf

4.4 支持回到頂部

方法:通過 jQuery 定義插件 jQuery GoUp 實現點擊回到頂部功能。

  • 效果圖如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4796rTQT-1587955261684)(https://wugenqiang.github.io/PictureBed/CS-Notes/20200316182427.jpg)]

操作如下:

  • (1)引用 jquery 庫和 jquery.goup.js 到 index.html
  <script src="https://wugenqiang.github.io/CS-Notes/plugin/jquery.js"></script>
  <script src="https://wugenqiang.github.io/CS-Notes/plugin/jquery.goup.js"></script>
  • 在調用下插件:
<script type="text/javascript">
    $(document).ready(function () {
      $.goup({
        trigger: 100,
        bottomOffset: 32,
        locationOffset: 32,
        title: 'TOP',
        titleAsText: true
      });
    });
  </script>

5 離線模式

漸進式 Web 應用程序(PWA)是將最好的網絡與最好的應用程序結合在一起的體驗。我們可以與服務人員一起增強我們的網站,以使其脫機工作或使用低質量的網絡。

5.1 創建 serviceWorker

在文檔根目錄中創建一個文件:sw.js,然後複製以下代碼:

/* ===========================================================
 * docsify sw.js
 * ===========================================================
 * Copyright 2016 @huxpro
 * Licensed under Apache 2.0
 * Register service worker.
 * ========================================================== */

const RUNTIME = 'docsify'
const HOSTNAME_WHITELIST = [
  self.location.hostname,
  'fonts.gstatic.com',
  'fonts.googleapis.com',
  'cdn.jsdelivr.net'
]

// The Util Function to hack URLs of intercepted requests
const getFixedUrl = (req) => {
  var now = Date.now()
  var url = new URL(req.url)

  // 1. fixed http URL
  // Just keep syncing with location.protocol
  // fetch(httpURL) belongs to active mixed content.
  // And fetch(httpRequest) is not supported yet.
  url.protocol = self.location.protocol

  // 2. add query for caching-busting.
  // Github Pages served with Cache-Control: max-age=600
  // max-age on mutable content is error-prone, with SW life of bugs can even extend.
  // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
  // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
  if (url.hostname === self.location.hostname) {
    url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
  }
  return url.href
}

/**
 *  @Lifecycle Activate
 *  New one activated when old isnt being used.
 *
 *  waitUntil(): activating ====> activated
 */
self.addEventListener('activate', event => {
  event.waitUntil(self.clients.claim())
})

/**
 *  @Functional Fetch
 *  All network requests are being intercepted here.
 *
 *  void respondWith(Promise<Response> r)
 */
self.addEventListener('fetch', event => {
  // Skip some of cross-origin requests, like those for Google Analytics.
  if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
    // Stale-while-revalidate
    // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
    // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
    const cached = caches.match(event.request)
    const fixedUrl = getFixedUrl(event.request)
    const fetched = fetch(fixedUrl, { cache: 'no-store' })
    const fetchedCopy = fetched.then(resp => resp.clone())

    // Call respondWith() with whatever we get first.
    // If the fetch fails (e.g disconnected), wait for the cache.
    // If there’s nothing in cache, wait for the fetch.
    // If neither yields a response, return offline pages.
    event.respondWith(
      Promise.race([fetched.catch(_ => cached), cached])
        .then(resp => resp || fetched)
        .catch(_ => { /* eat any errors */ })
    )

    // Update the cache with the version we fetched (only for ok status)
    event.waitUntil(
      Promise.all([fetchedCopy, caches.open(RUNTIME)])
        .then(([response, cache]) => response.ok && cache.put(event.request, response))
        .catch(_ => { /* eat any errors */ })
    )
  }
})

5.2 寄存器

現在,在 index.html 中添加下面代碼。由於它僅在某些現代瀏覽器上有效,因此我們需要判斷:

  <!-- 實現離線化 -->
  <script>
    if (typeof navigator.serviceWorker !== 'undefined') {
      navigator.serviceWorker.register('sw.js')
    }
  </script>

發佈您的網站並開始體驗神奇的離線功能。👻您可以關閉Wi-Fi並刷新當前站點以進行體驗。

6 效果展示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章