簡(jiǎn)介
一款使用marked和highlight.js開發(fā)的一款markdown編輯器,除常見markdown語法外,支持快捷輸入、圖片粘貼、代碼復(fù)制、全屏編輯、預(yù)覽等功能。
使用起來簡(jiǎn)單方便,只需幾行代碼,即可在你的頁面上引入一個(gè)markdown編輯器,編輯區(qū)支持像專業(yè)編輯器那樣。
編輯器涵蓋了常用的markdown編輯器功能,可通過已有屬性進(jìn)行配置,對(duì)編輯器功能和樣式進(jìn)行基本的配置,也可根據(jù)需求進(jìn)行深度定制。
項(xiàng)目地址
文檔地址
示例

特點(diǎn)
- 使用簡(jiǎn)單,只需要安裝npm包,引入項(xiàng)目即可使用,不需要繁瑣的初始化配置。
- 方便擴(kuò)展,根據(jù)實(shí)際需求,支持常見的功能配置,也可根據(jù)實(shí)際需求進(jìn)行深度定制。
- 體積小,加載速度快,npm包刪除了highlight.js和codemirror里的依賴。
- 靈活的主題,默認(rèn)支持四種代碼塊風(fēng)格,也可根據(jù)實(shí)際需求定制自己的主題樣式
- 功能強(qiáng)大,支持專業(yè)版的編輯器,使用codemirror實(shí)現(xiàn)編輯窗口,可識(shí)別markdown語法
- 鍵盤事件監(jiān)聽,如保存、粘貼、回車時(shí)上次輸入語法判斷等
- 可擴(kuò)展性強(qiáng),除了提供的屬性配置編輯器,也可直接在原有組件基礎(chǔ)上進(jìn)行二次開發(fā)
實(shí)現(xiàn)思路
通過監(jiān)聽文本輸入?yún)^(qū)域內(nèi)內(nèi)容的變化,實(shí)時(shí)將輸入的markdown語法進(jìn)行編譯,并渲染到預(yù)覽區(qū)域。
編輯器大致分為頭部菜單欄、左側(cè)內(nèi)容輸入?yún)^(qū)域、右側(cè)預(yù)覽區(qū)域三個(gè)部分。
頭部菜單主要為定自定義標(biāo)題區(qū)域和菜單按鈕,菜單按鈕可通過配置文件進(jìn)行顯示和隱藏;左側(cè)編輯區(qū)域,簡(jiǎn)單版使用textarea開發(fā),滿足基本需求,
專業(yè)版使用codemirror開發(fā),編輯區(qū)域支持手動(dòng)輸入文本和通過頭部菜單插入;右側(cè)預(yù)覽區(qū)域可實(shí)時(shí)預(yù)覽輸入文本,并可通過菜單按鈕,進(jìn)行編輯區(qū)域和預(yù)覽區(qū)域的切換。
安裝方式
使用npm安裝
- 安裝依賴
npm i -S vue-meditor
將組件復(fù)制到項(xiàng)目?jī)?nèi)
- 將git倉(cāng)庫代碼拉到本地
git clone https://github.com/zhaoxuhui1122/vue-markdown.git
- 復(fù)制src文件夾下內(nèi)容至components文件夾下
在項(xiàng)目使用
npm包安裝時(shí)
簡(jiǎn)單版
import Markdown from 'vue-meditor'
專業(yè)版
import { MarkdownPro } from 'vue-meditor'
預(yù)覽組件
import { MarkdownPreview } from 'vue-meditor'
復(fù)制組件到本地時(shí)(推薦)
簡(jiǎn)單版
import Markdown from '@/components/markdown/...';
專業(yè)版
import MarkdownPro from '@/components/markdown/pro';
預(yù)覽組件
import MarkdownPreview from '@/components/markdown/preview';
在頁面內(nèi)使用
<template>
<div class="markdown">
<Markdown/>
</div>
</template>
<script>
import Markdown from 'vue-meditor';
export default {
name: "markdown",
components: {
Markdown
}
}
</script>
API
編輯器基本屬性
value
- Type:
String/Number - Default:
''
編輯器輸入的文本,支持通過v-dodel數(shù)據(jù)雙向綁定設(shè)置編輯器內(nèi)容和獲取編輯器的值。
width
- Type:
String/Number - Default:
auto
編輯器的初始化寬度。
height
- Type:
Number - Default:
600
編輯器的初始化高度。
bordered
- Type:
Boolean - Default:
true
編輯器是否含有邊框。
toolbars
- Type:
Object - Default:
參見下表
頭部菜單按鈕,通過設(shè)置true or false控制決定是否顯示,目前配置支持持控制按鈕顯示隱藏,后續(xù)將支持根據(jù)配置顯示排列順序。
| 名稱 | 說明 | 默認(rèn)是否顯示 |
|---|---|---|
| strong | 粗體 | 是 |
| italic | 斜體 | 是 |
| overline | 刪除線 | 是 |
| h1 | 標(biāo)題1 | 是 |
| h2 | 標(biāo)題2 | 是 |
| h3 | 標(biāo)題3 | 是 |
| h4 | 標(biāo)題4 | 否 |
| h5 | 標(biāo)題5 | 否 |
| h6 | 標(biāo)題6 | 否 |
| hr | 分割線 | 是 |
| quote | 引用 | 是 |
| ul | 無序列表 | 是 |
| ol | 有序列表 | 是 |
| code | 代碼塊 | 是 |
| link | 鏈接 | 是 |
| image | image | 是 |
| table | 表格 | 是 |
| checked | 已完成列表 | 是 |
| notChecked | 未完成列表 | 是 |
| preview | 預(yù)覽 | 是 |
| split | 分屏模式切換 | 是 |
| 打印 | 否 | |
| theme | 主題切換 | 是 |
| fullscreen | 全屏 | 是 |
| exportmd | 導(dǎo)出為*.md文件 | 是 |
| importmd | 導(dǎo)入本地*.md文件 | 是 |
| save | 保存按鈕 | 否 |
| clear | 清空內(nèi)容 | 否 |
theme
- Type:
String - Default:
light
編輯器代碼塊主題,目前支持light、dark、oneDark、gitHub四種代碼塊風(fēng)格,可通過自定義theme并修改樣式文件進(jìn)行主題定制。
自定義theme時(shí),預(yù)覽區(qū)域的會(huì)增加一個(gè)為markdown-theme-[theme]的class。
autoSave
- Type:
Boolean - Default:
false
是否開啟自動(dòng)保存,設(shè)置為開啟時(shí)可通過綁定on-save事件獲取編輯器內(nèi)的值和代碼塊主題。
<Markdown @on-save="handleOnSave"/>
handleOnSave({value, theme}){
console.log(value, theme);
}
interval
- Type:
Number - Default:
10000
自動(dòng)保存間隔時(shí)間,單位:mm,默認(rèn)10000mm,需要autoSave = true時(shí)才有效。
exportFileName
- Type:
String - Default:
unnamed
導(dǎo)出的md文件名稱,默認(rèn)unnamed.md。
markedOptions
- Type:
Object - Default:
{}
marked配置項(xiàng),可以根據(jù)需求自定義。
<Markdown :markedOptions="{baseUrl:'http://***.oss-cn-shanghai.aliyuncs.com/'}"/>
isPreview
- Type:
Boolean - Default:
false
是否是預(yù)覽模式,開啟時(shí)可作為一個(gè)預(yù)覽組件使用,與預(yù)覽組件功能一致。
copyCode
- Type:
Boolean - Default:
true
是否支持復(fù)制代碼塊內(nèi)的內(nèi)容。
copyBtnText
- Type:
String - Default:
復(fù)制代碼
復(fù)制代碼按鈕顯示文字。
預(yù)覽組件基本屬性
initialValue
- Type:
String/Number - Default:
''
預(yù)覽組件初始化內(nèi)容,支持動(dòng)態(tài)更新。
theme
- Type:
String - Default:
light
代碼塊主題,與編輯器編輯器代碼塊主題一致。
markedOptions
- Type:
Object - Default:
{}
marked配置項(xiàng),與編輯器內(nèi)該配置一致。
copyCode
- Type:
Boolean - Default:
true
是否支持復(fù)制代碼塊內(nèi)的內(nèi)容。
copyBtnText
- Type:
String - Default:
復(fù)制代碼
復(fù)制代碼按鈕顯示文字。
on-ready
編輯器初始化完成時(shí)觸發(fā),返回值為Object,包含組件本身和insertContent方法。
on-save
編輯器保存事件,自動(dòng)保存或者手動(dòng)保存時(shí)觸發(fā),支持ctrl+s或command+s觸發(fā)保存,返回值類型為Object,包含當(dāng)前輸入值value和選擇的代碼塊主題theme。
on-paste-image
監(jiān)聽編輯器粘貼圖片事件,在編輯區(qū)域內(nèi)手動(dòng)粘貼圖片時(shí)觸發(fā),可用于支持粘貼插入圖片文件,返回file文件,上傳文件后可結(jié)合on-ready事件內(nèi)返回的insertContent插入圖片。
on-copy
復(fù)制代碼塊內(nèi)容,觸發(fā)時(shí)返回當(dāng)前代碼塊的text,copyCode開啟時(shí)才有效。
二次開發(fā)
粘貼插入圖片
on-paste-image雖然可以支持圖片粘貼事件的監(jiān)聽,但不會(huì)處理圖片上傳至服務(wù)器并將鏈接插入編輯器這段邏輯。
目前如果想要支持粘貼插入圖片,需要在on-paste-image方法里上傳圖片文件,拿到圖片地址后,使用on-ready方法里返回的insertContent方法插入圖片。
上述操作顯得過于復(fù)雜,可以直接在源碼里擴(kuò)展mixins里的handlePaste方法,圖片上傳完成后,直接調(diào)用this.insertContent方法插入圖片。
修改/markdown/mixins/common.js
handlePaste(_, e) {// 粘貼圖片
const { clipboardData = {} } = e;
const { types = [], items } = clipboardData;
let item = null;
for (let i = 0; i < types.length; i++) {
if (types[i] === 'Files') {
item = items[i];
break;
}
}
if (item) {
const file = item.getAsFile();
if (/image/gi.test(file.type)) {
e.preventDefault();
// 1.上傳操作
// 2.插入圖片 this.insertContent(``)
}
}
}
支持流程圖、甘特圖等語法
目前編輯器只支持常見code語法,如果需要實(shí)現(xiàn)如流程圖等功能,需要進(jìn)一步擴(kuò)展,以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的流程圖為例,具體實(shí)現(xiàn)思路如下:
默認(rèn)情況下,markedjs會(huì)使用renderer.code方法對(duì)輸入的代碼塊進(jìn)行解析,并會(huì)借助highlight.js支持語法高亮。
可以將流程圖語法輸入到代碼塊內(nèi),并標(biāo)明語言,重寫marked.Renderer的code解析方法,結(jié)合結(jié)合flowchart.js路程圖代碼進(jìn)行解析,返回文本內(nèi)容。
修改`/markdown/libs/js/simple.js
import hljs from './hljs';
import index from 'index';
import {parse} from 'flowchart.js'
hljs.initHighlightingOnLoad();
const renderer = new index.Renderer();
renderer.code = (code, language) => {
if (language === 'flow') {// 流程圖
const dom = document.createElement('div');
const flowchart = parse(code);
flowchart.drawSVG(dom, {/*相關(guān)配置*/});
return dom.innerHTML;
} else {// 默認(rèn)解析
return `<pre class="hljs"><code class="${language}">${hljs.highlightAuto(code).value}</code></pre>`
}
}
export default index.setOptions({
renderer,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
highlight: function (code) {
return hljs.highlightAuto(code).value;
}
})
自定義markdown語法轉(zhuǎn)換
項(xiàng)目?jī)?nèi)使用的`index.js均為其默認(rèn)配置功能,如需要特殊轉(zhuǎn)換,可重寫其內(nèi)部的解析方法,即重寫其renderer相關(guān)方法
參考文檔。
自動(dòng)生成文檔目錄
預(yù)覽區(qū)域和文檔預(yù)覽組件暫不支持自動(dòng)生成目錄,實(shí)現(xiàn)自動(dòng)生成目錄思路目前想到的大致有
- 重寫
renderer.heading方法,為生成的標(biāo)題添加id,輸入特定快捷鍵,如[TOC]時(shí),查找預(yù)覽區(qū)域內(nèi)的的所有標(biāo)題標(biāo)簽,分析等級(jí)關(guān)系,生成目錄標(biāo)簽
icon替換
項(xiàng)目?jī)?nèi)所有的icon和命名參考/assets/font/index.html,替換時(shí)需注意,預(yù)覽區(qū)域的checkbox為icon,注意一并替換,
修改/assets/css/index.less內(nèi)的input[type="checkbox"]的:after樣式。
代碼體積優(yōu)化
公共代碼提取
npm包構(gòu)建時(shí),三個(gè)組件完全獨(dú)立,沒有抽離公共文件,所以,當(dāng)同一個(gè)項(xiàng)目?jī)?nèi)引入其中的兩個(gè)或三個(gè)組件都引入時(shí),存在一定的重復(fù)代碼,
主要為highlight.js、marked、iconfont、css樣式幾個(gè)部分。
解決方式:將組件復(fù)制到本地項(xiàng)目,打包時(shí)將這些文件作為公共文件抽離出來。
注意:三個(gè)組件中使用的iconfont為同一套,如果只是單純的使用preview組件,
將會(huì)引入整個(gè)項(xiàng)目所使用的iconfont,可刪除iconfont的引入,
重寫input[type="checkbox"]的樣式,preview組件體積將會(huì)減少一半,樣式文件位于markdown/assets/css/index.less。
codemirror體積優(yōu)化
codemirror主要分為主文件、mode相關(guān)文件和樣式文件,主文件體積異常的大,mode文件目前只選用了css/jsvascript/markdown/meta/xml五個(gè)文件,
其中markdown.js和meta.js為必須引用的,項(xiàng)目中已將常見的編程語言代碼風(fēng)格定義為css/js/xml之一,例如less/sass/scss按照css規(guī)則解析,html/vue按照xml規(guī)則解析。
優(yōu)化可從一下方面入手
- 減少codemirror主文件體積
- 減少引用的mode依賴
highlight.js體積優(yōu)化
highlight.js原本體積也是較大的,主要原因?yàn)?,編譯時(shí)為支持各種代碼語言,引入了相應(yīng)的解析文件,
項(xiàng)目?jī)?nèi)已根據(jù)常見的代碼語言進(jìn)行了一次篩選,進(jìn)行按需引入,可根據(jù)自身需求,再次對(duì)引用文件進(jìn)行刪減
參見src/markdown/libs/js/hljs.js,目前支持的語言有
import hljs from 'highlight.js/lib/highlight'
import javascript from 'highlight.js/lib/languages/javascript'
import java from 'highlight.js/lib/languages/java';
import css from 'highlight.js/lib/languages/css';
import less from 'highlight.js/lib/languages/less';
import go from 'highlight.js/lib/languages/go';
import markdown from src;
import php from 'highlight.js/lib/languages/php';
import python from 'highlight.js/lib/languages/python';
import ruby from 'highlight.js/lib/languages/ruby';
import stylus from 'highlight.js/lib/languages/stylus';
import typescript from 'highlight.js/lib/languages/typescript';
import xml from 'highlight.js/lib/languages/xml';
const languages = {
javascript,
java,
css,
less,
markdown,
go,
php,
python,
ruby,
stylus,
typescript,
xml
}
Object.keys(languages).forEach(key => {
hljs.registerLanguage(key, languages[key])
})
export default hljs;
專業(yè)版編輯器codemirror/simple.js
優(yōu)化思路:無
iconfont 體積優(yōu)化
只需要preview組件時(shí),避免引入所有icon,參考功能擴(kuò)展里icon替換方法。
升級(jí)路線
- 普通版編輯器對(duì)選中文本進(jìn)行操作功能
- 文檔目錄功能
- 優(yōu)化專業(yè)版編輯器體積
- react版開發(fā)
- ...
問題反饋
對(duì)于功能上的缺陷、使用方法和希望擴(kuò)展的功能,可以提 Issues。