PR

WordPressのサイトでVue3の単一ファイルコンポーネントを使う

Vue3
スポンサーリンク
スポンサーリンク

 

 

 

環境

このページで使用しているフレームワークやライブラリのバージョンは、以下のとおりです。

vue.js3.2.31
vue3-sfc-loader0.8.4

Vueのコンポーネントとは?

Vueでは、大きなアプリケーション(下図の左側)を小さな部品の組み合わせで構成(下図の右側)することができます。
この小さな部品(下図の緑色の箱)に相当するのがコンポーネントです。

詳しくは、Vueの公式ドキュメント「コンポーネントによる構成」を参照してください。

単一ファイルコンポーネントとは?

単一ファイルコンポーネント(Single-File Component:以後SFCと呼ぶ)は、Vueを使う上で、とても便利な仕組みです。

SFCは、下図のように、「JavaScript:<script>タグ」「HTML:<template>タグ」「CSS:<style>タグ」を1つにまとめて記述したファイルのことです。
このファイルの拡張子は「*.vue」で、SFCのことを、別名「*.vue」ファイルということがあります。

SFCの「JavaScript:<script>タグ」でコンポーネントの動作や処理を記述することができ、「HTML:<template>タグ」「CSS:<style>タグ」でコンポーネントの外観を指定することができます。
つまり、「*.vue」ファイル1つで、コンポーネントの動作と外観が規定できるということです。

大きなアプリケーションを小さなコンポーネントに分解すれば、ソースコードの可読性も上がりますし、メンテナンスも容易になります。

また、このSFCに汎用性を持たせて、再利用できるように作っておけば、いろいろなアプリケーションで流用できます。

ブラウザ環境で単一ファイルコンポーネントを使うには?

とっても便利なSFCなのですが、ブラウザ環境で動作するJavaScriptでは、「*.vue」ファイルをそのまま読み込むことができません。

サーバーの「Node.js」にインストールした「weback」や「Vue CLI」、「Vite」などの開発環境では、「*.vue」ファイルを直接読み込むことができます。

Vueの便利な機能の1つであるSFCが使えないのは、とても残念です。

ところが、「vue3-sfc-loader」というライブラリを使えば、ブラウザ環境でも「*.vue」ファイルを直接読み込んで、SFCの仕組みを活用することができます。

Vue3/Vue2 Single File Component loader.

Load .vue files dynamically at runtime from your html/js. No node.js environment, no (webpack) build step needed.

上記「vue3-sfc-loader」の概要にも書いてあるように、"No node.js environment, no (webpack) build step needed."、つまり、「Node.js」も「webpack」も無いブラウザ環境で、VueのSFCを使えるようになります。
ブラウザ環境で、SFCがサクッと使えるようになると、WordPressサイトのような「Node.js」の無い環境で、Vueの便利さと簡便さが最大限発揮できると思います。

「vue3-sfc-loader」のオプションを工夫すると、読み込んだ「*.vue」ファイルから、別の「*.vue」ファイルを読み込んだり、外部サイトのJavaScriptライブラリをインポートすることもできます。

「vue3-sfc-loader」の使い方

実際に、「vue3-sfc-loader」を使って、SFCを読み込んでみましょう。

どうでしょうか?
赤い文字で「Hello World!」と表示されましたか?

Hello World!」と表示されている部分には、WordPressのブロックエディタで「カスタム HTML」ブロックを配置しています。

「カスタム HTML」ブロックには、以下のHTMLコードを入力します。

<script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.js"></script>

<div id="app"></div>

<script type="module">
    ////////////////////////////////////////////////////////////////////////////////
    // vue3-sfc-loader モジュール
    ////////////////////////////////////////////////////////////////////////////////
    import { loadModule } from "https://cdn.jsdelivr.net/npm/vue3-sfc-loader@0.8.4/dist/vue3-sfc-loader.esm.js";

    ////////////////////////////////////////////////////////////////////////////////
    // vue3-sfc-loader オプション
    // SFCファイルから外部のモジュールをimportできるオプション
    // 参考:https://github.com/FranckFreiburger/vue3-sfc-loader/issues/14#issuecomment-908849863
    ////////////////////////////////////////////////////////////////////////////////
    const vue3_sfc_loader_options = {
        moduleCache: { vue: Vue },
        getFile(url) {
            url = /.*?.js|.mjs|.css|.less|.vue$/.test(url)
                ? url
                : `${url}.vue`;
            const type = /.*?.js|.mjs$/.test(url)
                ? ".mjs"
                : /.*?.vue$/.test(url)
                ? ".vue"
                : /.*?.css$/.test(url)
                ? ".css"
                : ".vue";
            const getContentData = (asBinary) =>
                fetch(url).then((res) =>
                    !res.ok
                        ? Promise.reject(url)
                        : asBinary
                        ? res.arrayBuffer()
                        : res.text()
                );
            return { getContentData: getContentData, type: type };
        },
        addStyle(textContent) {
            let styleElement = document.createElement("style");
            document.head.insertBefore(
                Object.assign(styleElement, { textContent }),
                document.head.getElementsByTagName("style")[0] || null
            );
        },
        handleModule(type, getContentData, path, options) {
            switch (type) {
                case ".css":
                    return options.addStyle(getContentData(false));
                case ".less":
                    console.error(".......");
            }
        },
        log(type, ...args) {
            console.log(type, ...args);
        },
    };

    ////////////////////////////////////////////////////////////////////////////////
    // Vue.js アプリケーションインスタンス
    ////////////////////////////////////////////////////////////////////////////////
    const app = Vue.createApp({
        components: {
            "hello-world": Vue.defineAsyncComponent(() =>
                loadModule(
                    "../wp-content/themes/cocoon-child-master/vue-components/hello_world.vue",
                    vue3_sfc_loader_options
                )
            ),
        },
        template: `<hello-world />`,
    });
    app.mount("#app");
</script>

コードの解説

では、コードの中身を解説します。

<script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.js"></script>

VueをCDNサイト「https://cdn.jsdelivr.net」からインポートします。
"vue@3.2.31"なので、メジャーバージョンが「3」です。
なので、Vue3を読み込んでいます。

投稿やページの最初に1回インポートすれば、それ以降、Vueのメソッド(関数)は、"Vue.createApp();"のように「Vue.func()」で利用できます。

<div id="app"></div>

Vueのアプリケーションインスタンスをマウントする<div>タグを"app"というidで配置します。
この<div>要素が、この後に読み込むSFC、"hello_world.vue"で置き換えられます。

<script type="module">
 …
<script>

<script type="module">~</script>が、SFCを読み込むJavaScriptになります。
JavaScriptでimport文を使うので、「type="module"」にします。

    ////////////////////////////////////////////////////////////////////////////////
    // vue3-sfc-loader モジュール
    ////////////////////////////////////////////////////////////////////////////////
    import { loadModule } from "https://cdn.jsdelivr.net/npm/vue3-sfc-loader@0.8.4/dist/vue3-sfc-loader.esm.js";

「vue3-sfc-loader」をCDNサイト「https://cdn.jsdelivr.net」からインポートします。
ファイル名が"vue3-sfc-loader.esm.js"になっていて、"esm"という文字列が入っています。
これは、import文でインポートできる「ESモジュール」であることを示しています。

    ////////////////////////////////////////////////////////////////////////////////
    // vue3-sfc-loader オプション
    // SFCファイルから外部のモジュールをimportできるオプション
    // 参考:https://github.com/FranckFreiburger/vue3-sfc-loader/issues/14#issuecomment-908849863
    ////////////////////////////////////////////////////////////////////////////////
    const vue3_sfc_loader_options = {
        moduleCache: { vue: Vue },
        getFile(url) {
            url = /.*?.js|.mjs|.css|.less|.vue$/.test(url)
                ? url
                : `${url}.vue`;
            const type = /.*?.js|.mjs$/.test(url)
                ? ".mjs"
                : /.*?.vue$/.test(url)
                ? ".vue"
                : /.*?.css$/.test(url)
                ? ".css"
                : ".vue";
            const getContentData = (asBinary) =>
                fetch(url).then((res) =>
                    !res.ok
                        ? Promise.reject(url)
                        : asBinary
                        ? res.arrayBuffer()
                        : res.text()
                );
            return { getContentData: getContentData, type: type };
        },
        addStyle(textContent) {
            let styleElement = document.createElement("style");
            document.head.insertBefore(
                Object.assign(styleElement, { textContent }),
                document.head.getElementsByTagName("style")[0] || null
            );
        },
        handleModule(type, getContentData, path, options) {
            switch (type) {
                case ".css":
                    return options.addStyle(getContentData(false));
                case ".less":
                    console.error(".......");
            }
        },
        log(type, ...args) {
            console.log(type, ...args);
        },
    };

"vue3_sfc_loader_options"は、「vue3-sfc-loader」でSFCを読み込むときのオプションです。
読み込んだSFCファイルから外部のモジュールをimportできるオプションになっています。

    ////////////////////////////////////////////////////////////////////////////////
    // Vue.js アプリケーションインスタンス
    ////////////////////////////////////////////////////////////////////////////////
    const app = Vue.createApp({
        components: {
            "hello-world": Vue.defineAsyncComponent(() =>
                loadModule(
                    "../wp-content/themes/cocoon-child-master/vue-components/hello_world.vue",
                    vue3_sfc_loader_options
                )
            ),
        },
        template: `<hello-world />`,
    });
    app.mount("#app");

"Vue.createApp"で、Vueのアプリケーションインスタンスを作成した後、"app.mount("#app");"で、最初に作成した<div>タグに、作成したアプリケーションインスタンスをマウントしています。

                loadModule(
                    "../wp-content/themes/cocoon-child-master/vue-components/hello_world.vue",
                    vue3_sfc_loader_options
                )

「vue3-sfc-loader」の"loadModule"関数で、SFCファイルを読み込みます。
今回は、"hello_world.vue"という名前のSFCファイルを"../wp-content/themes/cocoon-child-master/vue-components/"フォルダの下に置いています。

「ちょいプラ素材」のブログは、WordPressのテーマに「Cocoon」を使っています。
なので、「Cocoon」の子テーマ「cocoon-child-master」の下に"vue-components"というフォルダを作りました。

        components: {
            "hello-world": Vue.defineAsyncComponent(() =>

読み込んだSFCは、"hello-world"という名前のコンポーネントになります。

        template: `<hello-world />`,

"hello-world"コンポーネントを<hello-world>タグでHTML要素として配置します。

"hello_world.vue"の中身

"hello_world.vue"の中身は、Vueの公式ガイド「単一ファイルコンポーネント」で紹介されている単純なテンプレート構文の例のSFCです。

<script>
export default {
  data() {
    return {
      greeting: 'Hello World!'
    }
  }
}
</script>

<template>
  <p class="greeting">{{ greeting }}</p>
</template>

<style>
.greeting {
  color: red;
  font-weight: bold;
}
</style>

"greeting"というテンプレートを"color: red;"と"font-weight: bold;"のスタイルで表示するというものです。
"greeting"というテンプレートの文字列は、"Hello World!"です。

まとめ

「vue3-sfc-loader」ライブラリを使えば、WordPressの「カスタム HTML」ブロックから、VueのSFCを読み込むことができます。
「カスタム HTML」ブロックには、SFCのロード処理だけを書いて、実際にやりたい表示や処理は、SFC(1つの「*.vue」ファイル)にまとめることができます。
さらに凝った表示や処理をさせたい場合は、適宜、いくつかのSFCに分割すれば良いでしょう。

この方法で、WordPressサイトに簡単にVueを導入し、WordPressサイトのページ中に、複雑な表示や処理を実装することができます。

次は、もう少し面白いSFCの例として、WordPressの投稿に「Chart.js」のチャート(グラフ)を表示してみましょう。

コメント