- 投稿日:2021-06-21T23:32:07+09:00
ルパンのサブタイトルをVueを使って再現してみた
はじめに Vue.jsの勉強がてら何か動きのあるページを作ってみようと思い、今回のものを作りました。 初めてVueを触った+基礎しか理解できていないということでコードが雑になっています。 あしからず。 概要 ルパンのサブタイトル(ググれば出てくる)を再現できるWebアプリを作ってみた。 理由は最初にある通り。細かく言えばVue独特の双方向のデータバインドを試したかった。 環境 開発環境 MacOS Sierra(10.12) vue(2.6.12) Vim 実行環境(サーバサイド) AWS EC2(Amazon Linux) Route53 S3 nginx AWSの構成図はこんなかんじ 実行環境(クライアントサイド) Chrome(91.0.4472.106) OSはWindows10/MacOS Sierra で実行できたことを確認済み 作成物 githubはこちら↓ https://github.com/KenFujita/Rupan_no_turn ソースコードは上記を参照されたし。 工夫点 双方向のデータバインドが活かされるように、リアルタイムで再現できるようにした。 あとは最後の「タッタラターンダダッダダッダダッダダッダッダッダン!」という音を鳴らしているときは文字入力を受け付けないようにしている <input type="text" v-bind:disabled="isDis" @keydown.enter="isEnter" @compositionstart="composing=true" @compositionend="composing=false" v-on:input="write" v-model="word" > こんな感じに入力フォームを作成し if(!this.composing || (this.composing && this.word.length > 25)){ this.isDis = true; this.enterPush.currentTime = 0; this.enterPush.play(); const res = await this.donotWrite(this.enterPush); console.log(res); this.isDis = false; this.word = ''; } donotWrite:function(aud) { return new Promise((resolve) => { aud.addEventListener('ended',()=>{ resolve('done'); }); }); } これらをmethodsで関数として宣言してあげると、音楽が鳴っている間は入力させないといった機能が実装できる。 難しかったところ あまり難しくないコードなので詰まるほどのものはなかった。 強いて言うなら、全角文字を扱う場合の文字変換の確定をするEnterとサブタイの音楽を鳴らすトリガーになるEnterの判定をしないといけなかった。 (そうしないと文字確定時のEnterキー押下イベントで音楽がなってしまうため) あとVueのライフサイクル(今回だとcreatedとmountedの違い)がよくわからなかった。 その他音楽ファイルを読み込むタイミング、文字入力を受け付けない処理の実装、divタグの使い方、CSS、AWSなど 今後 CI/CDで継続的うんたらをやってみたい。 今の所AWS構成図のPrivate EC2が無職なのでそこで実践してみたいと思う 参考 https://wordpress.ideacompo.com/?p=13989 https://aqua-engineer.com/post-884/ https://gotohayato.com/content/496/ おまけ 既に何名もの方々が再現アプリを作っていた模様。 https://qiita.com/Sirloin/items/3db49c97763042e95eb5 何番煎じになっているのかはさておき、先駆者様の作成したものも触れてみよう
- 投稿日:2021-06-21T23:17:09+09:00
Vue.js クリックしたボタンの色変化させる。
環境 jsfiddle vue@2.6.14 クリックしたボタンの色を変化させる! 完成したコード ボタンをクリックするとボタンの色が変化します。 クリック前 クリック後 詳細 css .buttoncolor { background-color: green; } 変更後のボタンの色を指定しています。 html <div id='app'> <button v-bind:class="{ buttoncolor: buttonState }" v-on:click="changeState"> 色を変えるためのボタン </button> {{buttonState}} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> ①v-bind:classではv-bind:class="{class名: バインド先(真偽値を持つ)}"とすることで、真偽値を持つbuttonState変数とbuttoncolorクラスを対応させている。 つまりbuttonStateがtrueの時、buttoncolorクラスが適用されるが、falseの際には適用されなくなることでボタンの色が変化する。 ②v-on:clickは、クリックした際に作成したchangeStateメソッドが実行され、buttonStateの真偽値が変更される。 真偽値が変更されることで①が実行される。(changeStateメソッドの詳細は、sctiptの詳細で説明します。) script var app = new Vue ({ el:'#app', data:{ buttonState: false }, methods:{ changeState: function(){ this.buttonState = !this.buttonState } } }) ①data内に、真偽値を持つbuttonState変数を指定している。 ②methodsでは、呼び出された際に、真偽値を変更するchangeStateメソッドを作成している。 動作 ①ボタンがクリックされる ②v-on:clickで指定された、changeStateメソッドが実行される。 ③②でbuttonStateの真偽が変更される。 ④③で真偽値が変更されたこと、v-bindでバインドされたクラスがtrueの時にだけ適用される。 まとめ 簡単なコードですが、いざ実装するとなると慣れるまでは時間がかかってしまいました。 私と同じ初心者さんの参考になればと嬉しいです!
- 投稿日:2021-06-21T18:36:37+09:00
DRF & Vue.js & Dockerでの環境構築
今回やる事 DRF、Vue.js、dockerでの環境構築をする上で、各ファイルの意味だったりコマンドだったりをよく忘れるので、とりあえずDRFとVueが連携出来るまでを、ほぼ自分用に残しておきます。 例として、ユーザー一覧を取得するだけの処理を実装します。 環境 docker $ docker version Version: 20.10.6 docker_compose $ docker-compose -v docker-compose version 1.29.1, build c34c88b2 python $ python --version Python 3.8.10 構築編 DBは開発段階ではsqliteを使い、DRF用にコンテナ1つ、Vue用にコンテナ1つを用意します。 サーバーはそれぞれの開発サーバーを使用するとします。 まずは、以下の様なディレクトリ構成でファイルを作成します。 アプリ名は「k2」です。 k2/ ├ api │ ├ Dockerfile │ └ requirements.txt ├ web │ └ Dockerfile └docker-compose.yml ※一部省略。 docker-composeを定義 まずは、docker-composeでDRF用のコンテナとVue用のコンテナを定義します。 docker-compose.yml version: '3' services: api: # Dockerfileの場所 build: ./api # どのimageを使うか。Dockerfileでimageは作成する。 image: k2-drf-image # コンテナ名を指定出来る。 container_name: k2-api # 公開用のポート。ホスト側:コンテナ側を指定 # コンテナ側のみ指定も可能だが、その時ホスト側はランダムになる。 ports: - '8000:8000' # マウントするパス volumes: - ./api/:/usr/src/k2/api/ # dockerコンテナを落とさず起動し続けるようにする tty: true web: build: ./web image: k2-vue-image container_name: k2-web ports: - '8080:8080' volumes: - ./web/:/usr/src/k2/web/ tty: true 次に、apiとwebのDockerfileを定義します。 apiのDockerfile # イメージを選択 FROM python:3.9.5-alpine # バイナリレイヤ下での標準出力とエラー出力を抑制 ENV PYTHONUNBUFFERED 1 # 開始ディレクトリ位置 WORKDIR /usr/src/k2/api # 必要なものをインストール # 今回はalpineなのでapkを使用する。 RUN apk update RUN apk add --no-cache --vertual=wow-ini-module \ net-tools \ sudo \ bzip2 \ curl \ gcc \ git \ python3-dev \ vim \ bash # ADDかCOPYを使うなら.dockerignoreを書いておく。 ADD . . RUN pip install --upgrade pip RUN pip install -r requirements.txt とりあえず最小限をrequirements.txtに記述する。 requirements.txt Django==3.2.4 django-cors-headers==3.4.0 django-environ==0.4.5 django-filter==2.3.0 djangorestframework==3.12.4 djangorestframework-jwt==1.11.0 gunicorn==20.0.4 requests==2.24.0 webのDockerfile FROM node:10.17.0 WORKDIR /usr/src/k2/web/ RUN apt-get update RUN apt-get install -y --no-install-recommends \ net-tools \ sudo \ bzip2 \ curl \ gcc \ git \ vim RUN apt-get clean RUN npm install -g yarn \ && yarn global add @vue/cli \ イメージ構築、コンテナ作成~立ち上げ $ docker-compose up -d --build コンテナに接続し、環境を整える ターミナルを2つ立ち上げてそれぞれで接続し、それぞれの最低限の環境を整える。 DRF側 $ docker-compose exec api bash $ django-admin startproject api $ django-admin startapp k2 $ python manage.py makemigrations $ python manage.py migrate $ python manage.py createsuperuser 上記で作ったファイル群を以下のフォルダ構成に直します。 k2/ └ api ├ api/ ├ k2/ ├ db.sqlite3 ├ Dockerfile ├ manage.py └ requirements.txt とりあえずこの段階で、python manage.py runserver 0.0.0.0:8000で開発サーバーは立ち上がる。 Vue側 $ docker-compose exec web bash vueプロジェクトの作成 ①現在のディレクトリにフォルダ群を展開する。 $ vue create . ? Generate project in current directory? (Y/n) y ②マニュアルで導入するものを選択する。 ? Please pick a preset: Default ([Vue 2] babel, eslint) Default (Vue 3) ([Vue 3] babel, eslint) ❯ Manually select features ③導入するものにスペースで☑を入れる。 ? Check the features needed for your project: ◉ Choose Vue version ◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◉ Router ◉ Vuex ◉ CSS Pre-processors ◉ Linter / Formatter ◉ Unit Testing ❯◉ E2E Testing ・Babel JSのコードを新しい書き方から古い書き方へと変換するツール。 ブラウザによって古いバージョンしか対応していないため、このようなツールが必要になる。 ・PWA ネイティブアプリのような使い勝手を実現したWEBアプリの事。 インストール不要で、ホーム画面やアイコン追加やプッシュ通知などが可能。 そのほかに読み込み速度や表示の高速化、オフラインで閲覧化などのメリットもある。 ・CSS Pre-processor Sass, Less, Stylusなどのプリプロセッサ独自の構文でCSSを作成するプログラム。 純粋なCSSにはない、ミックスイン、セレクターの入れ子、セレクター継承などが可能。 ・Linter ESLint等のコードフォーマッターなどを使用するか選択する。 ・Unit Testing(単体テスト) Mocha+Chainや、Jestなどを使用するか選択する。 ・E2E Testing(End to Endテスト) システム全体を通してテストを行う事。 それらのライブラリなどを使うか選択する。 ④vueのバージョンを選択する。 3.x系だとvuetify Alphaしか対応してないみたいなので、今回は普通の2.xを使います。 ? Choose a version of Vue.js that you want to start the project with ❯ 2.x 3.x ⑤routerのhistoryモードを使うか選択。 ? Use history mode for router? (Requires proper server setup for index fallback in producti on) (Y/n) y ・historyモード ページのリロード無しにURL遷移を実現するhistory.pushState APIを利用したルーターのモード。シングルページのクライアントアプリなので、適切なサーバーの設定をしないと、404エラーが起きる。そのため、どのアセットにもURLがマッチしなかった場合に、index.htmlを返す設定が必要。 ・hashモード(vue-routerのデフォルト) 完全なURLをhashを使ってシミュレートし、URLが変更された時にページのリロードが起きない。 ⑥ どのCSS pre-processorを使うか選択。 ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default) : (Use arrow keys) ❯ Sass/SCSS (with dart-sass) Sass/SCSS (with node-sass) Less Stylus ⑦ linterを選択する。 ? Pick a linter / formatter config: (Use arrow keys) ❯ ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config ESLint + Prettier ⑧ lintの追加の機能を設定 ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◉ Lint on save ◯ Lint and fix on commit ⑨ unitテストに何を使用するか。 ? Pick a unit testing solution: (Use arrow keys) ❯ Mocha + Chai Jest ・Mochaは、BDD/TDDをするための枠組みを提供、chaiはテストを実施するための便利なメソッドを提供してくれる。TDD(テスト駆動開発)/BDD(振る舞い駆動開発) ・JestはFacebookが開発したテストランナ。 ⑩ E2Eテストに何を使用するか。 ? Pick an E2E testing solution: ❯ Cypress (Chrome only) Nightwatch (WebDriver-based) WebdriverIO (WebDriver/DevTools based) ・Cypress E2Eテストフレームワーク ・Nightwatch Seleniumを基盤として作られており、Node.jsでブラウザを操作してE2Eテストを行うツール ・WebdriverIO ウェブブラウザによるテストを自動的に実行するライブラリ ⑪ Babel, ESLintなどをどのファイルで管理するか。 ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys) ❯ In dedicated config files In package.json ⑫ プリセットをセーブするか ? Save this as a preset for future projects? (y/N) n ⑬ どちらのパッケージマネージャーを使うか。 ? Pick the package manager to use when installing dependencies: (Use arrow keys) ❯ Use Yarn Use NPM これでvueのプロジェクトが出来上がり、yarn serveで開発サーバーが立ち上がる。 あと、必要に応じて下記のライブラリ等も導入しておく。 下の連携テストでは、 ・vuetify ・sass ・axios ・boxicons ・vue-loading-template を使っている。 # update yarn install # vuetify yarn add vuetify (vue add vuetify) # sass yarn add --dev sass # vuesax npm install vuesax # material-icons(アイコン) npm install material-icons --save # axios npm install --save axios vue-axios # boxicons(アイコン) npm i boxicons # lodash npm i --save lodash # vue-persistedstate npm i vuex-persistedstate # vue-session npm i vue-session # vue-loading-template npm install vue-loading-template --save 連携テスト とりあえず、ユーザー一覧をDRFから受け取ってそれを表示させるところまでをやります。 DRF側 やる事 初期設定 ルーティング View記述 DRF: 初期設定 k2/api/api/settings.py # 追加 import logging logging.basicConfig( level=logging.DEBUG, format='''%(levelname)s %(asctime)s %(pathname)s:%(funcName)s 行数:%(lineno)s:%(lineno)s %(message)s''' # 編集 ALLOWED_HOSTS = ['*'] # 追加 INSTALLED_APPS = [ ... 'rest_framework', # Json Web Token関連のライブラリ 'rest_framework_jwt', 'django_filters', 'k2.apps.K2Config', ] # 追加 REST_FRAMEWORK = { # デフォルトのパーミッションクラス 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', ], # デフォルトの認証クラス 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], # デフォルトのフィルターバックエンド 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', ], # デフォルトのスロットルクラス 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle', ], # デフォルトのスロットルの制限 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '5/sec', } } # 追加 if DEBUG: # CORS関連の設定追加。 INSTALLED_APPS += ['corsheaders'] MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware'] + MIDDLEWARE CORS_ORIGIN_ALLOW_ALL = True DRF: ルーティング k2/api/k2/urls.py from django.urls import path, include from . import views, viewsets from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('users', viewsets.UserViewSet) app_name = 'k2' urlpatterns = [ path('', include(router.urls)), ] k2/api/api/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('k2.urls')), ] DRF: View記述 今回はDjangoデフォルトのUserモデルを使用します。 k2/api/k2/viewsets.py from django.contrib.auth.models import User from serializers import UserSerializer from rest_framework import ( viewsets ) import logging logger = logging.getLogger(__name__) class BaseModelViewSet(viewsets.ModelViewSet): pass class UserViewSet(BaseModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) k2/api/k2/serializers.py from rest_framework import serializers from django.contrib.auth.models import User import logging import logging logger = logging.getLogger(__name__) class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = '__all__' これで、localhost:8000/api/users/にアクセスすると、python manage.py createsuperuserコマンドなどで作成したユーザー一覧が返る。 Vue側 やる事 初期設定 ルーティング 画面実装 ajax投げて表示 Vue: 初期設定 main.jsでライブラリ等を読み込む。 k2/web/src/main.js import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import http from '@/plugins/http' import vuetify from './plugins/vuetify' import eventHub from '@/plugins/eventHub' import 'boxicons/css/boxicons.min.css' Vue.config.productionTip = false Vue.use(http) Vue.use(eventHub) new Vue({ router, store, vuetify, render: h => h(App) }).$mount('#app') k2/web/src/App.vue <template> <v-app> <router-view/> </v-app> </template> <script> export default { name: 'App', } </script> axiosのデフォルト設定をしておく。 k2/web/src/plugins/http.js import Vue from 'vue' import axios from 'axios' import router from '@/router' export default { install: function (Vue, options) { const http = axios.create({ baseURL: 'http://localhost:8000/', xsrfCookieName: 'csrftoken', xsrfHeaderName: 'X-CSRFTOKEN', timeout: 10000, }) Vue.prototype.$axios = http } } k2/web/src/plugins/vuetify.js import Vue from 'vue' import Vuetify from 'vuetify/lib' Vue.use(Vuetify) export default new Vuetify({ }) 孫コンポーネントとか遠い関係のコンポーネントのイベントを検知するためのもの。 k2/web/src/plugins/eventHub.js import Vue from 'vue' const eventHub = { install: function (Vue, options) { Vue.prototype.$eventHub = new Vue() } } export default eventHub eslintがうるさいので最低限だまらせておく。 k2/web/.eslintrc.js ... rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-tabs': 'off', 'indent': 'off', 'comma-dangle': 'off', 'eol-last': 'off', 'no-unused-vars': 'off', 'no-mixed-spaces-and-tabs': 'off', 'no-unneeded-ternary': 'off', 'vue/no-unused-components': 'off', 'no-multi-spaces': 'off' }, ... Vue: ルーティング 全体のルート k2/web/src/router/index.js import Vue from 'vue' import VueRouter from 'vue-router' import pages from './pages' Vue.use(VueRouter) const routes = [...pages] const router = new VueRouter({ mode: 'history', routes }) export default router ページ関連のルート k2/web/src/router/pages.js import { Home } from '@/views/index' const routes = [ { path: '/', name: 'Home', component: Home }, ] export default routes Vue: 画面実装 index.jsにコンポーネントを纏める。 k2/web/src/views/index.js export { default as Home } from './Home' Home画面のコンポーネント。今回はコンポーネント分けないで直書きしちゃいます。 k2/web/src/views/Home.vue <template> <div id="home_wrap"> <v-card flat tile > <div v-if="loading"> <Loading/> </div> <div v-else> <v-card-title> users </v-card-title> <v-list three-line> <template v-for="(user, i) in users"> <v-list-item :key="i" > <v-list-item-avatar> <i class='bx bxs-user'></i> </v-list-item-avatar> <v-list-item-content> <v-list-item-title> {{ user.username }} </v-list-item-title> <v-list-item-subtitle> id: {{ user.id }} </v-list-item-subtitle> </v-list-item-content> </v-list-item> </template> </v-list> </div> </v-card> </div> </template> <script> import Loading from '../components/Loading' export default { name: 'Home', components: { Loading, }, data: () => ({ loading: true, users: [], }), mounted () { this.getUsers() }, methods: { getUsers () { this.loading = true this.$axios({ url: '/api/users/', method: 'GET', }) .then(res => { console.log(res) this.users = res.data this.loading = false }) .catch(e => { console.log(e) this.loading = false }) } } } </script> <style lang="scss" scoped> #home_wrap { width: 1200px; margin: 0 auto; } </style> ロード画面のコンポーネント k2/web/src/components/Loading.vue <template> <div class="loading_wrap"> <vue-loading type="cylon" color="#aaa" :size="{ width: '50px', height: '50px' }" ></vue-loading> </div> </template> <script> import { VueLoading } from 'vue-loading-template' export default { name: 'Loading', components: { VueLoading, }, } </script> <style lang="scss" scoped> .loading_wrap { width: 50px; height: 50px; margin: 50px auto; } </style> まとめ こんな感じの画面になりました。
- 投稿日:2021-06-21T07:14:52+09:00
文系卒4年目のエンジニアがFirebase×Vueでアニメレビューサイトをリリースしたった
作ったもの PC・スマホに対応しています。 通常時 ログイン時 みんなの推しアニメを確認できる キャスト情報・クレジットを確認できる みんなのレビュー平均とウォッチリスト追加数が確認できる みんなのコメントを一覧で確認できる アプリの概要 年・放送時期を指定して、その時期に放送されるアニメの一覧を取得することができる 気になるアニメを見つけたら、そのままウォッチリストに保存することができる レビューをすることができ、みんながウォッチリストに追加しているもの、みんながコメントしているものを確認することができる。 使用技術 フロント Vue.js Vuetify バックエンド Firebase その他 Google Domains Annict いろいろ技術面で語らせてください… どうやってアニメ情報を取得しているのか? Annictという別のサービスを使っています。 アニメ作品の情報から、キャスト・スタッフ情報などいろいろとAnnict上に登録されているので、 それをREST APIにて取得させていただいています。 また↑の画像の通り、2年ほど前からGraphQLにも対応しておられるようです。 2年前というと、まだそこまでGraphQLの知名度が日本ではなかった頃かと思うのですが、 そのころからすでに対応されているようで、開発者の方は常に最新技術にアンテナを張り、 それを使ってみようという精神を持った方なのだなと、尊敬の念を抱きました。 開発者様はTwitterをされているようで、DMで少しお話を伺ってみた際に 何より驚きだったのが、これだけのサービスを基本的には一人で運用されていることでした。 開発者様Twitter さすがにアニメ情報などは何人かの方が手動で登録していっているようです。 そのあたり、Webクローラーとか作ってアニメ情報の登録を自動化できないか、自分も考えてみたいですね。 レビューの平均値や総ウォッチリスト追加数をどうやって取得しているのか? その答えは公式がドキュメントを公開してくれています。 RDBMSの考え方だと、集約関数が存在するのでわざわざ集約結果をテーブルに持たせたりすることは、 特別な理由がない限りありません。 特別な理由=集計対象が膨大なため、毎回計算していると時間がかかるので、一日一度バッチを回して集計し、 その日一日は集計済みの値をユーザーに見せる など。 しかしFirestoreは集約関数を提供してくれていないため、毎度集約値をを計算する必要がありますが、 それをやってしまうと、読み取り回数が爆発的に増えてしまい、無料プランの上限50000/1日の制約を突破する恐れがあります。 そこでどうするかというと、あえてテーブルを正規化せず、集約値を持ち続けるコレクションと、 集約元となる値を持つコレクションに分離します。 このあたりの考え方は、公式ドキュメントを見ると理解できるかと思うので、これ以上の説明は割愛します。 そのほか、こちらの記事にてFirestoreの設計思想について解説してくださっているが参考になりました。 Google Domains(に限らず別ドメイン) × Firebase Authenticationについて Firebaseに別ドメインを追加するところで、少々ハマってしまいました。 こちらの記事を参考に設定していき、 ドメインが適用されるところまではうまくいっていたのですが、 なぜかドメインの追加後、ログイン処理がエラーになる事象が起こりはじめました。 ググっていくも上記事象について書かれた記事を見つけることができず、調査すること数十分… Firebase Authenticationの下の方にこんな画面を見つけます。 はい。 どうやら承認済みドメインに取得した別ドメインを追加してあげないとダメだったようです。 ステージング環境との併用 当然のことながら、 ・修正→いきなり本番デプロイ→公開 ではなく、 ・修正→ステージング環境に上げて検証→検証OK→本番デプロイ→公開 という流れを踏みたいのですが、そのためにはプロジェクトの設定をステージングと本番の二つ用意する必要があるわけで… その際にいろいろと調べたことを、この記事に書いてますので、 良ければ参考にしてください。 最後に このアプリが全てのアニメ好きの糧になりますように。
- 投稿日:2021-06-21T07:14:52+09:00
【個人開発】Firebase×Vueでアニメレビューアプリをリリースした
作ったもの PC・スマホに対応しています。 通常時 ログイン時 みんなの推しアニメを確認できる キャスト情報・クレジットを確認できる みんなのレビュー平均とウォッチリスト追加数が確認できる みんなのコメントを一覧で確認できる アプリの概要 年・放送時期を指定して、その時期に放送されるアニメの一覧を取得することができる 気になるアニメを見つけたら、そのままウォッチリストに保存することができる レビューをすることができ、みんながウォッチリストに追加しているもの、みんながコメントしているものを確認することができる。 使用技術 フロント Vue.js Vuetify バックエンド Firebase その他 Google Domains Annict いろいろ技術面で語らせてください… どうやってアニメ情報を取得しているのか? Annictという別のサービスを使っています。 アニメ作品の情報から、キャスト・スタッフ情報などいろいろとAnnict上に登録されているので、 それをREST APIにて取得させていただいています。 また↑の画像の通り、2年ほど前からGraphQLにも対応しておられるようです。 2年前というと、まだそこまでGraphQLの知名度が日本ではなかった頃かと思うのですが、 そのころからすでに対応されているようで、開発者の方は常に最新技術にアンテナを張り、 それを使ってみようという精神を持った方なのだなと、尊敬の念を抱きました。 開発者様はTwitterをされているようで、DMで少しお話を伺ってみた際に 何より驚きだったのが、これだけのサービスを基本的には一人で運用されていることでした。 開発者様Twitter さすがにアニメ情報などは何人かの方が手動で登録していっているようです。 そのあたり、Webクローラーとか作ってアニメ情報の登録を自動化できないか、自分も考えてみたいですね。 レビューの平均値や総ウォッチリスト追加数をどうやって取得しているのか? その答えは公式がドキュメントを公開してくれています。 RDBMSの考え方だと、集約関数が存在するのでわざわざ集約結果をテーブルに持たせたりすることは、 特別な理由がない限りありません。 特別な理由=集計対象が膨大なため、毎回計算していると時間がかかるので、一日一度バッチを回して集計し、 その日一日は集計済みの値をユーザーに見せる など。 しかしFirestoreは集約関数を提供してくれていないため、毎度集約値をを計算する必要がありますが、 それをやってしまうと、読み取り回数が爆発的に増えてしまい、無料プランの上限50000/1日の制約を突破する恐れがあります。 そこでどうするかというと、あえてテーブルを正規化せず、集約値を持ち続けるコレクションと、 集約元となる値を持つコレクションに分離します。 このあたりの考え方は、公式ドキュメントを見ると理解できるかと思うので、これ以上の説明は割愛します。 そのほか、こちらの記事にてFirestoreの設計思想について解説してくださっているが参考になりました。 Google Domains(に限らず別ドメイン) × Firebase Authenticationについて Firebaseに別ドメインを追加するところで、少々ハマってしまいました。 こちらの記事を参考に設定していき、 ドメインが適用されるところまではうまくいっていたのですが、 なぜかドメインの追加後、ログイン処理がエラーになる事象が起こりはじめました。 ググっていくも上記事象について書かれた記事を見つけることができず、調査すること数十分… Firebase Authenticationの下の方にこんな画面を見つけます。 はい。 どうやら承認済みドメインに取得した別ドメインを追加してあげないとダメだったようです。 ステージング環境との併用 当然のことながら、 ・修正→いきなり本番デプロイ→公開 ではなく、 ・修正→ステージング環境に上げて検証→検証OK→本番デプロイ→公開 という流れを踏みたいのですが、そのためにはプロジェクトの設定をステージングと本番の二つ用意する必要があるわけで… その際にいろいろと調べたことを、この記事に書いてますので、 良ければ参考にしてください。 最後に このアプリが全てのアニメ好きの糧になりますように。
- 投稿日:2021-06-21T07:14:52+09:00
Firebase×Vueでアニメレビューサイトをリリースしたった
作ったもの PC・スマホに対応しています。 通常時 ログイン時 みんなの推しアニメを確認できる キャスト情報・クレジットを確認できる みんなのレビュー平均とウォッチリスト追加数が確認できる みんなのコメントを一覧で確認できる アプリの概要 年・放送時期を指定して、その時期に放送されるアニメの一覧を取得することができる 気になるアニメを見つけたら、そのままウォッチリストに保存することができる レビューをすることができ、みんながウォッチリストに追加しているもの、みんながコメントしているものを確認することができる。 使用技術 フロント Vue.js Vuetify バックエンド Firebase その他 Google Domains Annict いろいろ技術面で語らせてください… どうやってアニメ情報を取得しているのか? Annictという別のサービスを使っています。 アニメ作品の情報から、キャスト・スタッフ情報などいろいろとAnnict上に登録されているので、 それをREST APIにて取得させていただいています。 また↑の画像の通り、2年ほど前からGraphQLにも対応しておられるようです。 2年前というと、まだそこまでGraphQLの知名度が日本ではなかった頃かと思うのですが、 そのころからすでに対応されているようで、開発者の方は常に最新技術にアンテナを張り、 それを使ってみようという精神を持った方なのだなと、尊敬の念を抱きました。 開発者様はTwitterをされているようで、DMで少しお話を伺ってみた際に 何より驚きだったのが、これだけのサービスを基本的には一人で運用されていることでした。 開発者様Twitter さすがにアニメ情報などは何人かの方が手動で登録していっているようです。 そのあたり、Webクローラーとか作ってアニメ情報の登録を自動化できないか、自分も考えてみたいですね。 レビューの平均値や総ウォッチリスト追加数をどうやって取得しているのか? その答えは公式がドキュメントを公開してくれています。 RDBMSの考え方だと、集約関数が存在するのでわざわざ集約結果をテーブルに持たせたりすることは、 特別な理由がない限りありません。 特別な理由=集計対象が膨大なため、毎回計算していると時間がかかるので、一日一度バッチを回して集計し、 その日一日は集計済みの値をユーザーに見せる など。 しかしFirestoreは集約関数を提供してくれていないため、毎度集約値をを計算する必要がありますが、 それをやってしまうと、読み取り回数が爆発的に増えてしまい、無料プランの上限50000/1日の制約を突破する恐れがあります。 そこでどうするかというと、あえてテーブルを正規化せず、集約値を持ち続けるコレクションと、 集約元となる値を持つコレクションに分離します。 このあたりの考え方は、公式ドキュメントを見ると理解できるかと思うので、これ以上の説明は割愛します。 そのほか、こちらの記事にてFirestoreの設計思想について解説してくださっているが参考になりました。 Google Domains(に限らず別ドメイン) × Firebase Authenticationについて Firebaseに別ドメインを追加するところで、少々ハマってしまいました。 こちらの記事を参考に設定していき、 ドメインが適用されるところまではうまくいっていたのですが、 なぜかドメインの追加後、ログイン処理がエラーになる事象が起こりはじめました。 ググっていくも上記事象について書かれた記事を見つけることができず、調査すること数十分… Firebase Authenticationの下の方にこんな画面を見つけます。 はい。 どうやら承認済みドメインに取得した別ドメインを追加してあげないとダメだったようです。 ステージング環境との併用 当然のことながら、 ・修正→いきなり本番デプロイ→公開 ではなく、 ・修正→ステージング環境に上げて検証→検証OK→本番デプロイ→公開 という流れを踏みたいのですが、そのためにはプロジェクトの設定をステージングと本番の二つ用意する必要があるわけで… その際にいろいろと調べたことを、この記事に書いてますので、 良ければ参考にしてください。 最後に このアプリが全てのアニメ好きの糧になりますように。