vue-markdown编辑器

简介

一款使用marked和highlight.js开发的一款markdown编辑器,除常见markdown语法外,支持快捷输入、图片粘贴、代码复制、全屏编辑、预览等功能。

使用起来简单方便,只需几行代码,即可在你的页面上引入一个markdown编辑器,编辑区支持像专业编辑器那样。

编辑器涵盖了常用的markdown编辑器功能,可通过已有属性进行配置,对编辑器功能和样式进行基本的配置,也可根据需求进行深度定制。

项目地址

文档地址

示例

image.png

特点

  • 使用简单,只需要安装npm包,引入项目即可使用,不需要繁琐的初始化配置。
  • 方便扩展,根据实际需求,支持常见的功能配置,也可根据实际需求进行深度定制。
  • 体积小,加载速度快,npm包删除了highlight.js和codemirror里的依赖。
  • 灵活的主题,默认支持四种代码块风格,也可根据实际需求定制自己的主题样式
  • 功能强大,支持专业版的编辑器,使用codemirror实现编辑窗口,可识别markdown语法
  • 键盘事件监听,如保存、粘贴、回车时上次输入语法判断等
  • 可扩展性强,除了提供的属性配置编辑器,也可直接在原有组件基础上进行二次开发

实现思路

通过监听文本输入区域内内容的变化,实时将输入的markdown语法进行编译,并渲染到预览区域。

编辑器大致分为头部菜单栏、左侧内容输入区域、右侧预览区域三个部分。
头部菜单主要为定自定义标题区域和菜单按钮,菜单按钮可通过配置文件进行显示和隐藏;左侧编辑区域,简单版使用textarea开发,满足基本需求,
专业版使用codemirror开发,编辑区域支持手动输入文本和通过头部菜单插入;右侧预览区域可实时预览输入文本,并可通过菜单按钮,进行编辑区域和预览区域的切换。

安装方式

使用npm安装

  1. 安装依赖
npm i -S vue-meditor

将组件复制到项目内

  1. 将git仓库代码拉到本地
git clone https://github.com/zhaoxuhui1122/vue-markdown.git
  1. 复制src文件夹下内容至components文件夹下

在项目使用

npm包安装时

简单版

import Markdown from 'vue-meditor'

专业版

import { MarkdownPro } from 'vue-meditor'

预览组件

import { MarkdownPreview } from 'vue-meditor'

复制组件到本地时(推荐)

简单版

import Markdown from '@/components/markdown/...';

专业版

import MarkdownPro from '@/components/markdown/pro';

预览组件

import MarkdownPreview from '@/components/markdown/preview';

在页面内使用

<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数据双向绑定设置编辑器内容和获取编辑器的值。

width

  • Type: String/Number
  • Default: auto

编辑器的初始化宽度。

height

  • Type: Number
  • Default: 600

编辑器的初始化高度。

bordered

  • Type: Boolean
  • Default: true

编辑器是否含有边框。

toolbars

  • Type: Object
  • Default: 参见下表

头部菜单按钮,通过设置true or false控制决定是否显示,目前配置支持持控制按钮显示隐藏,后续将支持根据配置显示排列顺序。

名称 说明 默认是否显示
strong 粗体
italic 斜体
overline 删除线
h1 标题1
h2 标题2
h3 标题3
h4 标题4
h5 标题5
h6 标题6
hr 分割线
quote 引用
ul 无序列表
ol 有序列表
code 代码块
link 链接
image image
table 表格
checked 已完成列表
notChecked 未完成列表
preview 预览
split 分屏模式切换
print 打印
theme 主题切换
fullscreen 全屏
exportmd 导出为*.md文件
importmd 导入本地*.md文件
save 保存按钮
clear 清空内容

theme

  • Type: String
  • Default: light

编辑器代码块主题,目前支持lightdarkoneDarkgitHub四种代码块风格,可通过自定义theme并修改样式文件进行主题定制。

自定义theme时,预览区域的会增加一个为markdown-theme-[theme]class

autoSave

  • Type: Boolean
  • Default: false

是否开启自动保存,设置为开启时可通过绑定on-save事件获取编辑器内的值和代码块主题。

<Markdown @on-save="handleOnSave"/>
 handleOnSave({value, theme}){
        console.log(value, theme);
    }

interval

  • Type: Number
  • Default: 10000

自动保存间隔时间,单位:mm,默认10000mm,需要autoSave = true时才有效。

exportFileName

  • Type: String
  • Default: unnamed

导出的md文件名称,默认unnamed.md。

markedOptions

  • Type: Object
  • Default: {}

marked配置项,可以根据需求自定义。

<Markdown :markedOptions="{baseUrl:'http://***.oss-cn-shanghai.aliyuncs.com/'}"/>

isPreview

  • Type: Boolean
  • Default: false

是否是预览模式,开启时可作为一个预览组件使用,与预览组件功能一致。

copyCode

  • Type: Boolean
  • Default: true

是否支持复制代码块内的内容。

copyBtnText

  • Type: String
  • Default: 复制代码

复制代码按钮显示文字。

预览组件基本属性

initialValue

  • Type: String/Number
  • Default: ''

预览组件初始化内容,支持动态更新。

theme

  • Type: String
  • Default: light

代码块主题,与编辑器编辑器代码块主题一致。

markedOptions

  • Type: Object
  • Default: {}

marked配置项,与编辑器内该配置一致。

copyCode

  • Type: Boolean
  • Default: true

是否支持复制代码块内的内容。

copyBtnText

  • Type: String
  • Default: 复制代码

复制代码按钮显示文字。

on-ready

编辑器初始化完成时触发,返回值为Object,包含组件本身和insertContent方法。

on-save

编辑器保存事件,自动保存或者手动保存时触发,支持ctrl+scommand+s触发保存,返回值类型为Object,包含当前输入值value和选择的代码块主题theme

on-paste-image

监听编辑器粘贴图片事件,在编辑区域内手动粘贴图片时触发,可用于支持粘贴插入图片文件,返回file文件,上传文件后可结合on-ready事件内返回的insertContent插入图片。

on-copy

复制代码块内容,触发时返回当前代码块的text,copyCode开启时才有效。

二次开发

粘贴插入图片

on-paste-image虽然可以支持图片粘贴事件的监听,但不会处理图片上传至服务器并将链接插入编辑器这段逻辑。

目前如果想要支持粘贴插入图片,需要在on-paste-image方法里上传图片文件,拿到图片地址后,使用on-ready方法里返回的insertContent方法插入图片。

上述操作显得过于复杂,可以直接在源码里扩展mixins里的handlePaste方法,图片上传完成后,直接调用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(`![image](imgUrl)`)
        }
    }
}

支持流程图、甘特图等语法

目前编辑器只支持常见code语法,如果需要实现如流程图等功能,需要进一步扩展,以实现一个简单的流程图为例,具体实现思路如下:

默认情况下,markedjs会使用renderer.code方法对输入的代码块进行解析,并会借助highlight.js支持语法高亮。
可以将流程图语法输入到代码块内,并标明语言,重写marked.Renderer的code解析方法,结合结合flowchart.js路程图代码进行解析,返回文本内容。

修改`/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, {/*相关配置*/});
        return  dom.innerHTML;
    } else {// 默认解析
        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语法转换

项目内使用的`index.js均为其默认配置功能,如需要特殊转换,可重写其内部的解析方法,即重写其renderer相关方法
参考文档

自动生成文档目录

预览区域和文档预览组件暂不支持自动生成目录,实现自动生成目录思路目前想到的大致有

  • 重写renderer.heading 方法,为生成的标题添加id,输入特定快捷键,如[TOC]时,查找预览区域内的的所有标题标签,分析等级关系,生成目录标签

icon替换

项目内所有的icon和命名参考/assets/font/index.html,替换时需注意,预览区域的checkbox为icon,注意一并替换,
修改/assets/css/index.less内的input[type="checkbox"]:after样式。

代码体积优化

公共代码提取

npm包构建时,三个组件完全独立,没有抽离公共文件,所以,当同一个项目内引入其中的两个或三个组件都引入时,存在一定的重复代码,
主要为highlight.jsmarkediconfont、css样式几个部分。

解决方式:将组件复制到本地项目,打包时将这些文件作为公共文件抽离出来。

注意:三个组件中使用的iconfont为同一套,如果只是单纯的使用preview组件,
将会引入整个项目所使用的iconfont,可删除iconfont的引入,
重写input[type="checkbox"]的样式,preview组件体积将会减少一半,样式文件位于markdown/assets/css/index.less

codemirror体积优化

codemirror主要分为主文件、mode相关文件和样式文件,主文件体积异常的大,mode文件目前只选用了css/jsvascript/markdown/meta/xml五个文件,
其中markdown.js和meta.js为必须引用的,项目中已将常见的编程语言代码风格定义为css/js/xml之一,例如less/sass/scss按照css规则解析,html/vue按照xml规则解析。
优化可从一下方面入手

  • 减少codemirror主文件体积
  • 减少引用的mode依赖

highlight.js体积优化

highlight.js原本体积也是较大的,主要原因为,编译时为支持各种代码语言,引入了相应的解析文件,
项目内已根据常见的代码语言进行了一次筛选,进行按需引入,可根据自身需求,再次对引用文件进行删减

参见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;

专业版编辑器codemirror/simple.js

优化思路:无

iconfont 体积优化

只需要preview组件时,避免引入所有icon,参考功能扩展里icon替换方法。

升级路线

  • 普通版编辑器对选中文本进行操作功能
  • 文档目录功能
  • 优化专业版编辑器体积
  • react版开发
  • ...

问题反馈

对于功能上的缺陷、使用方法和希望扩展的功能,可以提 Issues

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容