如何使用 Markedjs 渲染 Markdown,并进行格式拓展

目录

本文收录在《如何开发一个自己的笔记软件》系列中,该系列源码均可在 Blossom 笔记软件 仓库中查看。仓库地址:

安装

源码地址 Markedjs 源码地址

官方文档 Markedjs 官方文档

安装依赖

bash 复制代码
npm install marked
npm install @types/marked

npm install marked-highlight
npm install highlight.js

使用

你可以直接引入marked,默认是全局共享同一个实例

typescript 复制代码
// 引入默认实例
import { marked } from 'marked'

marked.use({
  // 开启异步渲染
  async: true,
  pedantic: false,
  gfm: true,
  mangle: false,
  headerIds: false
})

// 异步方式渲染
marked.parse(markdown, { async: true }).then((html: string) => {
	console.log(html)
})

// 同步方式渲染
const html = marked.parse(markdown)

如果你要创建一个新实例,可以使用如下方式

typescript 复制代码
import { Marked } from 'marked'

const newMarked = new Marked({ mangle: false, headerIds: false })

添加代码高亮

你需要安装下列两个依赖

bash 复制代码
npm install marked-highlight
npm install highlight.js

然后进行如下配置,代码块中的内容会自动添加高亮样式。

typescript 复制代码
import { marked } from 'marked'
import { markedHighlight } from "marked-highlight"
import hljs from 'highlight.js'
// 注意引入样式,你可以前往 node_module 下查看更多的样式主题
import 'highlight.js/styles/base16/darcula.css'


// 高亮拓展
marked.use(markedHighlight({
  langPrefix: 'hljs language-',
  highlight(code, lang) {
    const language = hljs.getLanguage(lang) ? lang : 'shell'
    return hljs.highlight(code, { language }).value
  }
}))

自定义渲染格式

你可以重写 renderer 函数来自定义渲染结果。下方会介绍一些笔者开发的笔记软件中用到的拓展方式。

源码地址:Markedjs 封装

拓展标题ID实现目录跳转

重写 heading 函数,并为所有的标题标签添加ID来实现目录跳转。

java 复制代码
let html:string = ''

const renderer = {
  /**
   * 标题解析为 TOC 集合, 增加锚点跳转
   *
   * @param text  标题内容
   * @param level 标题级别
   */
  heading(text: any, level: number): string {
	const realLevel = level
  	return `<h${realLevel} id="${realLevel}-${text}">${text}</h${realLevel}>`
  }
}

// 重写标题的渲染结果
marked.use({ renderer: renderer })

marked.parse('# 标题1', { async: true }).then((htmlResult: string) => {
	// 结果为 <h1 id="1-标题1">标题1</h1>
	html = htmlResult
})

然后解析 html 来自动获取标题标签(h1 ~ h6),并解析成一个数组。

typescript 复制代码
// 定义目录结构
export interface Toc {
  name: string,
  id: string,
  level: number
}

export const initTocs = (id: string): Toc[] => {
  // 获取指定ID,渲染后的html都在该标签内
  let ele = document.getElementById(id)
  if (ele == null || undefined) {
    return []
  }
  let heads = document.getElementById(id)!.querySelectorAll('h1, h2, h3, h4, h5, h6');
  let tocs: Toc[] = []
  for (let i = 0; i < heads.length; i++) {
    let head = heads[i]
    let level = 1;
    let name = (head as HTMLElement).innerText
    let id = head.id
    switch (head.localName) {
      case 'h2':
        level = 2;
        break;
      case 'h3':
        level = 3;
        break;
      case 'h4':
        level = 4;
        break;
    }
    let toc: Toc = {
      name: name,
      id: id,
      level: level
    }
    tocs.push(toc)
  }
  return tocs
}

随后无论你使用 vue 还是 react,你都可以将数组结果列渲染到你的页面上。并为每一个元素添加一个 click 事件,即可以实现跳转。比方说 vue 中:

html 复制代码
<template>
  <div :class="['toc', 'toc-' + toc.level]" v-for="toc in tocs" :key="toc.id" @click="toScroll(toc.id)">
    {{ toc.name }}
  </div>
</template>

<script setup lang="ts">
const toScroll = (id: string) => {
  let ele: HTMLElement = document.getElementById(id) as HTMLElement
  if (ele == null || ele == undefined) {
    return
  }
  (ele.parentNode as Element).scrollTop = ele.offsetTop
}
</script>

拓展图片的样式

可以通过约定 markdown 格式,来实现更加丰富的样式。比如下方的例子中,为图片增加了宽度阴影的拓展。只需要在图片名称中指定相关配置

text 复制代码
![图片名称##w200##shadow](http://domain.com)
typescript 复制代码
// 拓展语法标识
const grammar = '##'
  
const renderer = {
  /**
   * 拓展图片
   * 
   * @param href   图片路径
   * @param _title null
   * @param text   图片的名称
   */
  image(href: string | null, _title: string | null, text: string): string {
    let width = 'auto' // 宽度
    let style = ''     // 样式
    let tags: string[] = text.split(grammar)
    if (tags.length > 1) {
      for (let i = 0; i < tags.length; i++) {
        let tag = tags[i]
        if (tag === 'shadow') {
          style += 'box-shadow: 0 0 3px #000000;'
        }
        // 
        if (tag.startsWith('w')) {
          width = tags[i].substring(1)
          if (!width.endsWith('%')) {
            width += 'px'
          }
        }
      }
    }
    return `<img width="${width}" style="${style}" src="${href}" alt="${text}">`
  }
}

// 重写图片的渲染结果
marked.use({ renderer: renderer })

marked.parse('![图片名称##w200##shadow](http://domain.com)', { async: true }).then((htmlResult: string) => {
	// 结果为 <img width="200px" style="box-shadow: 0 0 3px #000000;" src="http://domain.com" alt="图片名称##w200##shadow">图片名称</img>
	html = htmlResult
})

更多拓展

除此之外,笔者还拓展了一些其他的 markedjs 渲染方法。这些拓展内容都可以在Markedjs 封装中查看。

1. 使用表格进行布局

2. 多色引用块

3. 视频引用

4. katex 语法拓展

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> { 0 = c x − a x 0 − d x 0 ( c x − a x 0 ) ⋅ d x 0 ∥ d x 0 ∥ 2 + c x − a x 1 − d x 1 ( c x − a x 1 ) ⋅ d x 1 ∥ d x 1 ∥ 2 0 = c y − a y 0 − d y 0 ( c y − a y 0 ) ⋅ d y 0 ∥ d y 0 ∥ 2 + c y − a y 1 − d y 1 ( c y − a y 1 ) ⋅ d y 1 ∥ d y 1 ∥ 2 \left\{ \begin{array}{l} 0 = c_x-a_{x0}-d_{x0}\dfrac{(c_x-a_{x0})\cdot d_{x0}}{\|d_{x0}\|^2} + c_x-a_{x1}-d_{x1}\dfrac{(c_x-a_{x1})\cdot d_{x1}}{\|d_{x1}\|^2} \\[2ex] 0 = c_y-a_{y0}-d_{y0}\dfrac{(c_y-a_{y0})\cdot d_{y0}}{\|d_{y0}\|^2} + c_y-a_{y1}-d_{y1}\dfrac{(c_y-a_{y1})\cdot d_{y1}}{\|d_{y1}\|^2} \end{array} \right. </math>⎩ ⎨ ⎧0=cx−ax0−dx0∥dx0∥2(cx−ax0)⋅dx0+cx−ax1−dx1∥dx1∥2(cx−ax1)⋅dx10=cy−ay0−dy0∥dy0∥2(cy−ay0)⋅dy0+cy−ay1−dy1∥dy1∥2(cy−ay1)⋅dy1


<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> R C O H R ′ S O 3 N a → Hydrolysis, Δ , D i l . H C l ( R C O R ′ ) + N a C l + S O 2 + H 2 O \begin{CD} RCOHR'SO_3Na @>{\text{Hydrolysis,\\Delta, Dil.HCl}}>> (RCOR')+NaCl+SO_2+ H_2O \end{CD} </math>RCOHR′SO3NaHydrolysis,Δ,Dil.HCl (RCOR′)+NaCl+SO2+H2O


<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ∑ n = 1 ∞ 1 n 2 → ∑ n = 1 ∞ 1 n 2 → ∑ n = 1 ∞ 1 n 2 \sum_{n=1}^\infty \frac{1}{n^2} \to \textstyle \sum_{n=1}^\infty \frac{1}{n^2} \to \displaystyle \sum_{n=1}^\infty \frac{1}{n^2} </math>n=1∑∞n21→∑n=1∞n21→n=1∑∞n21

5. mermaid 图表拓展

%%{init: { 'theme': 'neutral' } }%% timeline title Blossom 版本发布时间线 2023-06-10 : 写下了第一行代码 2023-08-10 : 发布了 1.0 版本 2023-08-15 : 发布了 1.1 版本 : 支持了 katex 和 mermaid 2023-09-01 : 发布了 1.2 版本 : 一个完整的笔记软件基本成型

6. 拓展链接来构造双链笔记

相关推荐
孜然卷k2 分钟前
前端导出word文件,并包含导出Echarts图表等
前端·javascript
家里有只小肥猫23 分钟前
uniApp小程序保存canvas图片
前端·小程序·uni-app
前端大全26 分钟前
Chrome 推出全新的 DOM API,彻底革新 DOM 操作!
前端·chrome
八角丶37 分钟前
元素尺寸的获取方式及区别
前端·javascript·html
冴羽1 小时前
Svelte 最新中文文档教程(16)—— Context(上下文)
前端·javascript·svelte
前端小臻1 小时前
关于css中bfc的理解
前端·css·bfc
白嫖不白嫖1 小时前
网页版的俄罗斯方块
前端·javascript·css
HappyAcmen1 小时前
关于Flutter前端面试题及其答案解析
前端·flutter
顾比魁1 小时前
pikachu之CSRF防御:给你的请求加上“网络身份证”
前端·网络·网络安全·csrf
林的快手1 小时前
CSS文本属性
前端·javascript·css·chrome·node.js·css3·html5