15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue3のすゝめ 〜外部テンプレートエンジン編〜

Last updated at Posted at 2023-08-29

はじめに

こんにちは!
先日はVue3をアゲアゲ↑↑する記事を投稿し、
ご好評頂けたようでありがとうございます:blush:

X (旧Twitter)を見ている限りだと肯定的な意見が多く、
これを機にVueのご利用を検討頂ける人が増えてくれると嬉しいです。

さてさて記事の中では、
out of boxで動作する=外部テンプレートエンジンとも相性が良い」という話をしたのですが、今回は具体例を交えながらこの点をもっと掘り下げていこうと思います。

この記事では、
サーバテンプレートエンジンとしてblade(Laravel9)を例にとって解説していきますが、どのテンプレートでも同様なのでご安心ください。

・・実例の前に

そもそも論として、
私はサーバ由来のテンプレートエンジンを使うことが好きではありません。

「フロントはフロントのテンプレートエンジンでやるから、サーバ実装は全部APIでやってくれ」というのが本音ですし、
SPAに慣れているフロントエンジニアの皆さんもほとんど同じような意見かなと思います:confused:

でもそれでも、お仕事なのでこちらの都合が通らない時もあります。
そんな時の処方箋としてもVue3は最適だよっていう話を今回はしたいと思います。

下準備

Laravel9はデフォルトがwebpackからViteに変わっています。
まずは公式を見て、エントリーポイントとなるapp.jsの作成と読み込みまで済ませてください。(雑)

次にVueインスタンスの作成ですが、
例えばid="modal"の要素に対してVueで作成したモーダルコンポーネントを当てるには以下のようになります。

<div id="modal"></div>
import CommonModal from './components/common/Modal.vue';

createApp(CommonModal).mount('#modal')

が、 モーダルが増える度に下の行を複製していくのはナンセンスでしょう。

以下のように書くと#app配下の要素に対して、
追加のJSの記述なしでいつでも何回でもコンポーネントが利用できるようになります。
(#appはblade内のbody直下などにするのが良いかと思います)

import CommonModal from './components/common/Modal.vue';

const app = createApp({
  components: {
    CommonModal,
  },
}).mount('#app');

window.app = app;
<div id="app">

  <common-modal></common-modal>
  <common-modal></common-modal>

</div>

window.app = app;と最終行に書いてVueインスタンスをグローバルに公開しているのですが、これはちょっとしたテクニックで後々役に立ってきます。

モーダル作成

そういえばモーダルファイル作成がまだでしたね、とりあえず最低限の記述として以下のようにしておきましょう。

<script setup>
import { ref } from 'vue';

// フラグ
const isOpen = ref(false);

// オン/オフ切り替え
const toggleModal = (_animation) => {
  isOpen.value = !isOpen.value;
};

// コンポーネント外に公開
defineExpose({
  toggleModal,
  isOpen,
});
</script>

<template>
  <div v-if="isOpen" class="modal">
    <h3 class="title" v-if="$slots.header">
      <slot name="header" />
    </h3>
    <div class="body">
      <slot />
    </div>
    <div class="footer">
      <button @click="toggleModal">閉じる</button>
    </div>
  </div>
</template>

<style lang="scss" scoped></style>

フラグは親からpropsで受け取るのではなく、
コンポーネント自身で保持してdefineExpose()外部に公開するようにしています。(これも理由があるので後述)

ではblade側にこのモーダルと、オープンするためのボタンを置きましょう。

<common-modal ref="modal">
  <template #title>タイトル</template>
  本文
</common-modal>
<button onclick="app.$refs.modal.toggleModal()">ボタン</button>
<!-- または -->
<button onclick="app.$refs.modal.isOpen = true">ボタン</button>

これを見て不思議に思った方がいるかも知れませんが、
ここではVueの@clickではなくonlickで発火しています。

ご存知ない方もいらっしゃるかと思いますが、
実はただのJavaScriptのコードでVueコンポーネントを外部から扱えます!

appというのは先ほどエントリーポイントでグローバル(window)に公開したVueインスタンスのことを指しており、
$refsには#app配下でref=*指定しているものが全て入っています。

toggleModalは、モーダルコンポーネント内でdefineExpose()した関数(=中身はモーダルの開閉をしている)になります。

なぜこんなことをしているかと言うと、
外部テンプレートとVueを使った開発をしていると、
Vueコンポーネントを扱うために毎回script用意しないといけないの面倒くさいな」とか「こっちはただのJSのコードだけど、こっちはVueのコードで操作しないといけないの分かりづらいな」とか、思った経験はありませんか・・?

Vueは状態を外に出しても機能するので、
通常のJavaScriptに思いっきり寄せた扱い方もでき、
それによりblade ⇆ Vue間の摩擦を更に減らすことが可能になります。

またよくある要素の取得もかなり楽にしてくれます。

例えば以下のようなVueコンポーネントとは全く無関係の要素があった時、

<div>ほげ</div>
<div>ふが</div>

jsファイル内でこの要素を取得するのはこんな感じでしょうか?

<div id="hoge">ほげ</div>
<div id="fuga">ふが</div>
document.getElementById('hoge')
document.querySelector('#fuga')

いえいえ、これで出来ます。

<div ref="hoge">ほげ</div>
<div ref="fuga">ふが</div>
const { hoge, fuga } = app.$refs

またよく使う処理に関しても、
Vueインスタンス内に含めておけば好きな時にただのJSの変数/関数として利用できます。

import CommonModal from './components/common/Modal.vue';
import { useUtility } from './composables/useUtility';
import { useAuth } from './composables/useAuth';

const { copyToClipboard } = useUtility();
const { user } = useAuth();

const app = createApp({
  components: {
    CommonModal,
  },
  methods: {
    copyToClipboard
  },
  data: () => ({
    user,
  }),
}).mount('#app');

window.app = app;
<button onclick="app.copyToClipboard(app.user)">コピー</button>

Vueインスタンス内に含めるのは、
JSファイルを作らずとも処理や値をhtml側で呼び出せるようにしたいというモチベーションが個人的に大きいからで、JSファイルを別で作ってimportしてきても勿論大丈夫です。
(むしろグローバルに置くのは見られても問題ない情報に留めましょう)

import { watchEffect } from 'vue';
import { useAuth } from '@/composables/useAuth';
const { user } = useAuth();

watchEffect(() => {
  console.log(`${user.value}がログインしました`)
})

上記はただのプレーンなJSの記述ですが、
Vueの状態はVueの外でもどこでも動作、検知するのでちゃんと動作します。神。

まとめ

いかがでしたでしょうか?
out of boxで動作することのメリットは至るところに散りばめられていると思います。

特に外部テンプレートエンジンを採用する場合はその理由が顕著になるので、
何を使ったら良いか迷っている方は是非フロントフレームワークにVue3を採用することをご検討してみてください:relaxed:

以上!

15
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?