服务端marked + highlight.js导致接口慢的问题
前言
由于博客后台只保存了markdown
内容,html
为后端实时渲染出来的,而目前代码高亮又是在前端渲染的,为了兼容博客前端小程序的代码高亮(之前的代码未显示高亮),在昨天把博客代码高亮从前端渲染改为了后端渲染。
import hljs = require('highlight.js');
import marked = require('marked');
marked.setOptions({
renderer: new marked.Renderer(),
highlight(code: string, lang: string) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
},
langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
pedantic: false,
gfm: true,
breaks: false,
sanitize: false,
smartLists: true,
smartypants: false,
xhtml: false,
});
这样确实是方便了,不需要各种端额外渲染文本,但是对于服务器是一笔不小的负担,在本地(m1)上渲染一个万字文章要40ms
以上,而在服务器(1核2g1m)上更久至少要200ms
。
而优化方式有三种:
- 提升服务器
- 服务器保存文章时保存一份渲染好的html文章
- 改回前端渲染高亮文本
第1种方式提升服务器
选项可是要花一笔不少的钱的,自然是pass掉了。
第2种方式以空间换时间,写文章和看文章的比例差距是很大的(只有我在写),把时间压缩在提交那刻也不失为一种好的优化方式。
不过需要存放一份渲染好的文本对于数据库压力较大,一份markdown
渲染成html
会膨胀好几倍,备份数据库时备份会很大。而且相当于写项目git
提交代码时把打包后的代码也git
保存起来一样。不是很妥,pass。
这样只剩最后一种方案了。为了安全,markdown
原文是不能给出的,所以获取文章时服务器还是需要用marked
把文章渲染成html
,而在客户端把代码高亮渲染出来。
把服务端高亮去掉后,本地接口返回万字文章只需要7ms
,比之前的40ms
可谓是质的提升。
前端渲染
浏览器和小程序渲染有一点不同。
浏览器端
浏览器端比较简单方便
import highlight from 'highlight.js';
const blocks = articleEl.querySelectorAll<HTMLElement>('pre code');
blocks.forEach((block) => {
highlight.highlightBlock(block);
});
可以使用浏览器api查找出所有的code,然后用 highlight
渲染一下就好了。
小程序端
小程序端由于没有浏览器那么方便的api,而且highlight.js
也不支持小程序端渲染方式,好在highlight.js
支持纯js渲染,而且可以使用正则查找所有的代码块。
import hljs from 'highlight.js';
Page({
data: {
article: '',
articleId: ''
},
onLoad({ id }) {
this.setData({ articleId: id }, () => {
this.getArticle();
});
},
async getArticle() {
const { data: article } = await getArticleDetail(this.data.articleId);
(article.content.match(/<code class="[^"]+">[^<]+<\/code>/gm) || []).forEach((code) => {
const lang = /"language-([^" ]+)"/.test(code) ? RegExp.$1 : '';
const codeContent = code.replace(/<code class="[^"]+">|\r?\n?<\/code>/g, '');
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
// 新`highlight.js`的language和code位置是反过来的,现在`towxml`用的是旧版的
const renderedCode = hljs.highlight(
language,
// 还原转译过的符号
codeContent
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/'/g, "'")
.replace(/"/g, '"'),
).value;
article.content = article.content.replace(codeContent, renderedCode);
});
wx.stopPullDownRefresh();
wx.setNavigationBarTitle({
title: article.title,
});
this.setData({ article: article.content });
},
});
评论