系列文章

前言

前一篇博客完成了文章列表的开发,现在要来写文章详情页面了(这篇更新应该没迟到吧,嘿嘿)。

博客网站最重要的可以说就是文章详情页面了,用户来看博客最关心首先是内容,其次是阅读体验,所以这个文章详情页面的设计不能马虎~

思路

文章正文是以markdown格式存储的,要在网页上展示的话,需要把markdown渲染成HTML才行。

那么就有两种思路:

  • 一种是在后端渲染,使用C#把markdown转换成HTML然后渲染成网页
  • 另一种是后端直接输出markdown,使用一些开源的JS库实现markdown渲染

一开始我是采用第一种的后端渲染方式,用到的C#库是Markdig,不过深入使用之后发现有一些想要的功能实现起来比较麻烦,特别是这个库几乎没有文档,要自定义一些功能全靠看源码+猜,最后只能放弃转而使用第二种方式。

本文对两种方式的实现都会介绍,着重介绍第二种前端渲染。

后端渲染

关于Markdig这个库的我之前写的博客有详细的介绍,这里不再重复,有兴趣的同学可以看看:C#解析Markdown文档,实现替换图片链接操作

首先Nuget安装Markdig这个库

一行代码就可以实现markdown转HTML

Markdig.Markdown.ToHtml(markdownContent);

当然直接渲染出来的页面是很简陋的,没有代码高亮、没有引用块、没有列表样式啥的,所以单纯这样肯定是不够的。

Markdig作为C#目前唯一积极维护的Markdown库,自然是考虑到了扩展性,它设计了扩展系统,本身内置了20多个扩展,还可以安装其他人开发的扩展用来实现例如代码高亮的效果。

使用扩展也很简单,加个pipeline参数就行

var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);

Markdig本身不自带代码高亮扩展,需要使用第三方组件,我测试了下面这两个能用

前端渲染

本项目最终选了前端渲染的方案,前端生态有众多的markdown组件,看了一圈之后我最终选了Editor.md这个组件。

主要看中它可以比较方便的实现文章的TOC(目录)功能,还有不错的高亮效果。

使用起来很简单

首先把markdown输出到网页里

<div id="test-editormd-view" class="post-content">
<textarea id="append-test" style="display:none;">@Model.Content</textarea>
</div>

加了display:none不显示这个textarea,给用户看markdown代码没用

引入edtor.md的样式文件

<link rel="stylesheet" href="~/lib/editormd/css/editormd.preview.min.css">

引入editor.md的js,你没看错,就是这么多。静态资源在之前的文章里已经安装好了,这里不再重复。详见:(5) 开始搭建Web项目

<script src="~/lib/editormd/examples/js/jquery.min.js"></script>
<script src="~/lib/editormd/lib/marked.min.js"></script>
<script src="~/lib/editormd/lib/prettify.min.js"></script> <script src="~/lib/editormd/lib/raphael.min.js"></script>
<script src="~/lib/editormd/lib/underscore.min.js"></script>
<script src="~/lib/editormd/lib/sequence-diagram.min.js"></script>
<script src="~/lib/editormd/lib/flowchart.min.js"></script>
<script src="~/lib/editormd/lib/jquery.flowchart.min.js"></script> <script src="~/lib/editormd/editormd.min.js"></script>

然后,使用js调用editor.md的渲染方法

let testEditormdView = editormd.markdownToHTML("test-editormd-view", {
// htmlDecode: "style,script,iframe", // you can filter tags decode
htmlDecode: true,
//toc : false,
tocm: true, // Using [TOCM]
tocContainer: "#custom-toc-container", // 自定义 ToC 容器层
//gfm : false,
//tocDropdown : true,
// markdownSourceCode : true, // 是否保留 Markdown 源码,即是否删除保存源码的 Textarea 标签
emoji: true,
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
})

搞定。

ViewModel

Post模型只是存在数据库中的数据,直接展示不能完全满足网页设计的需求,所以还是一样,需要定义一个ViewModel来用。

依然是放在StarBlog.Web/ViewModels

代码如下

public class PostViewModel {
public string Id { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string ContentHtml { get; set; }
public string Path { get; set; }
public DateTime CreationTime { get; set; }
public DateTime LastUpdateTime { get; set; }
public Category Category { get; set; }
public List<Category> Categories { get; set; }
}

相比起Post模型,多了ContentHtmlCategories改成列表

Service

关键的渲染部分介绍完了,讲一下一些次要的~

Service的作用是把Post模型转换成ViewModel

那直接上代码吧

public PostViewModel GetPostViewModel(Post post) {
var vm = new PostViewModel {
Id = post.Id,
Title = post.Title,
Summary = post.Summary,
Content = post.Content,
ContentHtml = Markdig.Markdown.ToHtml(post.Content),
Path = post.Path,
CreationTime = post.CreationTime,
LastUpdateTime = post.LastUpdateTime,
Category = post.Category,
Categories = new List<Category>()
}; foreach (var itemId in post.Categories.Split(",").Select(int.Parse)) {
var item = _categoryRepo.Where(a => a.Id == itemId).First();
if (item != null) vm.Categories.Add(item);
} return vm;
}

虽然不用后端渲染方案,不过我还是保留了Markdig的后端渲染。

View

PS:Controller部分被我略过了,实在是太简单,没必要贴代码了

这个好像也没啥好介绍的,那还是不贴完整代码了,详细代码在这:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Views/Blog/Post.cshtml

使用Bootstrap的Grid布局做左右两栏,左栏显示文章的TOC目录,右栏显示文章的主体内容。

页面顶部要展示分类的层级关系,不同分类之间用“/”分隔,但第一个分类前面不要有斜杠(复杂的表述方式)

这个需求的实现代码是这样

<div>
分类:
@foreach (var category in Model.Categories) {
@if (Model.Categories.IndexOf(category) > 0) {
<span> / </span>
}
<a asp-controller="Blog" asp-action="List"
asp-route-categoryId="@category.Id">
@category.Name
</a>
}
</div>

效果大概这样:

然后还要优化一下时间的显示

@Model.LastUpdateTime.ToShortDateString()
@Model.LastUpdateTime.ToString("hh:mm")

完成之后的效果如下

实现效果

大概就是这样,后续可能会再优化一下页面。

搞定~

基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面的更多相关文章

  1. 基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传

    前言 九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~ 之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了 ...

  2. 基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索

    前言 笔者认为,一个博客网站,最核心的是阅读体验. 在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能. 最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做 ...

  3. 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目

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

  4. 基于.NetCore开发博客项目 StarBlog - (3) 模型设计

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 一、cadence元件库绘制详细步骤

    一.元件库 1.打开如下图标的软件 2.勾选1选项,下次就直接打开,不用选择 3.新建库文件File-New-Library,如下图: 4.新建元件 5.绘制元件

  2. CEPH-4:ceph RadowGW对象存储功能详解

    ceph RadosGW对象存储使用详解 一个完整的ceph集群,可以提供块存储.文件系统和对象存储. 本节主要介绍对象存储RadosGw功能如何灵活的使用,集群背景: $ ceph -s clust ...

  3. 前端规范(ES6BEMOOCSSSMACSS)

    前端规范 在实际开发中,由于团队成员编码习惯不一,技术层次不同,开发前定制并遵循一种代码规范能提高代码质量,增加开发效率. Javascript Javascript规范直接参考airbnb: ES6 ...

  4. 一个关于小程序与单片机的通信实例(TCP/IP)

    前言 这是一个18年初的创业项目的核心功能要求,我们当时打算做一个共享类的项目,项目的主题是共享图书,线下的形式租借图书,我们当时是考虑做一个借书柜的形式,然后线下生产投放借书柜,这些借书柜本身能存放 ...

  5. web开发者踏入人工智能的利器_Tensorflow.js

    前言 最近公司向员工搜集公司杂志的文章,刚好最近学习了机器学习相关课程.为了赚取购买课程的费用,所以写了如下文章投稿赚取稿费. 如下文章可能涉及一些我所购买课程的内容,所以不便将所有资源进行展示. 当 ...

  6. jsp+servlet上传excel并将数据导入到数据库表的实现方法

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  7. vue中触发键盘事件的两种方法和如何自定义键位事件,完整代码!

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 《头号玩家》AI电影调研报告(四)

    五. 现实中对于技术的实现 1.自由变身 电影中,主人公借助"魔幻眼镜"让自己快速变成另一个人的模样,现实中我们一样也能做到! 在今年游戏开发者大会(GDC)上,一个名为" ...

  9. echarts饼图调整悬浮提示框的位置

    默认是跟随鼠标的位置 通过数组表示提示框浮层的位置,数字设置绝对位置,百分比设置相对位置. position: [10,10] //绝对位置,相对于容器左侧10px,上侧10px position: ...

  10. BurpSuite与Xray多级代理实现联动扫描

    Xray是长亭科技推出的,最经典的莫过于代理模式下的被动扫描,它使得整个过程可控且更加精细化: 代理模式下的基本架构为,扫描器作为中间人,首先原样转发流量,并返回服务器响应给浏览器等客户端,通讯两端都 ...