20200316のvue.jsに関する記事は14件です。

パワーワードが作れる?ランダム単語合成アプリを作ってみた

はじめに

クリックするだけ!ランダムに2つの単語を「反応」させて、パワーワードを作りましょう!

rand.gif

モードは

  • 形容詞+名詞
  • 副詞+動詞
  • 名詞+助詞+動詞

の3種類です!

phrases.gif

ソースコード

GitHub - Syuparn/LiteralReaction: ランダムに単語を「反応」させて、パワーワードを作ろう

(DockerやVue.jsをはじめて使ったので、汚いところもあると思います…ツッコミ、修正歓迎です)

Webアプリの形で作りましたが、web上では公開していません。
(単語が本当にランダムなので、暴言や不謹慎な表現も生成されるかもしれないからです…)

試したい方は上記リンクからダウンロードしてください。docker-composeを使用しているので、

$ docker-compose -f docker-compose.prod.yml up -d

でコンテナを起動すればlocalhost:8080からアクセスできます。

webアプリケーションの構成

WebサーバとAPIサーバの2段構成になっています。

network.png

WebサーバはUI(html)、APIサーバは単語情報(JSON)をやりとりします。

コンテナもWebサーバとAPIサーバの2つに分けています。
(UIと単語生成を疎結合にして修正しやすくするためです)

containers.png

docker-compose.prod.yml
version: '3'
services:
  apiserver:
    build:
      context: ./apiserver/
      dockerfile: Dockerfile
    ports:
      - "127.0.0.1:5050:5050" # host:container
    expose:
      - "5050" # port for other containers (not host)
  vue-frontend:
    build:
      dockerfile: Dockerfile
      context: ./vue-frontend
    ports:
      - "127.0.0.1:8080:80" # host:container

ただし、ユーザーから見ても別々のURLになってしまうと不格好なので、Webサーバの/api/ではじまるURLにリクエストされた場合はAPIサーバに転送するようにしています。

vue-frontend/nginx_config/default.conf
server {
    listen 80;
    server_name localhost;
    server_tokens off;

    location / {
        # デフォルトではhtmlを返す
        root /usr/share/nginx/html;
        try_files $uri $uri/ @dynamic;
    }

    location /api/ {
        # URLが"/api/"ではじまる場合のみ、APIサーバに転送
        # URLのうち"/api/"に続く部分のみを取り出す
        rewrite /api/(.*) /$1 break;
        # docker-composeの機能によって、exposeされた他コンテナのポートはアプリケーション名でアクセス可能
        proxy_pass http://apiserver:5050;
        # redirectを無効化(既にrewriteでリダイレクトを設定しているため)
        proxy_redirect off;
        proxy_set_header Host $host;
    }
}

単語情報GETの流れは以下の通りです。(例は「形容詞+名詞」の場合)

  1. クライアントは、単語生成(「反応」)ページを開いたときに、形容詞をlocalhost:8080/api/rand/adjectiveへGETリクエスト
  2. WebサーバがリクエストをAPIサーバhttp://apiserver:5050/rand/adjectiveへ転送
  3. APIサーバがリクエストに基づきランダムな形容詞1つをJSON形式で(Webサーバに)返す
  4. Webサーバはレスポンスをクライアントへ転送
  5. クライアントに形容詞データのJSONが届く
  6. 名詞についても同様

単語データ生成

「MeCab IPADIC」を使用しました。

MeCabは、文章を単語(正確には形態素、ことばの最小単位)ごとに分かち書きするソフトウェアです。

そして、MeCabが参照する形態素の辞書の1つがIPADICです。

品詞ごとにcsvファイルで書かれていて、各行に各形態素の表記、読み、品詞、活用などが格納されています。

Adj.csv
あらっぽい,19,19,6956,形容詞,自立,*,*,形容詞・アウオ段,基本形,あらっぽい,アラッポイ,アラッポイ
あらっぽし,23,23,6956,形容詞,自立,*,*,形容詞・アウオ段,文語基本形,あらっぽい,アラッポシ,アラッポシ
あらっぽから,27,27,6956,形容詞,自立,*,*,形容詞・アウオ段,未然ヌ接続,あらっぽい,アラッポカラ,アラッポカラ
...

このアプリの単語合成モードは

  • 形容詞(終止形)+名詞
  • 副詞+動詞(終止形)
  • 名詞+(助詞)+動詞(終止形)

の3種類です。
この形式で最大限単語を利用するため、(文法的用語には少し不正確ですが)以下のルールで単語を抽出しました。

apiserver/db/format-ipadic-csv.bash
# 形容詞として利用

# 形容詞終止形
cat $PATH_FROM/Adj.csv       | awk -F"," '$10~/^基本形$/ {print $1}' >  $PATH_TO/adj.txt
# (「な」を付けると形容詞になる名詞)+「な」
cat $PATH_FROM/Noun.adjv.csv | awk -F"," '{print $1 "な"}'         >> $PATH_TO/adj.txt
# (「ない」を付けると形容詞になる名詞)+「ない」
cat $PATH_FROM/Noun.nai.csv  | awk -F"," '{print $1 "ない"}'       >> $PATH_TO/adj.txt

# 名詞として利用
# 「ナンセンスさ」を出したいので人名、地名、専門用語は除外し、一般名詞だけ使用

# 名詞
cat $PATH_FROM/Noun.csv          | awk -F"," '{print $1}' >  $PATH_TO/noun.txt
# 「する」を付けると動詞になる名詞
cat $PATH_FROM/Noun.verbal.csv   | awk -F"," '{print $1}' >> $PATH_TO/noun.txt
# 「な」を付けると形容詞になる名詞
cat $PATH_FROM/Noun.adverbal.csv | awk -F"," '{print $1}' >> $PATH_TO/noun.txt
# 「ない」を付けると形容詞になる名詞は単体で使うと不自然なので未使用)

# 副詞として使用

# 副詞
cat $PATH_FROM/Adverb.csv        | awk -F"," '{print $1}'                    >  $PATH_TO/adverb.txt
# 副詞としても使える名詞(「毎日」等)
cat $PATH_FROM/Noun.adverbal.csv | awk -F"," '{print $1}'                    >> $PATH_TO/adverb.txt
## 形容詞連用形(小さい「っ」で終わるものは動詞が後続できないので削除)
cat $PATH_FROM/Adj.csv           | awk -F"," '$10~/^連用テ接続$/ {print $1}' \
                                 | awk       '!/っ$/'                        >> $PATH_TO/adverb.txt

# 動詞として利用

# 動詞
cat $PATH_FROM/Verb.csv        | awk -F"," '$10~/^基本形$/ {print $1}'        >  $PATH_TO/verb.txt
# (「する」を付けると動詞になる名詞)+「する」
cat $PATH_FROM/Noun.verbal.csv | awk -F"," '{print $1 "する"}'                >> $PATH_TO/verb.txt

上記で生成したcsvをSQLiteに流し込んだものを、単語データベースとして使用しました。

apiserver/db/csv2sqlite.bash
sqlite3 $SQL_PATH/$SQL_NAME << EOS

/* create tables */
.read ./db/init.sql

/* let col separator "," */
.separator ','

/* import word data csvs */
.mode csv
.import $CSV_PATH/adj.csv adjectives
.import $CSV_PATH/adverb.csv adverbs
.import $CSV_PATH/noun.csv nouns
.import $CSV_PATH/verb.csv verbs
EOS
apiserver/db/init.sql
drop table if exists adjectives;
create table adjectives (
    id integer primary key,
    word text
);

drop table if exists adverbs;
create table adverbs (
    id integer primary key,
    word text
);

drop table if exists nouns;
create table nouns (
    id integer primary key,
    word text
);

drop table if exists verbs;
create table verbs (
    id integer primary key,
    word text
);

(個人的)面白かった生成結果

Screenshot from 2020-03-14 23-17-22.png
私たちがコードを書けるのは、数学様のおかげでございます。

Screenshot from 2020-03-14 22-43-34.png
ウケ狙いちゃうわ!

Screenshot from 2020-03-14 22-08-33.png
こんなアプリ作っちゃうくらいだからしょうがないね

Screenshot from 2020-03-14 22-42-12.png
文章が成立している!

意外とフレーズが成立する割合が高く(体感100回に1回くらい?)、他にも以下のような「まともな」フレーズが生成されました。

居場所を失う
酷い蛇行
自我を失う
泥棒を訴える
移り気なロマンチスト
やさしいインターフェース

はまったところ

docker-compose buildできない

docker-composeapt installでインストールすると古いバージョンが入ってしまいます…

そして、古いバージョンのdocker-composeでバージョン3のdocker-compose.ymlを使うとビルドに失敗します。

apt installではなくcurlで最新バージョンをダウンロードしましょう

Install Docker Compose | Docker Documentation

(公式ドキュメントはちゃんと読まねば…反省)

go buildできない

Go1.13からはライブラリ管理の方法が変わったため、最初にgo mod initをする必要があります。

このコマンドを打つと、モジュールの依存関係ファイルgo.modやモジュールが本物か確かめるためのチェックサムgo.sumが生成されます。

Go Modules - Qiita

Go 1.13 に向けて知っておきたい Go Modules とそれを取り巻くエコシステム - blog.syfm

その代わり、go.modのおかげでライブラリを手動でgo getする必要がなくなりました!
go build時に自動でダウンロードされる)

コンテナ作成中に生成した実行ファイルが消える

volumesCOPYを混同していました。

Dockerfile中でCOPYすると、コンテナのビルド時にホストのディレクトリをコピーします。

docker-compose.yml中でvolumesを指定すると、コンテナの起動時にコンテナのディレクトリにホストのディレクトリをマウントします。

そのため、ホストのソースをCOPYしてせっかく実行ファイルをビルドしても、同じディレクトリをvolumesに指定するとホストのディレクトリ(もちろん実行ファイルは無い)に隠されてしまいます…

docker-composeのvolumesで指定したホストのディレクトリがマウントされずハマった

conic-gradientが使えない

単語生成画面の集中線はCSSのconic-gradient関数を使用する予定でした

…が、この関数はFirefoxでは非対応です。

conic-gradient() - CSS: カスケーディングスタイルシート | MDN

そこで、同名のnpmパッケージを使ってjs側で模様を生成しました。

conic-gradient - npm

しかし、このパッケージはES5で書かれているのでVue CLI内部でimportできません。

結局、

  1. index.htmlで直接グローバルに読み込む
  2. Vue側ではwindow.ConicGradientの形で呼び出す
  3. npm run buildではビルドされないので、別途モジュールをstaticにコピーする

という手順を取りました。

vue-frontend/index.html
<!DOCTYPE html>
<html>
...
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <!-- ここでモジュールを読み込む(prefixfreeはconic-gradientの依存モジュール) -->
    <script src="./static/prefixfree.min.js"></script>
    <script src="./static/conic-gradient.js"></script>
  </body>
</html>
vue-frontend/src/components/bangBackground.vue
<script>
export default {
  name: 'bangBackground',
  data: function () {
    return {
      bangSVG: new window.ConicGradient({
        repeating: true,
        stops: `#ffffff 0,
                #ffffff 2.0%,
                #AAAAAA 2.125%,
                #AAAAAA 2.375%,
                #ffffff 2.5%`
      })
    }
  }
}
</script>
vue-frontend/Dockerfile
FROM node:lts-alpine as builder

RUN mkdir -p /app
WORKDIR /app

RUN npm install -g http-server

COPY ./package*.json ./
RUN npm install \
    # conic-gradientモジュールをstaticにコピー
    && mkdir -p static/ \
    && cp node_modules/conic-gradient/conic-gradient.js static/ \
    && cp node_modules/prefixfree/prefixfree.min.js static/

COPY . .
RUN npm run build

FROM nginx:stable-alpine as product

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx_config/default.conf /etc/nginx/conf.d/default.conf

CMD ["nginx", "-g", "daemon off;"]

参考文献

Go言語

APIサーバ
golangでREST APIをやってみた① - Qiita

SQLiteとの接続
golangでSQLite3を使ってデータベースを操作する方法まとめ | Black Everyday Company

MeCab

IPADICダウンロード方法
Alpine LinuxでMeCab with NEologd - Qiita

Vue.js

SPA(シングルページアプリケーション)のつくり方
【Vue.js】爆速でSPAを作る - Qiita

axiosを使ったJSONのGET/POST
Vue-CLIのプロジェクトでaxiosを使ってAPIや外部リソースからのデータを取得する | 大阪市天王寺区SOHOホームページ制作 | デザインサプライ-DesignSupply.-

コンポーネント内部に要素を入れる(公式)
スロット — Vue.js

nginx

URLの一部分だけ取り出してポートフォワーディング(/api/hoge -> apiserver:5050/hoge)
Nginx reverse proxy + URL rewrite - Server Fault

Docker,docker-compose

docker-compose基本操作
docker-compose コマンドまとめ - Qiita

複数サーバ(複数コンテナ)を協調させる方法
マイクロサービスほどじゃないけどウェブサービスを分割開発したい人向けDocker設定を集めるスレ - Qiita

vue-cliビルド方法(公式)
Vue.js アプリケーションを Docker 化する — Vue.js

Golangマルチステージビルド方法

(マルチステージビルドのメリット)
Dockerのマルチステージビルドを使う - Qiita
(Go1.7の記事ですが、Go1.13以上の場合Dockerfileでgo getをする必要はありません)

(go-sqlite3ライブラリを使う場合)
go-sqlite3 が入った状態での Docker のマルチステージビルドを行う - Pistatium note

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vueプロジェクトファイルににfirebaseを設定する firestore/storage/authentication

はじめに

vueプロジェクトにfirebase(firebaseAuthentication/firestore/cloudStorage)を導入したのですが、設定ファイルをどのように書けばいいのか等わからないことが多々あったため、メモがてら書き残しておこうと思います。

前提として

vue、firebaseの導入はしてある前提です
各バージョン
スクリーンショット 2020-03-16 7.36.36.png

ファイルの構成

firebase login
firebase init をして firebase.jsonなど が出来てる状態で
srcフォルダー配下に
src/
 ┣ components/
 ┣ firebase
  ┣ credentials.js/
  ┣ firebase.js/
  ┣ firebaseAuth.js/
  ┣ fireStorage.js/
  ┗ fireStore.js/
 ┣ plugins/
 ┣ router/
 ┣ store/
 ┣ views/
 ┣ App.vue/
 ┗ main.js/

とします。
今回自分はvueRouter、vuex、vuetifyを使ったのでplugins、router、storeフォルダが存在しています。

それぞれのファイルの中身

credentials.js
export default {
  firebase: {
   apiKey: "api-key",
   authDomain: "project-id.firebaseapp.com",
   databaseURL: "https://project-id.firebaseio.com",
   projectId: "project-id",
   storageBucket: "project-id.appspot.com",
   messagingSenderId: "sender-id",
   appID: "app-id",
   measurementId: 'measurement-id'
  }
}

firebase consoleのsettingにあるユニークなapiKeyなどの値を入力してください。

firebase.js
import Firebase from 'firebase/app';
import credentials from './credentials.js';
export const firebaseApp = Firebase.initializeApp(credentials.firebase);
firebaseAuth.js
import { firebaseApp } from './firebase.js';
import 'firebase/auth';
export const firebaseauth = firebaseApp.auth();
fireStorage.js
import { firebaseApp } from './firebase.js';
import 'firebase/storage';
export const firestorage = firebaseApp.storage();
fireStore.js
import { firebaseApp } from './firebase.js';
import 'firebase/firestore';
export const firestore = firebaseApp.firestore();

ファイル名や定数名は任意なので必ずここに書いてあるものである必要はありません!!
ただ変更する際はimportするときに気をつけてください。

*ただしimport 'firebase/auth';などfirebaseの機能のimportについては必ずこのようにimportしなくてはいけないです。

それぞれの機能について

バリデーションなどなくガバガバですがサンプルコードおいときます。
vuetifyで書いてるコードをvueに戻して書いたつもりなので間違ってるかも、、、
一部のみの抜き出しです

firebaseAuthentication

登録のファイル.vue
<template>
 <form @submit.prevent="submit">
  <input v-model="email" label="email">
  <input v-model="password" label="password">
 </form>
 <botton @click="submit">登録</botton>
</template>
<script>
import { firebaseAuth } from "@/firebase/firebaseAuth.js";
export default {
 data: () => ({
   email: "",
   password: ""
 }),
 methods: {
  submit() {
   .createUserWithEmailAndPassword(this.email, this.password)
     .then(() => {});
     .catch(() => {});
  };
 };
};
</script>

注意点としてはfirebase側でメールとパスワードでの認証を有効にしないといけない点に注意してください。
ログインページは上のファイルのcreateUserWithEmailAndPassword関数signInWithEmailAndPassword関数に変更すればできます。

firestore

データ追加.vue
<template>
 <form @submit.prevent="addData">
  <input v-model="title" placeholder="タイトルを入力してください">
 </form>
 <botton @click="addData">追加</botton>
</template>
<script>
import { firestore } from "@/firebase/fireStore.js";
export default {
 data: () => ({
   title: ""
 }),
 methods: {
  addData() {
   firestore.collection('コレクション名').add({
    title: this.title
   })
 };
};
</script>

データ取得の場合はdataにworks: []とかを作って
ライフサイクルフックのcreated関数をつかって

created() {
 firestore.collection('コレクション名').get().then((querySnapshot) => {
  const array = [];
  querySnapshot.forEach((doc) => {
    array.push(doc.data())
  })
  this.works = array
 });
}

をよんでhtmlかくところで

<div v-for='(work, i) in works' :key='i'>
 <p>{{ work.title }}</p>
</div>

と読んだらいい感じだと思います。

気力が尽きてしまったのでこのへんでまたstorageの使い方も追加しときます

まだまだ初学者なので間違っているところ、改善できるところがあれば優しく教えてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLIで作成したindex.htmlからオブジェクトを取得する

タイトルの内容をもうちょっと詳しく

  • Vue CLIで作ったプロジェクト
    • ディレクトリ構成はデフォルトのまま
  • 画面表示用にオブジェクトを渡したいが、諸事情によりindex.htmlに書くことしかできない
    • index.htmlにscriptタグを書いて埋め込む
  • なのでVue側からindex.htmlに記載された値を取得する

サンプルコード

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <title>args sample</title>
    </head>
    <body>
        <div id="app"></div>
        <script>
        var htmlArgs = [
        {
            "key1":"hoge",
            "key2":"fuga"
        },
        {
            "key1":"foo",
            "key2":"bar"
        }
        ];
        </script>
    </body>
</html>
App.vue
<template>
  <div id="app">
    <ul>
      <li v-for="arg in args" v-bind:key="arg.key1">
        key1={{arg.key1}} / key2={{arg.key2}}
      </li>
    </ul>
  </div>
</template>
<script>
/* global htmlArgs */
export default {
  name: 'App',
  data: function(){
    return {
      args: htmlArgs
    }
  },
}
</script>
<style>
</style>

解説

今回のキモになるのは、App.vueの/* global htmlArgs */です。
これが無い状態で実行すると、以下のようなエラーとなります。

cmd
ERROR  Failed to compile with 1 errors
error  in ./src/App.vue

Module Error (from ./node_modules/eslint-loader/index.js):

C:\Users\anonymous\sample\src\App.vue
  12:34  error  'htmlArgs' is not defined no-undef

✖ 1 problem (1 error, 0 warnings)

App.vueの中にhtmlArgsがいないのでエラーになります。
無いものを使おうとしているわけですから、当然ですね。

先ほどの/* global htmlArgs */htmlArgsはglobalにいる(からファイル内に無くてもスルーしてくれ)と宣言して、エラーを防ぐという効果があります。

これでindex.htmlにあるオブジェクトを取得できるようになりました。
めでたしめでたし。

参考

アンビエント宣言が’no-undef’ルールにひっかかる件
https://teratail.com/questions/226017

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js ネコ本チュートリアルのToDoリストを少しアレンジしてみた

はじめに

最近Vue.jsの学習を始めたので、アウトプットがてらToDoリストを作成してみました。
※プログラミングの勉強を始めて3か月ほどなので突っ込みどころありましたらご教授いただけますと幸いです。

自作したToDoリスト

以下が、自作したToDoリストです。
コメント 2020-02-06 211639.jpg
自作ToDoリスト
勉強がてらデザインにはBootstrapを使用してみました。

参考

参考にさせていただいたのは以下のチュートリアルです。
ToDoリストを作りながら学習しよう!

個人的にはVue.jsを学ぶ上で大変お世話になっている本(通称ネコ本)です。

Let's アレンジ

アレンジがカタカナになってるのは見栄えです。(センス・・・)

本チュートリアルでは、状態が完了と着手中の2パターンの切り替えになってるところを、
アレンジ精神で3パターンの切り替えにしたいなと思い保留を追加してみました。

具体的に変更したところは2点。

①使用するデータを追記

変更前

main.js
  data: {
    todos: [],
    current: -1,
    options: [
      { value: -1, label: 'すべて' },
      { value: 0, label: '作業中' },
      { value: 1, label: '完了' }
    ]
  },

⇓⇓⇓

変更後

main.js
  data: {
    todos: [],
    current: -1,
    options: [
      { value: -1, label: 'すべて'},
      { value: 0, label: '着手'},
      { value: 1, label: '完了'},
      { value: 2, label: '保留'}
    ]
  },

※個人的に扱いやすいように数字を変更してます。

②メソッドを変更する

main.js
    doChangeState: function (item) {
      item.state = !item.state ? 1 : 0
    },

⇓⇓⇓

変更後

main.js
    doChangeState: function(item){
      if(2 > item.state){
        item.state = item.state + 1
      } else {
        item.state = 0
      }
    },

三項演算子から条件分岐に変更しました。

JavaScript初心者なので、三項演算子だとわかるまでめちゃくちゃ理解に苦しみました。
(もう少し基礎を学んだほうがいいのは間違いない・・・)

補足

三項演算子は以下の形で条件式がtrueなら式1をfalseなら式2を返します。

条件式 ? 式1 : 式2 

まとめ

Laravelを学んでいく中でフロントサイドでVue.jsを使ってみたくなり学び始めました。

両FWともに様々なチュートリアルがありますが、アレンジ~自作ポートフォリオ完成までには、
しっかり1つ1つのスクリプトの意味を理解していかなければいけないと痛感しました。

引き続きアウトプットしながら学習をしていきたいと思います。
拙いアウトプットですがどなたかの参考になりましたら幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発でやっておきたい】CircleCIで環境ごとに異なるfirebaseに接続する設定

初めに

背景

  • Vue.jsを用いたWebアプリ(Vue.jsに限った話では無いと思うが念の為)
  • FirebaseのFirestoreをDBとして用い、Firebase上にHostingがしたい.
  • CI/CDにはCircleCIを用いる.

やりたいこと

プレゼンテーション1.png
  • Production環境とStaging環境で接続するDBやHosting先をいい感じに分けてほしい.

ざっくりとした方針

  • Production環境とStaging環境でそもそもFirebase上のプロジェクトを分離し、prodcutionブランチにpushされたときはProduction環境に、 StagingブランチにpushされたときはStaging環境に接続するように設定する.
  • Production環境とStaging環境の両方のAPIキーなどをCircleCI上の環境変数として定義& .envファイルに書き出すように設定
  • build時はNODE_ENVの値により.envから読み出す値を分ける.
  • deploy時はfirebase use {project}でプロジェクトを指定した上でデプロイする.

なお、hostingのみの場合はわざわざプロジェクトを分けたりする必要はありません.
しかし、firestoreはプロジェクトごとに1個しか使えないので、prodcutionとstaging等で分けたい場合はそもそものプロジェクトから分ける必要があります.

前提

  • GithubにPushしたらCircleCIでjobが走るところまでは設定できているとします.

手順

1. Firebase上にプロジェクトを2つ作成する.

production用のプロジェクトと、とStaging用のプロジェクトを2つ作成します.
便宜上以下のふたつを作成したとします.
- MyProjectProd
- MyProjectStag

2. それぞれのAPIキー等を.envに登録する.

本記事ではdotenvを用いて環境変数を読み出します.
最終的には.envはCircleCI上で自動的に作られるようにするので、この手順は飛ばしても構いません.
念の為ローカルで動作確認用に作成しているだけです.
まず、dotenvをインストールしてなければインストールしてください.

yarn add dotenv

project直下に.envを作成してください.

FIREBASE_API_KEY_PROD=****************
FIREBASE_AUTH_DOMAIN_PROD=****************
FIREBASE_DATABASE_URL_PROD=****************
FIREBASE_PROJECT_ID_PROD=****************
FIREBASE_STORAGE_BUCKET_PROD=****************
FIREBASE_MESSAGING_SENDER_ID_PROD=****************

FIREBASE_API_KEY_STAG=****************
FIREBASE_AUTH_DOMAIN_STAG=****************
FIREBASE_DATABASE_URL_STAG=****************
FIREBASE_PROJECT_ID_STAG=****************
FIREBASE_STORAGE_BUCKET_STAG=****************
FIREBASE_MESSAGING_SENDER_ID_STAG=****************

_PRODがついている方にはMyProjectProdの値を、
_STAGがついている方にはMyProjectStagの値を、入力してください.
なお、これらの値はFirebase Console上から確認することが出来ます.

3. firebaseの初期化処理を記述する.

2で登録した値を用いてfirebaseの初期化処理を書いていきます.
使用しているフレームワークや構成によって記述する場所は違うと思いますが、筆者の環境(Vue.js+Nuxt.js)ではplugins/firebase.jsに記述しています.

if (!firebase.apps.length) {
  if (process.env.NODE_ENV === "production") {
    //Prodcution環境
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_PROD,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_PROD,
      databaseURL: process.env.FIREBASE_DATABASE_URL_PROD,
      projectId: process.env.FIREBASE_PROJECT_ID_PROD,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_PROD,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_PROD
    });
  } else {
    //それ以外(Staging環境,develop環境)
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_STAG,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_STAG,
      databaseURL: process.env.FIREBASE_DATABASE_URL_STAG,
      projectId: process.env.FIREBASE_PROJECT_ID_STAG,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_STAG,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_STAG
    });
  }
}

このようにprocess.env.NODE_ENVの値によって.envから読み取る値を変えています.

4. NODE_ENVの値を設定できるようにする.

NODE_ENVの値によって処理を変える記述が出来たので、次はNODE_ENVを設定できるようにします.
本記事では、cross-envを使用します.

yarn add cross-env

cross-env自体の使い方は
環境変数設定は基本cross-envだけどたまにenv-cmdも使う
等に書いてあるとおりです.
package.jsonに以下のような設定をします.(nuxt.js仕様)


{ ...
 "scripts": {
    ...
    "build:stag": "cross-env NODE_ENV=\"staging\" nuxt build",
    "build:prod": "cross-env NODE_ENV=\"prod\" nuxt build",
    ...
  },
  ...
}

これで、

yarn build:stag

を実行すればstaging用の変数を用い、

yarn build:prod

を実行すればproduction用の変数を用いてbuildが走ります.

5. firebaseへのhostingの設定

buildしたものをfirebaseにhosting出来る設定をしていきます.
この手順は
Firebase Hosting でWebサイトを公開する方法
こちらの記事などで詳しく解説されていますので、参考にしながら進めると良いと思います.
本記事では簡易的に記述します.

staging用のプロジェクトと、prodcution用のプロジェクトがあるので、両方にエイリアスをつけて設定していきます.

firebase use --add 

で、staging用のプロジェクトをstag,
prodcution用のプロジェクトをprodとして登録します.
これで、例えばstaging用にdeployしたい場合は、

firebase use stag
firebase deploy

で実行できます.

ここまでの動作確認

これで、手動であればprodcutionとstagingを分ける事ができます.
手順としては、
1. 任意の環境用にbuildする.
2. firebaseのプロジェクトを切り替える
3. deployする

となります.
例えば、staging環境であれば

yarn build:stag
firebase use stag
firebase deploy

を実行すればOKです.
ここまで手動で想定通りの動きになるか試してみましょう.
うまく行けば、これらをCircleCI上で実現できるようにします.

6. .envに記述した値の移植

2.で作成した.envファイルは、gitの管理化には入れないため、CircleCI上の環境変数に必要な値を登録し、build時に.envファイルを作成できるようにします.
具体的には、2.で記述したキーと値のセットをそのままCircleCI上のプロジェクトの環境変数に登録します.

7. deployのためのキーの設定

CircleCI上からfirebaseのhostingにdeployするためには、キー等を設定する必要があります.
この手順にも、参考になる記事があるのでそちらを参考に進めてください.
Circle CIからFirebase Hostingに自動ビルド&デプロイする
上記記事に従って、

firebase login:ci

により得られたキーをCircleIC上の環境変数に設定します.
但し、今回はprodcution用とstaging用の2つがあるので

firebase use prod
firebase login:ci

で得られたキーをFIREBASE_TOKEN_PRODとし、

firebase use stag
firebase login:ci

で得られたキーをFIREBASE_TOKEN_STAGとして登録してください.

8. .circleci/config.ymlの記述

最後にcircleCIの設定ファイルを記述していきます.
基本的には、先程手動で行った、

yarn build:stag
firebase use stag
firebase deploy

を実行するような設定をすればOKです.
変更点は、
- 最初にcircleCIの環境変数から.envを書き出す必要があること
- firebaseコマンドのパスを相対パスで指定すること
- deploy時にキー等を指定すること
の3点です.

必要な部分だけを記述したものが以下になります.

version: 2.1
orbs: #CircleCIが準備したnode用の設定を元として使う
  node: circleci/node@1.1.6

commands:
  make_env: #.envを書き出すコマンドをまとめておく
    steps:
      - run:
          name: make .env
          command: |
            echo "FIREBASE_API_KEY_PROD=$FIREBASE_API_KEY_PROD" > .env
            echo "FIREBASE_AUTH_DOMAIN_PROD=$FIREBASE_AUTH_DOMAIN_PROD" >> .env
            echo "FIREBASE_DATABASE_URL_PROD=$FIREBASE_DATABASE_URL_PROD" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_PROD=$FIREBASE_MESSAGING_SENDER_ID_PROD" >> .env
            echo "FIREBASE_PROJECT_ID_PROD=$FIREBASE_PROJECT_ID_PROD" >> .env
            echo "FIREBASE_STORAGE_BUCKET_PROD=$FIREBASE_STORAGE_BUCKET_PROD" >> .env
            echo "FIREBASE_API_KEY_STAG=$FIREBASE_API_KEY_STAG" >> .env
            echo "FIREBASE_AUTH_DOMAIN_STAG=$FIREBASE_AUTH_DOMAIN_STAG" >> .env
            echo "FIREBASE_DATABASE_URL_STAG=$FIREBASE_DATABASE_URL_STAG" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_STAG=$FIREBASE_MESSAGING_SENDER_ID_STAG" >> .env
            echo "FIREBASE_PROJECT_ID_STAG=$FIREBASE_PROJECT_ID_STAG" >> .env
            echo "FIREBASE_STORAGE_BUCKET_STAG=$FIREBASE_STORAGE_BUCKET_STAG" >> .env
            cat .env

jobs:
  deploy_stag: # staging環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13" # 念の為バージョン(正確にはdockerのtag)を指定しておく
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use stag
      - run:
          name: build
          command: yarn build:stag
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_STAG  --token=$FIREBASE_TOKEN_STAG

  deploy_prod: # prodction環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13"
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use prod
      - run:
          name: build
          command: yarn build:prod
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_PROD --token=$FIREBASE_TOKEN_PROD

workflows:
  deploy:
    jobs:
      - deploy_stag:
          filters: # stagingブランチの場合はstaging環境へのデプロイ
            branches:
              only: staging

      - deploy_prod:
          filters: # releaseブランチの場合はrelease環境へのデプロイ
            branches:
              only: release

これで、
stagingブランチにpushされた場合はdeploy_stagが、
releaseブランチにpushされた場合はdeploy_prodが実行されるはずです.
意図したとおりに挙動していれば無事終了です.

最後に

これがベストなやり方かどうかは分かりませんが、一応このやり方で特に問題は無いと思います.
方針さえ立てばそれぞれの手順自体は難しくないはずです.
やってみれば大したことないですが、自分はどういう方針で行くか結構手探りで進めたので、この記事が他の誰かのお役に立てれば幸いです.

また、firestoreのルールに関してCircleCI上で走らせるのも少しだけ手間取ったりしたので自分の備忘録のためにもそのうち書きたいと思ってます.

参考

環境変数設定は基本cross-envだけどたまにenv-cmdも使う
Firebase Hosting でWebサイトを公開する方法
Circle CIからFirebase Hostingに自動ビルド&デプロイする
FirebaseをStaging環境とかDebug環境とかRelease環境で切り替えをする(Webアプリ編)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsのpagesでコンポーネントを定義する方法(2パターン)

環境

  • Nuxt.js
  • Vue.jp

ディレクトリ構成

my-project/
├ components
│ └ OriginalDialog.vue
├ pages
│ └ index.vue
└ 以下省略

コンポーネント定義(パターン1)

よくみるコーディング

index.vue
<original-dialog ref="originalDialog" />

<script>
import OriginalDialog from '@/components/OriginalDialog'

export default {
  components: {
    OriginalDialog
  },
</script>

コンポーネント定義(パターン2)

すっきりしたコーディング

index.vue
<original-dialog ref="originalDialog" />

<script>
export default {
  components:{
    originalDialog: () => import('@/components/OriginalDialog'),
  }
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Qiita記事の評価の考察と多段階評価の試験導入

はじめに

皆さんご存知の通り、Qiitaの"いいね"が"LGTM"に変更されて数日が立ちました。
https://blog.qiita.com/like-to-lgtm/
賛否両論があるようですが、自分はQiitaさんの方針は技術情報投稿サイトとして正しい判断のように思います。
ただ、Qiitaもユーザが増えてきて、使われ方も多様になったように感じます。初期の頃はプログラミングのコーディングに関する記事が多かったように記憶していますが、最近は「エンジニアの〜〜」や「つくってみた」系の記事も増えてきました。これらの記事はエンジニアの専門性に関わらず、幅広いユーザ層が読むことになるので、結果的に”いいね”の評価が多くなる傾向にあるようです。
自分はこれらの記事を全く否定するつもりはなく、いつも面白く読ませていただいております。
ただ、思うこともあったのでこの記事を書いてみました。

"いいね"とは?

多くのSNSで”いいね”が使われていますが、Qiitaの技術記事の評価は"0" or "1"で決めれないような気がします。

技術記事の評価

自分はQiitaの記事を読んだときに、4種類の評価が存在します。

  1. 自分の調べていたことの解決策が書いてあって技術的に良かった(LGTM)。
  2. 自分が今知りたい情報ではないけど、将来的に役に立ちそうな情報が書いてある(ストック)。
  3. ブログ記事のような作ってみた系やエンジニアポエム系の読んで面白かった(LGTM以下の"いいね")
  4. 記事が数行しかなく、「また追加します」のような下書き状態で公開されていて残念なもの

他サイトの評価方法について

Facebook

いいね!本家?のFacebookを見てみると、いいねの他に評価が増えています。様々な投稿内容に”いいね”だけの評価しかないのは限界があることを意味しているような気がします。
Screenshot from 2020-03-13 01-03-31.png

DISQUS reactions

ブログの埋め込みコメントサービスのDISQUSはReactionsという絵文字でSlackのように反応できる機能があります。コメントするまではないけど、ちょっとした反応があることで投稿者としては嬉しいと思います。
https://help.disqus.com/en/articles/2199501-reactions
image.png

dev.to

海外の技術投稿系サイトdev.toではQiitaの"LGTM"相当の"heart"、"ストック"相当の"reading list"に加え”unicorn”という評価が存在します。ユニコーンの評価についてはdev.toでも議論されていますがあえて、抽象的な絵文字にすることで”いいね”以下のリアクションを吸収しているように考えられます。

image.png

評価数を増やす?

これらより個人的な結論は、解決策は評価ボタンを増やすことだと考えます。増やしたところで、ユーザが評価に混乱するということも考えられますが、書き手はたくさんリアクションをもらえ、読む側もLGTM以下の評価を気軽にできるのではないでしょうか。

そこで、この記事ではFacebookとDISQUSを参考に4段階の評価を試験的に導入してみます!
”いいね!”はLGTMと同じ意味で、"すごい!"と"面白い!"は作ってみた系やポエム記事に対する評価として使えそうです。
”うーん…”はネガティブ評価で、これも自分はあったほうが良いのかなと思います。これは、以前話題になった”ゴミ記事”問題とも関連しますが、書き手としてはネガティブな評価が多いと記事の書き方を改めるきっかけになるのではないでしょうか。(自戒)

4つの絵文字の画像のどれかをクリックして評価してみてください。

        ↓クリック!!!!

いいね! すごい! 面白い! うーん…

この評価方法がうまくいったら、Qiitaさんには評価を増やす検討をお願いしたいですね…??

技術的な話

上の評価ボタンは、画像に評価をパラメータに設定したリンクを貼ってあり、遷移先のWebサイトでGASのAPIにリクエストを送り、結果をレスポンスに返しているだけです。
WebサイトはVue.jsによるSPAで、同じユーザの重複投稿を防ぐために、簡易的にFingerprint2.jsでブラウザ情報からidを生成しています。これはシークレットモードでもトラッキング可能にできる技術として有名ですね。気になる方は、シークレットページで開いてみて、Finger Print Hashが一致することを確認してみてください。また、取得しているブラウザの情報はコンソールに出力されています。
ブラウザが異なると、もちろん違った結果になりますが、シークレットモードも追跡されるのは怖いですね()
image.png

グラフは無難にchart-jsをラッピングしたvue-chartjsを使いました。ホスティングはもちろんNetlifyです。

思いついたことを簡単にまとめてみただけですが、何かあればコメントお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetifyで既存ページのデザインを改善してみた

はじめに

最近 Vue.jsとVuetifyに出会って
「こんな簡単に綺麗なサイト作れるんかよ!」
って衝撃を受けました。

せっかくなら前に作ったツールのデザインもVuetifyで改善してみたいと思ってやってみることに。

ビフォー

改善前のページがこちら

before

htmlとcssだけで短時間で作ろうとするとこんな出来栄えになりました・・・;

良い点を挙げるならシンプルなとこかな(?)
なのでシンプルを維持したままデザインを改善してみました。

アフター

Vuetifyでデザインを改善したものがこちら

after

なかなかいい感じ!

全部 <v-card> の中に納めました。
リンク生成ボタンを押した後に表示したい要素は<v-expand-transition>に入れることでスッキリしました。

さいごに

Vuetifyで書くとめちゃくちゃ簡単にUI改良できるので楽しすぎる。

実はフレームワーク使うのって今回が初めて。
もうフレームワークなしで何かを作るのは無理かもしれない笑

おわりです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

管理者権限のないWindowsでvue開発(サンプル付き)

コロナ禍で、在宅で、与えられたリモート環境には管理者権限がなくて、でも開発はやらないと。。
なんて状態でもvue開発するための環境構築手順です

ついでに vue の動作確認サンプルも載せときます

手順

1.node環境の構築
2.vue-cli のインストール
3.vue プロジェクトの作成
4.vue 動作確認用サンプル

1.node環境の構築

公式サイトから ZIP版をダウンロードしてきて、展開
環境変数を設定します
ファイルを展開して置くだけなので管理者権限は不要です

1.1. nodeをダウンロード

https://nodejs.org/ja/download/
Windows Binary の ZIPファイルをダウンロードです

01.png

1.2. nodeをインストールするフォルダの準備

ユーザ(自分)のフォルダにインストール用フォルダを準備

コマンドプロンプトを開いてフォルダを作成しときます

c:\Users\ユーザ名> mkdir App

1.3. nodeをインストール(ファイル置くだけ)

まず落としてきたZIPを展開
 node-v12.16.1-win-x64.zip
 ※ Windows標準の「すべて展開」を使った前提で記述してます

展開してできたフォルダを準備したAppフォルダに移動

C:\Users\ユーザ名 > move  Downloads\node-v12.16.1-win-x64\node-v12.16.1-win-x64  App\
        1 個のディレクトリを移動しました。

移動したフォルダ名を今後のためにリネームしときます
「node-v12.16.1-win-x64」->「node」

C:\Users\ユーザ名> cd App

C:\Users\ユーザ名\App> rename node-v12.16.1-win-x64 node

1.4. PATHの設定

管理者権限がないのでコントロールパネルから環境変数PATHを設定します

1.4.1. コントロールパネル -> ユーザー アカウント を開き

02.png

1.4.2. ユーザー アカウント を開き

03.png

1.4.3. 環境変数の変更 を開く

04.png

1.4.4. 環境変数 PATH に nodeを追加する

開いた「環境変数」ウィンドウで上部のユーザの環境変数から Path を選択
(下部のシステム環境変数は権限がなくて変更できないと思います)
「編集」ボタンを押して、開いた「環境変数名の編集」ウィンドウで「新規」でnodeをインストールしたフォルダを追加します
[ C:\Users\ユーザ名\App\node ]

1.5. nodeのインストール確認

新たにコマンドプロンプトを開いてバージョンを確認してみます

C:\Users\ユーザ名> node --version
v12.16.1

C:\Users\ユーザ名> npm --version
6.13.4

どうでしょう
ちゃんとバージョン出たら、インストール成功です
※ 上記バージョンは 2020/3/16 時点の最新だと思います

2.vue-cli のインストール

npm でサクッとインストール

コマンドプロンプトで以下を実行

C:\Users\ユーザ名> npm install @vue/cli
・・・・
+ @vue/cli@4.2.3
added 1115 packages from 654 contributors and audited 16661 packages in 590.218s

23 packages are looking for funding
  run `npm fund` for details

4.2.3 がインストールされました

3.vue プロジェクトの作成

コマンドプロンプトで以下を実行

> vue create test
・・・
 $ cd test
 $ npm run serve

無事にインストールされた模様

インストールパッケージを確認

> cd test

test> type package.json
{
  "name": "test",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.4",
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.2.0",
    "@vue/cli-plugin-eslint": "~4.2.0",
    "@vue/cli-service": "~4.2.0",
    "babel-eslint": "^10.0.3",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.1.2",
    "vue-template-compiler": "^2.6.11"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ]
}

vue 2.6.11 がインストールされました

4.vue 動作確認用サンプル

そのまま実行してもつまらない?ので
動作確認用のサンプルをば

src/App.vue
<template>
  <div>
    {{ messages }}
    <div>
      <ol>
        <li v-for="(it, idx) in items" :key="it.id">
          {{ it }}
          <button v-on:click="del_item( idx )"> x </button>
        </li>
      </ol>
      <input v-model="item" />
      <button v-on:click="add_item()">Add Item</button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      messages: 'Hello World!',
      items: [ 'aaa', 'bbb', 'ccc', ],
      item: 'Hello Vue.js!',
    }
  },
  methods: {
    add_item: function () { this.items.push( this.item ); this.item = ''; },
    del_item: function ( _idx ) { this.items.splice( _idx, 1 ) }
  }
}
</script>

こちらを参考とさせていただきました。よいサンプルをありがとうございます。
 https://qiita.com/yamazaki3104/items/c793d77a19f104c2a63e
ちょこっと Vue-cli 用? に変更してます

コマンドプロンプトで以下を実行することで動作確認できます
管理者権限なくてもポート開けるのね

> npm run serve
・・・・
 DONE  Compiled successfully in 11754ms 

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://10.20.30.40:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

以上
管理者権限のない Windows で Vue 開発する手順でした

ちなみに Visual Studio Code も管理者権限なくインストール可能です
こちら https://code.visualstudio.com/download の 「User Installer」をダウンロードして実行するだけです
管理者権限なくても普通にインストールできちゃいました

これで開発はかどります

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでファイル名に連番を付けていく方法

vue-awesome-swiperを使うときにファイル名をバラバラにせず、連番にすればスッキリできるのではと思いそのやり方をまとめてみました。

開発環境はNuxt.js + Vuetifyにvue-awesome-swiperを使った状態です。

Nuxt.jsにvue-awesome-swiperを導入し、カルーセルを表示させてみる
【Nuxt.js】imgファイルの指定方法について
【Nuxt.js】Universalモードで「window is not defined」エラーが出たときの対処法

変数を宣言する

const items = []

ループを作る

for (let i=1; i<=5; i++) {
//ここに処理を入力する
}

ループ内の処理を記入する

itemsに数字を足していく処理が必要なのでpushを使用します。

items.push({
  no: i,
  title: 'title' + i,
  slideimage: `/images/home/slide${i}.jpg`
})

※1.画像ファイルの部分ですがシングルクォートで囲っているのではなくバッククォートで囲っている点注意。
※2.スライドショー等で見せたいような画像はコンパイルの時間も減るでstaticに格納するほうがおすすめです。

ただしこのままだとファイル名に変数iによる連番を付加することができますが、ファイル名の連番部分の前の0を付けた"slide01.jpg"のようなフォーマットだと、変数のiが一桁のときに前0を付加する処理が必要になります。

数値のゼロを埋め、桁を揃える

zeroPadding方を応用します。

zeroPadding(index, size) {
  let tmp = index + ''
  while (tmp.length < size) tmp = '0' + tmp
  return tmp
}

まとめる

上記をまとめたコードになります。

index.vue
<template lang="pug">
    section.py-12
        v-layout(column align-center justify-center)
            v-flex(xs12 sm4 class="my-4")
        swiper(:options="swiperOption")
          swiper-slide(v-for="(item, index) in items" :key="index")
            img(:src="item.slideimage" :alt="item.title" :id="item.no")
</template>

<script>
export default {    
    data () {
        const items = []    
        for (let i=1; i<=5; i++) {
      items.push({
        no: i,
                title: 'title' + i,
        slideimage: `/images/home/slide${this.zeroPadding(i, 2)}.jpg`
            })
        }
        return {
            items,
        }
    },
    methods: {
    zeroPadding(index, size) {
      let tmp = index + ''
      while (tmp.length < size) tmp = '0' + tmp
      return tmp
    }
  }
}
</script>

これでファイルの連番については実装できました。
このやり方だと連番はできたのですが個別ごとのテキスト名とかの設定でできないので
複数の項目があるときはjsonで読み込んでv-forで回したほうがいいかもしれませんね。

参考:
数値のゼロ埋め(桁を揃える)
第一回 Vue.jsでWebアプリをつくろう!

追記

String.prototype.padStart()をつかったほうがいいという意見をいただいたので使った方法で書き換えてみました。

<script>
export default {    
    data () {
        const items = []    
        for (let i=1; i<=5; i++) {
      items.push({
        no: i,
                title: 'title' + i,
        slideimage: `/images/home/slide${this.setDigits(i, 2)}.jpg`
            })
        }
    },
    methods: {
        setDigits(number, digits) {
            const number_string = String(number).padStart(digits, '0');
    return number_string;
        }
    }
}
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでHello Worldを出力してみた

はじめに

自己学習とアウトプットを兼ねて作成してみました
本記事がQiita初投稿となります

やってみたこと

Vue.jsを使って簡単なコードを書いてみた

公式サイト
参考サイト
上記のサイトを参考にしつつ環境構築

そもそもVue.jsって何?

Vue.js(ビュージェイエス)は、クライアントサイドで使われるJavaScriptのフレームワークのこと

、、、なるほど良く分からんとなったのでざっくり説明します

▶︎クライアントサイド
Webサーバーにアクセスした得られた結果をWebページで表示する部分

▶︎JavaScript
Webページで複雑な機能をできるようにしたプログラミング言語

▶︎フレームワーク
少ないコードで効率的に作れるようにした機能の集まり

要は、Webページで良い感じに表示させるための機能の一つなんだなーという解釈で良いと思います

Vue.jsって何が良いの?

学習コスト低い

JavaScriptのフレームワークは他にも沢山存在するが、学習コストが低く学習しやすい利点がある
公式サイトが日本語対応しており、技術ブログうあ記事も充実しているので調べやすい

小規模から大規模開発まで

個人開発から、大規模なWebサイトまで対応している

採用企業例)Google、Apple、LINE、note、ZOZO

SPA開発

SPA(シングルページアプリケーション)開発に使用される
SPAとは、ブラウザで出来る処理はJavaScriptで終わらして、サーバーとの通信は必要最低限に抑えましょうという意味

MVVMモデル

Webサービスを開発する上では
Model
View
ViewModel
に分けて開発する設計思想があり、ModelのデータをViewで扱いやすくするようなものを設け、データバインディングという機能を使い、データそのものを共有して取り扱う考え方

文字だけでは、良く分からない(私も)と思うので気になった方はこちらを参考に

概要が分かってきた所で作成してみよう

今回作成したのは静的なhtmlファイルになります

index.htmlの作成

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello Worldを表示</title>
  </head>
  <body>
    <!--- "Hello World" を表示する --->
  </body>
</html>

作成したindex.htmlファイル内にCDNを記述

Vue.jsを導入する方法は幾つかありますが、今回はCDNを利用しました
bodyタグの中に記述します

  <body>
    <!--- "Hello World" を表示する --->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </body>

表示用htmlを記述

messageという変数を用意して、"Hello World"の文字列を格納して表示していきます
messeageという変数を{{ }}の二重カッコで囲み、divタグで括ることでid要素を割当てています

  <body>
    <!--- "Hello World" を表示する --->
    <div id="app">{{ message }}</div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </body>

htmlはこれで準備OK

Vue関数を使用

ここでやっとVueを記述してきます
初めて書くときには構文がやや複雑ですが、お作法と思いましょう

const app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  }
})

まず、Vueインスタンスの生成を行います
el:'#app'で、先ほどid要素を割当てたappの値にVue関数が使用出来るように設定しています
data:{}の中で用意したmessage変数に出力した文字列を格納

scriptタグで括る

最後にscriptタグで括り以下のように記述します

<body>
  <div id="app">{{ message }}</div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        message: 'Hello World'
      }
    })
  </script>
</body>

出力

以上でプログラムの作成は完了したのでブラウザで表示させてみましょう
スクリーンショット 2020-03-14 15.15.04.png
と表示されたらOKです!

index.html全文

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello Worldを表示</title>
  </head>
  <body>
    <div id="app">{{ message }}</div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          message: 'Hello World'
        }
      })
    </script>
  </body>
</html>

最後に

今回はVue.jsで簡単なプログラムを作成しましたが、まだまだVue.jsの利点を十分に発揮できていないので
これからまた勉強次第、記事を投稿しようと思います

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

まだ使っていないけどtype-safeなvuex alternative として piniaどうだろう?

  • 注意: piniaリポジトリを斜め読みしただけなので大したこと書いていない
  • けど vuex だとどうしても不便な場合があるので、 reactive(Vue.observable)によるVanilla State Managementや、 pinia みたいな方法でtype-safeなStoreレイヤーほしいよね.
    そんな気持ちの人が増えたらいいよね! 宮ちゃん!

話のコンテキスト

  • 今現在 Vue(2.x), vuexを使っているプロジェクトでの利用を想定。
  • わりとごりごりしたフロントエンド。 エンドユーザーが触る管理画面 がある
  • 複数のvuexモジュールがある。namespaced true. vuexモジュール間でもアクセスしうる。(単方向)
  • 徐々にTypescript + compositon-apiを使い始めた。
  • actionやgetterの引数や返り値の厳密な型がほしい.........

はて、typescript+composition-api利用している場合、vuex使う理由は?

  • 以前からVue.observable()使ったno vuexでのstate managementでもよかった。
  • vuexのメリットがあるとすれば、比較的「Vue標準のスタイル」と呼べる. documentも存在するという点だと思う。 (独自の方法でglobal state管理をする場合、プロジェクトローカルな方法をメンバーに伝えて、これに沿って書いてもらう必要はある。)
  • vuex4でもtypescriptへの注力はあまりなさそうなので、Typescript+CompositionAPI利用時はvuexは選択肢としてかなりモチベーションが低くなる

こんな話もある。
https://speakerdeck.com/ntepluhina/you-might-not-need-vuex

そこで pinia

https://github.com/posva/pinia

まだ experimental だけど

Vuex 定義側

src/stores/adminUsersStore.ts
import { createStore } from 'pinia'


export const useAdminUsersStore = createStore({
  state: () => ({
    usersWithDeleted: [],
  }),  
  getters: {
    activeUsers(state, getters) { return state.users.filter((u: User) => !u.retiredAt) 
  } 
  action: {
    fetchUsers() {
      ....then((response) => {
       this.state.users = response.data.usersWithRetired;
      });
    }
  }
}

利用側

App.vue
<script lang="ts">
import { useAdminUsersStore } = "@/stores/adminUsersStore.ts";
export default defineComponent({ // 旧 createComponent
  setup(props, _ctx) {
    const adminUsersStore = useAdminUsersStore();
    mounted(() => { adminUsersStrore.fetchUsers() })
    return {
      users: adminUsersStore.activeUsers,
    };
  }
});
</script>

<template>
  <ul>
    <li v-for="u in users">{{ u.name }}</li>
  </ul>
</template>

いまのところ感じていること

  • compositionAPIの reactive<T>({ ... }) を使って、fooBarStore.ts 内でvanila state管理をおこなっても良いんだけど、 createStore({ state, getters, actions }) みたいにある程度 形式が決まっているとメリットがある
    • チームで開発をする場合、各個人が迷わないかも
    • ある程度グルーコードは出てくるだろうから pinia みたいなレイヤーがあると都合がよい。(createStore内部でいろいろやる)
    • 例: vanila state management on composition-api の場合違って、piniaの場合、各getters functionごとに computed(() => ... )しなくてよい。

斜め読みしたPinia のmemo

  • inject/provide使っていない. export xxx = createStorestore({ ... }) で定義して、 これを直接importして使う。 createStore内部で pinia 内部の管理テーブル(storesMap) にidだけ突っ込んでいる
  • devtoolサポートやってて偉い。これはちょっとだけ便利かも

疑問的など

  1. storeYからstoreXのアクセスってどうやるのかまだ調べてない。

storeY = createStore({ ... }) の外側で storeX = useStoreX() するわけにも行かないし.
https://github.com/posva/pinia#usage

There is one important rule for this to work: the useMainStore (or any other useStore function) must be called inside of deferred functions. This is to allow the Vue Composition API plugin to be installed. Never, ever call useStore like this:

  1. stateの型かけないの....? (もしくは書かなくてもこまらない? そんなわけないやろ...???)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発でやっておきたい】CircleCIで環境ごとに異なるfirebaseに接続する設定

初めに

背景

  • Vue.jsを用いたWebアプリ(Vue.jsに限った話では無いと思うが念の為)
  • FirebaseのFirestoreをDBとして用い、Firebase上にHostingがしたい.
  • CI/CDにはCircleCIを用いる.

やりたいこと

  • Production環境とStaging環境で接続するDBやHosting先をいい感じに分けてほしい.

ざっくりとした方針

  • Production環境とStaging環境でそもそもFirebase上のプロジェクトを分離し、prodcutionブランチにpushされたときはProduction環境に、 StagingブランチにpushされたときはStaging環境に接続するように設定する.
  • Production環境とStaging環境の両方のAPIキーなどをCircleCI上の環境変数として定義& .envファイルに書き出すように設定
  • build時はNODE_ENVの値により.envから読み出す値を分ける.
  • deploy時はfirebase use {project}でプロジェクトを指定した上でデプロイする.

なお、hostingのみの場合はわざわざプロジェクトを分けたりする必要はありません.
しかし、firestoreはプロジェクトごとに1個しか使えないので、prodcutionとstaging等で分けたい場合はそもそものプロジェクトから分ける必要があります.

前提

  • GithubにPushしたらCircleCIでjobが走るところまでは設定できているとします.

手順

1.Firebase上にプロジェクトを2つ作成する.

production用のプロジェクトと、とStaging用のプロジェクトを2つ作成します.
便宜上以下のふたつを作成したとします.
- MyProjectProd
- MyProjectStag

2. それぞれのAPIキー等を.envに登録する.

本記事ではdotenvを用いて環境変数を読み出します.
最終的には.envはCircleCI上で自動的に作られるようにするので、この手順は飛ばしても構いません.
念の為ローカルで動作確認用に作成しているだけです.
まず、dotenvをインストールしてなければインストールしてください.

yarn add dotenv

project直下に.envを作成してください.

FIREBASE_API_KEY_PROD=****************
FIREBASE_AUTH_DOMAIN_PROD=****************
FIREBASE_DATABASE_URL_PROD=****************
FIREBASE_PROJECT_ID_PROD=****************
FIREBASE_STORAGE_BUCKET_PROD=****************
FIREBASE_MESSAGING_SENDER_ID_PROD=****************

FIREBASE_API_KEY_STAG=****************
FIREBASE_AUTH_DOMAIN_STAG=****************
FIREBASE_DATABASE_URL_STAG=****************
FIREBASE_PROJECT_ID_STAG=****************
FIREBASE_STORAGE_BUCKET_STAG=****************
FIREBASE_MESSAGING_SENDER_ID_STAG=****************

_PRODがついている方にはMyProjectProdの値を、
_STAGがついている方にはMyProjectStagの値を、入力してください.
なお、これらの値はFirebase Console上から確認することが出来ます.

3.firebaseの初期化処理を記述する.

2で登録した値を用いてfirebaseの初期化処理を書いていきます.
使用しているフレームワークや構成によって記述する場所は違うと思いますが、筆者の環境(Vue.js+Nuxt.js)ではplugins/firebase.jsに記述しています.

if (!firebase.apps.length) {
  if (process.env.NODE_ENV === "production") {
    //Prodcution環境
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_PROD,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_PROD,
      databaseURL: process.env.FIREBASE_DATABASE_URL_PROD,
      projectId: process.env.FIREBASE_PROJECT_ID_PROD,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_PROD,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_PROD
    });
  } else {
    //それ以外(Staging環境,develop環境)
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_STAG,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_STAG,
      databaseURL: process.env.FIREBASE_DATABASE_URL_STAG,
      projectId: process.env.FIREBASE_PROJECT_ID_STAG,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_STAG,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_STAG
    });
  }
}

このようにprocess.env.NODE_ENVの値によって.envから読み取る値を変えています.

4. NODE_ENVの値を設定できるようにする.

NODE_ENVの値によって処理を変える記述が出来たので、次はNODE_ENVを設定できるようにします.
本記事では、cross-envを使用します.

yarn add cross-env

cross-env自体の使い方は
環境変数設定は基本cross-envだけどたまにenv-cmdも使う
等に書いてあるとおりです.
package.jsonに以下のような設定をします.(nuxt.js仕様)


{ ...
 "scripts": {
    ...
    "build:stag": "cross-env NODE_ENV=\"staging\" nuxt build",
    "build:prod": "cross-env NODE_ENV=\"prod\" nuxt build",
    ...
  },
  ...
}

これで、

yarn build:stag

を実行すればstaging用の変数を用い、

yarn build:prod

を実行すればproduction用の変数を用いてbuildが走ります.

5. firebaseへのhostingの設定

buildしたものをfirebaseにhosting出来る設定をしていきます.
この手順は
Firebase Hosting でWebサイトを公開する方法
こちらの記事などで詳しく解説されていますので、参考にしながら進めると良いと思います.
本記事では簡易的に記述します.

staging用のプロジェクトと、prodcution用のプロジェクトがあるので、両方にエイリアスをつけて設定していきます.

firebase use --add 

で、staging用のプロジェクトをstag,
prodcution用のプロジェクトをprodとして登録します.
これで、例えばstaging用にdeployしたい場合は、

firebase use stag
firebase deploy

で実行できます.

ここまでの動作確認

これで、手動であればprodcutionとstagingを分ける事ができます.
手順としては、
1. 任意の環境用にbuildする.
2. firebaseのプロジェクトを切り替える
3. deployする

となります.
例えば、staging環境であれば

yarn build:stag
firebase use stag
firebase deploy

を実行すればOKです.
ここまで手動で想定通りの動きになるか試してみましょう.
うまく行けば、これらをCircleCI上で実現できるようにします.

6..envに記述した値の移植

2.で作成した.envファイルは、gitの管理化には入れないため、CircleCI上の環境変数に必要な値を登録し、build時に.envファイルを作成できるようにします.
具体的には、2.で記述したキーと値のセットをそのままCircleCI上のプロジェクトの環境変数に登録します.

7. deployのためのキーの設定

CircleCI上からfirebaseのhostingにdeployするためには、キー等を設定する必要があります.
この手順にも、参考になる記事があるのでそちらを参考に進めてください.
Circle CIからFirebase Hostingに自動ビルド&デプロイする
上記記事に従って、

firebase login:ci

により得られたキーをCircleIC上の環境変数に設定します.
但し、今回はprodcution用とstaging用の2つがあるので

firebase use prod
firebase login:ci

で得られたキーをFIREBASE_TOKEN_PRODとし、

firebase use stag
firebase login:ci

で得られたキーをFIREBASE_TOKEN_STAGとして登録してください.

8. .circleci/config.ymlの記述

最後にcircleCIの設定ファイルを記述していきます.
基本的には、先程手動で行った、

yarn build:stag
firebase use stag
firebase deploy

を実行するような設定をすればOKです.
変更点は、
- 最初にcircleCIの環境変数から.envを書き出す必要があること
- firebaseコマンドのパスを相対パスで指定すること
- deploy時にキー等を指定すること
の3点です.

必要な部分だけを記述したものが以下になります.

version: 2.1
orbs: #CircleCIが準備したnode用の設定を元として使う
  node: circleci/node@1.1.6

commands:
  make_env: #.envを書き出すコマンドをまとめておく
    steps:
      - run:
          name: make .env
          command: |
            echo "FIREBASE_API_KEY_PROD=$FIREBASE_API_KEY_PROD" > .env
            echo "FIREBASE_AUTH_DOMAIN_PROD=$FIREBASE_AUTH_DOMAIN_PROD" >> .env
            echo "FIREBASE_DATABASE_URL_PROD=$FIREBASE_DATABASE_URL_PROD" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_PROD=$FIREBASE_MESSAGING_SENDER_ID_PROD" >> .env
            echo "FIREBASE_PROJECT_ID_PROD=$FIREBASE_PROJECT_ID_PROD" >> .env
            echo "FIREBASE_STORAGE_BUCKET_PROD=$FIREBASE_STORAGE_BUCKET_PROD" >> .env
            echo "FIREBASE_API_KEY_STAG=$FIREBASE_API_KEY_STAG" >> .env
            echo "FIREBASE_AUTH_DOMAIN_STAG=$FIREBASE_AUTH_DOMAIN_STAG" >> .env
            echo "FIREBASE_DATABASE_URL_STAG=$FIREBASE_DATABASE_URL_STAG" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_STAG=$FIREBASE_MESSAGING_SENDER_ID_STAG" >> .env
            echo "FIREBASE_PROJECT_ID_STAG=$FIREBASE_PROJECT_ID_STAG" >> .env
            echo "FIREBASE_STORAGE_BUCKET_STAG=$FIREBASE_STORAGE_BUCKET_STAG" >> .env
            cat .env

jobs:
  deploy_stag: # staging環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13" # 念の為バージョン(正確にはdockerのtag)を指定しておく
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use stag
      - run:
          name: build
          command: yarn build:stag
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_STAG  --token=$FIREBASE_TOKEN_STAG

  deploy_prod: # prodction環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13"
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use prod
      - run:
          name: build
          command: yarn build:prod
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_PROD --token=$FIREBASE_TOKEN_PROD

workflows:
  deploy:
    jobs:
      - deploy_stag:
          filters: # stagingブランチの場合はstaging環境へのデプロイ
            branches:
              only: staging

      - deploy_prod:
          filters: # releaseブランチの場合はrelease環境へのデプロイ
            branches:
              only: release

これで、
stagingブランチにpushされた場合はdeploy_stagが、
releaseブランチにpushされた場合はdeploy_prodが実行されるはずです.
意図したとおりに挙動していれば無事終了です.

最後に

これがベストなやり方かどうかは分かりませんが、一応このやり方で特に問題は無いと思います.
方針さえ立てばそれぞれの手順自体は難しくないはずです.
やってみれば大したことないですが、自分はどういう方針で行くか結構手探りで進めたので、この記事が他の誰かのお役に立てれば幸いです.

また、firestoreのルールに関してCircleCI上で走らせるのも少しだけ手間取ったりしたので自分の備忘録のためにもそのうち書きたいと思ってます.

参考

環境変数設定は基本cross-envだけどたまにenv-cmdも使う
Firebase Hosting でWebサイトを公開する方法
Circle CIからFirebase Hostingに自動ビルド&デプロイする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FirebaseとCircleCIを用いたWebアプリ開発でproductionとstagingを分ける

初めに

筆者は趣味で個人開発してるだけの学生なので、いわゆる実務におけるstagingとはちょっとニュアンスが違うかもしれません.
firebaseを用いた個人開発で、「本番環境と限りなく同じ環境だが、本番環境とは分離されているもの」くらいの感覚です.

背景

  • Vue.jsを用いたWebアプリ(Vue.jsに限った話では無いと思うが念の為)
  • FirebaseのFirestoreをDBとして用い、Firebase上にHostingがしたい.
  • CI/CDにはCircleCIを用いる.

やりたいこと

  • Production環境とStaging環境で接続するDBやHosting先をいい感じに分けてほしい.

ざっくりとした方針

  • Production環境とStaging環境でそもそもFirebase上のプロジェクトを分離し、prodcutionブランチにpushされたときはProduction環境に、 StagingブランチにpushされたときはStaging環境に接続するように設定する.
  • Production環境とStaging環境の両方のAPIキーなどをCircleCI上の環境変数として定義& .envファイルに書き出すように設定
  • build時はNODE_ENVの値により.envから読み出す値を分ける.
  • deploy時はfirebase use {project}でプロジェクトを指定した上でデプロイする.

なお、hostingのみの場合はわざわざプロジェクトを分けたりする必要はありません.
しかし、firestoreはプロジェクトごとに1個しか使えないので、prodcutionとstaging等で分けたい場合はそもそものプロジェクトから分ける必要があります.

前提

  • GithubにPushしたらCircleCIでjobが走るところまでは設定できているとします.

手順

1.Firebase上にプロジェクトを2つ作成する.

production用のプロジェクトと、とStaging用のプロジェクトを2つ作成します.
便宜上以下のふたつを作成したとします.
- MyProjectProd
- MyProjectStag

2. それぞれのAPIキー等を.envに登録する.

本記事ではdotenvを用いて環境変数を読み出します.
最終的には.envはCircleCI上で自動的に作られるようにするので、この手順は飛ばしても構いません.
念の為ローカルで動作確認用に作成しているだけです.
まず、dotenvをインストールしてなければインストールしてください.

yarn add dotenv

project直下に.envを作成してください.

FIREBASE_API_KEY_PROD=****************
FIREBASE_AUTH_DOMAIN_PROD=****************
FIREBASE_DATABASE_URL_PROD=****************
FIREBASE_PROJECT_ID_PROD=****************
FIREBASE_STORAGE_BUCKET_PROD=****************
FIREBASE_MESSAGING_SENDER_ID_PROD=****************

FIREBASE_API_KEY_STAG=****************
FIREBASE_AUTH_DOMAIN_STAG=****************
FIREBASE_DATABASE_URL_STAG=****************
FIREBASE_PROJECT_ID_STAG=****************
FIREBASE_STORAGE_BUCKET_STAG=****************
FIREBASE_MESSAGING_SENDER_ID_STAG=****************

_PRODがついている方にはMyProjectProdの値を、
_STAGがついている方にはMyProjectStagの値を、入力してください.
なお、これらの値はFirebase Console上から確認することが出来ます.

3.firebaseの初期化処理を記述する.

2で登録した値を用いてfirebaseの初期化処理を書いていきます.
使用しているフレームワークや構成によって記述する場所は違うと思いますが、筆者の環境(Vue.js+Nuxt.js)ではplugins/firebase.jsに記述しています.

if (!firebase.apps.length) {
  if (process.env.NODE_ENV === "production") {
    //Prodcution環境
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_PROD,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_PROD,
      databaseURL: process.env.FIREBASE_DATABASE_URL_PROD,
      projectId: process.env.FIREBASE_PROJECT_ID_PROD,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_PROD,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_PROD
    });
  } else {
    //それ以外(Staging環境,develop環境)
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_STAG,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_STAG,
      databaseURL: process.env.FIREBASE_DATABASE_URL_STAG,
      projectId: process.env.FIREBASE_PROJECT_ID_STAG,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_STAG,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_STAG
    });
  }
}

このようにprocess.env.NODE_ENVの値によって.envから読み取る値を変えています.

4. NODE_ENVの値を設定できるようにする.

NODE_ENVの値によって処理を変える記述が出来たので、次はNODE_ENVを設定できるようにします.
本記事では、cross-envを使用します.

yarn add cross-env

cross-env自体の使い方は
環境変数設定は基本cross-envだけどたまにenv-cmdも使う
等に書いてあるとおりです.
package.jsonに以下のような設定をします.(nuxt.js仕様)


{ ...
 "scripts": {
    ...
    "build:stag": "cross-env NODE_ENV=\"staging\" nuxt build",
    "build:prod": "cross-env NODE_ENV=\"prod\" nuxt build",
    ...
  },
  ...
}

これで、

yarn build:stag

を実行すればstaging用の変数を用い、

yarn build:prod

を実行すればproduction用の変数を用いてbuildが走ります.

5. firebaseへのhostingの設定

buildしたものをfirebaseにhosting出来る設定をしていきます.
この手順は
Firebase Hosting でWebサイトを公開する方法
こちらの記事などで詳しく解説されていますので、参考にしながら進めると良いと思います.
本記事では簡易的に記述します.

staging用のプロジェクトと、prodcution用のプロジェクトがあるので、両方にエイリアスをつけて設定していきます.

firebase use --add 

で、staging用のプロジェクトをstag,
prodcution用のプロジェクトをprodとして登録します.
これで、例えばstaging用にdeployしたい場合は、

firebase use stag
firebase deploy

で実行できます.

ここまでの動作確認

これで、手動であればprodcutionとstagingを分ける事ができます.
手順としては、
1. 任意の環境用にbuildする.
2. firebaseのプロジェクトを切り替える
3. deployする

となります.
例えば、staging環境であれば

yarn build:stag
firebase use stag
firebase deploy

を実行すればOKです.
ここまで手動で想定通りの動きになるか試してみましょう.
うまく行けば、これらをCircleCI上で実現できるようにします.

6..envに記述した値の移植

2.で作成した.envファイルは、gitの管理化には入れないため、CircleCI上の環境変数に必要な値を登録し、build時に.envファイルを作成できるようにします.
具体的には、2.で記述したキーと値のセットをそのままCircleCI上のプロジェクトの環境変数に登録します.

7. deployのためのキーの設定

CircleCI上からfirebaseのhostingにdeployするためには、キー等を設定する必要があります.
この手順にも、参考になる記事があるのでそちらを参考に進めてください.
Circle CIからFirebase Hostingに自動ビルド&デプロイする
上記記事に従って、

firebase login:ci

により得られたキーをCircleIC上の環境変数に設定します.
但し、今回はprodcution用とstaging用の2つがあるので

firebase use prod
firebase login:ci

で得られたキーをFIREBASE_TOKEN_PRODとし、

firebase use stag
firebase login:ci

で得られたキーをFIREBASE_TOKEN_STAGとして登録してください.

8. .circleci/config.ymlの記述

最後にcircleCIの設定ファイルを記述していきます.
基本的には、先程手動で行った、

yarn build:stag
firebase use stag
firebase deploy

を実行するような設定をすればOKです.
変更点は、
- 最初にcircleCIの環境変数から.envを書き出す必要があること
- firebaseコマンドのパスを相対パスで指定すること
- deploy時にキー等を指定すること
の3点です.

必要な部分だけを記述したものが以下になります.

version: 2.1
orbs: #CircleCIが準備したnode用の設定を元として使う
  node: circleci/node@1.1.6

commands:
  make_env: #.envを書き出すコマンドをまとめておく
    steps:
      - run:
          name: make .env
          command: |
            echo "FIREBASE_API_KEY_PROD=$FIREBASE_API_KEY_PROD" > .env
            echo "FIREBASE_AUTH_DOMAIN_PROD=$FIREBASE_AUTH_DOMAIN_PROD" >> .env
            echo "FIREBASE_DATABASE_URL_PROD=$FIREBASE_DATABASE_URL_PROD" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_PROD=$FIREBASE_MESSAGING_SENDER_ID_PROD" >> .env
            echo "FIREBASE_PROJECT_ID_PROD=$FIREBASE_PROJECT_ID_PROD" >> .env
            echo "FIREBASE_STORAGE_BUCKET_PROD=$FIREBASE_STORAGE_BUCKET_PROD" >> .env
            echo "FIREBASE_API_KEY_STAG=$FIREBASE_API_KEY_STAG" >> .env
            echo "FIREBASE_AUTH_DOMAIN_STAG=$FIREBASE_AUTH_DOMAIN_STAG" >> .env
            echo "FIREBASE_DATABASE_URL_STAG=$FIREBASE_DATABASE_URL_STAG" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_STAG=$FIREBASE_MESSAGING_SENDER_ID_STAG" >> .env
            echo "FIREBASE_PROJECT_ID_STAG=$FIREBASE_PROJECT_ID_STAG" >> .env
            echo "FIREBASE_STORAGE_BUCKET_STAG=$FIREBASE_STORAGE_BUCKET_STAG" >> .env
            cat .env

jobs:
  deploy_stag: # staging環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13" # 念の為バージョン(正確にはdockerのtag)を指定しておく
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use stag
      - run:
          name: build
          command: yarn build:stag
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_STAG  --token=$FIREBASE_TOKEN_STAG

  deploy_prod: # prodction環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13"
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use prod
      - run:
          name: build
          command: yarn build:prod
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_PROD --token=$FIREBASE_TOKEN_PROD

workflows:
  deploy:
    jobs:
      - deploy_stag:
          filters: # stagingブランチの場合はstaging環境へのデプロイ
            branches:
              only: staging

      - deploy_prod:
          filters: # releaseブランチの場合はrelease環境へのデプロイ
            branches:
              only: release

これで、
stagingブランチにpushされた場合はdeploy_stagが、
releaseブランチにpushされた場合はdeploy_prodが実行されるはずです.
意図したとおりに挙動していれば無事終了です.

最後に

これがベストなやり方かどうかは分かりませんが、一応このやり方で特に問題は無いと思います.
方針さえ立てばそれぞれの手順自体は難しくないはずです.
やってみれば大したことないですが、自分はどういう方針で行くか結構手探りで進めたので、この記事が他の誰かのお役に立てれば幸いです.

また、firestoreのルールに関してCircleCI上で走らせるのも少しだけ手間取ったりしたので自分の備忘録のためにもそのうち書きたいと思ってます.

参考

環境変数設定は基本cross-envだけどたまにenv-cmdも使う
Firebase Hosting でWebサイトを公開する方法
Circle CIからFirebase Hostingに自動ビルド&デプロイする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む