[小心得]快速搭建一個Vue Live Markdown

程序猿們對markdown(後面都簡稱md)都應該很熟悉了,非常方便的一種標記語法幫助我們快速的寫文檔,也有很多開源庫的在線文檔(比如:elementui)也都是使用md來編寫的,而且具有示例代碼運行的功能,這無疑讓靜態的文檔變得更加具有活力,閱讀者可以進行操作和體驗,更容易理解文檔,但是原生的md並不支持,如何DIY一個呢?

擼碼前先整理一下需求:

1、能夠在md中運行代碼

2、擴展md,增加一些有活力的東西,比如emoji表情

然後梳理一下思路:

1、能在md運行代碼,勢必需要把md中的代碼塊提取出來並編譯

2、選擇一個好的擴展md的插件體系

最終確定實現的技術棧:

1、vue-md-loader就能滿足編譯md

2、markdown-it系列能夠良好的擴展md

3、簡單的單頁應用選擇使用POI進行構建

4、UI組件選擇Element

先提前預覽一下效果:

簡單扼要說明一下核心步驟:

第一步:搭建工程,用yarn開始

第二步:為了能夠「運行md」文件,需要配置vue-md-loader

{ test: /.md$/, use: [ { loader: vue-loader }, { loader: vue-md-loader, options: { rules: { table_open: function () { return <table class="falcon-table"> }, table_close: function () { return </table> } }, plugins: [ require(markdown-it-abbr), require(markdown-it-attrs), require(markdown-it-checkbox), require(markdown-it-sub), require(markdown-it-sup), [ require(markdown-it-task-lists), { enabled: true, label: true, labelAfter: true } ], [ require(markdown-it-emoji), { shortcuts: emojiShortcutsNow } ], // 運行代碼的容器定義,用":::"匹配md文件中的需要運行的代碼塊 [ require(markdown-it-container), live, { validate: function (params) { return new RegExp((all|code|live)).test(params.trim()) }, // 將捕獲的代碼塊裝進自定義組件falcon-demo中 render: function (tokens, idx) { if (tokens[ idx ].nesting === 1) { var info = tokens[ idx ].info // opening tag return <falcon-demo info=" + info.trim() + "> } else { // closing tag return </falcon-demo> } } } ] ], // 用"<!--"匹配代碼塊,給運行結果加一個父容器 afterProcessLiveTemplate: function (template) { return `<div class="falcon-demo__live">${template}</div>` } } } ], exclude: /node_modules/}

第三步:為了實現可運行代碼塊的各種效果,自定義組件falcon-demo

<template> <div class="falcon-demo" :custom-style="customStyle" :class="classComputed"> <slot></slot> <section class="falcon-demo__control" v-if="style.codeFolder"> <el-button type="primary" size="small" plain @click="trigger"> {{ showCode ? 收起代碼 : 查看代碼 }} </el-button> </section> </div></template><script type="text/babel"> export default { name: falcon-demo, props: [ info ], // 接收loader編譯後傳遞過來的控制風格的字元串 data () { return { showCode: false, style: { allHidden: 0, codeFolder: 0, codeHidden: 0, codeNoBorder: 0, liveDark: 0, liveFull: 0, liveNoBorder: 0 } } }, computed: { customStyle () { // 解析控制風格的字元串,生成對應的樣式配置 this.info.split(-).forEach((name, index) => { const id = name.trim() const cur = index + 1 this.style.hasOwnProperty(id) && (this.style[ id ] = cur) }) // 互斥 if (this.style.codeFolder && this.style.codeHidden) { if (this.style.codeFolder > this.style.codeHidden) { this.style.codeHidden = 0 } else { this.style.codeFolder = 0 } } return this.info }, classComputed () { // 基於樣式配置,生成最終的class組合 const obj = {} if (this.style.codeFolder) obj[ falcon-demo--showcode ] = this.showCode else if (this.style.codeHidden) obj[ falcon-demo--showcode ] = false else obj[ falcon-demo--showcode ] = true for (let k in this.style) { obj[ falcon-demo-- + k.toLowerCase() ] = !!this.style[ k ] } return obj } }, methods: { trigger () { this.showCode = !this.showCode } } }</script>

第四步:還記得開始說的emoji嘛,先定義emoji快捷鍵,然後配置擴展

// emoji 快捷鍵const emojiFullJson = require(markdown-it-emoji/lib/data/full.json)const emojiShortcuts = require(markdown-it-emoji/lib/data/shortcuts.js)const emojiShortcutsNow = JSON.parse(JSON.stringify(emojiShortcuts))for (let k in emojiFullJson) { const sk = :e: + k if (emojiShortcutsNow[ k ]) { emojiShortcutsNow[ k ] = emojiShortcutsNow[ k ].concat([ sk ]) } else { emojiShortcutsNow[ k ] = [ sk ] }}

第五步:增加文檔之後需要添加路由且可以靈活的分組(嵌套),使用vue-router配合tree-menu組件,修改routes直接更新菜單,這裡就用到了之前的 [小心得]Vue樹形菜單-遞歸實現

第六步:在docs目錄下創建一個md文件,在routes.js中加入路由,開始寫你的文檔吧

完整代碼:github.com/xinxin-huang


推薦閱讀:

發布 umi 1.0 ??????
探秘 React Hot Loader
橫行的前端(上)
[閱 #40] 幾點關於更好書寫 CSS 選擇器的建議
前端日刊-2017.12.25

TAG:前端開發 | Vuejs | Webpack |