Leafage 诞生记(四、nuxt/content使用限制、如何进行markdown解析并高亮代码块)

Leafage 诞生记(四、nuxt/content使用限制、如何进行markdown解析并高亮代码块)

上一篇中介绍了nuxtjs的基本配置项,以及自动引入components的使用,那么接下来说一说在nuxtjs中如何读取markdown文档并将其中的代码进行高亮显示。

第一篇文章中创建项目的时候,提过没有选择@nuxt/content这个模块,因为网站的所有数据都是通过接口的方式请求后端数据来进行加载的,而content不持支从接口获取数据,只能支持从项目本地的 /content 目录下获取数据,在content模块中,默认使用的prismjs来高亮代码,但是呢,它提供的集中高亮主题都不好看,而且可供选择的主题很少,因此也没有使用他来做代码高亮,而是选择了highlight.js来做。

要解析markdown内容就需要对应的解析器,有marked和markdown-it两种选择,在nuxtjs的插件或者模块中marked并不好配置,所以选择markdown-it,使用markdown-it和highlight.js比较容易,添加@nuxtjs/markdownit模块,然后在nuxt.config.js配置文件中进行配置,如下所示:

  markdownit: {
    preset: 'default',
    linkify: true,
    breaks: true,
    use: [
      'markdown-it-div',
      'markdown-it-highlightjs',
      'markdown-it-mark',
      'markdown-it-deflist'
    ],
    runtime: true // Support `$md()`
  },

这里的配置几乎和原生的markdown-it配置一样,哦,对了,这里不要忘了yarn add命令添加对应的插件。

然后在页面中就可以直接使用$md.render(“xx”)来完成markdown的解析,如下所示:

<p
  class="my-4 leading-relaxed tracking-wide markdown-body"
  v-html="$md.render(data.content)"
></p>

这些都做了之后你会发现,代码高亮不是喜欢的样式,那需要在nuxt.config.js中的css配置下添加样式文件的引用,如下所示:

  // Global CSS (https://go.nuxtjs.dev/config-css)
  css: [
    '~/node_modules/highlight.js/styles/github.css'
  ],

以上就是nuxtjs下markdown解析和代码高亮的配置,但是有一个问题是,这样是默认的配置,会把highlight.js的所有依赖都安装,并且会一起打包,但是可能只需要针对某几个类型的代码进行高亮,那就需要对配置进行修改,这个问题可以通过自己配置markdown-it和highlight.js来解决。

  1. 首先在plugins目录下添加markdown.js文件,然后参照markdown-it和highlight.js文档来进行配置,示例如下:
import MarkdownIt from "markdown-it";
import hljs from "highlight.js/lib/core";
import "highlight.js/styles/ir-black.css";

import bash from 'highlight.js/lib/languages/bash'
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import java from 'highlight.js/lib/languages/java';
import sql from 'highlight.js/lib/languages/sql';
import nginx from 'highlight.js/lib/languages/nginx';
import json from 'highlight.js/lib/languages/json';
import yaml from 'highlight.js/lib/languages/yaml';
import xml from 'highlight.js/lib/languages/xml';
import htmlbars from 'highlight.js/lib/languages/htmlbars'
import shell from 'highlight.js/lib/languages/shell'

hljs.registerLanguage('bash', bash)
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('java', java);
hljs.registerLanguage('sql', sql);
hljs.registerLanguage('nginx', nginx);
hljs.registerLanguage('json', json);
hljs.registerLanguage('yaml', yaml);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('htmlbars', htmlbars);
hljs.registerLanguage('shell', shell);

const md = new MarkdownIt({
  html: true,
  linkify: true,
  breaks: true,
  xhtmlOut: true,
  typographer: true,
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return (
          '<pre class="hljs"><code>' +
          hljs.highlight(lang, str, true).value +
          "</code></pre>"
        );
      } catch (__) { }
    }

    return (
      '<pre class="hljs"><code>' +
      md.utils.escapeHtml(str) +
      "</code></pre>"
    );
  },
});

export default md

因为markdown-it不支持typescript,highlight.js的types直接使用有问题,会报错,所以直接使用js来做。

  1. 在posts/detail/_slug.vue文件中引入markdown.js,然后进行渲染,示例如下:
import { defineComponent } from "@vue/composition-api";

import { SERVER_URL } from "~/assets/request";
import md from "~/plugins/markdown";

export default defineComponent({
  name: "Slug",

  scrollToTop: true,

  async asyncData({ app: { $axios, store }, params }) {
    let [data, previous, next, topDatas] = await Promise.all([
      // detail
      await $axios.$get(SERVER_URL.posts.concat("/", params.slug, "/details")),
      // previous
      await $axios.$get(SERVER_URL.posts.concat("/", params.slug, "/previous")),
      // next
      await $axios.$get(SERVER_URL.posts.concat("/", params.slug, "/next")),
      // topThree
      await $axios.$get(SERVER_URL.posts.concat("?page=0&size=3&order=viewed")),
    ]);

    store?.commit("CHANGE_CODE", data.code);
    store?.commit("CHANGE_TITLE", data.title);
    store?.commit("CHANGE_DESCTIPTION", data.subtitle);

    let rendered = md.render(data.content);

    return { data, previous, next, topDatas, rendered };
  },

})

中直接使用就可以了。