返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
StarBlog博客开发笔记

StarBlog Development Notes (19): Exploring Markdown Rendering Solutions

Preface In my view, the core of a blog website is the reading experience. During the development of StarBlog, the most time-consuming part was precisely the article display functionality. At first I hadn't figured out how to do backend rendering well, so I had to use the Editor.md component for frontend rendering as a stopgap. I wasn't satisfied with frontend rendering because of poor performance—the page would flash after loading, creating a disjointed feel that hurt the experience. Now that I've built a fairly complete backend rendering solution, frontend rendering can retire. This article introduces StarBlog blog development

曦远 (DealiAxy)约 26 分钟
目录18 个章节

Preface

In my view, the core of a blog website is the reading experience.

During the development of StarBlog, the most time-consuming part was precisely the article display functionality.

At first I hadn't figured out how to do backend rendering well, so I had to use the Editor.md component for frontend rendering as a stopgap. I wasn't satisfied with frontend rendering because of poor performance—the page would flash after loading, creating a disjointed feel that hurt the experience.

Now that I've built a fairly complete backend rendering solution, frontend rendering can retire. This article covers the various Markdown rendering approaches used while developing StarBlog (mainly focusing on backend rendering).

Two Approaches

Frontend Rendering

Rendering with the Editor.md component was barely satisfactory. I mainly chose it for its ability to generate a ToC (table of contents), but the generated ToC wasn't great either—I later forked the source and hacked it to make it somewhat better.

The hacking process is in this article: Hacking the editormd component to improve ToC rendering

I submitted a PR to the official repo for this ToC optimization, but got no response. Looking at GitHub, there are dozens of PRs, and the last commit was almost 4 years ago—looks like this project is truly dead...

Besides editor.md, there are a few other frontend options:

They all look decent. I didn't look into whether they have ToC. An expert on CNBlogs wrote a comparison article—interested readers can check it out in the references~

Backend Rendering

Currently the only usable Markdown library for C# seems to be Markdig. At first I complained that missing docs made it hard to use, and I even considered reinventing the wheel myself. Things have improved recently though—after studying several newly added official docs, I understand the library better. It really has a lot of features, is well designed, and is highly extensible~

So I'll stick with this for now~

My current approach is to use Markdig to generate HTML from Markdown, then display that HTML on the frontend, combined with style libraries like Bootstrap or github-markdown-css to beautify the article body, and JS libraries like highlight.js for syntax highlighting.

As for the article ToC, Markdig doesn't have a ready-made solution, so I rolled my own~

See this article: Generating a Markdown Document Table of Contents Tree in C#

It's actually a kind of hybrid approach~

The rest of this article focuses on backend rendering.

Handling ToC

The previous article already covered generating the table of contents tree in some detail, so I won't repeat all that here—I'll just cover the differences~

First parse the Markdown document and get all heading nodes

var headings = new List<Heading>();

foreach (var heading in document.Descendants<HeadingBlock>()) {
  var item = new Heading {Level = heading.Level, Text = heading.Inline?.FirstChild?.ToString()};
  headings.Add(item);
}

Then iterate and process them.

Originally I used the heading text directly as the anchor href attribute, but that doesn't work in practice. Based on testing, Markdig's rules for generating anchor IDs are as follows

For Chinese:

  • section
  • section-1
  • section-2
  • ...

The number after section is the order of appearance among all Chinese headings, not among all headings.

For English, replace spaces and convert to lowercase (other cases not considered; in fact special characters should also be replaced)

So when processing href, there are two cases. Use the regex [\u4e00-\u9fbb] to detect whether it contains Chinese characters.

var chineseTitleCount = 0;
for (var i = 0; i < headings.Count; i++) {
  var item = headings[i];
  var text = item.Text ?? "";
  if (Regex.IsMatch(text, "[\u4e00-\u9fbb]")) {
    item.Slug = chineseTitleCount == 0 ? "section" : $"section-{chineseTitleCount}";
    chineseTitleCount++;
  }
  else {
    item.Slug = text.Replace(" ", "-").ToLower();
  }
  // ...
}

Done

Styling

Bootstrap default styles

The default styles are okay, but something feels missing. Maybe I could look at the styles of various online Markdown editors~

github-markdown-css

As the name suggests, this is GitHub's markdown style

URL: https://www.npmjs.com/package/github-markdown-css

After installation there are three files

  • github-markdown.css: (default) Automatically switches between light/dark themes via @media (prefers-color-scheme)
  • github-markdown-light.css: Light theme
  • github-markdown-dark.css: Dark theme

The official site also has this sentence, but I don't know how to generate them myself—do I have to scrape the CSS from GitHub?

You may know that now GitHub supports more than 2 themes including dark_dimmed, dark_high_contrast and colorblind variants. If you want to try these themes, you can generate them on your own!

Install

yarn add github-markdown-css

Include

<link rel="stylesheet" href="~/lib/github-markdown-css/github-markdown-light.css">

Usage

<div class="markdown-body">
  @Html.Raw(Model.ContentHtml)
</div>

Result

It really has that GitHub flavor, but still no syntax highlighting

image-20221026225955639

Syntax Highlighting

Currently using the highlight.js package, official site: https://www.npmjs.com/package/highlight.js

There are many other tools, but I won't go into them—this one is enough~

Download

To use it directly on a webpage you can't install via an NPM package; you have to download from: https://highlightjs.org/download/

If you don't want to download, you can use a CDN, but then only some languages are supported for highlighting.

There are so many languages, and surprisingly no select-all button. Checking them one by one is too much trouble, so I wrote a select-all script. Paste it into the browser console to select all, then download.

document.querySelectorAll('input').forEach( item => {
    if(item.getAttribute('type')==='checkbox') item.checked=true
})

Include

After downloading, unzip and put it under wwwroot/lib

<link rel="stylesheet" href="~/lib/highlight/styles/default.min.css">
<script src="~/lib/highlight/highlight.min.js"></script>
<script>hljs.highlightAll();</script>

Or skip downloading and use a CDN directly

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>

Result

With syntax highlighting, it looks completely different

image-20221026235224582

There are many other themes—lots of them under the styles directory. Just pick one when including the CSS.

Let me try a dark theme

image-20221026235552813

Series Articles

References

_EOF_

StarBlog Development Notes (19): Exploring Markdown Rendering Solutions

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

StarBlog博客开发笔记(23):文章列表接口分页、过滤、搜索、排序

前言 上一篇留的坑,火速补上。 在之前的第6篇中,已经有初步介绍,本文做一些补充,已经搞定这部分的同学可以快速跳过,基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表 对标准的WebApi来说,分页、过滤、搜索、排序是很常见的功能,既可以方便用户查看数据,又可以提升程序性能。 通用请求参数 定义一个类来作为通用的请求参数 列表接口通用的参数是这几个:PageSi

2023-01-02 · StarBlog博客开发笔记 · 约 1 分钟

StarBlog博客开发笔记(20):图片显示优化

前言 我的服务器带宽比较高,博客部署在上面访问的时候几乎没感觉有加载延迟,就没做图片这块的优化,不过最近有小伙伴说博客的图片加载比较慢,那就来把图片优化完善一下吧~ 目前有两个地方需要完善 图片瀑布流 图片缩略图 图片瀑布流 关于瀑布流之前的文章有介绍: 基于.NetCore开发博客项目 StarBlog - (10) 图片瀑布流 不过当时直接套用的 Bootstrap5 例子代码,有偶尔显示错位

2022-11-27 · StarBlog博客开发笔记 · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…