umi插件开发仿dumi项目加载markdown文件实现详解
kukiiu 人气:0引言
前面章节中我们已经顺利将tsx
组件转换为页面展示,但是目前提供的功能和umi
的约定式路由功能差不多,接下来我们将实现将markdown
文件转换为页面展示。
为什么不能直接展示markdown
我们前面所使用的页面写法都是react组件式写法,umi
通过webpack
将react组件打包,这是react项目通用的模式。由于webpack
不认识markdown
文件,所以我们直接引入markdown
文件会报错。所以我们只需要让webpack
认识markdown
,通过自定义loader
来加载markdown
文件即可。
chainWebpack
umi
提供chainWebpack
插件api,通过 webpack-chain 的方式修改 webpack 配置。
webpack loader
loader
是用于webpack
解析文件的工具,不同的loader可以解析不同类型的文件,使其解析的内容可被其他模块使用。
我们需要解析markdown
文件,那么就需要写一个能认识markdown
文件的loader
,它的功能就是识别.md
文件并将文件内容解析成对象返回给import
这个文件的代码使用。
实现过程
新建插件
跟前面一样,我们新建一个插件来处理文件解析:
// /src/features/compile.ts import type { IApi } from 'umi'; export default (api: IApi) => { api.describe({ key: 'domi:compile' }); api.chainWebpack(async (memo) => { const loaderPath = require.resolve('../loaders/markdown/loader.js'); memo.module // 通过链式处理,向`webpack`添加了一条名为`domi-md`的处理规则 .rule('domi-md') // 该规则用于处理`.md`文件 .test(/\.md$/) // 给这个loader取个名字 .use('md-loader') // loader的路径 .loader(loaderPath) return memo; }); };
新建loader
接下来创建loader文件,注意这里loader要使用js
文件,因为webpack无法直接解析ts
类型的loader,第一个入参是文件内容的字符串形式,我们先直接返回。
// /src/loaders/markdown/loader.js function mdLoader(context) { return context } module.exports = mdLoader
为什么`dumi`的loader是用`ts`写的?
因为在`dumi`开发环境下,先将`ts`文件转成了`js`,`webpack`在运行时其实还是加载的`js`形式的loader。
dumi: 编译 => 启动umi(webpack) => 开发环境
domi: 启动umi(webpack) => 开发环境
新建测试文档
// /docs/markdown.md # 我是markdown
运行项目
启动项目可以看到markdown
文件已经正确解析到导航栏中了
点开链接一看,啥也没有,报错了
解决文件加载类型错误
看上面的报错信息,意思好像是懒加载的组件元素类型错误,打开请求列表看看加载了什么东西
应该就是这里在加载markdown
文件时,只导出了个url链接,我们打开链接看看
这里就返回了markdown内容,看来目前不能直接从页面打开。
我们换一种方式,在jsx
中直接导入这个文件看看:
// /docs/index.tsx import react from 'react' import md from './markdown.md' const Home = () => { return (<div>hello domi! {md}</div>) } export default Home
刷新页面可以看到,import进来的对象确实只是一个地址,那我们直接放个iframe来显示:
// /docs/index.tsx import react from 'react' import md from './markdown.md' const Home = () => { return (<> <div>hello domi!</div> <iframe src={md} /> </>) } export default Home
哈哈终于显示出来了
webpack ruletype
当然上面并不是我们想要的效果,从前面的尝试大概能判断出来是webpack在打包时并没有想我们想象那样能直接导出我们想要的对象。这时候我们就要使用webpack一个配置ruletype
,告诉他我们想要将markdown
文件import成一个包含正文内容的对象,而不是一个资源地址。
这里webpack将文件视为Resource资源,其将所有 .md
文件都发送到输出目录,并且其路径将被注入到 bundle中,与我们常使用的在jsx
中导入图片等一样,具体可参考资源模块 | webpack 中文文档 (docschina.org)
要改变这一默认行为,只需要配置时改变资源类型即可
// /src/features/compile.js api.chainWebpack(async (memo) => { const loaderPath = require.resolve('../loaders/markdown/loader.js'); memo.module .rule('domi-md') .test(/\.md$/) // 表示文件经过这个loader处理后转换为可导入的js模块 .type('javascript/auto') .use('md-loader') .loader(loaderPath) return memo; });
重启后运行,发现又报了另一个错误
解决错误
从报错上看,意思大概是.md
文件在经过loader解析后,解析返回值失败,还告诉我们可能需要其他loader来处理返回值。
这个就比较好理解了,因为上面我们指定了经过loader处理后应该返回一个可导出的js模块,而我们目前loader只返回了markdown
的正文内容,并不是js数据,所以我们只需要改动以下loader的返回值即可:
// /src/loaders/markdown/loader.js function mdLoader(content) { return ` const content = '${JSON.stringify(content)}' export default { content }; ` } module.exports = mdLoader
此时经过loader处理后,将会导出一个带有content
属性的对象,再改变一下导入展示的组件:
import react from 'react' import md from './markdown.md' const Home = () => { return (<div>hello domi! {md.content}</div>) } export default Home
重启后可以看到如下所示,此时我们已经成功通过loader加载到markdown文件显示
加载全部内容