🐺

Vue3とhighlight.jsを使って非同期にシンタックスハイライトを再現する

2024/05/02に公開

やったこと

WordPressのREST APIから取ってきた記事情報のcodeタグに対して、Vue上で非同期にシンタックスハイライトを表現したい。
APIから情報を取ってくる関係で、レイアウトの更新を非同期で実行する必要があった。

使用したライブラリ・技術など

  • WordPress(apiとして叩いて記事情報を取ってくる用)
  • highlight.js(11.9)
  • Vue3
  • axios(1.6.8)

実装した手順

  1. WordPressから非同期でデータを取りに行く(axios)
  2. データが問題なく取れたらrefの値を更新する
  3. refの値が更新されたタイミングでnextTickを使用してDOMの更新が完了してからhighlight.jsを実行する

実際に実装したコード

<script setup>
import axios from "axios";
import hljs from "highlight.js";
import "highlight.js/styles/a11y-dark.css";// シンタックスハイライトのレイアウト

import { onMounted, ref, watch, nextTick } from "vue";
// 一部省略

// axiosを使ってwordpressのAPIからデータを取得する
// データが取れたかどうかを待つ必要がある
let is404 = ref(0);

// wordpressのAPIから取得したデータ(記事周り)を格納する
let page = ref(null);
onMounted(() => {
  //const response = axios.get("/posts");
  const response = axios.get("http://localhost:8000/wp-json/wp/v2/posts/8");
  console.log(response);
  response
    .then((res) => {
      is404.value = 1;
      console.log(res);
      page.value = res.data;
    })
    .catch((error) => {
      // サーバーからの応答が遅すぎる場合は404の画面を出すために切り分け
      console.log("サーバーが立ち上がっていません");
      is404.value = -1;
    });
});

// もし表示されなかったら関数実行する等であればここに書く
// たとえば表示されなくてリダイレクトする場合とか
watch(is404, (newVal) => {
    if(is404 === 1){
      codeHighLight();
    }
});

// コードハイライト系
// 確実にDOMが描画された後に実行するためにnextTickを使う
// nextTickを使う関係上、async functionを使う必要がある
// 参考:https://zenn.dev/kazu1/articles/8e02c3aa3269b9
async function codeHighLight() {
  await nextTick();
  const codes = document.querySelectorAll("code");
  console.log(codes);
  codes.forEach((code) => {
    hljs.highlightElement(code);
  });
}
</script>

<template>
  <div v-if="is404 === 0">loading...</div>
  <article v-else-if="is404 === 1">
    <section class="article">
      <div class="article__inner">
        <SectionHeadline :headline="props.headline" />
        <div class="article__container" v-html="page.content.rendered"></div>
        <ArrowAnkerLink href="#work" anker-text="WORKS" />
        <pre>
          <code class="javascript">
            function test() {
              console.log("hello");
            }
          </code>
        </pre>
      </div>
    </section>
  </article>
  <ErrorDisplay v-else />
</template>

該当リポジトリ/コードは以下。
https://github.com/Karakure178/wordpress-test/blob/main/test1/front/src/components/page/article-template/article/Article.vue

実装する際詰まった所・課題等

  • WordPressから非同期でcodeタグ周りの情報を取ってくる影響で、クラス名等変えることが容易でないため、highlight.jsの公式が用意しているvue用プラグインがうまく使えなかった。
  • DOMが更新された後にレイアウトを変更してほしかったが、どうやればいいのかわからず詰まった(watchとnextTickを組み合わせることでどうにかなることに気づかなかった)

参考文献

https://www.torat.jp/how-to-use-highlight-js/

http://blog.livedoor.jp/nnmy/archives/55093136.html

https://ja.vuejs.org/api/general#nexttick

https://ja.vuejs.org/guide/essentials/watchers

Discussion