网站首页 » 前端开发 » Vue » Vue2 实现代码高亮功能
上一篇:
下一篇:

Vue2 实现代码高亮功能

前言

本来这是一个很简单的问题,直接引入第三方插件,然后安装第三方插件的教程写几行代码就可以了,但这是理想的状态,在实际开发中,肯定会遇到各种情况,导致代码没有亮度。

基于 highlight.js 实现

highlight.js

安装 highlinght.js

npm install highlight.js

下面我们先来基本按照 highlight.js 官方教程来实践一下。

引入 js 库 highlight.js 和样式表 github.css,官方提供了很多种主题,所有的主题样式文件存放 node_modules\highlight.js\styles 目录中,你可以根据自己的口味进行选择。

在组件模板中添加如下代码

<pre>
    <code v-html="html"></code>
</pre>

变量 html 就是我们的代码字符串

在组件的 methods 中添加一个 modifyCode 方法,代码如下:

modifyCode() {
    let blocks = document.querySelectorAll("pre code");
    blocks.forEach(block => {
      hljs.highlightBlock(block);
    });
}

然后在组件的 created 中执行这个方法。

但是你会发现代码根本就没有高亮效果。原因在于 code 元素没找到,也就是说在创建组件后,还没能成功的找到 code 元素,因为 Vue 还没渲染到页面中。此时我们可以通过万能的定时器来延迟执行就可以解决了。

setTimeout(() => {
  let blocks = document.querySelectorAll("pre code");
  console.log(blocks);
  blocks.forEach(block => {
    hljs.highlightBlock(block);
  });
}, 1000);

虽然这样可以解决问题,但是这是一种不是很完美的解决方案,毕竟还没有达到准确的时刻来执行高亮的 JS 代码,而是硬生生的在一秒之后才执行。不管我的元素是否已经加载完成,这会带来一个问题就是代码高亮的效果可能会在代码渲染到页面这后很久才会生效,让种用户体验肯定不是最好的。要想解决这个问题,我们可以使用 this.$nextTick() 来替代 setTimeout();

this.$nextTick(function() {
  let blocks = document.querySelectorAll("pre code");
  blocks.forEach(block => {
    hljs.highlightBlock(block);
  });
});

此时,当代码渲染到页面时,给人感觉就是我天生就是长成这样的(代码已高亮),完美地接近同步,而不是代码渲染到页面跟代码高亮之间有一段时间的等待。

this.$nextTick() 的作用就是在下一次 DOM 循环结束之后执行延迟回调。

下面是一个完整的代码:

<template>
<div>
  <pre>
    <code v-html="html"></code>
  </pre>
</div>
</template>
<script>
import hljs from "highlight.js";
import "highlight.js/styles/github.css"; //样式文件
export default {
  name: "Home",
  data() {
    return {
      html: `
var express = require('express');
var app = express();
app.get('/', function (req, res) {
    res.send('http://yunkus.com');
});
var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('云库网', host, port);
});`
    };
  },
  methods: {
    modifyCode() {
      this.$nextTick(function() {
        let blocks = document.querySelectorAll("pre code");
        blocks.forEach(block => {
          hljs.highlightBlock(block);
        });
      });
    }
  },
  created() {
    this.modifyCode();
  }
};
</script>

在实践中,我们希望做到的是在文章的详情页数据渲染到页面后进行代码高亮。这时也很简单,我们只需要把 <pre><code> 放到详情页数据中就可以了。然后修改下模板中的调用方式:

模板中修改如下
<template>
<div>
  <div v-html="html"></div>
</div>
</template>
data 数据修改如下
  data() {
    return {
      html: `
<pre><code>var express = require('express');
var app = express();
app.get('/', function (req, res) {
    res.send('http://yunkus.com');
});
var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('云库网', host, port);
});</code></pre>`
    };
  },

这里所做的也只是把两个标签从模板中般到了数据字符串中。这个数据就是从后台返回来的 html 字符串。

效果如下:

Vue 实现代码高亮功能

封装成指令

在 src 目录下新建一个 directives 目录,在此目录下新建一个名为 highlight.js 的指令文件,把 highlight.js 相关的代码搬到这个文件:

import Vue from 'vue'
import hljs from "highlight.js";
import "highlight.js/styles/github.css"; //样式文件
Vue.directive('highlight', {
  inserted(el) {
    let blocks = el.querySelectorAll('pre code');
    blocks.forEach((block) => {
      hljs.highlightBlock(block);
    })
  },
})

然后在 main.js 中引入这个指令即可:

import Vue from 'vue'
import App from './App'
import router from './router'
import highlight from './directives/highlight'

Vue.config.productionTip = false

/* eslint-disable no-new */
let vm = new Vue({
  el: '#app',
  router,
  store,
  components: {
    App
  },
  template: '<App/>'
})

在 main.js 中引入了指令之后我们就可以在项目中的所有组件任意使用这个指令了。如果你不想在 main.js 中引入highlight 指令,你也可以在对应的组件中引入。

封装成插件

你也可能把它个高亮功能封装成插件,在 src 目录下新建一个 plugins 目录,在此目录下新建一个名为 highlight.js 的指令文件,把 highlight.js 相关的代码搬到这个文件:

import hljs from "highlight.js";
import "highlight.js/styles/github.css"; //样式文件
let Highlight = {};

Highlight.install = function (Vue, optioins) {
  Vue.directive('highlight', {
    inserted(el) {
      let blocks = el.querySelectorAll('pre code');
      blocks.forEach((block) => {
        hljs.highlightBlock(block);
      })
    },
  })
}

export default Highlight;

然后在 main.js 中引入这个插件,并通过 use() 方法启用

import Vue from 'vue'
import App from './App'
import router from './router'

import highlight from './plugins/highlight'
Vue.use(highlight);

Vue.config.productionTip = false
/* eslint-disable no-new */
let vm = new Vue({
  el: '#app',
  router,
  store,
  components: {
    App
  },
  template: '<App/>'
})

不管是封装成指令,还是插件,我们都是以指令的形式来实现的,所以在使用时,我们像下面这样在模板中调用就可以:

<div v-highlight>
    <div v-html="html"></div>
</div>

如果我们是通过请求后端返回数据的(在项目中也确实是这样的),我们可以通过 setTimeout 来进行模拟:

  created() {
    setTimeout(() => {
      this.html = `
<pre><code>var express = require('express');
var app = express();
app.get('/', function (req, res) {
    res.send('http://yunkus.com');
});
var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('云库网', host, port);
});</code></pre>`;
    }, 1000);
  }

注意,记得在 data 中声明 html 变量。

此时我们就需要把指令中的 inserted 改成 componentUpdated ,这时请求完数据回来并渲染到页面中后,代码才能得到高亮效果。他们两个的区别:

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

更多属性及用法可以到官方文档中查阅。

相关资料

https://highlightjs.org/

https://cn.vuejs.org/v2/guide/custom-directive.html

  • 微信扫一扫,赏我

  • 支付宝扫一扫,赏我

声明

原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

本文永久链接:http://yunkus.com/vue-code-highlighting/

Leave a Reply

Your email address will not be published. Required fields are marked *

评论 END