前言

笔者认为,一个博客网站,最核心的是阅读体验。

在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能。

最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做前端渲染,过渡一下。前端渲染我是不满意的,因为性能较差,页面加载出来还会闪一下,有割裂感,影响体验。

现在我已经做出了比较完善的后端渲染方案,前端渲染就可以直接退休了。本文介绍StarBlog博客开发过程中的各种Markdown渲染方案(主要是介绍后端渲染)。

两种方案

前端渲染

使用 Editor.md 组件进行渲染,效果差强人意,主要是看中了其可以生成 ToC(文章目录) 的功能,但生成的 ToC 效果也比较差,后面是我fork了一份源码进行魔改才好一点。

魔改过程在这篇文章:魔改editormd组件,优化ToC渲染效果

优化 ToC 的这个功能我给官方提了PR,但没有响应,看了一下GitHub里有几十个PR,上次提交也是快4年前的事了,看来这个项目真的凉了……

除了这个 editor.md ,还有其他几个前端的方案:

看起来都不错,有没有 ToC 我没研究,博客园上有大佬写了一篇比较的文章,有兴趣的同学可以在参考资料中看看~

后端渲染

目前 C# 可用的 Markdown 库似乎只有 Markdig ,一开始我还在吐槽文档缺失导致很难用,甚至一度想自己造轮子重新做一个,不过最近有所改善,在研究了官方新增的几个文档之后,我对这个库的了解又加深了一些,功能确实很多,设计得也不错,扩展性很好~

所以暂时就用这个啦~

目前我的做法是用 Markdig 将 Markdown 生成 HTML,然后前端展示这个 HTML ,再结合 Bootstrap 或者 github-markdown-css 等样式库来美化正文显示效果,用 highlight.js 之类的JS库实现代码高亮。

至于文章的 ToC ,Markdig 没有现成的,我自己造轮子实现~

详见这篇文章:C#实现生成Markdown文档目录树

其实算是一种混合式的方案吧~

接下来介绍的内容围绕后端渲染展开。

处理 ToC

上一篇文章对于生成目录树已经说得比较清楚了,本文不再重复那么多,只说一下有区别的地方~

先解析Markdown文档,拿到所有标题节点

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);
}

遍历进行处理。

原本直接把标题作为锚点的 href 属性,实际使用的时候是不行的,根据测试,Markdig生成锚点ID的规则如下

中文按照

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

section后面的数字是在所有中文标题里出现的顺序,不是在全部标题里面的顺序。

英文就替换空格 + 转小写 (未考虑其他情况,事实上应该把特殊符号也一并替换掉)

所以处理 href 的时候分两种情况,用正则表达式 [\u4e00-\u9fbb] 检测是否包含中文字符。

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();
}
// ...
}

搞定

样式

bootstrap 默认样式

默认样式还可以,不过会觉得少了点啥,或许可以研究一下各种在线Markdown编辑器的样式~

github-markdown-css

顾名思义是GitHub的markdown样式

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

安装后有三个文件

  • github-markdown.css: (默认) 通过 @media (prefers-color-scheme) 实现自动切换亮色/暗色主题
  • github-markdown-light.css: 亮色主题
  • github-markdown-dark.css: 暗色主题

官网还有一句话,但我不知道怎么自己生成,难道要我去github扒css下来?

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!

安装

yarn add github-markdown-css

引入

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

使用

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

效果

确实有GitHub内味了,但还没代码高亮

代码高亮

目前使用 highlight.js 包,官网: https://www.npmjs.com/package/highlight.js

有很多其他的工具,不展开了,用这个足够了~

下载

要在网页上直接用没办法通过安装NPM包的方式,只能通过网址下载: https://highlightjs.org/download/

如果不想下载的话可以用CDN,但就只能支持部分语言高亮。

里面有好多种语言,竟然没全选按钮,一个个选太麻烦了,我写了个全选脚本,复制到浏览器控制台执行就能全选,然后下载。

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

引入

下载后把zip解压放到 wwwroot/lib

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

或者不下载,直接使用 CDN

<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>

效果

有了代码高亮,一下就不一样了

还有很多其他主题,styles 目录下很多,引入css的时候自行选择即可

我来换个深色的主题看看

系列文章

参考资料

基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索的更多相关文章

  1. 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  2. 基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  3. 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  4. 基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  5. 基于.NetCore开发博客项目 StarBlog - (8) 分类层级结构展示

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  6. 基于.NetCore开发博客项目 StarBlog - (9) 图片批量导入

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. 基于.NetCore开发博客项目 StarBlog - (10) 图片瀑布流

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  8. 基于.NetCore开发博客项目 StarBlog - (11) 实现访问统计

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  9. 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

随机推荐

  1. 大家都能看得懂的源码 - 如何封装 cookie/localStorage/sessionStorage hook?

    本文是深入浅出 ahooks 源码系列文章的第九篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 今天来看看 ahooks 是怎么封装 cookie/localSt ...

  2. 快速创建springboot项目,并进行增删改

    创建普通maven项目,pom依赖如下 <parent> <artifactId>spring-boot-starter-parent</artifactId> & ...

  3. Linux 破解mysql密码(详细步骤)

    当mysql密码忘记时 [root@master ~]# mysql -uroot -p1 mysql: [Warning] Using a password on the command line ...

  4. 如何做raid级别磁盘(rhel和centos系统皆可)

    添加磁盘,自己需要多少磁盘即可添加多少数量 此处只添加了三块200MB大小的磁盘 此处三块磁盘,只有两块做raid,一块与raid磁盘为实验测读写速率,不测速率可三块都做raid. 进入虚拟机给三个磁 ...

  5. mysql_阻塞和死锁

    什么是阻塞 由于不同锁之间的兼容关系,造成一个事务需要等待另一个事务释放其所占用的资源的现象 称为 阻塞 如何发现阻塞 mysql_8.0 SELECT waiting_pid as '被阻塞的线程' ...

  6. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(下)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  7. 华南理工大学 Python第5章课后小测-2

    1.(单选)下面语句的输出结果是: ls = [] def func(a, b): ls.append(b) return a*b s = func("hi", 2) print( ...

  8. 在Windows Server 2019上安装edge浏览器

    在Windows 2016和2019的正式版本中是不带Edge浏览器的.有些工具.网站也不支持IE浏览器了.对于偶尔需要在服务器上访问这些站点的管理员来说有些不方便.不过可以通过安装三方浏览器或者Ed ...

  9. 基于 OpenTelemetry 的链路追踪

    链路追踪的前世今生 分布式跟踪(也称为分布式请求跟踪)是一种用于分析和监控应用程序的方法,尤其是使用微服务架构构建的应用程序.分布式跟踪有助于精确定位故障发生的位置以及导致性能差的原因. 起源 链路追 ...

  10. 查看docker程序使用的内存脚本

    #!/bin/bash # 找出所有运行的容器 idNames=`docker ps --format "{{.ID}}|{{.Names}},"` # 按,号分隔 OLD_IFS ...