Closed8

Vue2 x Composition APIでアプリを開発する過程メモ

ピン留めされたアイテム
Yo IwamotoYo Iwamoto

このスクラップについて

今までvue createrails newのおかげで生きてきた人間が,初めてスクラッチでアプリケーションを立ててみる回です.「そんなことも知らないで書いてたの?」ということが100回くらいでてきます.

開発の途中でつまづいたことや,初めて知ったことをメモしてます.(本来の使い方と違うかもしれません)

このスクラップで主に触れる部分としては

  • Vue2
  • Composition API
  • TypeScript
  • TailwindCSS
  • Babel
  • Webpack
  • MySQL
  • Express

あたりになります.
https://github.com/you-5805/todo-app-front

Yo IwamotoYo Iwamoto

ES6 + TypeScript環境

https://qiita.com/tmzkysk/items/35ef6be9b5162a6bf2fe
Babelはv7からTypeScriptのビルドに対応したが,型検査ができないのでtscを利用する必要がある.

  1. tscでTypeScript → ES6にbuild
  2. BabelでES6 → ES5にトランスパイル

する設定を/webpack.config.jsに記入.

module.export = {

  <-- snip -->

  module: {
    rules: [
      {
        test: /\.ts?$/,
        use: [
          { loader: "babel-loader" },
          { loader: "ts-loader" }
        ],
        exclude: /node_modules/
      }
    ]
  }
}

use部分に設定したloaderは下から順番に処理されるらしい.
この場合,tscでコンパイル → babelでトランスパイル,となる.

Vueの単一ファイルコンポーネントをコンパイルするならvue-loaderがいるし,scssを書くならscss-loaderとかを別途書くのかな?
順番は
vue-loader → ts-loader → babel-loader → scss-loader
とかでいいんだろうか?

Yo IwamotoYo Iwamoto

単一ファイルコンポーネントをコンパイルする

まだloaderの設定が全く分かっていないが,せっかくスクラップなので,記録という意味でも,ちょっと自分で考えて色々失敗してみる.
まずは

$ yarn add --dev vue-loader

上記のwebpack.config.jsのmodule.rules.useにvue-loaderを追記してみる.

use: [
  { loader: "vue-loader" }
],

とりあえず*.vueだけをコンパイルしたいので,これだけ.
あれ,templateとstyleってどうなるんだ.
ということで一旦templateとstyleなしで以下の形でApp.vueを作成

<script>
new Vue({
  el: '#app',
  data: () => ({
    message: 'Hello, World!'
  }),
  template: '<h1>{{ message }}</h1>'
})
</script>

そして

$ yarn run build
~~~
Module not found: Error: Can't resolve './src/index.ts' in '/Users/youiw/environment/node_demo'
~~~

おっと,webpack.config.jsでindex.tsを指定してしまっていたので,App.vueに変えてから再度

$ yarn run build
~~~
ERROR in ./src/App.vue 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
~~~

これは,vue-loaderの設定が反映されてないのか,vue-loaderじゃこれをコンパイルできないのか.
と思って調べたら,まずvueパッケージをinstallしてませんでした.論外です,すみません.

Yo IwamotoYo Iwamoto

くじけた

上の後かなり調べて,色々試したんですが,まったくできませんでした…
開発の期日(業務ではないですが)との兼ね合いで今回はVue CLIなしの単一ファイルコンポーネントのコンパイルは断念して,この部分は@vue/cli-serviceにお願いしようかと思います.

Vue CLIで初期化したプロジェクトを見てみる

  • webpack.config.jsは生成されていない

さっきまでWebpackのバンドリングの設定をしていたwebpack.config.jsは生成されていませんでした.じゃあ何をしているのかと思ってpackage.jsonのscriptsを見ると,

"scripts": {
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build"
}

なるほど,バンドリングはvue-cli-serviceに包含されているんですかね?
あまりNPM周辺は詳しくないんですが,とりあえず@vue/cli-serviceパッケージの依存パッケージを確認してみます.

$ yarn info @vue/cli-service
{
  <-- snip -->
  dependencies: {
    <-- snip -->
    webpack: '^4.0.0',
    'webpack-bundle-analyzer': '^3.8.0',
    'webpack-chain': '^6.4.0',
    'webpack-dev-server': '^3.11.0',
    'webpack-merge': '^4.2.2',
    'vue-loader-v16': 'npm:vue-loader@^16.1.0'
  }
}

Webpackを使ってるっぽいです(確認方法として正しいかは不明).
ということでnode_modules/@vue/cli-service/を見てみると,いました.

中身はこんな感じです.

// this file is for cases where we need to access the
// webpack config as a file when using CLI commands.

let service = process.VUE_CLI_SERVICE

if (!service || process.env.VUE_CLI_API_MODE) {
  const Service = require('./lib/Service')
  service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())
  service.init(process.env.VUE_CLI_MODE || process.env.NODE_ENV)
}

module.exports = service.resolveWebpackConfig()

vue-loader関連の設定はlib/config/base.jsで定義されていたので確認してみましたが,かなり複雑なコードがあったので,やはり自力でのコンパイルは断念.といっても,仕組みは理解したいので,アプリが完成して時間ができたらまた見にきます.

Yo IwamotoYo Iwamoto

MySQLでDB作成,Expressで接続

一応,DB作成の部分からメモしておきます.

// DB作成
mysql> CREATE DATABASE <DB_NAME>;

// アプリケーション用のユーザーを作成
mysql> CREATE USER '<USER_NAME>'@'localhost' IDENTIFIED BY '<PASSWORD>';

// 作成したユーザーに,DBへの全権限を与える
mysql> GRANT ALL PRIVILEGES ON  <DB_NAME>.* TO '<USER_NAME>'@'localhost';

それから,初めて触るのでExpressの動作確認.

const express = require('express');
const app = express();

app.get('/', function (req, res) {
  res.send('Hello, World!');
});

app.listen(3000);
console.log('server is running on port 3000')
$ node index.js
server is runnning on port 3000

// 別タブ
$ curl localhost:3000
Hello, World!

というわけで,簡単にサーバーが立てられることがわかったところでMySQLのDBと接続します.
まずはドライバをインストール.

$ yarn add mysql

そして,さっきのファイルを編集して

const express = require('express');
const app = express();

const mysql = require('mysql');
const connection = mysql.createConnection({
  host: 'localhost',
  user: '<USER_NAME>',
  password: '<PASSWORD>'
})

connection.connect((err) => {
  if (err) throw err;
  console.log('Successfully connected to MySQL database!');
});
$ node index.js
Successfully connected to MySQL database!

という感じです.

Yo IwamotoYo Iwamoto

TailwindCSS導入

順番が前後しますが,結局Vue CLIを使うことになったので,TailwindCSSの導入だけ済ませてしまおうと思います.
https://tailwindcss.com/docs/guides/vue-3-vite

$ yarn add tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9

postcss.config.jsを以下の内容で作成します.

const autoprefixer = require('autoprefixer');
const tailwindcss = require('tailwindcss');

module.exports = {
  plugins: [
    tailwindcss,
    autoprefixer,
  ],
};

postcss.config.jsは,Sassのようなcss pre-proccessorの一つであるPostCSSの設定ファイルです.単純なSassのコンパイルに比べると非常に高速らしい.

src/index.cssを作成し,以下を記入.

@tailwind base;
@tailwind components;
@tailwind utilities;

そして,main.tsでrequire('./index.css')して完了.
動作確認

<template>
  <div id="app">
    <h1 class="text-center text-5xl text-red-500">{{ message }}</h1>
  </div>
</template>

<script>
export default ({
  data: () => ({
    message: 'Hello, World!'
  })
})
</script>


動いてます!

Yo IwamotoYo Iwamoto

そういえば

webpack.config.jsをいじってたあたりで何かのタイミングで,.js,.tsファイルのアイコンが全てReactのアイコンになっちゃいました.なぜ?

Yo IwamotoYo Iwamoto

Composition APIを使ってみる

今までちゃんと調べていなかったので,Composition APIはVue3から追加されたものだと思っていましたが,@vue/composition-apiプラグインはVue 2でも動かせるようです.
ということで,

$ yarn add @vue/composition-api

src/main.ts

import CompositionApi from '@vue/composition-api'
Vue.use(CompositionApi)

src/App.vue

<template>
  <div id="app">
    <div class="text-center p-5">
      <h1>{{ state.count }}</h1>
      <button class="rounded border-2 p-3" @click="increment">increment</button>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from '@vue/composition-api'

export default defineComponent({
  setup () {
    const state = reactive<{ count: number }>({
      count: 0
    })

    const increment = () => {
      state.count += 1
    }

    return {
      state,
      increment
    }
  }
})
</script>

おお〜動きました.初めて書いたので楽しい.

GIFじゃないので分かりにくいですが.

このスクラップは2021/04/02にクローズされました