使用语法高亮 Prism.js 让你写的代码美化起来(以Vue3为例)

tinymce编辑器界面
【tips】:tinymce是国外的一个功能超全开箱即用的富文本编辑器,他是一个开源的编辑器,除了基本的功能外还可以通过插件的形式拓展,插件也只需要简单的添加插件名称即可,插件分为开源插件和高级插件,其中高级插件功能需要付费,但开源的功能完全够用,而且提供了多种主题选择,还可以高度自定义编辑器样式,可以在 Vue、React、Angular、Blazor、Svelte、Node+Express等多种环境中使用。
下载地址:https://prismjs.com/download.html
目录
1.前台展示tinymce编辑器编辑的内容时踩的坑
2.安装PrimsJS代码高亮库
3.解决PrimsJS添加后代码不高亮的问题
4.给代码块添加行号
5.PrimsJS代码高亮的原理
最近做了一个小项目,该项目主要是将tinymce富文本编辑器编辑的内容在前台展示,我很简单的以为只需要用Vue的v-html解析就能正常展示了,像下面这样
1. <template>
2. <div class="article-wrapper">
3. <template v-for="item in articleInfoData">
4. <h2 class="title">{{ item.title }}</h2>
5. <p class="desc" v-if="item.desc">摘要:{{ item.desc }}</p>
6. <div class="content" v-html="item.content"></div>
7. <p class="date">{{ item.created_at }}</p>
8. </template>
9. </div>
10.</template>
然而并没有那么简单,纯文本编辑的内容可以正常渲染,但是代码块就是显示纯文本,代码没有高亮,就像下面这样

这并非是我们所期待的,我们所期待的应该是下面这样的,让不同代码更加突出展示

后来才发现代码高亮是需要像prismjs这样的库来进行解析的,我们审查tinymce编辑器的代码块元素后会发现,pre->code标签下他给代码块添加了好多标签及样式,但是

我们获取到编辑器提交给后台的数据捶⑾謕re->code标签下就只有我们插入的代码块,就像下面这样,

起初我还以是tinymce编辑器配置的问题,导致缺失了pre->code标签下面的标签及样式,后来看完了tinymce官方文档的配置也没有找到类似的配置。
后来在网上找到了代码高亮库prismjs并愉快的添加到了Vue3+Ts项目中
什么是prismjs?

官方是这样描述的,他是一个非常简单快速可拓展且支持超多语言的轻量级代码高亮库。官网地址
1. //安装
2. npm install prismjs
1. //引入Prism(我们只需要在局部引入即可,我这里是在文章内容展示组件中引入)
2. <script setup lang="ts">
3. import { ref,
reactive, onMounted } from 'vue'
4. import type {Ref } from 'vue'
5. import {
useRouter, useRoute } from 'vue-router'
6. import { api
} from "@/assets/config/api"
7. import {
request } from "@/assets/common/request"
8. import Prism from "prismjs"//导入代码高亮插件的core(里面提供了其他官方插件及代码高亮样式主题,你只需要引入即可)
9. import "prismjs/themes/prism-tomorrow.min.css"//引入代码高亮主题(这个去node_modules的安装prismjs中找到想使用的主题即可)
10.
11.const router = useRouter()
12.const route = useRoute()
13.const articleInfoData: Ref<Array<object>> = ref([]);
14.
15.onMounted(() => {
16. getArticleInfo()
17. Prism.highlightAll()// 全局代码高亮
18.})
19.function getArticleInfo(): void {//从后台请求数据
20.
request.get(api.GetArticleInfo,
{
21. id: route.params.id
22.
}).then(res => {
23.
articleInfoData.value = res?.data?.data
24.
}).catch(err => {
25. console.log(err)
26.
})
27.
})
28.
29.}
30.</script>
但结果又出现了下面的这种问题,明显可以看到prismjs以及css是被加载了的,但代码还是没有被高亮

后来研究才发现是因为prismjs执行的时机不对导致pre->code中的代码没有被prismjs解析添加相应的标签。
解决办法也很简单,我们可以给Prismjs高亮方法添加一个定时器延迟加载,或者使用
1. onMounted(() => {
2. getArticleInfo()
3. setTimeout(() => {
4. Prism.highlightAll()// 全局代码高亮
5. }, 100)
6. })
async/await配合Promise进行加载,等数据完全加载完后再用Prismjs进行解析即可实现代码高亮,就像下面那样(需要注意的是一定要等v-html中后台返回的数据加载完毕后才可以被Prismjs解析)
1. <script setup lang="ts">
2. import { ref,
reactive, onMounted } from 'vue'
3. import type {Ref } from 'vue'
4. import {
useRouter, useRoute } from 'vue-router'
5. import { api
} from "@/assets/config/api"
6. import {
request } from "@/assets/common/request"
7. import Prism from "prismjs"//代码高亮插件的core
8. import "prismjs/themes/prism-tomorrow.min.css"//高亮主题
9. const router
= useRouter()
10.const route = useRoute()
11.const articleInfoData: Ref<Array<object>> = ref([]);
12.onMounted(async () => {
13. await getArticleInfo().then(res => {
14.
articleInfoData.value = res
15.
}).catch(err => {
16. console.log(err);
17.
})
18. Prism.highlightAll()// 全局代码高亮(必须等获取数据之后,代码高亮才能生效,也可以用定时器定时)
19.})
20.function getArticleInfo(): Promise<Array<object>> {//从后台请求数据
21. return new Promise((resolve, reject) => {
22.
request.get(api.GetArticleInfo,
{
23. id: route.params.id
24.
}).then(res => {
25. resolve(res?.data?.data)
26.
}).catch(err => {
27. reject(err)
28.
})
29.
})
30.}
31.</script>

我们从上图中可以发现虽然我们完美的给代码添加理论高亮,但我们在vscode中使用时会发现,每行代码都有相应大行号,这样代码出错时我们可以直接定位到某一行。
我们可以看到Prism官方 https://prismjs.com/index.html#plugins 提供了一些常用的插件,我们选择红框中的行号

那么如何使用行号插件呢?其实很简单,我们刚刚在上文中安装了Prism,只需要在node_modules中找到刚刚安装的prismjs中的plugins,然后找到对应的插件引入即可。

在项目中需要这样引入
1. <script setup lang="ts">
2. import { ref,
reactive, onMounted } from 'vue'
3. import type {Ref } from 'vue'
4. import {
useRouter, useRoute } from 'vue-router'
5. import { api
} from "@/assets/config/api"
6. import {
request } from "@/assets/common/request"
7. import Prism from "prismjs"//代码高亮core
8. import "prismjs/plugins/line-numbers/prism-line-numbers.min.js"//行号插件
9. import "prismjs/themes/prism-tomorrow.min.css"//高亮主题
10.import "prismjs/plugins/line-numbers/prism-line-numbers.min.css"//行号插件的样式
11.const router = useRouter()
12.const route = useRoute()
13.const articleInfoData: Ref<Array<object>> = ref([]);
14.onMounted(async () => {
15. await getArticleInfo().then(res => {
16.
articleInfoData.value = res
17.
}).catch(err => {
18. console.log(err);
19.
})
20. Prism.highlightAll()// 全局代码高亮(必须等获取数据之后,代码高亮才能生效,也可以用定时器定时)
21.})
22.function getArticleInfo(): Promise<Array<object>> {//从后台请求数据
23. return new Promise((resolve, reject) => {
24.
request.get(api.GetArticleInfo,
{
25. id: route.params.id
26.
}).then(res => {
27.
resolve(res?.data?.data)
28.
}).catch(err => {
29. reject(err)
30.
})
31.
})
32.}
33.</script>
引入完之后刷新你会发现还是没有添加行号

官网已经说得很清楚,除了引入行号插件及样式,我们还需要指定一个line-numbers类,将这个类添加到pre标签或者他的祖先,只要他或他的祖先添加的了line-numbers类,那么他的子元素就会被行号插件自动添加line-numbers,从而达到添加行号。

添加line-numbers类名到自己的项目,在这里我添加到了v-html要解析的那个标签上,因为后台返回的编辑数据都是在该标签内渲染,所以该标签属于pre标签的祖先元素,你也可以将line-numbers类名添加到该div的祖先父级元素中,又或者可以添加到body上,但建议添加到这个v-html要渲染的签上,因为只有该标签内的数据是要被渲染解析的。
1. <template>
2. <div class="article-wrapper">
3. <template v-for="item in articleInfoData">
4. <h3 class="title">{{ item.title }}</h3>
5. <p class="desc" v-if="item.desc">摘要:{{ item.desc }}</p>
6. <section class="author">
7. <span>作者:三叶雨</span>
8. <span>{{ item.created_at }}</span>
9. </section>
10. <div class="content line-numbers" v-html="item.content"></div>
11.
</template>
12.
</div>
13.</template>
添加line-numbers类名后我们可以看到行号已经被添加上去,其他插件的使用方式同本插件,大家按需引入然后看对应的插件即可。

在说原理之前我们应该弄明白的一个问题是,编辑器编辑的内容输出数据格式是怎么样的,这里以tinymce编辑器为例,我们可以发现返回给前台的数据是这样的,我们可以发现代码块被pre->code被这两个标签所包裹,简单来说就是prismjs是在pre->code

从后台返回的编辑器内容数据
上做了一些处理,我们可以从prismjs的源码中可以发现,获取到pre->code之后,首先获取到pre标签类上的language-xxxx给对应语言的关键字用正则匹配替换或添加标签及相应的样式,这样就达到了代码高亮的效果。

prism解析js的源码

被prism解析后代码高亮的HTML源码
所以只要符合pre->code标签结构内的代码块都会被prism js解析高亮。
到此完结,希望能为你节省一些时间。