在 vue3 中使用 markdown 编辑器 md-editor-v3

本文将介绍编辑器的使用和伴随的某些开发技巧。
该编辑器支持的功能有:基础的md编辑、md语法快捷键、记录保存、暗黑主题、图片上传/复制图片上传/裁剪图片上传、格式化内容、浏览器全屏/屏幕全屏、仅预览模式等功能,静待使用。
详细的编辑器api参考:文档。

  • 图片裁剪预览
在 vue3 中使用 markdown 编辑器 md-editor-v3
文章图片

【在 vue3 中使用 markdown 编辑器 md-editor-v3】在 vue3 中使用 markdown 编辑器 md-editor-v3
文章图片

  • 编辑器预览
在 vue3 中使用 markdown 编辑器 md-editor-v3
文章图片

1. 基本使用 这里演示两种环境三种写法:
1.1 npm安装用法
这种方式支持两种写法,除了.vue模板写法,还有jsx语法。
安装
yarn add md-editor-v3

.vue模板基础使用

jsx语法基础使用
import { defineComponent, ref } from 'vue'; import MdEditor from 'md-editor-v3'; import 'md-editor-v3/lib/style.css'; export default defineComponent({ name: 'MdEditor', setup() { const text = ref(''); return () => ( (text.value = https://www.it610.com/article/v)} /> ); } });

1.2 script标签引入用法
链接可前往https://cdn.jsdelivr.net搜索md-editor-v3

注册组件
const App = { data() { return { text: 'Hello Editor!!' }; } }; Vue.createApp(App).use(MdEditorV3).mount('#md-editor-v3');

使用组件

2. 渲染内容 该编辑器使用marked解析mdhtml,没有扩展语法。
通常来讲,编辑内容存储为md格式,渲染内容时,通过marked解析为html。
2.1 默认渲染
1.3.0版本后,编辑器支持了previewOnly功能,可以直接使用编辑器预览文章,没有bar、编辑等等。

2.2 主动解析演示
这种方式用于保存md,然后自行解析md内容。
import marked from 'marked'; // 代码高亮 import hljs from 'highlight.js'; // 自选代码高亮样式 import 'highlight.js/scss/atom-one-dark.scss'; // 用于记录标题数,根据业务代替 let count = 0; // 记录标题内容 const headstemp = []; // marked设置 const rendererMD = new marked.Renderer(); // 调整标题内容 rendererMD.heading = (text, level) => { headstemp.push({ text, level }); count++; return `${text}`; }; // 设置图片内容,统一显示一张缓存图,用于懒加载~ rendererMD.image = (href, _, text) => `在 vue3 中使用 markdown 编辑器 md-editor-v3
文章图片
`; marked.setOptions({ highlight(code) { return hljs.highlightAuto(code).value }, renderer: rendererMD }); // 这里的html就是插入到页面的元素文本了 const html = marked('## md内容');

2.3 标题导航实现
上面的例子headstemp记录了解析过程中的所有标题,作用是借助UI库的组件Anchor,构建一个标题导航。
下面演示一个基于ant-design-vue的版本,如果你使用的UI库是类似的锚点组件,那么代码将只需要小改动即可。代码使用jsx语法,vue模板语法请自行分离代码~
Recursive.tsx 导航中的链接组件
import { Anchor } from 'ant-design-vue'; import { defineComponent, PropType } from 'vue'; const { Link } = Anchor; export interface Head { text: string; level: number; }export interface TocItem extends Head { anchor: string; children?: Array; }const Recursive = defineComponent({ props: { tocItem: { type: Object as PropType, default: () => [] } }, setup({ tocItem }) { return ( {tocItem.children && tocItem.children.map((item) => )} ); } }); export default Recursive;

Topicfy.tsx 用于生成整个导航内容
import { Anchor } from 'ant-design-vue'; import { computed, defineComponent, PropType, ref, watch } from 'vue'; import Recursive, { Head, TocItem } from './Recursive'; const Topicfy = defineComponent({ props: { // 解析得到的标题列表 heads: { type: Array as PropType> } }, setup(props) { const topics = computed(() => { const tocItems: TocItem[] = []; // 标题计数器 let count = 0; const add = (text: string, level: number) => { count++; const item = { anchor: `heading-${count}`, level, text }; if (tocItems.length === 0) { // 第一个 item 直接 push tocItems.push(item); } else { let lastItem = tocItems[tocItems.length - 1]; // 最后一个 itemif (item.level > lastItem.level) { // item 是 lastItem 的 children for (let i = lastItem.level + 1; i <= 6; i++) { const { children } = lastItem; if (!children) { // 如果 children 不存在 lastItem.children = [item]; break; } // 重置 lastItem 为 children 的最后一个 item lastItem = children[children.length - 1]; if (item.level <= lastItem.level) { // item level 小于或等于 lastItem level 都视为与 children 同级 children.push(item); break; } } } else { // 置于最顶级 tocItems.push(item); } } }; props.heads?.forEach((item) => { add(item.text, item.level); }); return tocItems; }); return () => ({topics.value.map((item) => ( ))}); } }); export default Topicfy;

该组件是19年参考了网络上的实现完成的,非本人完全原创,react版本参考 Topicfy
2.4 获取html代码 编辑器考虑到了可能后端不存储md格式的文本,而是html内容,所以提供了onHtmlChanged方法,用于编辑内容变化后,marked编译了内容的回调,入参即是html内容。

jsx语法相同。
3. 编辑器的功能演示 3.1 扩展库链接
编辑器扩展内容大多使用了cdn,考虑了无外网情况,支持了内网链接扩展,演示(假设外部库都在根目录下):

v1.2.0版本目前支持上述链接,图标链接将在后续补丁中添加。
3.2 工具栏自定义
默认的全部工具栏,并且每个功能都绑定了快捷键,如果需要选择性显示工具栏,提供了两个api:toolbarstoolbarsExclude,前者显示数组中的全部,后者屏蔽数组中的全部,后者的权重更大。下面是个参考:
案例不显示github按钮

3.3 扩展语言
编辑器默认内置了中文和英文,并且两者都可以通过扩展api覆盖,该功能主要用来设置内容提示,比如弹窗中的标题等。
扩展一门语言,我们取名为zh-NB

如果key = 'zh-CN',就可以实现中文覆盖,依次类推。
3.4 主题切换
这一块相对比较简单了,内置了暗黑主题和默认主题,通过themeapi切换,demo如下:

4. 结尾 更多的更新请关注:[md-editor-v3]()

    推荐阅读