火曜日, 8月 19, 2025
火曜日, 8月 19, 2025
- Advertisment -
ホームニューステックニュースビルド不要! Google Apps Script のウェブアプリを Vue.js と Tailwind CSS で実装しよう

ビルド不要! Google Apps Script のウェブアプリを Vue.js と Tailwind CSS で実装しよう


この記事は ヌーラボブログリレー2025夏 の1日目として投稿しています。

ビルド不要! Google Apps Script のウェブアプリを Vue.js と Tailwind CSS でモダンに実装しよう #ヌーラボブログリレー2025夏

明日以降の執筆予定については 夏の恒例!「ヌーラボブログリレー2025 夏」を開催します! #ヌーラボブログリレー2025夏 をご覧ください!

Google Apps Script (GAS) は Google の各サービスと連携したアプリケーションを開発できるプラットフォームです。

GAS には doGet メソッドを定義することで、 UI を持つ Web アプリケーションを公開する機能があります。
この機能を使えば、サーバーサイドの GAS の関数を JavaScript から呼び出したり、動的な値を埋め込んだ HTML を生成したりすることが可能です。

https://developers.google.com/apps-script/guides/web?hl=ja

しかし、 GAS には一つ大きな制約があります。
それは、 JavaScript や CSS といったクライアントサイドのアセットファイルを単体でホスティングできない点 です。
このため、 GAS では素朴な JavaScript や CSS を書くことが多いかなと思います。

そこでこの記事では、 近年のブラウザに標準搭載された ES Modules などの機能を活用し、 GAS 上でモダンなフロントエンド技術スタックを利用する方法 を紹介します。

script.gs
function doGet() {
  const template = HtmlService.createTemplateFromFile("index");

  return template.evaluate();
}

function getCurrentTime() {
  return {
    currentTime: new Date().toISOString(),
  };
}
index.html
DOCTYPE html>
html>
  head>
    base target="_top" />
    meta charset="UTF-8" />
    meta name="viewport" content="width=device-width, initial-scale=1.0" />
    script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4">script>
    script type="importmap">
      {
        "imports": {
          "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
        }
      }
    script>
  head>
  body>
    script type="module">
      import { createApp, ref, computed, onMounted } from "vue";

      const app = createApp();

      const useGoogleScript = (name) => {
        const isPending = ref(false);
        const error = ref(null);

        const callGoogleScript = (...args) =>
          new Promise((resolve, reject) => {
            google.script.run
              .withSuccessHandler(resolve)
              .withFailureHandler(reject)
              [name](...args);
          });

        const run = async (...args) => {
          try {
            isPending.value = true;
            error.value = null;

            return await callGoogleScript(...args);
          } catch (err) {
            error.value = err;
          } finally {
            isPending.value = false;
          }
        };

        return { run, isPending, error };
      };

      app.component("ServerCurrentTime", {
        template: "#server-current-time",
        setup() {
          const isoString = ref(null);
          const isInitializing = ref(true);

          const formattedDate = computed(() =>
            Intl.DateTimeFormat(navigator.language, {
              dateStyle: "long",
              timeStyle: "long",
            }).format(new Date(isoString.value))
          );

          const { run, isPending } = useGoogleScript("getCurrentTime");

          const getCurrentTime = async () => {
            const { currentTime } = await run();

            console.log(currentTime);

            isoString.value = currentTime;
          };

          onMounted(() => {
            getCurrentTime().finally(() => {
              isInitializing.value = false;
            });
          });

          return {
            isoString,
            formattedDate,
            isInitializing,
            isPending,
            getCurrentTime,
          };
        },
      });

      app.mount("#root");
    script>

    template id="server-current-time">
      div class="grid gap-5 border rounded px-6 py-4">
        template v-if="isInitializing">
          p class="text-gray-400">取得中...p>
        template>
        template v-else>
          dl class="grid gap-3">
            dt class="font-bold">現在の日時(サーバー)dt>
            dd>
              time :datetime="isoString">{{ formattedDate }}time>
            dd>
          dl>
          div class="flex justify-center">
            button
              :class="['px-4 py-1 rounded border border-blue-300 bg-blue-200 text-blue-600 hover:opacity-60 disabled:opacity-60', { 'animate-pulse': isPending }]"
              :disabled="isPending"
              @click="getCurrentTime"
            >
              日時を更新
            button>
          div>
        template>
      div>
    template>

    div id="root">
      div class="py-10 mx-auto max-w-md">
        server-current-time>server-current-time>
      div>
    div>
  body>
html>

ES modules (ESM) は JavaScript 公式のモジュールシステムです。
2018 年ごろには主要なブラウザでサポートされました。

ESM では

Source link

Views: 1

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -