页面内容
Pagic 支持将 md/tsx 文件渲染成静态页面,下面将分别介绍这两种文件支持的特性。
md 文件§
Pagic 使用 markdown-it 来编译 Markdown 文件,它支持通过添加第三方插件来扩展原有的功能。
Pagic 支持了以下特性:
title§
文章中的第一个 <h1> 标签将会被提取出来作为整个页面的 title(再加上 pagic.config.ts 中的 title 作为后缀)。
若文章中没有 <h1> 标签,则会使用 pagic.config.ts 中的 title 作为页面的 title。
toc§
文章中所有的 <h2> 和 <h3> 标签会被提取出来作为页面的目录。
若文章中没有 <h2> 或 <h3>,则 toc 为 undefined。
可以通过配置 md.tocLevel 来修改提取的标题等级。
标题中的锚点§
文章中所有的 <h2>, <h3>, <h4>, <h5>, <h6> 标签会被插入一个可点击的锚点 §。
可以通过配置 md.anchorLevel 来修改会被插入锚点的标题等级。
链接替换§
文章中的链接如果是以 .md 结尾,则会在构建过程中被替换为 .html,比如:
[配置文件](./config.md)
会被构建为:
<a href="./config.html">配置文件</a>
真实的构建结果:配置文件,不妨点击看看跳转效果。
为什么要这么设计呢?
因为这样的链接不仅在生成的页面中支持点击跳转,在 GitHub 的 Markdown 预览中支持点击跳转,而且在 VSCode(或其他编辑器)中也支持 cmd/ctrl + click 跳转。
不妨在本页面的 GitHub 版试试吧。
需要注意的是,链接中的 README.md 不仅后缀会被替换为 .html,路径也会被替换为 index:
[首页](/README.md)
会被构建为:
<a href="/index.html">首页</a>
真实的构建结果:首页,不妨点击看看跳转效果。
另外,如果链接是以 http:// 或 https:// 开头,则表示它是一个外部链接,那么无论后缀是什么都不会被替换了。
头信息§
Markdown 文件的顶部允许设置一些头信息(frontMatter),它们将会作为 props 传递到 _layout.tsx 中,注意它的优先级是最高的,会覆盖掉任何插件添加的 props,比如:
设置 outputPath 可以指定输出页面的路径(默认情况下输出路径是文件路径):
---
outputPath: foo/bar.html---
# 页面内容
...
设置 layoutPath 可以指定本页面使用的模版文件:
---
layoutPath: posts/_layout.tsx---
# 页面内容
...
设置 toc 为 null 可以禁用本页的目录:
---
toc: null---
# 页面内容
...
设置 prev 或 next 可以指定上一页下一页的路径(需要配合 prev_next 插件):
---
prev: README.md---
# 页面内容
...
头信息的设置很灵活,搭配各种插件,或者自定义的 _layout.tsx,可以实现各种各样的效果。
科学公式 TeX (KaTeX)§
行内公式:
行内公式:$E=mc^2$
多行公式:
$$
\frac{1}{
\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{
\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {
1+\frac{e^{-6\pi}}
{1+\frac{e^{-8\pi}}{1+\cdots}}
}
}
$$
git log 信息§
解析 Markdown 文件时,Pagic 会运行脚本来获取它的 git log 并提取其中的有用信息,它们包括:
author: 该文件的第一个提交者contributors: 该文件的所有提交者(包括第一个提交者),以第一次提交的时间排序(先提交的排在前面)date: 该文件第一次提交的日期updated: 该文件最后一次提交的日期
这些信息都会写入到页面的 props 中。
局限性§
目前 Pagic 的 Markdown 解析还存在一些局限性,这也是将来的改进方向:
- 不支持配置
markdown-it的选项 - 不支持流程图等高级语法
- 不支持内嵌 jsx
tsx 文件§
tsx 文件渲染成静态页面是 Pagic 的特色之一,借助 React 组件的可编程性,极大的扩展了静态网站的能力。
基本用法§
任何不以 _ 开头的 tsx 文件都会被视为一个 tsx 页面文件。
我们不妨在之前的 site 项目中创建一个 hello.tsx 文件:
site/
├── pagic.config.ts
├── hello.tsx└── README.md
它的内容是:
import { React } from 'https://deno.land/x/pagic@v1.6.3/mod.ts';
const Hello = () => <h1>Hello world</h1>;
export default Hello;
接下来我们运行:
pagic build --serve
然后打开 http://127.0.0.1:8000/hello.html ,可以看到页面中显示出了 Hello world。
同时 dist 目录下多了一个 hello.html 文件:
site/
|── dist # 构建结果目录
| |── hello.html| └── index.html
├── pagic.config.ts
├── hello.tsx└── README.md
hello.tsx中的默认导出(export default)会被视为页面的内容。
逻辑脚本§
Pagic 不仅会在渲染页面时执行 tsx 文件中的逻辑,而且其中的逻辑在浏览器中也会运行。
比如,我们可以使用 React.setState 实现一个计数器页面:
import { React } from 'https://deno.land/x/pagic@v1.6.3/mod.ts';
const Hello = () => {
const [count, setCount] = React.useState(0); return (
<>
<h1>Hello world</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Count +1</button> </>
);
};
export default Hello;
访问 /hello.html 可以查看其真实的渲染结果。
由于
react.d.ts的限制,使用React的子方法时必须用React.xxx,而不能在导入时将子方法直接导入(import React, { useState } ...)。
组件化§
组件化是 React 的重要特性之一,我们可以通过拆分 tsx 页面为一个个子组件来复用代码。不过在 Pagic 中,由于需要支持 tsx 文件渲染为页面,所以我们需要对子组件做一个约定——以 _ 开头的组件为子组件:
site/
|── dist # 构建结果目录
| └── hello.html
├── _count.tsx├── hello.tsx
└── pagic.config.ts
在上面的例子中,hello.tsx 会被构建为 dist/hello.html,而 _count.tsx 由于是 _ 开头,所以不会被构建为页面。这样就可以实现对 hello.tsx 的拆分,将 Count 组件拆分到 _count.tsx 文件中,然后在 hello.tsx 中引用即可:
import { React } from 'https://deno.land/x/pagic@v1.6.3/mod.ts';
import Count from './_count.tsx';
const Hello = () => (
<>
<h1>Hello world</h1>
<Count /> </>
);
export default Hello;
头信息§
与 md 文件类似,tsx 文件也支持头信息,它是通过导出一个 frontMatter 对象实现的:
import { React } from 'https://deno.land/x/pagic@v1.6.3/mod.ts';
const Hello = () => <h1>Hello world</h1>;
export default Hello;
export const frontMatter = { outputPath: 'foo/bar.html',};git log 信息§
同 md 文件一样,tsx 文件也会获取 author, contributors, date, updated 等信息并写入到页面的 props 中。
局限性§
使用 tsx 文件同样存在一些局限性,这也是将来的改进方向:
- 不支持代码高亮
- 不支持内嵌 Markdown
- 不支持自动生成目录