- 投稿日:2019-02-03T23:37:52+09:00
VueとFirebaseの基本機能全部使ってぬるぬる動くポートフォリオサイトを作ったのでソースと解説
絵描きとかUXとかやりつつフロントもやってる「ゆき」です。ポートフォリオサイトは10年くらい前にMoveableTypeで作ったきり。最近流石に「これでフロントやってますとか言ったら絶対次転職できなくね?」と危機を感じたので0から作り直しました。
サイト: https://pf.nekobooks.com/
ソース: https://github.com/yuneco/portfolio
機能・性能・運用を考えて作った結果、VueとFirebase(Web)の機能を一通り使ったサイトが出来上がりました。これからちょっと凝ったポートフォリオサイトを作りたい方向けに、どういう目的でどの機能を使ったのか、その時のポイントはなんだったのかを共有します。
今回の要件(=ぼくのかんがえたさいきょうのポートフォリオ)
デザイン
- レスポンシブ!スマホ・PC両対応
- 全画面でぬるぬる動くアニメーション
Webデザイン自体は専門ではないのですが、絵やUX系もやっているので見た目はがんばりたい。。
機能
- かわいい・動くトップページ
- 絵を載せるギャラリーページ
- アプリ紹介とスキルセット書くページ
- コンタクトフォーム(メールで飛ばす)
- Webから新しい絵を追加できる管理ページ
- ギャラリーページは作品ごとにOGP対応すること
基本的には絵とアプリの紹介を載せるサイトです。
以前のポートフォリオサイトはFlickrに載せた絵をAPIで引っ張って来ていたのですが、そもそもFlickrに絵をアップするのが面倒になってしまった(というかパスワードを毎回忘れる)ので今回はサイト自体に管理者ページを作ります。性能
- iPhone6でもぬるぬる動く。目標60fps
- とにかく軽く。目標ロード時間 < 1s
私見ですが、エンジニアのポートフォリオサイトに軽さは超重要だと思ってます。
プラグイン盛り盛りとか背景に動画使ったりとかは作る身としても避けたい。やらないこと
- IE対応
だってそういう仕事したくないじゃないですか
Firebaseの機能と作ったものの対応
使ったFirebaseの機能 作ったポートフォリオサイトの機能 Authentication サイト管理者機能のログイン Database ギャラリー画像の一覧・メタデータ管理 Storage ギャラリー画像とサムネイルのアップロード先 Hosting デプロイ先・独自ドメインの接続 Functions サムネイルの自動生成・OGP生成・メール送信 作ったものと解説
ここから、作ったものの機能ベースでVueやFirebaseのどんな機能を使ったのか紹介します。GitHubで公開したソースのリンクもちょこちょこ貼るので、詳細はソースをご参照くださいませ
![]()
Vue.jsのみでトップページ・アニメーション
トップページはSVGを使った全画面のアニメーションになっています(というか元々はこれが一番やりたかった)。固定のアニメーションではなく、女の子のキャラクターが時々アクロバティックに飛んだり跳ねたり、あとギャラリーページで絵を選ぶと、絵の配色に合わせてアニメーションの配色も変わるようになってます。
自己満って言ってしまえばそれまでなのですが、ポートフォリオサイトは仕事ではなかなかできない技術や表現をこだわれる場所なので、こういうのも大切だと思うのです。アニメーションの種類とライブラリは何を使うか?
この手のアニメーションを作りたいと思った時の選択肢は色々あるので私見でまとめました。
アニメーションの種別 ライブラリ例 Pros(つよみ) Cons(つらみ) Canvasアニメーション Pixi.jsとかCreate.jsとか WebGLを使えばぬるぬる動かせる。要素が増えても強い。エフェクトやフィルタが豊富 描画面積が負荷に直結するので全画面になると辛い。レスポンシブとRetinaの対応もちょっと辛い。 SVGアニメーション Snap.jsとかSVG.jsとか リソースが軽い(ただしこれはCanvasでもできる)。流行りの流体シェイプみたいにぬるぬる動くデザインができる 流体シェイプ含めSVG固有のアニメーションは重くなりがち(多分GPUレンダリングが効かない) DOM/CSSアニメーション jQueryとかAnime.js1とか 普通のWebのテクニックが使える。アニメーション以外の機能・要素と相性がいい。GPUが効けば速い 要素が増えると重くなりやすい。GPUレンダリングの効くアニメーションは限定的 今回は熟慮の結果
「SVG・CSSアニメーションを?Vue.jsだけで?作る」
にしました。つまり、Vue.js以外のアニメーションライブラリは使っていません。理由は「圧倒的な軽さ」。今回のアニメーションはサイトの中ではあくまで背景なので、このアニメーションのためにロード待ちになったりスマホがカイロになる事態は避けないといけません。
どうやったか
実装の解説はここではしませんが、面倒なCSSトランジションをプロパティとして扱えるコンテナコンポーネントを作ってアニメーションを構成していきます。
コンテナコンポーネント:
/src/components/anime/core/ECont.vue
要約すると↓こういうコンポーネントです。シンプル![]()
<template> <div @click="clicked" :style="{ transformOrigin: `${ox}px ${oy}px`, transform: `translate3d(${x}px, ${y}px, ${z}px) scale(${s}) rotate(${r}deg)`, transition: ` transform ${dur}ms 0s ${easing}, transform-origin ${dur}ms 0s ${easing}` }"> <slot></slot> </div> </template>これを組み合わせてキャラクターのコンポーネントを作ります。
( https://pf.nekobooks.com/cnfy でデバッグ画面を実際にさわれます)
もうAnimateCCつかえよ...って話もあるのですが。それでも今回はVueの力試しというのと、やっぱりコンポーネントになって
<!-- 首の角度15度・お辞儀の角度30度(※cnfyはこの子の名前です) --> <cnfy :headAngle="15" :bowAngle="30" />みたいに宣言的にかけるのは楽しい。しかもリアクティブに動かせる!Vueたのしい!?
....と、この辺りは書き始めると長くなるので、ニーズがあれば別な記事にまとめようと思います。最終的に、全てのアニメーションがCSSの
transform: translate3d(x, y, z) scale(s) rotate(r)とopacityプロパティのトランジションとしてレンダリングされるようにすることで、そこそこややこしいアニメーションでも60fpsが実現できました。管理(絵のアップロード)ページとギャラリーページ
楽しいアニメーションができたのでここからは真面目に管理画面を作ります。
Firebase Authでログイン
最初にFirebaseコンソールのAuth機能でgoogleログインを有効化します。
TwitterやFacebookと違って、googleログインであれば「有効にする」をポチッとするだけでおしまい。超楽。管理者用のページはfirebase Authを使ってgoogle認証します。
/src/admin/pages/ImgUploader.vueasync mounted () { this.user = await AdminApis.Auth.loginWithGoogle() }mountedで問答無用でログインに飛ばします。
ログインの実装はこのあたり参照してください。
/src/admin/api/AdminAuth.jsちなみに、FirebaseAuthを使ったログインの詳しい解説は↓こちらのページあたりが素敵です
Vue.js + Firebase を使って爆速でユーザ認証を実装するここでは単にgoogleで認証してもらうことだけが目的で「ログインしたユーザーが管理者かどうか」は判定しません。(クライアントサイドで動いている以上、画面側で判定をしたところでセキュリティ的には大した意味はないので)
正しい管理者かどうかは、DB(firestore)/ストレージ(Firebase storage)のセキュリティルールでチェックして弾きます。(もちろん、通常の利用者が使う画面であれば画面側でもちゃんと判定してあげてください)
allow read; allow write: if request.auth.token.email == '管理者のgmail';Firebase Storageにファイルをアップロード
画像のアップロードにはFirebase Storageを使います。
ソースはこのあたり↓/src/admin/api/ImageUploaderApi.js
/src/admin/pages/ImgUploader.vue#L111だいたいチュートリアル通りな感じなので問題ないかと。
Firebase Functionsでサムネイルとメタ情報を作成
Storageへのアップロードをトリガーに、サムネイルとメタデータ(タイトル・縦横サイズ・配色情報...etc)を生成する処理をサーバ側で走らせます。このあたりもクライアントで生成してアップロードする方法もあるのですが、将来的にTwitterの自分の投稿から絵を拾ってギャラリーに追加したい、という野望があったので、今回はFunctionに登場してもらいました。
(そうでなくても、不整合を避けたいトランザクション的な処理はサーバ側にまとめておいた方が安全ではあります)ソースはこの辺り↓
/functions/index.js#L13
functions.storage.object().onFinalize(callback)の形でコールバックを登録します。サムネイル作成処理の本体はここ↓
/functions/src/generateThumbnail.js#L98
元画像・サムネ共にこのタイミングでキャッシュを有効にしておきます。忘れるとバズった時にあっさり無料枠を使い果たすはずなので注意。公式のサンプル(Automatically Generate Thumbnails)とほぼ同じですが、ImageMagicを起動する代わりにCanvasに画像を読み込んでJavaScriptで縮小・jpeg生成まで全部やっています。このあたりはお好みで(こっちの方が軽いかなぁ...って思ったけどそんなに変わらない?)。
サムネ生成に続けて、DBにメタ情報を書き込みます。
Cloud Firestoreにサムネと元画像両方のパス・サイズ・画像のメインカラーを保存します。画像を扱うアプリであれば、画像ロード前にレイアウトを確定させるために画像のサイズは是非とも保存しておくべきです。今回は色情報も保存しておいて色付きのプレースホルダーを表示できるようにしています。一枚高々数十KBのサムネですが、モバイルでのUX改善のためには有効な方法です。Cloud Firestoreでメタ情報の更新と画像URLの書き込み
DB書き込み後、管理画面側でDB更新をトリガにメタ情報の更新画面を表示します。タイトルや画像の説明(今の所画面には表示していませんが)を編集するのと、画像のダウンロードURLを生成するのが目的です。
本来であればStorageにアップした画像のダウンロードURLは、一つ前のFunctionsの中で生成してDBに保存すべきなのですが、これが今の所これが簡単にはできません。クライアント用のFirebase SDKでは一行ですむURL取得が、Functionsで使えるAdmin SDKだとサービスアカウントを作って面倒なプロセスを踏まないといけないようです。。
詳細は下のStackOverFlowの回答が詳しいです。一応簡単に生成するための逃げ道はあるようですが、正攻法ではないので今回はパスしました。
Get Download URL from file uploaded with Cloud Functions for Firebase
Cloud Firestoreからギャラリー画像のメタデータを取得
公開(非管理者)ページに戻ってギャラリーページを作ります。
ギャラリーはこのサイトのメイン機能の一つなので、トップページを表示した時点でCloud Firestoreからデータを取得します。
/src/api/ImgListApi.js#L14今の所数十件なので全件まとめて取ってきていますが、もし数百になるならページングを考えるべきかもしれません。
また、DBのロードが終わったら順次サムネイルのプリロードも走らせています。VueRouterによる個別のギャラリー画像へのリンク対応
やっぱり新作をUPしたらその絵に直リンクしたいですよね。GAでログを取るためにも、絵一つ一つにURLでアクセスできる必要があります。
こんな感じ↓のURLで個別の絵にURLでアクセスする仕組みを作ります。
https://pf.nekobooks.com/gallery/1547483353992_933アクセスの制御はVueRouterで画像のIDをパラメータとして受け取って、そのIDと画像リストの選択項目を連動させます。ただ、それだけではアプリ内のリンクは動作しますが、URL直リンクで飛んできた時にはうまく動きません。VueRouterから画像のIDを受け取った時点ではまだDBのロードが終わっていないからです。
今回はDBロードをwatchsで監視して、ロードされたデータがセットされたタイミングで連動を走らせることで対処します。
/src/pages/GalleryView.vue画像のグリッドレイアウトをVueでがんばる
色々プラグインはあるようですが、今回は普通にVueコンポーネントで手書きします。普通に座標を計算して
position: absoluteでdivを並べているだけです。(CSS的にはグリッドレイアウトを使うべきなのかもですが、今回は選択やリサイズ時にぬるぬる動かしたかったのでパス)
/src/components/PhotoList.vue仕事だとなかなか難しい場面も多いですが、この程度のものであればプラグインやたらと組み込むよりも手書きした方が細かい調整利くし勉強にもなりますね。
Firebase Functionsでメールフォームを作る
今日日メールとかいらなくない?という話もあるのですが、まあお約束ということで。
画面は手抜き感満載ですが許してください。。FirebaseFunctionsでメールを送信
メール送信にはFunctionsを使ってWebAPIを作ります。
exports.contactmail = functions.https.onRequest(callback)のような形でFunctionを作るとWebからアクセスできる
contactmailという名前のFunctionができます。実際にアクセスするURLはFirebaseのコントールから確認できます。サーバ側のメール発信処理にはnodemailerを使います。こちらの記事が超絶丁寧です↓
VuejsとFirebaseでメール送信機能を実装するこの記事にだいたい書かれているので注意点だけ箇条書きすると、
- Firebaseの無料プランではgoogleの外へ通信ができないため、無料プランで頑張る場合はgmailを使う
- 発信に利用するgmailのアカウントはセキュリティレベルを落とす必要あり。Firebaseのログインやサイトの管理者アカウントとしてセキュリティルールに設定したものとは別のアカウントを利用すべきです(gmailで転送設定をすれば好きなアカウントで受信できます)
- ID/パスワードはソースに直書きせずに環境変数を利用すること
環境変数は
firebase functions:config:set admin.contact.mail="メアド" admin.contact.pass="パス"を(ローカルの)コマンドラインで叩けば、Functions側から
const config = functions.config() const mail = config.admin.contact.mail const pass = config.admin.contact.passのように簡単に取得できます。
あと今回はメールの送信先が完全固定なので大丈夫ですが、送信先もなんらかの条件で動的に変更する合、Functionsの呼び出しパラメータをいじって任意のアドレスにメールを飛ばせてしまうことのないよう、厳重にチェックが必要です。スパムメールの踏み台やなりすまし詐欺に悪用されてしまったら目も当てられないので気をつけましょう(こわい)。
Firebase Hosting で独自ドメイン公開
Firebase Hostingで公開するサイトはデフォルトでは
https://プロジェクトID.firebaseapp.comのドメインで公開されます。これだけでも十分ありがたいのですが、やはりポートフォリオサイトなので独自ドメインを使いたいですよね。FirebaseコンソールのHosting画面から「ドメインを接続」を押すと、自分の所有するドメイン・サブドメインをFirebaseのサイトにつなぐことができます。ドメインをどこでとったかにもよるのですが、大体同じような流れです。↓こちらの記事はお名前.comの場合のやり方を解説してくれています。
Gatsby+firebaseで独自ドメインのHTTPSサイトを作る(その2 Firebaseの設定)
ギャラリーページのOGP対応
ギャラリーの絵ひとつひとつにURLでアクセスできるようにしたのでTwitterで新作を宣伝できるようにはなったのですが、やっぱりそこまでやったらOGPしたいですよね。
今回はギャラリーページは作品の画像とタイトルを使い、その他のページは一律のOGPとしました。
トップページ
ギャラリーページ(の個々の作品)
この部分のやり方は年末にアドベントカレンダーでかなり詳しく書いたので、手前味噌ですが↓こちらをご参照くださいませ。
SNS映えするWebアプリを...!FirebaseとVue.jsでSPAのOGP画像の動的生成をやってみたら案外楽だった
今回は手抜きのリダイレクト方式で、かつ画像も動的生成ではなくStorageにアップロード済みの画像のURLをそのまま返しています。性能評価と最適化
キャッシュなしでLoadまで1秒(1.6MB|1.1minってあるのはLoad後にギャラリーのサムネを裏でゆっくり読んでるから)。アニメーション用のSVGは全てapp.jsの中に含まれています。
管理者機能部分をチャンク分割していますが、他の最適化はそれほどやっていません(この辺りは勉強不足なのであまり語れない...どなたかこれでいいのか教えてください)。
VueRouter-遅延ローディングルート
Auditsでもかなりいいスコアが出てます。ただ、正直な実感としてはアクセシビリティやSEOにはあまり配慮していないのでこの点数は高過ぎ気がします。この辺りはVueのテンプレートが形だけよしなにやってくれてしまっているせいなのかもしれません。今後のタスク(備忘+自戒)
- AtomicDesign?なにそれ?みたいなコンポーネント分割をなんとかしたい
- ファビコンつくる
- ギャラリー画像のタイトル再編集・並べ順変更
- キャラの表情・衣装のバリエーションを増やす(趣味全開)
まとめ
最近はWebページを作るツールやサービスもどんどん進化していて、ちょっとしたサイトならノンコーディングでもそれなりにできる時代になってしまいました。そんな中でも最近の技術をキャッチアップしつつ自分で組んでみると中々勉強になるものです。特にデザイナさんやエンジニアさんであれば、トレンドを取り入れつつ自力でしっかり実装しているサイトは良いPRになるはず。この記事を見てポートフォリオサイト作ってみようかなー、って思ってくれる人がいたらとても嬉しいです。
ポートフォリオサイトをがっつり作るとVueとFirebaseの基本機能を一通り習得できるよ
全画面アニメーションするならSVGが軽くて最強!専用のライブラリを使わなくても、Vueだけで結構作れちゃう
エンジニアやデザイナーはみんなもっとオリジナルのポートフォリオサイト作ろう
ポートフォリオ作ったらブログとかQiitaとかに記事も書こう
SVGも使えるらしい ↩
- 投稿日:2019-02-03T22:53:18+09:00
Vue.jsでForm部品をcomponent化してStoreで管理
多様なFormモジュールが増えても対応できるように各部品をComponent化して、且つvalueの値をStoreのstateで管理したいと思い、実装しました
component化したのは下記部品です
- input type="text"
- textarea
- button type="submit"
開発環境
package.json"vue": "^2.5.22" "vuex": "^3.1.0"input type="text"
- componentの
InputText.vueは汎用性を持たすためpropsを元に展開Add.vueに$emitのイベント購読名(eventName="hogeTitle")を定義してInputText.vueにpropsで渡す- イベントハンドラ
titleの値をcommit()でStoreのstateにアクセスして渡す- inputのvalueが更新されるとStoreのmutationsでstateを更新
Add.vue<template> <div> <input-text name="hogeTitle" :model="hoge.title" placeholder="" eventName="hogeTitle" @hogeTitle="title" > </input-text> </div> </template> <script> import InputText from './form/InputText'; export default { data() { return { hoge: { title: '', }, }; }, methods: { title(value) { const data = { title: value }; this.$store.commit('addData', data); }, }, components: { InputText, }, }; </script>./form/InputText.vue<template> <div> <input type="text" :name="name" :value="model" :placeholder="placeholder" @input="updateValue" > </div> </template> <script> export default { props: { name: String, model: String, placeholder: String, eventName: String, }, methods: { updateValue(e) { this.$emit(this.eventName, e.target.value); }, }, }; </script>store.jsconst state = { add: { title: '', text: '', } }; const mutations = { addData(data, value) { state.add = Object.assign({}, { ...data.add }, { ...value }); }, }; export default { state, mutations, };textarea
input type="text"とほぼ同じ- 違うのはpropsに
rowsの値を入れてるだけstore.jsは割愛Add.vue<template> <div> <input-textarea name="hogeText" :model="hoge.text" placeholder="" eventName="hogeText" rows="10" @hogeText="text" > </input-textarea> </div> </template> <script> import InputText from './form/InputTextarea'; export default { data() { return { hoge: { text: '', }, }; }, methods: { text(value) { const data = { text: value }; this.$store.commit('addData', data); }, }, components: { InputTextarea, }, }; </script>./form/InputTextarea<template> <div> <textarea :name="name" :value="model" :rows="rows" :placeholder="placeholder" @input="updateValue" > </textarea> </div> </template> <script> export default { props: { name: String, model: String, placeholder: String, eventName: String, rows: String, }, methods: { updateValue(e) { this.$emit(this.eventName, e.target.value); }, }, }; </script>button type="submit"
- componentの
SubmitButton.vueは汎用性を持たすためpropsを元に展開Add.vueに$emitのイベント購読名(eventName="saveData")を定義してSubmitButton.vueにpropsで渡す- イベントハンドラ
saveでStoreで管理してたinputの値(state.add)をcommit()でStoreのstateにアクセスしてデータをpushAdd.vue<template> <div> <submit-button eventName="saveData" text="保存" @saveData="save" > </div> </template> <script> import SubmitButton from './button/SubmitButton'; export default { methods: { save() { this.$store.commit('saveData', this.$store.state.add); }, }, components: { SubmitButton, }, }; </script>./button/SubmitButton<template> <div> <button @click="save" type="submit">{{text}}</button> </div> </template> <script> export default { props: { eventName: String, text: String, }, methods: { save() { this.$emit(this.eventName); }, }, }; </script>store.jsconst state = { hoge: {} add: { title: '', text: '', } }; const mutations = { saveData(data, addData) { state.hoge.push(addData); }, addData(data, value) { state.add = Object.assign({}, { ...data.add }, { ...value }); }, }; export default { state, mutations, };参考サイト
- 投稿日:2019-02-03T18:12:42+09:00
vue.jsとfirebaseでサクッと認証周り実装
めんどくさい
認証周りの実装は楽しくないし、パスワードの再設定やらメールの設定やら考えることが結構多いです。
そこで、エンジニアらしく怠惰になるべく、firebaseさんに力をお借りすることにしました。お借りした機能
- ユーザーの作成
- ユーザーログイン
- パスワード再設定
- メール認証
実装
firebaseのプラグイン追加 参考
import firebase from 'firebase' const config = { apiKey: '', authDomain: '', databaseURL: '', projectId: '', storageBucket: '', messagingSenderId: '' } firebase.initializeApp(config) export default firebase↑のfirebaseを呼び出して使っていきます。
store/user.js
import firebase from '../plugins/firebase' export const user = { namespaced: true, state: () => ({ }), mutations: { }, getters: { isAuth (getters) { // メール認証するまで const user = firebase.auth().currentUser return user ? user.emailVerified : false } }, actions: { // ユーザー作成 async createUser ({commit, dispatch}, {email, password}) { const auth = firebase.auth() await auth.createUserWithEmailAndPassword(email, password) .then(async () => { await dispatch('confirmEmail') }) .catch(error => { dispatch('alertMessage', {errorCode: error.code, errorMessage: error.message}) }) }, async confirmEmail () { const user = firebase.auth().currentUser user.sendEmailVerification().then(() => { alert('確認メールを送信しました。メールアドレスをご確認ください') }).catch(error => { console.log(error) }) }, // パスワードの再設定 async sendPasswordResetEmail ({dispatch}, email) { const auth = firebase.auth() auth.sendPasswordResetEmail(email) .then(() => { alert('メールを送信しました') }).catch(error => { dispatch('alertMessage', {errorCode: error.code, errorMessage: error.message}) }) }, async login ({dispatch}, {email, password}) { firebase.auth().signInWithEmailAndPassword(email, password) .catch(function (error) { dispatch('alertMessage', {errorCode: error.code, errorMessage: error.message}) }) }, // アラートメッセージ alertMessage ({commit}, {errorCode, errorMessage}) { switch (errorCode) { case 'auth/wrong-password': alert('パスワードが違います') break case 'auth/invalid-email': alert('無効のメールアドレスです') break case 'auth/user-not-found': alert('ユーザーが存在しません') break case 'auth/weak-password': alert('6文字以上でパスワードを設定してください') break case 'auth/email-already-in-use': alert('すでに存在しているメールアドレスです') break default: alert(errorMessage) break } } } }storeにユーザーモジュールを追加し、作成しました。
storeを使い実装することで、vueファイルにfirebaseのコードを書かないよう努めました。
このuser.jsのコードをvueファイルから呼び出すだけで使用することができます。login.vue
※Vuetifyを使用しております。<template> <div> <v-layout align-center justify-center> <v-flex xs12 sm8 md4> <v-dialog v-model="dialog"> <v-card> <v-card-title class="title elevation-1">ログイン</v-card-title> <v-card-text> <v-form> <v-text-field v-model="email" label="学校メールアドレス" /> <v-text-field v-model="password" label="パスワード" /> </v-form> </v-card-text> <v-card-actions> <v-spacer></v-spacer> <v-btn @click="submit" class="white--text">ログイン</v-btn> </v-card-actions> </v-card> </v-dialog> </v-flex> </v-layout> </div> </template> <script> import { mapActions } from 'vuex' export default { data () { return { email: '', password: '' } }, methods: { ...mapActions({ login: 'user/login' }), async submit () { await this.login({email: this.email, password: this.password}) } } } </script>今回はログインの機能のみのvueファイルを書いています。
反響があれば、追記していきます。
ざっくり書いている部分が多いので、細かい解説や他のvueファイルなどはちょっとずつ追記していきます。終わり
firebase全く無知の状態からほんの数時間(他のユーザー作成などの部分含め)で、実装することができたので
実装コストがかからず本当に良いと思います。個人的によかったのは、ログインなどでエラーになった際に適切なエラーコードを返してくれるところです。
これのおかげでユーザーに適切なエラーメッセージを伝えることができます。エラーコードに関する詳細 → firebaseドキュメント
最後に
firebase最高。
- 投稿日:2019-02-03T16:55:53+09:00
ゼロからVue.jsを触ってみる 第5回
前回の続きでdirectives紹介をしていきます。
- 第1回目 - Vue CLIの導入、プロジェクトの作成、起動
- 第2回目 - Hello World、単一ファイルコンポーネントを作ってみる
- 第3回目 - 単一ファイルコンポーネントとは
- 第4回目 - Vue.jsの基本的な機能
- 第5回目
Directivesの続き
v-onDOMのイベントをlistenして、イベント発火時にメソッドをトリガーできます。
例<template> <p v-on:click="handleClick">Click me</p> </template> <script> export default { methods: { handleClick: function() { alert('test') } } } </script>この例の場合、Click meというところをクリックすると
handleClickというメソッドが発火してtestというアラートが表示されます。この
v-onにはショートハンドが用意されており、v-onと書く代わりに@で代用できます。ショートハンドで書き直した例<a @click="handleClick">Click me!</a>(Qiitaのシンタックスハイライトには対応してないっぽいですね。)
click以外にもイベントはあり、submitなどもよく使います。
Event Modifiers
v-onのイベントに対してEvent Modifiersというものが用意されています。例えば
.preventというEvent Modifierを使うと自動的にpreventDefault()を呼んでくれます。他にも
.stop.prevent.capture.self.once.passiveなどがあります。ここでは解説しません。詳細は公式ドキュメントを参考にしてください。
元のイベントオブジェクトにアクセスする
$eventという変数を使って元のイベントオブジェクトをメソッドに渡すことができます。例<template> <p @click="handleClick($event)">Click me</p> </template> <script> export default { methods: { handleClick: function(event) { console.log(event); } } } </script>上記の例ではClick meをクリックするとコンソールに
MouseEventのオブジェクトが表示されます。この
MouseEventオブジェクトに関していえば例えばShiftキーを押しながらのクリックかどうか?みたいなことだったり、ほかにもいろんな値が入ってるのでそういったイベント関連の情報を引っ張ってきて処理をしたいときに便利です。
v-show表示したり隠したりできます。値がTruthyなら表示、Falsyなら非表示(
display: none)になります。例<template> <p v-show="isThree(3)">3です</p> </template> <script> export default { methods: { isThree: function(num) { return num === 3; } } }; </script>上記の例では
isThree()の引数に3を与えるとTrueになって「3です」と表示されます。引数を3以外にするとFalseになって何も表示されません。
Directivesに関してはこれで大体一通り見たかな?と思うので今回はここまで。もっと細かいところは公式ドキュメントなどを参考にお願いします。
次回はそろそろDirectives以外を見ていきます。
参考
- 投稿日:2019-02-03T16:06:49+09:00
Vue.js 基礎 [クリックイベント/データバインド]
Vue.jsが気になっていたので、猫本を買ってきました。
しばらく自分のOUTPUTに記事を書いてこうと思います。クリックイベントとデータバインド
hoge.jsvar app = new Vue({ el: '#app', data: { show :true, message :'HelloWorld!!', }, methods: { handleClick: function(event) { if( app.show == false) { app.show = true; } else { app.show = false; } } } })hoge.html<div> <p v-if="show">{{message}}</p> <input v-if="show" v-model="message"> <button v-on:click="handleClick">button</button> </div>理解
- divタグのid"app"と、jsの"#app"が紐づく
- dataはモデル、これがバインドされる
- methodsはイベント、ここにクリックイベントとか書くらしい
ボタンを押すとラベルと入力フォームが消えたり表示されたりします。
入力フォームの値が、ラベルにバインドされているのでリアルタイムで連動します。バインドがWPFのMVVMよりわかりやすくてとても良い。
- 投稿日:2019-02-03T16:06:49+09:00
Vue.js 基礎 【クリックイベント/データバインド】
Vue.jsが気になっていたので、猫本を買ってきました。
しばらく自分のOUTPUTに記事を書いてこうと思います。クリックイベントとデータバインド
hoge.jsvar app = new Vue({ el: '#app', data: { show :true, message :'HelloWorld!!', }, methods: { handleClick: function(event) { if( app.show == false) { app.show = true; } else { app.show = false; } } } })hoge.html<div> <p v-if="show">{{message}}</p> <input v-if="show" v-model="message"> <button v-on:click="handleClick">button</button> </div>理解
- divタグのid"app"と、jsの"#app"が紐づく
- dataはモデル、これがバインドされる
- methodsはイベント、ここにクリックイベントとか書くらしい
- {{}}のことをMustacheという
ボタンを押すとラベルと入力フォームが消えたり表示されたりします。
入力フォームの値が、ラベルにバインドされているのでリアルタイムで連動します。バインドがWPFのMVVMよりわかりやすくてとても良い。
- 投稿日:2019-02-03T15:38:11+09:00
vue-cli3でbuildした時にTypeError: Cannot read property 'minify' of undefined
概要
vue-cli3で生成したばかりのプロジェクトでbuildが出来なかったので調べた所、直近出現したバグだったので日本語でも残しておこうと思います。
解決策
terser@3.4.0のバグなのでpackage.json"resolutions": { "terser": "3.14.1" }と記述して
npm installまたは
npm i -D terser@3.14.1とかでバージョンを下げれば大丈夫
参考
https://github.com/vuejs/vue-cli/issues/3407#issuecomment-459985313
- 投稿日:2019-02-03T12:45:03+09:00
自分用メモ(v-if、v-show)
- 投稿日:2019-02-03T04:56:13+09:00
VueLoaderPluginのせいでエラーが起きていると言われたら確認したいこと
概要
土曜日なので、vueはじめました。
はじめてのwebpackに戸惑いながらもSPAに挑戦しています。しかし、さっそくのビルドエラーに遭遇してしまいました。
なにやらvue-loaderが悪さをしているようです。ERROR in ./src/pages/About.vue Module Error (from ./node_modules/vue-loader/lib/index.js): vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.今回はわたしが解決した方法をご紹介します。
原因
https://vue-loader.vuejs.org/migrating.html
どうやらvue-loaderには最近著しい変更があり、v15からプラグインとして読み込んであげてねってことになったようです。ちなみに、自分の環境だとバージョンは
"vue-loader": "^15.6.2",。
まさにこれでした。解決方法
https://vue-loader.vuejs.org/migrating.html
以下のようにconstで定義して、module.exportsのなかのpluginsにぽこっとnewしてあげればいいようです。// webpack.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { // ... plugins: [ new VueLoaderPlugin() ] }私はこれで無事に解決できました。
もし、同じエラーが出た場合はお試しくださいね!おまけ
次はcss-loaderを解決しなくては..汗
今までgulp一本な人生だったので、webpackは不思議な感覚です。(かといって、gulpをそんなに高い頻度で使っているわけでもないw)
- 投稿日:2019-02-03T00:03:03+09:00
Onsen UI、Vue.jsでアプリを作る初めの一歩(開発→本番)
はじめに
初めの一歩ができると、わかる人はどんどん進んでいったりすると思っています。
ちなみにOnsen UIはCSSのライブラリっぽいです。
よろしくお願いします。Onsen UIのサイトはこちらから
以下記事の進行は
Onsen UI、Vue.js
を元に進めて行きます。
では早速!開始
ターミナルを開いて、Onsen UI + Vueプロジェクトの設定を簡単にするために、Vue CLIをインストールします。以下コマンド。
$ npm install -g @vue/cliインストールが終わるとプロジェクトが早速作れるようになっているので作りたい場所(フォルダ)まで移動して以下コマンドを打ちます。打つと質問されるので答えて行きましょう!!
最初の質問は私だけかな?通信のせい?とりあえずnにしました。わら
ここでの[my-onsen-app]は作るプロジェクトのフォルダ名になります。
ちなみに名に大文字入れるとエラーになります$ vue create my-onsen-app
? Your connection to the default npm registry seems to be slow. Use https://registry.npm.taobao.org for faster installation? (Y/n)n 上記、デフォルトのnpmレジストリへの接続は遅いようです。より速いインストールのためにhttps://registry.npm.taobao.orgを使用しますか? と聞かれているのでnをうって断りました。
Vue CLI v3.4.0 ? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select features ここでは、デフォルトの設定か自分でカスタマイズされているかなので、最初は上記のdefaultでいいと思います。そのままエンターで。
っとするとプロジェクトの生成が始まります!少し待ちましょう!
そして終了すると以下のようなフォルダ構成になります。treeで全部表示しようと思いましたがあまりにもフォルダがありすぎてちょっとあれだったので割愛します。できたプロジェクトに移動してください。以下コマンド
$ cd my-onsen-app以下コマンドで今度はonsenuiとvue-onsenuiをプロジェクトにインストールします。
$ npm install onsenui vue-onsenui次に以下画像にある、main.jsをお好きなエディタで開いてください。
開くと以下のようになっているので書き足していきます。
main.jsimport Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')書き足すと以下のようになると思います。
要は、インストルしたcssを読み込み、VueファイルでOnsen UIを使うようにしたということですね。main.jsimport 'onsenui/css/onsenui.css'; import 'onsenui/css/onsen-css-components.css'; import Vue from 'vue' import VueOnsen from 'vue-onsenui'; import App from './App.vue' Vue.config.productionTip = false Vue.use(VueOnsen); new Vue({ render: h => h(App), }).$mount('#app')次に、以下画像のApp.Vueをお好きなエディタで開いてください。そのファイルに以下内容をコピペしてください。
App.Vue<template id="main-page"> <v-ons-page> <v-ons-toolbar> <div class="center">Title</div> </v-ons-toolbar> <p style="text-align: center"> <v-ons-button @click="$ons.notification.alert('Hello World!')"> Click me! </v-ons-button> </p> </v-ons-page> </template>全て終わったら以下コマンドで開発環境を立ち上げます。
$ npm run serve終わると、http://localhost:8080/ で開いて見てねと書いてあるので貼り付けて見てみましょう!!以下の画像が出て、[Click me!]タップでアラートが出ます。
で、色々開発で試したあとは本番にのせたいですよね!
まず一回ローカル環境を閉じるため、macならControl+Cを押します。そしたら以下コマンドを打ちます。$ npm run buildと以下のように、distというフォルダができるのでそのフォルダの中身を公開するところに一式おいてあげればいいです。
ちなみに、デフォルトの設定だとエラーが出る可能性があります。エラー解決は以下リンクからお願いします。
TypeError: Cannot read property 'minify' of undefinedで、ここでそのあげるのに何使えばいいのかなという方に朗報です!!
簡単にWebアップまでの記事を以下で書いたの是非!!先ではstaticフォルダとindex.htmlだけのところをここのファイル一式に変えればOKです!!FirebaseにてWeb(https、無料)を公開してみるをやる
参考
ここのサイトはVue.jsについてとてもわかりやすそうなサイトです!!
https://www.monster-dive.com/blog/web_creative/20180608_001789.php

















