MDX 对于博客、幻灯片和组件文档等内容来说是一个杀手级功能。它允许您编写 Markdown 而无需担心 HTML 元素、其格式和位置,并在必要时加入自定义 React 组件的魔力。
让我们利用这种魔力,看看如何通过用我们自己的 MDX 组件替换 Markdown 元素来自定义 MDX。在此过程中,我们将介绍在使用这些组件时的“短代码”概念。
提醒一下,这里的代码片段基于 GatsbyJS 和 React,但 MDX 也可以与 不同的框架 一起编写。如果您需要了解 MDX 的入门知识,请 先从这里开始。本文在此基础上扩展了更多高级概念。
设置布局
我们几乎总是希望在通用布局中渲染基于 MDX 的页面。这样,就可以将它们与我们网站上的其他组件一起排列。我们可以使用正在使用的 MDX 插件指定默认的 Layout
组件。例如,我们可以使用 gatsby-plugin-mdx
插件定义一个布局,如下所示
{
resolve: `gatsby-plugin-mdx`,
options: {
defaultLayouts: {
default: path.resolve('./src/templates/blog-post.js'),
},
// ...other options
}
}
这将需要 src/templates/blog-post.js
文件包含一个组件,该组件将渲染它接收到的 children
属性。
import { MDXRenderer } from 'gatsby-plugin-mdx';
function BlogPost({ children }) {
return (
<div>{children}</div>
);
}
export default BlogPost;
如果我们正在以编程方式创建页面,则必须使用名为 MDXRenderer
的组件来实现相同的功能,如 Gatsby 文档 中所述。
自定义 Markdown 元素
虽然 MDX 是一种允许我们编写自定义 HTML 和 React 组件的格式,但它的强大之处在于使用自定义内容呈现 Markdown。但是,如果我们想自定义这些 Markdown 元素在屏幕上的呈现方式呢?
我们当然可以 为其编写一个 remark 插件,但 MDX 为我们提供了一个更好、更简单的解决方案。默认情况下,这些是 Markdown 呈现的一些元素
名称 | HTML 元素 | MDX 语法 |
---|---|---|
段落 | <p> | |
标题 1 | <h1> | # |
标题 2 | <h2> | ## |
标题 3 | <h3> | ### |
标题 4 | <h4> | #### |
标题 5 | <h5> | ##### |
标题 6 | <h6> | ###### |
无序列表 | <ul> | - |
有序列表 | <ol /> | 1. |
图片 | <img /> |  |
为了用我们的自定义 React 组件替换这些默认值,MDX 附带了一个名为 MDXProvider
的 Provider
组件。它依赖于 React Context API 来注入新的自定义组件并将它们合并到 MDX 提供的默认组件中。
import React from 'react';
import { MDXProvider } from "@mdx-js/react";
import Image from './image-component';
function Layout({ children }) {
return (
<MDXProvider
components={{
h1: (props) => <h1 {...props} className="text-xl font-light" />
img: Image,
}}
>
{children}
</MDXProvider>
);
}
export default Layout;
在此示例中,MDX 文件中的任何 H1 标题(#
)都将被 Provider
组件的 prop 中指定的自定义实现替换,而所有其他元素将继续使用默认值。换句话说,MDXProvider
能够获取我们用于 H1 元素的自定义标记,将其与 MDX 默认值合并,然后在我们在 MDX 文件中编写标题 1(#
)时应用自定义标记。
MDX 和自定义组件
自定义 MDX 元素很棒,但如果我们想将我们自己的组件引入其中呢?
---
title: Importing Components
---
import Playground from './Playground';
Here is a look at the `Playground` component that I have been building:
<Playground />
我们可以将组件导入 MDX 文件,并像使用任何 React 组件一样使用它。当然,虽然这对于博客文章中的组件演示等内容很有效,但如果我们想在所有博客文章中都使用 Playground 呢?将它们导入所有页面会很麻烦。相反,MDX 为我们提供了使用短代码的选项。以下是 MDX 文档对短代码的描述
[短代码]允许您将组件公开到应用程序或网站中的所有文档。对于常见的组件(如 YouTube 嵌入、Twitter 卡片或文档中经常使用的任何其他内容)来说,这是一个有用的功能。
要在 MDX 应用程序中包含短代码,我们必须再次依赖 MDXProvider
组件。
import React from 'react';
import { MDXProvider } from "@mdx-js/react";
import Playground from './playground-wrapper';
function Layout({ children }) {
return (
<MDXProvider
components={{
h1: (props) => <h1 {...props} className="text-xl font-light" />
Playground,
}}
>
{children}
</MDXProvider>
);
}
export default Layout;
将自定义组件包含到 components 对象中后,我们就可以在 MDX 文件中使用它们,而无需导入。
---
title: Demoing concepts
---
Here's the demo for the new concept:
<Playground />
> Look ma! No imports
直接操作子组件
在 React 中,我们获得了用于使用 React.Children
操作子级的顶级 API。我们可以使用它们将新属性传递给子组件,从而更改其顺序或确定其可见性。MDX 为我们提供了一个特殊的包装器组件来访问 MDX 传递的子组件。
要添加包装器,我们可以像以前一样使用 MDXProvider
import React from "react";
import { MDXProvider } from "@mdx-js/react";
const components = {
wrapper: ({ children, ...props }) => {
const reversedChildren = React.Children.toArray(children).reverse();
return <>{reversedChildren}</>;
},
};
export default (props) => (
<MDXProvider components={components}>
<main {...props} />
</MDXProvider>
);
此示例反转子级,以便它们按我们编写的相反顺序出现。
我们甚至可以放开手脚,在所有 MDX 子级进入时为它们添加动画
import React from "react";
import { MDXProvider } from "@mdx-js/react";
import { useTrail, animated, config } from "react-spring";
const components = {
wrapper: ({ children, ...props }) => {
const childrenArray = React.Children.toArray(children);
const trail = useTrail(childrenArray.length, {
xy: [0, 0],
opacity: 1,
from: { xy: [30, 50], opacity: 0 },
config: config.gentle,
delay: 200,
});
return (
<section>
{trail.map(({ y, opacity }, index) => (
<animated.div
key={index}
style={{
opacity,
transform: xy.interpolate((x, y) => `translate3d(${x}px,${y}px,0)`),
}}
>
{childrenArray[index]}
</animated.div>
))}
</section>
);
},
};
export default (props) => (
<MDXProvider components={components}>
<main {...props} />
</MDXProvider>
);
总结
MDX 在开箱即用时就具有灵活性,但通过扩展插件可以使其功能更加强大。感谢 gatsby-plugin-mdx
,我们在短时间内实现了以下功能:
- 创建帮助格式化 MDX 输出的默认 Layout 组件。
- 用自定义组件替换从 Markdown 渲染的默认 HTML 元素。
- 使用短代码来摆脱在每个文件中导入组件的麻烦。
- 直接操作子级以更改 MDX 输出。
同样,这只是 MDX 为简化静态网站内容创作所做贡献的一小部分。