MDX 让 Markdown 步入组件时代

sxkk20082年前知识分享195

前言

在 MDX 出现之前,将 JSX 与 Markdown 混合书写时,Markdown 的优势就不存在了。 通常采是用基于模板字符串的方式,因此就需要大量的转义和繁琐的语法。

MDX 试图让书写 Markdown 和 JSX 更简单、更具有表现力。当你将组件 (甚至可以是动态的或需要加载数据的组件)与 Markdown 混合书写时,你将写出更有趣的内容。

MDX 是什么

MDX 是一种书写格式,允许你在 Markdown 文档中无缝地插入 JSX 代码。 你还可以导入(import)组件,例如交互式图表或弹框,并将它们嵌入到内容当中。

前提条件

您应该熟悉 markdown 语法和 JavaScript 语法 (特别是 JSX)。

MDX 示例

比如官网的一个例子,如下代码

import {Chart} from './snowfall.js'
export const year = 2018

# Last year’s snowfall

In {year}, the snowfall was above average.
It was followed by a warm spring which caused
flood conditions in many of the nearby rivers.

<Chart year={year} color="#fcb32c" />

展示效果

image.png

如何使用

create-react-app 中 只需要安装 @mdx-js/loadercreate-react-app@5 支持自定义 loader,代码示例如下

src/App.jsx

/* eslint-disable import/no-webpack-loader-syntax */
import Content from '!@mdx-js/loader!./content.mdx'

export default function App() {
  return <Content />
}

src/content.mdx

# Hello, world!

This is **markdown** with <span style={{color: "red"}}>JSXspan>: MDX!

如果是 webpack 项目只需要加一个 @mdx-js/loader 就可以支持

module.exports = {
  module: {
    // …
    rules: [
      // …
      {
        test: /\.mdx?$/,
        use: [
          // `babel-loader` is optional:
          { loader: 'babel-loader', options: {} },
          {
            loader: '@mdx-js/loader',
            /** @type {import('@mdx-js/loader').Options} */
            options: {
              /* jsxImportSource: …, otherOptions… */
            },
          },
        ],
      },
    ],
  },
}

插件支持

MDX 也支持插件配置,也就是原先的 markdown 插件

比如要让 markdown 支持表格和 checkboxList ,可以使用 remark-gfm 插件

比如要让 markdown 支持数学公式 可以使用 rehype-katex 插件

在的 option 可以传入参数,代码如下:

import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'


module.exports = {
  module: {
    // …
    rules: [
      // …
      {
        test: /\.mdx?$/,
        use: [
          // `babel-loader` is optional:
          {loader: 'babel-loader', options: {}},
          {
            loader: '@mdx-js/loader',
            /** @type {import('@mdx-js/loader').Options} */
            options: {remarkPlugins: [remarkGfm], rehypePlugins: [rehypeKatex]}}
          }
        ]
      }
    ]
  }
}

当然也可以支持自定义插件,比如 img 要加上默认样式,限制其最大宽度,href 跳转要改成新窗口打开等 ,详情可以查看 unifiedjs.com

定义组件

在支持了 mdx 之后, 可以给 mdx 定义组件, 比如给 H1 传递默认样式等,或者加入默认组件,例如 https://beta.reactjs.org/ 就加入了很多自定义组件,代码示例如下:

import React from 'react'
import ReactDom from 'react-dom'
import Post from './post.mdx' // Assumes an integration is used

export const H1 = ({ className, ...props }: HeadingProps) => (
  <H1 className={cn(className, 'text-5xl font-bold leading-tight')} {...props} />
)

const components = {
  h1: H1,
}

ReactDom.render(<Post components={components} />, document.querySelector('#root'))

MDX provider

每个文档都要传 components 很麻烦? 我们可以在最外层导入 MDXProvider,提供 components,组件就可以了。

import React from 'react'
 import ReactDom from 'react-dom'
 import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.
 import {Heading, /* … */ Table} from './components/index.js'
+import {MDXProvider} from '@mdx-js/react'

 const components = {
   h1: Heading.H1,
   // …
   table: Table
 }

 ReactDom.render(
-  <Post components={components} />,
+  <MDXProvider components={components}>
+    <Post />
+  MDXProvider>,
   document.querySelector('#root')
 )

在线运行

以上实例都是在构建时运行,那么能到让 MDX 在浏览器运行呢,比如未来,博客的编辑器支持 MDX, 那么我们的博客文章页面就可以有更多交互了。

比如数据通过服务端返回,下面代码是 next.js 示例

import { useState, useEffect, Fragment } from 'react'
import * as runtime from 'react/jsx-runtime.js'
import { compile, run } from '@mdx-js/mdx'

export default function Page({ code }) {
  const [mdxModule, setMdxModule] = useState()
  const Content = mdxModule ? mdxModule.default : Fragment

  useEffect(() => {
    ;(async () => {
      setMdxModule(await run(code, runtime))
    })()
  }, [code])

  return <Content />
}

export async function getStaticProps() {
  const code = String(await compile('# hi', { outputFormat: 'function-body' /* …otherOptions */ }))
  return { props: { code } }
}

实时运行

列如官方的 palyground,就可以实时运行,左边写代码,右侧展示文档

s19130202242022

最简单的代码如下

import { VFile } from 'vfile'
import { evaluate } from '@mdx-js/mdx'

const value = '## header'

const file = new VFile({ basename: 'example.mdx', value })

const { default: Result } = await evaluate(file, runtime)

这个 Result 就是一个 react 组件,如要实现相似功能可以参考官方 github 中的 editor.client.js。

小结

Markdown 所有程序员都爱,Markdown 在标准化、结构化、组件化都存在硬伤,有了 MDX ,Markdown 有了富交互、内容形态的编写,希望 MDX 尽早尽快更多的投入到的互联网产品中,也希望 MDX 的解析也来越标准化。

推荐阅读 精读《对 Markdown 的思考》

以上就是本文全部内容,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

相关文章

用 TailwindCSS 打造美好祝福:给大家送上新年祝福的 demo

前几天,群里有有伙伴问,在 Next.js 中,该使用哪个 UI 框架?我强烈推荐 tailwindcss, 因为你不再需要写 CSS 代码了,这 2 天还录制了一个视频,包括录屏、录音、剪辑、动画等...

我们使用相同步骤,建立标签表,并且添加数据到表中。

建立表关联

题目表和标签表是多对一的关系,一个标签下有多道题目,一个题目只有一个标签

Notion 建立表关联

在题目表添加属性tag,选择 Relation,让后选择“标签”表

Notion 建立表关联

这样题目表和标签表就建立了关系

创建 Notion 集成

在使用 Notion API 之前,我们需要创建一个 Notion 的应用集成,获取 API Key。 打开 https://www.notion.so/my-integrations,打开 Notion 集成页面,登录自己的账号,点击 New integration 创建一个新的应用:

创建 Notion 集成

名称可以自己起,上传一个 LOGO,然后关联一个 Notion 的工作空间:

创建 Notion 集成,填写信息

点击提交,这个应用就创建好了,在跳转的新页面里,把Internal Integration Token复制下来,不要泄露,否则拿到这个 key 的人都能操作你的笔记啦。

复制 Notion Token

使用 Notion 数据库进行 Next.js 应用全栈开发

文章为稀土掘金技术社区首发签约文章,14 天内禁止转载,14 天后未获授权禁止转载,侵权必究!前言在上一篇中,我们使用了 strapi 和 Next.js 开发了一个简易微博,但是我没有部署上线,因为...

为什么要写这个脚本

最近开了个前端公众号,需要推送一些优质的文章,但由于时间的关系,原创内容太少,常规的做法是转载一些优秀的文章到自己的公众号。

流程

image.png

[油猴脚本]文章拷贝助手,文章一键拷贝到微信公众平台

文章拷贝助手,文章一键拷贝到微信公众平台、知乎 下载 markdown为什么要写这个脚本最近开了个前端公众号,需要推送一些优质的文章,但由于时间的关系,原创内容太少,常规的做法是转载一些优秀的文章到自...

使用 esbuild 来打包一个 React 库

前言esbuild 的大名相信大家也有耳闻,它是一个非常快的 Javascript 打包工具,用 GO 语言编写,是 figma 的 cto Evan Wallace 著作的,一个 30 min 的...

AI绘画:当机器学习遇上创意

AI绘画:当机器学习遇上创意

  在过去的几十年里,人工智能一直是科技领域中备受关注的热门话题之一。随着技术的不断进步,AI也逐渐融入了我们的生活,成为了我们工作、学习和休闲的重要工具和娱乐手段。而AI绘...

人工智能技术:改变世界的机遇与挑战

人工智能技术:改变世界的机遇与挑战

  人工智能技术正日益成为21世纪最为瞩目的领域之一。随着科技的飞速发展,人们对于人工智能技术的期望也日益增加。然而,这项前沿技术所带来的机遇与挑战亦不容忽视。本文将对人工智...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。