常见示例
使用 Markdoc,可以轻松添加通常与文档站点关联的功能。以下示例涵盖循环、语法高亮、标签页等内容。
如果您正在寻找其他示例代码,请查看我们的示例仓库集合,或查看本站源码。
循环
Markdoc 不支持直接在文档中编写循环。如果需要遍历内容,请在自定义 Node 或 Tag 的 transform 函数中,或在自定义 React 组件 中进行。
import { Tag } from '@markdoc/markdoc';
export const group = {
render: 'Group',
attributes: {
items: { type: Array }
},
transform(node, config) {
const attributes = node.transformAttributes(config);
const children = node.transformChildren(config);
for (const item of attributes.items) {
/* 对每个项目执行某些操作 */
}
return new Tag('Group', attributes, children);
}
};
{% group items=[1, 2, 3] /%}
语法高亮
您可以通过创建自定义 fence 节点 为代码块添加语法高亮。此示例展示了如何使用 Prism 实现。
// [源码示例](https://github.com/markdoc/docs/blob/main/components/Code.js)
import 'prismjs';
import 'prismjs/themes/prism.css';
import Prism from 'react-prism';
export function Fence({ children, language }) {
return (
<Prism key={language} component="pre" className={`language-${language}`}>
{children}
</Prism>
);
}
const fence = {
render: 'Fence',
attributes: {
language: {
type: String
}
}
};
const content = Markdoc.transform(ast, {
nodes: {
fence
}
});
Markdoc.renderers.react(content, React, {
components: {
Fence
}
});
Switch 语句
您可以使用自定义 Markdoc 标签创建自己的 switch/case 语义。
import { transformer } from '@markdoc/markdoc';
/** @type {import('@markdoc/markdoc').Config} */
const config = {
tags: {
switch: {
attributes: { primary: { render: false } },
transform(node, config) {
const attributes = node.transformAttributes(config);
const child = node.children.find(
(child) => child.attributes.primary === attributes.primary
);
return child ? transformer.node(child, config) : [];
}
},
case: {
attributes: { primary: { render: false } }
}
}
};
然后可以在文档中使用:
{% switch $item %}
{% case 1 %}
Case 1
{% /case %}
{% case 2 %}
Case 2
{% /case %}
{% /switch %}
目录
要创建目录,首先从页面内容中收集所有标题:
// [源码示例](https://github.com/markdoc/docs/blob/bae62d06109e3e699778fe901c8015d41b1c7c9f/pages/_app.js#L58-L79)
function collectHeadings(node, sections = []) {
if (node) {
// 匹配所有 h1、h2、h3 等标签
if (node.name.match(/h\d/)) {
const title = node.children[0];
if (typeof title === 'string') {
sections.push({
...node.attributes,
title
});
}
}
if (node.children) {
for (const child of node.children) {
collectHeadings(child, sections);
}
}
}
return sections;
}
const content = Markdoc.transform(ast);
const headings = collectHeadings(content);
然后,将标题渲染为列表:
// [源码示例](https://github.com/markdoc/docs/blob/main/components/Shell/TableOfContents.js)
function TableOfContents({ headings }) {
const items = headings.filter((item) => [2, 3].includes(item.level));
return (
<nav>
<ul>
{items.map((item) => (
<li key={item.title}>
<a href={`#${item.id}`}>{item.title}</a>
</li>
))}
</ul>
</nav>
);
}
最后,使用 ID 注解 为标题添加 ID。
# 我的标题 {% #my-id %}
标签页
首先,创建 Markdoc 标签。
import { Tag } from '@markdoc/markdoc';
const tabs = {
render: 'Tabs',
attributes: {},
transform(node, config) {
const labels = node
.transformChildren(config)
.filter((child) => child && child.name === 'Tab')
.map((tab) => (typeof tab === 'object' ? tab.attributes.label : null));
return new Tag(this.render, { labels }, node.transformChildren(config));
}
};
const tab = {
render: 'Tab',
attributes: {
label: {
type: String
}
}
};
/** @type {import('@markdoc/markdoc').Config} */
const config = {
tags: {
tabs,
tab
}
};
然后,创建映射到 tab 和 tabs 标签的 Tab 和 Tabs React 组件:
// components/Tabs.js
import React from 'react';
export const TabContext = React.createContext();
export function Tabs({ labels, children }) {
const [
currentTab,
setCurrentTab
] = React.useState(labels[0]);
return (
<TabContext.Provider value={currentTab}>
<ul role="tablist">
{labels.map((label) => (
<li key={label}>
<button
role="tab"
aria-selected={label === currentTab}
onClick={() => setCurrentTab(label)}
>
{label}
</button>
</li>
))}
</ul>
{children}
</TabContext.Provider>
);
};
// components/Tab.js
import React from 'react';
import { TabContext } from './Tabs';
export function Tab({ label, children }) {
const currentTab = React.useContext(TabContext);
if (label !== currentTab) {
return null;
}
return children;
}
并在文档中使用这些标签。
{% tabs %}
{% tab label="React" %}
React 内容放在这里
{% /tab %}
{% tab label="HTML" %}
HTML 内容放在这里
{% /tab %}
{% /tabs %}