如何使用 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. 拓展链接来构造双链笔记

相关推荐
Python私教6 分钟前
Go语言现代web开发15 Mutex 互斥锁
开发语言·前端·golang
A阳俊yi7 分钟前
Vue(13)——router-link
前端·javascript·vue.js
好看资源平台20 分钟前
前端框架对比与选择:如何在现代Web开发中做出最佳决策
前端·前端框架
4triumph22 分钟前
Vue.js教程笔记
前端·vue.js
程序员大金39 分钟前
基于SSM+Vue+MySQL的酒店管理系统
前端·vue.js·后端·mysql·spring·tomcat·mybatis
清灵xmf42 分钟前
提前解锁 Vue 3.5 的新特性
前端·javascript·vue.js·vue3.5
白云~️1 小时前
监听html元素是否被删除,删除之后重新生成被删除的元素
前端·javascript·html
金灰1 小时前
有关JS下隐藏的敏感信息
前端·网络·安全
Yxmeimei1 小时前
css实现居中的方法
前端·css·html
6230_1 小时前
git使用“保姆级”教程2——初始化及工作机制解释
开发语言·前端·笔记·git·html·学习方法·改行学it