🐺
Vue3とhighlight.jsを使って非同期にシンタックスハイライトを再現する
やったこと
WordPressのREST APIから取ってきた記事情報のcodeタグに対して、Vue上で非同期にシンタックスハイライトを表現したい。
APIから情報を取ってくる関係で、レイアウトの更新を非同期で実行する必要があった。
使用したライブラリ・技術など
- WordPress(apiとして叩いて記事情報を取ってくる用)
- highlight.js(11.9)
- Vue3
- axios(1.6.8)
実装した手順
- WordPressから非同期でデータを取りに行く(axios)
- データが問題なく取れたらrefの値を更新する
- 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>
該当リポジトリ/コードは以下。
実装する際詰まった所・課題等
- WordPressから非同期でcodeタグ周りの情報を取ってくる影響で、クラス名等変えることが容易でないため、highlight.jsの公式が用意しているvue用プラグインがうまく使えなかった。
- DOMが更新された後にレイアウトを変更してほしかったが、どうやればいいのかわからず詰まった(watchとnextTickを組み合わせることでどうにかなることに気づかなかった)
参考文献
Discussion