作为研发人员,我不相信您编写文档没有使用过markdown;如果使用markdown编写文档,那肯定是离不开一款markdown编辑器。
但是呢,市面上markdown编辑器又有蛮多槽点的:
大部分开源编辑器,它是编辑区与预览区分离

上图为掘金平台使用的编辑器,编辑区与预览区分离老碍眼了。古板,我觉得两者结合做到所见即所得会更好。
收费
做到所见即所得的markdown编辑器,当然有,例如市面上的Typora、wolai、notion。
这三款编辑器产品,用过的人都说好,强烈推荐。
它们有个共同的问题:商业化,商业化的产品目的就是搞钱,所以呢,它们有部分高级功能是需要付费的。
但对大部分无特殊功能要求的人,它们已经够用了。
不开源
MilkdownGitHub Star数: 6k+
Milkdown是什么?
官方自我介绍:
Milkdown 是一个轻量但强大的 WYSIWYG(所见即所得)的 markdown 编辑器
为什么选择Milkdown?
官方理由:

在线体验:milkdown.dev/zh-hans/onl…
GitHub: github.com/Saul-Mirone…
Mildown以插件驱动,它由核心模块、官方插件组成。
安装核心模块:
- npm i @milkdown/core @milkdown/transformer @milkdown/prose @milkdown/ctx
- 复制代码
创建Editor实例:
- import { Editor } from "@milkdown/core"
- Editor.make().create()
- 复制代码
执行,两行代码就报错啦!!
错误1:Uncaught (in promise) Timing InitReady timeout.
原因是缺少主题,再安装一个主题包:
- npm i @milkdown/theme-nord
- 复制代码
- import { Editor } from "@milkdown/core"
- import { nord } from "@milkdown/theme-nord"
- Editor.make().use(nord).create()
- 复制代码
- 再次运行,紧接着第二个报错出现。
- 复制代码
错误2:Uncaught (in promise) RangeError: Schema is missing its top node type ('doc')
错误大概意思是缺少doc节点,这是原因缺少markdown预设指令插件。
官方提供了两款插件:@milkdown/preset-commonmark、@milkdown/preset-gfm,推荐@milkdown/preset-gfm。
同样的,下载使用:
- import { Editor } from "@milkdown/core"
- import { nord } from "@milkdown/theme-nord"
- import {gfm} from "@milkdown/preset-gfm"
- Editor.make().use(nord).use(gfm).create()
- 复制代码

至此,Milkdown成功运行。接下来,只需要按需添加插件。
- npm i @milkdown/plugin-menu
- 复制代码
还是链式调用use ,注册插件。
- import { Editor } from "@milkdown/core"
- import { nord } from "@milkdown/theme-nord"
- import { gfm } from "@milkdown/preset-gfm"
- // 工具栏
- import { menu } from '@milkdown/plugin-menu';
- Editor
- .make()
- .use(nord)
- .use(gfm)
- .use(menu)
- .create()
- 复制代码
但得到的是一堆英文单词:

功能是存在的,但展示有问题,需要把一堆英文单词替换成字体图标。
- import { Editor ,ThemeIcon} from "@milkdown/core"
- import { nord } from "@milkdown/theme-nord"
- import { gfm } from "@milkdown/preset-gfm"
- // 工具栏
- import { menu } from '@milkdown/plugin-menu';
- import { getIcon } from "./icon"
- Editor
- .make()
- .use(nord.override((emotion, manager) => {
- manager.set(ThemeIcon, (icon) => {
- if (!icon) return;
- return getIcon(icon);
- });
- }))
- .use(gfm)
- .use(menu)
- .create()
- 复制代码
针对@milkdown/theme-nord 设置,添加一个getIcon 方法。
icon 文件内容过多此处不粘贴,请移动GitHub阅读:github.com/CatsAndMice…。
另外需要加载字体图标资源:
- <html>
- //...
- <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
- </html>
- 复制代码

- import { clipboard } from '@milkdown/plugin-clipboard';
- 复制代码
- //...
- import { clipboard } from '@milkdown/plugin-clipboard';
- Editor
- .make()
- .use(nord.override((emotion, manager) => {
- manager.set(ThemeIcon, (icon) => {
- if (!icon) return;
- return getIcon(icon);
- });
- }))
- .use(gfm)
- .use(menu)
- .use(clipboard)
- .create()
- 复制代码
- npm i @milkdown/plugin-tooltip
- 复制代码
设置工具栏选中后置顶:
- //...
- import { tooltipPlugin, tooltip } from '@milkdown/plugin-tooltip';
- Editor
- .make()
- .use(nord.override((emotion, manager) => {
- manager.set(ThemeIcon, (icon) => {
- if (!icon) return;
- return getIcon(icon);
- });
- }))
- .use(gfm)
- .use(menu)
- //新增
- .use(tooltip.configure(tooltipPlugin, {
- top: true,
- }))
- .create()
- 复制代码

/ 命令插件- npm i @milkdown/plugin-slash
- 复制代码
输入/,自定义添加面板功能配置:
- import { defaultValueCtx, Editor, rootCtx, ThemeIcon, themeManagerCtx } from "@milkdown/core";
- //...
- import {slash,createDropdownItem,defaultActions,slashPlugin} from '@milkdown/plugin-slash';
-
- Editor
- .make()
- // 新增
- .use(slash.configure(slashPlugin, {
- config: (ctx) => {
- return ({ content, isTopLevel }) => {
- if (!isTopLevel) return null;
- if (!content) {
- return { placeholder: "键入文字或'/'选择" };
- }
-
- const mapActions = (action) => {
- const { id = "" } = action;
- switch (id) {
- case "h1":
- action.dom = createDropdownItem(
- ctx.get(themeManagerCtx),
- "标题1",
- "h1"
- );
- return action;
- //...
- default:
- return action;
- }
- };
-
- if (content.startsWith("/")) {
- return content === "/"
- ? {
- placeholder: " ",
- actions: defaultActions(ctx).map(mapActions)
- }
- : {
- actions: defaultActions(ctx, content).map(mapActions)
- };
- }
- return null;
- };
- }
- }))
- 复制代码

设置默认内容是核心模块提供的功能,不必安装插件。
- import { defaultValueCtx, Editor, rootCtx, ThemeIcon, themeManagerCtx } from "@milkdown/core";
- //...
-
- Editor.make()
- .config((ctx) => {
- ctx.set(rootCtx, document.querySelector('#app'));
- ctx.set(defaultValueCtx, "## 点赞+评论+关注=学会");
- })
- //...
- 复制代码
添加事件:
- npm i @milkdown/plugin-listener
- 复制代码
@milkdown/plugin-listener分别对应七个生命周期函数:
| 事件名 | 事件描述 |
|---|---|
| beforeMount | 挂载前 |
| mounted | 挂载后 |
| updated | 状态更新 |
| markdownUpdated | markdown 更新 |
| blur | 失焦 |
| focus | 聚焦 |
| destroy | 销毁 |
- //...
- import { listenerCtx, listener } from "@milkdown/plugin-listener"
-
- Editor.make()
- .config((ctx) => {
- ctx.set(rootCtx, document.querySelector('#app'));
- ctx.set(defaultValueCtx, "## 点赞+评论+关注=学会");
- //新增
- ctx.get(listenerCtx).markdownUpdated((ctx, markdown, prevMarkdown) => {
- console.log(markdown);
- });
- })
- //...
- 复制代码
借助上述事件,可以自定义获取内容等功能。
Milkdown官方还有其他功能插件,例如:图片上传等,文章不逐一列举了。
文章演示代码已开放GitHub: github.com/CatsAndMice…
原创不易!如果我的文章对你有帮助,点赞+评论+关注就是对我的最大支持^_^。