vue实现v-html代码块高亮+行号

一、前言

由于最近开发了自己的博客,懂的都懂,既然要写博客,那肯定就得用到文本编辑器,我这里用的是 vue-quill-editor。具体怎么用百度都有的,就不多说了。

通过编辑器编辑的文章发布以后,发现样式实在是太丑了,对于我这种只看颜值的人,实在是不能忍。

这里主要针对代码块做一个代码高亮及添加行号的样式优化。

网上查了一些资料,发现有2种插件可用(highlight.js 和 prismjs)。

二、依赖

  • vue 2.x
  • element ui
  • vue-quill-editor
  • highlight.js
  • highlightjs-line-numbers.js
  • prismjs
  • babel-plugin-prismjs

三、实现

3.1、highlight 代码高亮+行号

3.1.1、安装

less 复制代码
// 代码高亮
npm i highlight.js
// 行号
npm i highlightjs-line-numbers.js

3.1.2、创建 highlightjs-line-numbers.js

由于引用 highlightjs-line-numbers.js 源码,会报错:highlight.js not detected!

所以我这里手动改一下该源码,在项目目录下创建一个文件夹 resources,在该文件夹下创建文件 highlightjs-line-numbers.js,将 highlightjs-line-numbers.js 源码拷贝进去,并做一些修改,需要修改的代码如下:

scss 复制代码
// jshint multistr:true
function lineNumberInit(hljs, w, d) {
  .......

  if (hljs) {
    hljs.initLineNumbersOnLoad = initLineNumbersOnLoad
    hljs.lineNumbersBlock = lineNumbersBlock
    hljs.lineNumbersValue = lineNumbersValue

    addStyles()
  } else {
    w.console.error('highlight.js not detected!')
  }

 .......
}

export { lineNumberInit }

3.1.3、创建自定义指令

我这里选择使用 vue 自定义指令的方式来实现,在项目 utils 目录下创建 highLight.js 文件,内容如下:

javascript 复制代码
import Vue from 'vue'

// 引入依赖
import hljs from 'highlight.js'
import 'highlight.js/styles/atom-one-dark.css' // 代码高亮的样式

import { lineNumberInit } from '../resources/highlightjs-line-numbers.js' // 行号
lineNumberInit(hljs, window, window.document)

// 创建vue自定义指令
Vue.directive('highlight', el => {
  const blocks = el.querySelectorAll('pre')
  blocks.forEach(block => {
    hljs.highlightBlock(block)
    hljs.lineNumbersBlock(block)
  })
})

3.1.4、注册自定义指令

main.js 中注册自定义指令,代码如下:

arduino 复制代码
import '@/utils/highLight.js'

3.1.5、使用自定义指令

给渲染内容的标签添加该自定义指令即可。

ini 复制代码
<div v-highlight ref="blogContentRef" v-html="blogInfo.content"></div>

3.1.6、效果展示

3.1.7、存在问题

不知道什么原因,如果文章添加了目录功能或图片预览功能后,当点击目录或者预览图片后,代码块的行号格式会消失,体验特别不好。

3.2、prismjs 代码高亮+行号

3.2.1、安装

less 复制代码
// 安装 prismjs
npm i prismjs
// 安装 prismjs 的编译器插件
npm i babel-plugin-prismjs -D

3.2.2、配置 prismjs

在项目下找到 babel.config.js,在 module.exports 下的 plugins 中追加以下配置,如果没有则手动添加。

arduino 复制代码
plugins: [
  [
    "prismjs",
    {
      languages: ["javascript", "css", "markup"],
      plugins: ["line-numbers"], // 配置显示行号插件
      theme: "okaidia", // 主题名称
      css: true,
    },
  ],
],

3.2.3、创建自定义指令

我这里也选择使用 vue 自定义指令的方式来实现,在项目 utils 目录下创建 highLight.js 文件,在该文件中引入 prismjs 插件并创建 Highlight 自定义指令,内容如下:

javascript 复制代码
// 引入 prismjs 插件
import Prism from 'prismjs'

// 创建指令 v-highlight
const Highlight = {}
Highlight.install = Vue => {
  Vue.directive('highlight', {
    // 指令所在组件的 VNode 及其子 VNode 全部更新后调用
    componentUpdated: () => {
      // 代码美化
      Prism.highlightAll()
    }
  })
}
export default Highlight

3.2.4、注册自定义指令

在 main.js 中注册自定义指令,代码如下:

javascript 复制代码
import Highlight from '@/utils/highLight.js'
Vue.use(Highlight)

3.2.5、使用自定义指令

给渲染内容的标签添加该自定义指令即可。

ini 复制代码
<div v-highlight ref="blogContentRef" v-html="blogInfo.content"></div>

3.2.6、注意(重点)

如果你已经按以上步骤做了,但是你发现代码块并没有任何变化。其实这里重点需要注意的一个问题是:

通过文本编辑器编辑的代码块,在页面上通过 v-html 指令渲染出来后,你会发现只有 pre 标签,里面并没有 code 标签。

而这个插件只对 pre 标签嵌套的 code 标签起作用,插件规定有的类名必须写在 code 标签上,比如设置行号的类名:line-numbers 和设置编程语言的类名:language-js,所以没效果是正常的。

那么知道问题所在,我们就可以对文章内容做一个处理,那就是在文章内容存入数据库前,做一个全局替换工作,手动将 code 标签和一些必要的类名加进去。

以我的代码为例:

this.blogForm.content 为原本的内容

newContent 为全局替换后的内容

xml 复制代码
let newContent = this.blogForm.content.replace(/<pre class="ql-syntax" spellcheck="false">/g, '<pre class="ql-syntax line-numbers language-js" spellcheck="false"><code class="language-js">')
newContent = newContent.replace(/</pre>/g, '</code></pre>')

3.2.7、效果展示

再次刷新页面,代码块样式生效了,效果如下:

四、总结

以上就是代码块高亮+行号的两种实现方式,我个人比较喜欢 prismjs,因为没有什么 bug,不影响体验。对于这两种插件,对比如下:

highligh 优势:

  • highligh 不需要对博客内容进行全局替换增加 code 标签;
  • highligh 代码高亮样式可选择比较多。

highligh 劣势:

  • highlightjs-line-numbers.js 源码有问题;
  • 代码块的行号格式会有消失的情况;

prismjs 优势:

  • 没有什么bug,代码块的行号格式不会莫名消失;
  • 添加行号使用 line-numbers 类名即可。

prismjs 劣势:

  • 需要手动添加 code 标签;
  • 样式风格似乎是固定,不能选择;
相关推荐
小盼江33 分钟前
水果生鲜农产品推荐系统 协同过滤余弦函数推荐水果生鲜农产品 Springboot Vue Element-UI前后端分离 代码+开发文档+视频教程
vue.js·spring boot·ui
祈澈菇凉1 小时前
如何结合使用thread-loader和cache-loader以获得最佳效果?
前端
垣宇1 小时前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
java1234_小锋1 小时前
一周学会Flask3 Python Web开发-客户端状态信息Cookie以及加密
前端·python·flask·flask3
化作繁星1 小时前
如何在 React 中测试高阶组件?
前端·javascript·react.js
初遇你时动了情1 小时前
react module.scss 避免全局冲突类似vue中scoped
vue.js·react.js·scss
Au_ust1 小时前
千峰React:函数组件使用(2)
前端·javascript·react.js
爱吃南瓜的北瓜2 小时前
npm install 卡在“sill idealTree buildDeps“
前端·npm·node.js
TTc_2 小时前
记录首次安装远古时代所需的运行环境成功npm install --save-dev node-sass
前端·npm·sass
翻滚吧键盘2 小时前
npm使用了代理,但是代理软件已经关闭导致创建失败
前端·npm·node.js