JSひろば開発7日目:Vueアプリのデプロイとデザイン

JSひろばアプリもほぼ完成に近づいていきました。今日は最終的にvue-routerを入れて、URLで指定したコードのIDをコンソールに貼り付けできるようにします。これで、これからJSのチュートリアルを作成する際にリンクを貼り付けて、そこからサンプリコードを実行できるようにすることが目的です。

今後はユーザーがコードを登録できたり、シェアできるようにできたら楽しいなと思います。オープンソースなので皆さんでカスタマイズして試してください。

作業日2022年12月28日
作業にかけた時間6時間
合計作業時間30時間
作業内容モジュールインポートエラーの対応
favアイコンの作成
英語版に対応させる
DBモデルの改修
Google Analyrticsの追加
ローディング画面の設定
Vue-routerの追加

モジュールインポートエラーの対応

Vueアプリをnpm run buildのコマンドでプロダクション用にコンパイルするとデプロイしようとすると下記のエラーが出ました。

[vite]: Rollup failed to resolve import "highlight.js/scss/github-dark.css" from "src/App.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
error during build:
Error: [vite]: Rollup failed to resolve import "highlight.js/scss/github-dark.css" from "src/App.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
    at onRollupWarning (file:///home/dan/JS-Hiroba/node_modules/vite/dist/node/chunks/dep-5605cfa4.js:45832:19)
    at onwarn (file:///home/dan/JS-Hiroba/node_modules/vite/dist/node/chunks/dep-5605cfa4.js:45603:13)
    at Object.onwarn (file:///home/dan/JS-Hiroba/node_modules/rollup/dist/es/shared/rollup.js:23263:13)
    at ModuleLoader.handleResolveId (file:///home/dan/JS-Hiroba/node_modules/rollup/dist/es/shared/rollup.js:22158:26)
    at file:///home/dan/JS-Hiroba/node_modules/rollup/dist/es/shared/rollup.js:22119:26

調べてみると、npmでインストールしたハイライトのプラグインのCSSをインポートしていることが原因の様でした。rollupでモジュール内のCSSをエクスターナルのファイルとして特例で設定しないといけないようです。

vite.config.jsで下記の様にbuildの部分を追加して再度npm run buildを実行したところ、問題が解決できました。

import { fileURLToPath, URL } from "url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  build: {
    rollupOptions: {
      // https://rollupjs.org/guide/en/#big-list-of-options
      external: [/^node:.*/],
    },
  },
});

favアイコンの作成

Adobeのイラストレーターでブックマークに使われるアイコンを作りました。SVGで作成したのでデスクトップのブックマークでも画像がぼやけないようにしました。

英語版に対応させる

日本語だけだとユーザーが限られるので英語版に対応したアプリにさせます。このようにToggle(トグル)させるボタンをつくりRefでisJapaneseがtrueかfalseかを判定させるようにしました。このデータはPiniaに保管されグローバルにアクセスできるようにしています。

DBモデルの改修

では英語版に対応したデータがないのでDjangoのモデルを追加します。英語用のフィールドと難易度をチョイスで追加しました。

class Command(models.Model):
    DIFFICULTY_CHOICES = [
    ('Easy', 'Easy'),
    ('Intermediate', 'Intermediate'),
    ('Difficult', 'Difficult'),
    ('Not Rated', 'Not Rated'),
]
    title = models.CharField(max_length=300, blank=True, null=True)
    keyword = models.CharField(max_length=100, blank=True, null=True)
    command = models.TextField(max_length=300, blank=True, null=True)
    title_en = models.CharField(max_length=300, blank=True, null=True)
    difficulty = models.CharField(
        max_length=20,
        choices=DIFFICULTY_CHOICES,
        default="Not Rated",
    )
    
    class Meta:
        verbose_name_plural='Commands'

    def __str__(self):
        return self.title

Google Analyticsの追加

Google Analyticsを追加しました。特に説明する必要はないと思いますが、headタグ内に用意されたscriptを配置しただけでOKです。 

難易度のフィルター

次にコマンドの例を初級、中級、上級に分けてソートする機能を付けました。

ボタンが順番に表示されるようになり、それに合わせてデータがフィルターされます。

Vue-Routerの追加

最後に、データベースから引っ張ってきたコードはIDでURLの最後にくっつけます。これでブログとかでコードを指定して表示させたい場合は、URLでそのまま引用できるようになります。

npm install vue-router@4

#mian.jsに追加
import router from "./router";

const app = createApp(App);
app.use(router);

で、検索されたコマンドがペーストされた後に、URLにIDを飛ばしました。

import { useRouter, useRoute } from "vue-router";

const emitSearchPaste = (data) => {
  resetCommand();
  router.push({ name: 'command', params: { id: data.id } })
  consoleValue.value = data.command;
  mainMsg.msg = data.title;
  mainMsg.msgEn = data.title_en;
  closeDialog();
};

URLを監視する

Vue-Routerを使う一番の理由である、URLから目的のコードをコピペされた状態にするということを達成するために、watchEffect()を使いました。

// Update console value with URL param
const updateConsole = (commandId) => {
  let currentCommand = store.keywordData.find((el) => el.id == commandId);
  mainMsg.msg = currentCommand.title;
  mainMsg.msgEn = currentCommand.title_en;
  return (consoleValue.value = currentCommand.command);
};

// Update IF URL CHANGES
watchEffect(() => {
  if (store.keywordData && route.params.id) {
    updateConsole(route.params.id);
  }
})

これでアプリのイニシャライゼーション(初期読込)の時とURLが変わったときにフェッチしたデータをコンソールにペーストできるようになりました。

とりあえず完成とします。あとは、Django側でデータを入れて、JSの記事に使えるか試してみます。

お疲れ様です。