龙空技术网

Markdoc:新一代 Markdown 文档内容发布框架!

高级前端进阶 3054

前言:

今天兄弟们对“htmlmark”都比较关切,看官们都想要学习一些“htmlmark”的相关知识。那么小编也在网摘上汇集了一些关于“htmlmark””的相关文章,希望看官们能喜欢,大家一起来了解一下吧!

家好,很高兴又见面了,我是"高级前端‬进阶‬",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

高级前端‬进阶

今天给大家介绍的主题是 Markdoc,即由 Stripe 开发的一种基于 Markdown 的文档格式和内容发布框架。话不多说,直接开始!

1.什么是 Markdoc

Markdoc 是一种基于 Markdown 的文档格式和内容发布框架, 它由是 Stripe 内部设计的,以满足面向用户的产品文档的需求。 Markdoc 使用标签和注释的自定义语法扩展了 Markdown,提供了一种为个人用户定制内容并引入交互元素的方法。

可以通过如下方式快速使用 Markdoc:

npm install @markdoc/markdoc// 或者yarn add @markdoc/markdoc

安装后就可以在代码中直接引用:

const Markdoc = require('@markdoc/markdoc');//或者import Markdoc from '@markdoc/markdoc';

然后调用 parse, transform 和 render 函数来渲染内容。

const source = '# Markdoc';const ast = Markdoc.parse(source);const content = Markdoc.transform(ast, /* config */);const html = Markdoc.renderers.html(content);

目前 Markdoc 在 Github 上有 6.2k 的 star、150+ 的 fork、超过 1k 的项目依赖它,是一个值得长期关注的前端项目。

2.Markdoc 如何工作2.1 Markdoc 的三大渲染器

按照设计,Markdoc 不是一种成熟的模板语言,并且不允许混合任意代码和内容。 然而,它是一种完全声明的格式,从上到下都是机器可读的:它解析为可以遍历的数据结构,以支持强大的静态分析、验证和程序化内容转换。

Markdoc 渲染器解释自定义标签和节点定义,将文档数据结构转换为可渲染节点树,最终转换为所需的输出格式。 Markdoc 框架目前包括三个渲染器:

一个 HTML 字符串渲染器一个将文档转换为 JavaScript 代码的静态 React 渲染器一个将可渲染树节点直接转换为 React 元素的动态 React 渲染器

Markdoc 的 React 渲染器使在 Markdown 内容中使用 React 组件成为可能,其支持选项卡切换器和可折叠部分等交互功能。 可以实现引入对其他输出格式和客户端框架的支持的自定义渲染器。

2.2 解析器生成器 PEG.js

PEG.js 是一个简单的 JavaScript 解析器生成器,可生成具有出色错误报告的快速解析器。开发者可以使用它来处理复杂的数据或计算机语言,并轻松构建转换器、解释器、编译器和其他工具

PEG.js 具有以下显著特征:

简单而富有表现力的语法集成词法和句法分析解析器具有开箱即用的出色错误报告基于解析表达式语法形式主义——比传统的 LL(k) 和 LR(k) 解析器更强大可从浏览器、命令行或通过 JavaScript API 使用

目前 PEG.js 在 Github 上有 4.6k 的 star、450+ 的 fork、191k 的项目依赖它。可以使用下面的示例快速使用 PEG.js:

npm install -g pegjs$ pegjs -o arithmetics-parser.js arithmetics.pegjs
2.3 markdown-it 的助力

Markdoc 的解析器建立在一个名为 markdown-it 的流行开源 Markdown 库之上。 Markdoc 使用 markdown-it 作为标记器,从 markdown-it 输出的标记数组构建抽象语法树 (AST)。

Markdown-it 解析器的特性包括:具有 100% CommonMark 支持、 扩展支持、语法插件、安全和极致的速度。目前 Markdown-it 在 Github 上有 15.2k 的 star、1.6k 的 fork、432k 的项目依赖它。可以使用下面的示例快速使用 markdown-it:

// node.js经典方式var MarkdownIt = require('markdown-it'),  md = new MarkdownIt();var result = md.render('# markdown-it rulezz!');// node.js的语法糖var md = require('markdown-it')();var result = md.render('# markdown-it rulezz!');// 没有 AMD 的浏览器,在脚本加载时添加到 window// 注意,“markdownit”中没有破折号。var md = window.markdownit();var result = md.render('# markdown-it rulezz!');

Markdoc 的自定义标记语法在 markdown-it 插件中实现, 解析标记语法的逻辑是从 peg.js 语法生成的。 Markdoc 有自己专用的渲染架构,而不是依赖 markdown-it 来生成它的输出。 为了处理 Markdoc 的自定义标签和支持多种输出格式,其开发了一个独立的渲染系统。

3.Markdoc 在 Markdown 中添加标记

Markdoc 选择 Markdown 作为起点,因为它易于阅读和推理,许多工程师和技术作家已经非常熟悉,并且得到众多现有工具的大型生态系统的广泛支持。 然而,Markdown 本身并不适合编写复杂的、高度结构化的内容,如文档等等。

Markdoc 提供了一个可扩展的系统,用于定义可以在 Markdown 内容中无缝使用的自定义标签。 使用自定义标记语法,开发者能够表达更精细的文档层次结构,插入交互式组件,并支持条件内容、内容包含和变量插值等功能。 Markdoc 对 Markdown 语法的扩展被设计为可组合且侵入性最小,在不影响可读性的情况下提供关键功能。

比如下面示例使用 .my-class-name 和 #my-id 作为 class=my-class-name 和 id=my-id 的简写。

# Examples {% #examples %}{% table .striped #exampletable %}- One- Two- Three{% /table %}

Markdoc 也允许开发者为每个标签配置自定义属性类型,比如以下示例定义了 Callout 标记的属性。 默认情况下,该属性设置为注意并根据匹配数组进行验证。

{  render: 'Callout',  children: ['paragraph', 'tag', 'list'],  attributes: {    type: {      type: String,      default: 'note',      required: true,      matches: ['caution', 'check', 'note', 'warning'],      errorLevel: 'critical',    },  }};
4.在 React 中使用 Markdoc

Markdoc 支持使用 React 开箱即用地渲染 Markdoc 语法。按照以下步骤使用 create-react-app 和 express 构建 Markdoc 应用程序。

按照 create-react-app 入门步骤创建初始应用程序设置 Markdoc 架构

schema/├── Callout.markdoc.js└── heading.markdoc.js

schema/Callout.markdoc.js 的内容如下:

// schema/Callout.markdoc.jsmodule.exports = {  render: 'Callout',  children: ['paragraph', 'tag', 'list'],  attributes: {    type: {      type: String,      default: 'note',      matches: ['check', 'error', 'note', 'warning'],    },  },};

schema/heading.markdoc.js 的内容如下:

// schema/heading.markdoc.jsconst { nodes } = require('@markdoc/markdoc');function generateID(children, attributes) {  if (attributes.id && typeof attributes.id === 'string') {    return attributes.id;  }  return children    .filter((child) => typeof child === 'string')    .join(' ')    .replace(/[?]/g, '')    .replace(/\s+/g, '-')    .toLowerCase();}module.exports = {  ...nodes.heading,  transform(node, config) {    const base = nodes.heading.transform(node, config);    base.attributes.id = generateID(base.children, base.attributes);    return base;  },};
解析服务器上的 Markdoc 文档
// ...const rawText = fs.readFileSync(file, 'utf-8');const ast = Markdoc.parse(rawText);
在服务端调用 Markdoc.transform
// server.jsconst express = require('express');const app = express();const callout = require('./schema/callout.markdoc');const heading = require('./schema/heading.markdoc');// ...app.get('/markdoc', (req, res) => {  const ast = contentManifest[req.query.path];  const config = {    tags: {      callout,    },    nodes: {      heading,    },    variables: {},  };  const content = Markdoc.transform(ast, config);  return res.json(content);});app.listen(4242, () => {  console.log(`Example app listening on port ${4242}`);});
在客户端调用 Markdoc.renderers.react
// src/App.jsimport React from 'react';import Markdoc from '@markdoc/markdoc';import { Callout } from './Callout';export default function App() {  const [content, setContent] = React.useState(null);  React.useEffect(() => {    (async () => {      const response = await fetch(        `/markdoc?` + new URLSearchParams({ path: window.location.pathname }),        { headers: { Accept: 'application/json' } }      );      if (response.status === 404) {        setContent('404');        return;      }      const content = await response.json();      setContent(content);    })();  }, []);  if (content === '404') {    return <p>Page not found.</p>;  }  if (!content) {    return <p>Loading...</p>;  }  const components = {    Callout,  };  return Markdoc.renderers.react(content, React, { components });}
启动客户端和服务器
npm run start:clientnpm run start:server

除了与React集成外,Markdoc还支持与HTML、Next.js等集成。

5.本文总结

本文主要和大家介绍 Markdoc,即新一代 Markdown 文档内容发布框架!因为篇幅原因,本文并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

参考资料

标签: #htmlmark