げっとシステムログ

WEB開発メモ

Vue 3 を TypeScript で

CONTENTS
  1. なにがやりたいのか
  2. 必要なパッケージのインストール
  3. webpack のセットアップ
  4. vue を使うための TypeScript の設定をする
  5. vue のコードを書く
  6. まとめ
  7. 参考資料
ENVIRONMENTS
  • node : 14.4.0
  • vue : 3.0.0-beta.15

なにがやりたいのか

HTML の描画とイベントハンドリングがしたい。 とりあえずやりたいのはこれだけなので、Vue にした。 Vue 3 は現時点ではまだ beta だけど、Vue 2 で TypeScript を書くためにはいろいろめんどくさかった。

TypeScript を選択したのは、テンプレートで使用可能な変数がコードでドキュメント化されるから。

bundler は webpack を使用する。 vitejs も気になっているが、いろいろやってくれる分、サポートされていないことをやる時はトラブルシューティングがつらくなると判断した。 snowpack も気になっているが、デプロイするときには結局 webpack を使う感じのことが書いてあるので、今は webpack で行くことにした。

TOP

必要なパッケージのインストール

以下、ほぼこの記事の通りに進めていく。

以下のパッケージをインストールする。 バージョンは 2020/06/15 現在のもの。

"devDependencies": {
  "@vue/compiler-sfc": "^3.0.0-beta.15",
  "ts-loader": "^7.0.5",
  "typescript": "^3.9.5",
  "vue": "^3.0.0-beta.15",
  "vue-loader": "^16.0.0-beta.3",
  "webpack": "^4.43.0",
  "webpack-cli": "^3.3.11",
  "webpack-dev-server": "^3.11.0"
}

TOP

webpack のセットアップ

以下の通り webpack.config.js を作成する。

const fs = require("fs");
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  entry: () => {
    const entry = {};

    entry["index"] = path.join(__dirname, `src/index.ts`);

    return entry;
  },
  output: {
    path: path.join(__dirname, "dist"),
    filename: "[name].js",
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      },
      {
        test: /\.ts$/,
        loader: "ts-loader",
        options: {
          appendTsSuffixTo: [/\.vue$/],
        },
      },
    ],
  },
  resolve: {
    alias: {
      'vue': '@vue/runtime-dom',
    }
  },
  plugins: [
    new VueLoaderPlugin(),
  ],
  devServer: devServer(),
};

function devServer() {
  if (!process.env.WEBPACK_DEV_SERVER) {
    return {};
  }

  return {
    contentBase: path.join(__dirname),
    publicPath: "/dist/",

    host: "0.0.0.0",
    port: process.env.APP_PORT,
    sockPort: process.env.SOCK_PORT,
    disableHostCheck: true,

    https: true,
    cert: fs.readFileSync(process.env.TLS_CERT),
    key: fs.readFileSync(process.env.TLS_KEY),

    hot: true,
  };
}

ts-loaderappendTsSuffixTo についてはよくわかっていない。 Vue を使うときの設定、くらいの理解。

resolve.alias についてもよくわかっていない。 これがないと Vue の createApptemplate を指定するとエラーになる。

devServer については適当に指定する。 ここでは https のオプションを指定しているので、本番用 build でエラーにならないよう関数で定義している。

TOP

vue を使うための TypeScript の設定をする

以下の内容で tsconfig.json を作成する。 この記事を参考にした。

{
  "compilerOptions": {
    "target": "es2019",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}

また、tsconfig.json と同じディレクトリに shims-vue.d.ts を以下の内容で作成する。

declare module "*.vue" {
  import { defineComponent } from "vue";
  const Component: ReturnType<typeof defineComponent>;
  export default Component;
}

type 定義用ディレクトリを用意している場合はそこに作成すればいい。

TOP

vue のコードを書く

先に作成した webpack.config.jssrc/index.ts をエントリーにした。 なので、以下の内容で src/index.ts を作成する。 ここがエントリーポイントになる。

import { createApp } from 'vue';
import Main from "./main.vue";

createApp({
  components: {
    Main,
  },
  template: "<Main/>",
}).mount('#main');

この例ではコンポーネントが1つしかないので、以下のようにも書ける。

import { createApp } from 'vue';
import Main from "./main.vue";

createApp(Main).mount('#main');

次に、src/index.ts から呼ばれる src/main.vue を作成する。

<template>

<article>
  <h1>{{ state.data.message }}</h1>
  <ul v-if="state.data.items">
    <li v-for="item in state.data.items"><a href="#" @click.prevent="clicked(item)">{{ item.label }}</a></li>
  </ul>
</article>

</template>

<script lang="ts">
import { reactive } from "vue";

type State = {
  data: Data,
};

type Data = {
  message: string,
  items: Array<Item>,
};

type Item = {
  label: string,
};

export default {
  setup() {
    const state = reactive<State>({
      data: {
        message: "Hello, Vue!",
        items: [
          { label: "item1" },
          { label: "item2" },
          { label: "item3" },
        ],
      },
    });

    const clicked = (item: Item) => {
      console.log(item);
    };

    return {
      state,
      clicked,
    };
  }
};
</script>

<style>
</style>

setup() で返している statereactive() を使用して構築している。 これについては Composition API RFC に詳しく書いてある。

とりあえず、setup() で返すオブジェクトはすべて reactive() を通す、という理解でいい。 プリミティブな値であれば ref() を使用する。

これを動かすと、Hello, Vue! と、item のリストが表示されるはず。 また、item のリストをクリックするとコンソールに item の内容が出る。

TOP

まとめ

Vue 3 を TypeScript で書く方法をまとめた。

最初は Vue 2 で TypeScript を書くやり方を調べていたのだけれど、Vue 3 で書いてみたらすっとかけたので beta だけどこっちで行くことにした。

TOP

参考資料

TOP