loading...
上次更新: 2024-05-10
本篇文章使用 Next.js App router 及 MDXRemote
假設我們的 markdown 長這樣
# 這是大標題
內文內文內文....
結果會長這樣
<h1>這是大標題</h1>
<p>內文內文內文....</p>而這是我們希望的樣子
<a href="#unique-id">
<h1 id="unique-id">這是大標題</h1>
</a>
<p>內文內文內文....</p>要達到希望的樣子,我們需要:
rehype-slugrehype-autolink-headings ,他會把有 id 的標題都加上連結rehype-slugyarn add rehype-slug
rehype-slugimport rehypeSlug from 'rehype-slug';
// ...
const options = {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [rehypeSlug],
},
};
// ...
export default function Markdown() {
return <MDXRemote source={data} options={options} />;
}檢查:我們打開 f12 看看元素,會發現標題都多了一個 id 屬性了。
rehype-autolink-headingsyarn add rehype-autolink-headings
使用方法及參數詳見這邊。
稍微說明幾個參數:
behavior:可以是 'after' | 'append' | 'before' | 'prepend' | 'wrap',代表連結要放在哪邊,例如 wrap,代表要包住標題;使用 before 則會把連結放在標題元素前。content:連結元素的內容,預設會是 <span class="icon icon-link"></span>。我們也可以自訂,如下面程式碼,就是自訂為 <span class="text-tertiary text-3xl"></span> 。import rehypeAutolinkHeadings from 'rehype-autolink-headings';
// ...
const options = {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: 'prepend',
content: {
type: 'element',
tagName: 'span',
properties: { class: 'text-tertiary text-3xl' },
children: [{ type: 'text', value: '#' }],
},
},
],
],
},
};
// ...
export default function Markdown() {
return <MDXRemote source={data} options={options} />;
}簡單兩步驟,就完成啦🎉🎉🎉
也許你不想在所有 headings 都加上連結。像在這個部落格中,只有 h2 有連結,要如何做到呢?
我們可以使用 unist-util-visit 套件,它會遍歷語法樹並修改樹中的節點。
unist-util-visityarn add unist-util-visit
functionimport { visit } from 'unist-util-visit';
// ...
function addIdToH2() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'h2') {
if (!node.properties.id) {
const id = node.children
.filter((child) => child.type === 'text')
.map((child) => child.value)
.join(' ')
.toLowerCase()
.replace(/\s+/g, '-');
node.properties.id = id;
}
}
});
};
}const options = {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [
addIdToH2,
],
},
};