使用create-react-app和Typescript时如何在构建时加载.md文件?

8

我试图在构建时加载一个Markdown文件。 我设置了一个使用Typescript的create-react-app,并使用material_ui博客主题。

import ReactMarkdown from 'markdown-to-jsx';
import post1 from './blog-post.1.md';

export default function Main(props: MainProps) {
  const classes = useStyles();
  const { posts, title } = props;

  return (
    <Grid item xs={12} md={8}>
      <Typography variant="h6" gutterBottom>
        {title}
      </Typography>
      <Divider />
      {posts.map((post) => (
        <Markdown className={classes.markdown} key={post.substring(0, 40)}>
          {post}
        </Markdown>
      ))}
    </Grid>
  );
}

如果一个帖子被指定为Markdown格式,它将会正确地呈现:const post = '#示例博客文章####2020年4月1日由[Olivier](/)发布'

问题在于.md文件没有加载到变量中。

当运行npm start时,HTML 显示:/static/media/blog-post.1.09e7833f.md。这是什么原因,文件路径中的 09e7833f 是什么意思?

我希望在构建时加载文件,这样就可以更快地在像 Netlify 这样的地方提供服务了。我不想使用类似这个答案建议的异步获取。

我想我需要创建一个 webpack loader 吗?问题是 create-react-app 在 node_modules 中有一个webpack.config.js,这意味着在新构建期间自定义内容将被覆盖。如果我无法进行配置,则我认为使用raw-loader 没有帮助。

我也尝试过 import post1 from 'raw-loader!./blog-post.1.md';,如此处建议,但 TypeScript 不知道该怎么做。

任何建议都将有所帮助。

2个回答

8

这似乎是之前曾经提出过的问题(https://github.com/facebook/create-react-app/issues/3025),但不幸的是,目前在JavaScript/TypeScript中直接导入markdown字符串似乎是不可能的。

当使用create-react-app导入一个md文件时,它似乎会得到一个包含文件URL(带有十六进制字符串)的字符串,而不是其中包含的原始字符串。以下是一种解决方法:

  1. 创建一个.d.ts文件,告诉TypeScript如何解释md文件:
declare module '*.md' {
  const value: string; // markdown is just a string
  export default value;
}
  1. 将您的md文件(the_text.md)导入如下
import theTextFile from './the_text.md';
  1. fetch文件内容以获取实际的Markdown(https://github.com/facebook/create-react-app/issues/2961#issuecomment-322916352
const [postMarkdown, setPostMarkdown] = useState('');

// useEffect with an empty dependency array (`[]`) runs only once
useEffect(() => {
  fetch(theTextFile)
    .then((response) => response.text())
    .then((text) => {
      // Logs a string of Markdown content.
      // Now you could use e.g. <rexxars/react-markdown> to render it.
      // console.log(text);
      setPostMarkdown(text);
    });
}, []);

将Markdown文本放入Markdown标签中,它将被渲染:
<Markdown className={classes.markdown}>
  {post}
</Markdown>

记住,您还可以覆盖用于显示最终内容的JSX元素。对于Material-UI,这可能看起来像这样:
import { Typography } from '@material-ui/core';
import { MarkdownToJSX } from 'markdown-to-jsx';

const Foo = (): JSX.Element => {

  const MdOverrideTypography = ({
    children,
  }: // ...props
  React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLParagraphElement>,
    HTMLParagraphElement
  >): JSX.Element => (
    <Typography paragraph>
      {children}
    </Typography>
  );

  const mdOverrides: MarkdownToJSX.Overrides = {
    // "p" paragraph elements are substituted with MUI "Typography"
    p: MdOverrideTypography,
  };

  // ...

  return (
    <Markdown options={{ overrides: mdOverrides }}>
      {post}
    </Markdown>
  )
}

(https://www.npmjs.com/package/markdown-to-jsx#optionsoverrides---rendering-arbitrary-react-components)


4
有人知道 material-ui 是如何在他们的在线示例网站上实现这一点的吗?(https://material-ui.com/getting-started/templates/blog/)他们是在 SSR 构建期间加载它吗?他们的代码结构与上面的问题类似。谢谢。 PS:“SSR”指服务器端渲染(Server-Side Rendering)。 - jboxxx
小的更新您可能想考虑:现在包括排版:import Typography from '@mui/material/Typography'; - Jamie Nicholl-Shelley

1

一个简单的方法似乎是使用raw.macro包(我引用自其README开头):

Installation

In order to use raw.macro in your own project, you can use one of the following commands:

# or $ npm install --save-dev raw.macro

Make sure babel-plugin-macros already installed. If you're using Create React App, it's installed by default.

Usage

raw.macro is similar to Node’s require call:

import raw from "raw.macro";

const markdown = raw("./README.md");

TypeScript将markdown变量视为string类型。

唯一的问题是,编辑markdown文件不会导致项目重新构建以包括更改。相反,您必须触摸(例如重新保存)导入它的源文件。


是的 - 我可以确认这一点。这是一个已知的问题 https://github.com/pveyes/raw.macro/issues/20对我来说,这是决定采用 raw-loader 方法的关键因素。除了缓存问题外,raw.macro 运行良好。 - DanielH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接