- 投稿日:2020-11-20T21:57:27+09:00
ショートサーキット?何それ、美味しいの?という話
あらすじ
Vue.js入門で、ナビゲーションガードをかける実装(10章)に取り組んでいたが、以下のコードで特に矢印の部分が気になった。
※今回の記事で使っている、Vue、Vuex、ナビゲーションガードなどは解説しません
export const authorizeToken= (to,from,next) => { if(to.matched.some(record => record.meta.requiresAuth)) { if(!store.state.auth || !store.state.auth.token){ ⇦ これ next({ path:'/login' }) // ログイン画面に飛べ } else { next()} // でなければ次のステップに進め } else { next() } }こちら。
if(!store.state.auth||!store.state.auth.token){Vuex storeのstateオブジェクトの中身はこちら
Vue.use(Vuex) //ログイン認証用のVuex const state = { auth: { //状態`Auth` token: null, //`token`はnullで初期化 userId: null //`userId`はnullで初期化 }, export default new Vuex.Store({ state, getters, actions, mutations, strict: process.env.NODE_ENV !== 'production' })stateオブジェクトの中には、認証tokenとuserIdのセットが入った、authオブジェクトが入っている。これを踏まえて下記の文は、こう読める(直訳)。
if(!store.state.auth||!store.state.auth.token) { } // authオブジェクトが有効でないならば(オブジェクトが存在しなければ)、 // もしくは // authオブジェクトのtoken属性が有効でないならば((値が存在しなければ)) // { }以降を実行せよ(ログイン画面に飛べ)やりたいことは、ナビゲーションガードだから、「別に両方書かなくても、
!store.state.auth.token
だけで良くない?」と思ったので、先輩エンジニアに聞いてみると「いや、これが正しい」ということだった。どういうこと?小さなものからパフォーマンスを改善させろ
先輩いわく「アプリケーション開発で必要なのは、いかにパフォーマンスを上げる工夫ができるか?」とのこと。確かに、先の文で言えば、
if(!store.state.auth.token){}
でもプログラムは動くのだが、論理演算子の特性を活かし、左側を先に書いておけば、プログラムの処理は早くなるとのこと。この、論理演算子の特性の1つが、ショートサーキット。ショートサーキットって?
左オペランドのみ評価し、右オペランドを評価しないこと。
&&演算子と | | 演算子は、左オペランドを先に評価する。
その際左オペランドが、&&演算子はfalse、 | | 演算子はtrueの場合には、右オペランドを評価しない。つまり「右オペランドの評価を省略する」ため、これを「ショートサーキット」「短絡評価」と言う。また&&演算子と | | 演算子を「ショートサーキット演算子」「短絡演算子」とも言う。
引用:http://www.kab-studio.biz/Programing/JavaA2Z/Word/00001028.html
要は、左側の式を先に評価するという仕組みを活かし、今回のケースで言えば「そもそもauthオブジェクトの中身まで見るまでもなく、authオブジェクトそのものが無効なら(有効でないならば)」と言っておけば、
||
演算子はTRUE
(!
は否定の演算子)を返すので、右側をプログラムは見なくていい、ということだったんですね。先輩からは、「こういう行間というか、意図を正しく解釈できないとプログラムを正しく書き直すことができないから、細かいけどこういう所も含めてきちんと理解して」とお叱り頂きました?
補足(2020年11月23日)
今回扱ったこちらのコードに関して、
if(!store.state.auth||!store.state.auth.token) { }こちらを、下記のように変えるべきでは?とのご指摘を頂きました。
if(!isObject(store.state.auth) || isNull(store.state.auth.token)) { }これだと、authはオブジェクトなのかそうでないのかが即座に理解でき、
token
がnull
かそうじゃないのかを判定しているとすぐわかります。では、なぜ、そこまで書き方を工夫する必要があるのでしょうか?理由は「単に制御構文を書くんじゃなく、プログラマーが一発で何を制御させたいのかわかりやすく明記することで、想定されるバグを未然に防ぎやすくなる」ためです。
今回のケースで言うと「JavaScriptで制御構文を書く時に、False属性で判定させると、それが不具合の温床になりかねない」という点がポイントです。JavaScript の言語仕様として、
- 条件分岐は、True or False属性で分岐する
false
も0
も""(空文字)
もnull
もundefined
もFalse属性
であるif (!store.state.auth) {}
は、if (store.state.auth == false) {}
と同じ
- ※
==
は、JavaScript では勝手にデータ型を変換してしまうので、0
でも""
でもFalse
と判定されてしまうというのがあります。これらのことから、元のコードの場合だと、プログラマーは「authに配列が入ってきたとき、あるいは文字列が入ってきたときに、エラーにならないな?」とか「auth.tokenが、nullじゃなくて、0が入ったときも、動作するんだろうな、、、」など色々考えないといけなくなります。
こういう、プログラマーに考えさせる負担を少しでも減らして分かりやすく表現する力をつけることも、プログラマーとして必要なのだと学びました。@standard-softwareさん、ありがとうございました。
まとめ
他人のコードを読む時に「あれ?なんでこんな風に書いているんだろう?」と考えるシーンが出てくると思いますが、「どんな工夫をしているんだろう?」と想像しながら読んでみると、コードリーディングが楽しくなるのかな〜と思いました。あとは、できる人のプログラムをたくさん読んで、引き続き勉強していきます?
参考
下記2点追加(2020年11月23日)
- JavaScript の == と === の違いを詳しくまとめてみる
- Quora プログラミングでは、より短いコードで同じ目的を達成した方が、優れているのですか?
- 投稿日:2020-11-20T16:39:49+09:00
デフォルト引数が素敵【2020/11/20】
javascriptでは、デフォルト引数が設定できる
ある条件がtrueの時だけ、モデルを呼び出して処理をしたい
- メソッド側
model.method(data, true) model.method(data)
- モデル側(model.js)
method(data, isHTML = false) { if(isHTML) { console.log(data) } }とすれば、メソッド側でtrue/falseの分岐を一々書かなくても、必要なものにtrueを追加で引数に入れるだけで良い。
コードがシンプルになる。参照: MDN-Docs
- 投稿日:2020-11-20T15:16:30+09:00
FirebaseAuthentication + Vue.jsでシンプルなログイン認証を実装する
前置き
Vue.jsとFirebaseAuthでどシンプルなログイン機能をつくってみました。素人につき解説できることがほぼないためサンプルコードと画像マシマシかつ3ステップ構成でお送りします。なお今回はVue-CLIとVue-Routerを使用してSPAプロジェクトの雛形が用意されている前提で作成を進めていきます。
STEP1
STEP1では各画面の作成をします。今回新規に作成するのは会員登録画面とログイン画面の2つです。デフォルトのホーム画面とアバウト画面は認証が必要なページとしてそのまま流用することにします。
Bootstrap-vueの導入
では早速実装に、といきたいところですがその前にBootstrapを導入します。見た目は大事です。ターミナル(orコマンドプロンプト)で下記のコマンドを実行します。
cd xxxxx #Bootstrapを適用したいプロジェクトへ移動 npm install bootstrap-vueyarnの場合は、
yarn add bootstrap-vueでオッケーです。
インストールが完了したら、src/main.jsに下記の記述を追加してBootstrapをテンプレート内で使用できるようにします。import Vue from 'vue' import App from './App.vue' import router from './router' import 'bootstrap/dist/css/bootstrap.css' // 追加 import 'bootstrap-vue/dist/bootstrap-vue.css' // 追加各画面の作成
では改めて各画面を作成していきましょう。まずは会員登録画面とログイン画面です。ファイルはsrc/viewsの配下に設置します。
新規登録画面(src/views/Signup.vue)<template> <div class="container"> <h2>新規会員登録</h2> <div class="row d-flex flex-column align-items-center justify-content-center"> <div class="col-md-4"> <div class="form-group text-left"> <label for="email">メールアドレス:</label> <input type="text" class="form-control" id="email" v-model="email"> </div> <div class="form-group text-left"> <label for="password">パスワード:</label> <input type="password" class="form-control" id="password" v-model="password"> </div> <div class="form-group text-left"> <label for="confirm-password">確認用パスワード:</label> <input type="password" class="form-control" id="confirm-password" v-model="confirmPassword"> </div> </div> <div class="col-md-4 text-right"> <button @click="userSignup" class="btn btn-primary my-3">登録</button> </div> <div class="col-md-4 text-center text-danger"> {{ errorMessage }} </div> </div> </div> </template>ログイン画面(src/views/Signin.vue)<template> <div class="container"> <h2>ログイン</h2> <div class="row d-flex flex-column align-items-center justify-content-center"> <div class="col-md-4"> <div class="form-group text-left"> <label for="email">メールアドレス:</label> <input type="text" class="form-control" id="email" v-model="email"> </div> <div class="form-group text-left"> <label for="password">パスワード:</label> <input type="password" class="form-control" id="password" v-model="password"> </div> </div> <div class="col-md-4 text-right"> <button @click="userSignin" class="btn btn-primary my-3">ログイン</button> </div> </div> </div> </template>次に認証成功時の遷移先としてホーム画面に手を加えます。デフォルトではsrc/components内のHelloWorld.vueを子コンポーネントとしてsrc/views内のHome.vueで読み込んで表示しています。今回はログインが確認できればいいので、下記のとおりtemplateの内容をまるっと書き換えます。
src/components/HelloWorld.vue<template> <div class="hello"> <h1>{{ msg }}</h1> <button @click="signout" class="btn btn-danger">ログアウト</button> </div> </template>src/views/Home.vue<template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Success!!!"/> </div> </template>ここまでで今回使用する画面のガワが完成しました。
routerの設定
追加した画面を表示するにはrouterの設定が必要になります。まずは
src/router/index.js
に下記のコードを追記してログイン画面と新規登録画面のルーティングを設定します。src/router/index.js{ path: '/signup', name: 'Signup', component: () => import(/* webpackChunkName: "signup" */ '../views/Signup.vue') }, { path: '/signin', name: 'Signin', component: () => import(/* webpackChunkName: "signin" */ '../views/Signin.vue') }次に認証後に表示したい画面にメタフィールドを定義します。今回はホームとデフォルトで用意されているAboutページを認証ページとして設定しました。
meta: {requiresAuth: true}最後にグローバルビフォーガードを設定して未承認時のリダイレクト先を指定します。
ここら辺の解説については公式に詳細があるのでそちらをご参照ください。src/router/index.jsimport firebase from 'firebase' router.beforeEach((to, from, next) => { const requiresAuth = to.matched.some(record => record.meta.requiresAuth) if (requiresAuth) { firebase.auth().onAuthStateChanged(function (user) { if (user) { next() } else { next({ name: 'Signin' }) } }) } else { next() } })STEP2
STEP2ではアプリケーション内でFirebaseの機能を利用できるようにしていきます。
Firebaseのインストール
ターミナル(orコマンドプロンプト)を開き、下記のコマンドを実行してFirebaseをインストールします。
npm install --save firebaseyarnの場合は
yarn add firebaseFirebaseAuthを利用するための設定
FirebaseAuthを利用するにはFIrebase側でプロジェクトを用意する必要があります。まずはこちらからFirebaseのプロジェクトを作成しましょう。
プロジェクトが作成できたら、プロジェクトの概要>Authenticationを開きます。
今回はメールとパスワードに認証のみ実装するので、ログインプロバイダは「メール/パスワード」を選択し有効にします。
次にアプリケーションとFirebaseを接続します。
プロジェクトの概要を開き、下記画像のアイコンをクリックをクリックするとアプリの登録画面が開きます。
「アプリの登録」画面で適当なニックネームをつけて「次へ」をクリックすると下記のような画面が表示されるので、赤枠内をすべてコピーします。
最後に先ほどコピーした内容をsrc/main.jsにペーストして、FirebaseをインポートすればVueアプリケーション内でFirebaseが提供するさまざまな機能を利用することができるようになります。src/main.jsimport firebase from 'firebase' // Your web app's Firebase configuration // For Firebase JS SDK v7.20.0 and later, measurementId is optional var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxx", authDomain: "xxxxxxxxxxxxxxxx", databaseURL: "xxxxxxxxxxxxxxxx", projectId: "xxxxxxxxxxxxxxxx", storageBucket: "xxxxxxxxxxxxxxxx", messagingSenderId: "xxxxxxxxxxxxxxxx", appId: "xxxxxxxxxxxxxxxx", measurementId: "xxxxxxxxxxxxxxxx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); firebase.analytics();STEP3
STEP3では、ユーザーの新規登録と認証機能の実装をおこないます。処理のほとんどはFirebase側がよろしくやってくれるので、基本的なVueの知識があれば躓くことはないと思います。
新規登録&ログイン機能の実装
まずはユーザーを新規で登録する処理を実装していきます。
src/views/Signup.vue<template> //省略 </template> <script> import firebase from 'firebase' export default { data () { return { email: '', password: '', confirmPassword: '', errorMessage: '', isDisabeld: true } }, methods: { checkPassword () { if (this.password !== this.confirmPassword) { this.errorMessage = 'パスワードが一致しません' this.isDisabeld = true } else { this.errorMessage = '' this.isDisabeld = false } }, userSignup () { firebase .auth() .createUserWithEmailAndPassword(this.email, this.password) .then(() => { this.$router.push('/') //認証完了後のリダイレクト先を指定 }) .catch((e) => (this.errorMessage = e.message)) //エラーメッセージを格納 } } } </script>
methods
のuserSignup
メソッドでFirebaseにアクセスする処理を記述します。createUserWithEmailAndPassword
メソッドの引数に入力されたメールアドレスとパスワードを設定することで値が送られ、登録と認証が行われます。.then(() => {処理内容})
で成功時の処理を、.catch((e) => (処理内容)
で失敗時の処理を記述しています。checkPassword
メソッドでは簡易的にパスワードの相違チェックをしていて、パスワードが確認用パスワードと一致しない場合は登録ボタンを無効化しメッセージを表示します。
最後にhttp://localhost:8080/signup
にアクセスして実際にユーザー登録を行い、Firebase上に登録内容が反映されているかを見てみます。
Firebase>Authentication>usersにアクセスして以下のように表示されていれば登録は成功です!
次にログイン認証に実装を行います。新規登録と同様にFirebaseにメールアドレスとパスワードの情報を送るだけです。
src/views/Signin.vue<template> //省略 </template> <script> import firebase from 'firebase' export default { data () { return { email: '', password: '', errorMessage: '' } }, methods: { userSignin () { firebase .auth() .signInWithEmailAndPassword(this.email, this.password) .then(() => { this.$router.push('/') }) .catch((e) => (this.errorMessage = e.message)) } } } </script>新規登録の処理とほとんど変わりません。ログインボタンの
@click
イベントでuserSignin
メソッドを呼び出し、その中で入力内容をsignInWithEmailAndPassword
メソッドでFirebaseに送っています。
追記が完了したらhttp://localhost:8080/signup
にアクセスして先ほど登録したメールアドレスとパスワードで正常にログインできるか確認します。正しく入力してログインボタンを押下した際エラーメッセージを表示せずホーム画面に遷移すれば成功です。ログアウト機能の実装
FirebaseAuthenticationの
signOut
メソッドをmethodsに定義することで簡単にログアウト機能を実装することが可能です。今回はログアウトボタンの@click
イベントでsignoutメソッドを呼び出し処理を実行しています。src/components/HelloWorld.vue<template> //省略 </template> <script> import firebase from 'firebase' export default { name: 'HelloWorld', props: { msg: String }, methods: { signout () { firebase.auth().signOut() .then(() => { this.$router.push('/signin') }) .catch(error => { alert('ログアウトに失敗しました') }) } } } </script>最後に
http://localhost:8080/
にアクセスして認証が正常に動いているかを確認して完成です。さいごに
僕自身、Firebaseに触れたのは今回がはじめてだったのですが、サーバーサイドの知識なしに認証がこんなにも簡単に実装できるのには感動しました!この記事が、お手軽にフルスタック開発をしたい皆々様のご参考になれば幸いです。
- 投稿日:2020-11-20T13:37:53+09:00
席替え用のWebアプリ作ってみたよ
こんにちは。すずともです。
席替えするとき
- 番号書いてあるくじ作って
- みんなに引いてもらって
- 自分の引いた席に移動してもらって
- 自分の番号と名前をLINEグループに投げて
- 代表がExcelで座席表を作り
- 印刷して教卓に貼る
っていう感じで、多くの工程がありますね。(クラスによっても違うと思うけど…)
僕のクラスは自治性が強いので全部学生でやってますが、クラスによっては先生がしてくれるところもありますし。
まぁ、何でもいいとして、結構めんどくさいなぁと。
これ、Webアプリ作れば楽になるんじゃね?と思って、Webアプリ作ってみました。成果物
とりあえず最初に成果物置いておきます。
Github pagesにて公開しました。
よかったら使ってくださいな。https://kamekyame.github.io/seat_change/
使い方
使い方はいたってシンプル。
- 生徒・座席情報の入ったJSONファイルをアップロード
- 席替えボタンを押す!
- 印刷!(またはファイルダウンロード)
試しに使ってみたい人は、
Githubの方に、sample.jsonというファイルがあるので、それを自分のPCにダウンロードして、ファイルアップロードにぶち込んでください!いろいろな機能
席替え自体は、上記の手順でできるわけですが、僕のクラスはこんな単純な席替えではありません笑
というのも、
- 6列6行の座席配置ですが、人数が35人なので1マス空席
- 席替え前に目の悪い人や前行きたい人が前方の席を占領
っていう条件・風習がありまして、席替え前に席を固定する必要があります。そういうクラス向けに
- 席の交換機能
- 席の固定機能
も実装してあります。
1.席の交換機能
名前部分をクリックすると、このように、名前一覧が出てきます。
ここで好きな名前を指定すると、席の交換ができます。
2.席の固定機能
席の名前以外の部分(黄色いところ)をクリックすると座席の固定が出来ます。
生徒1を固定するとこのようにグレーになります。
この状態でいくら席替えしても生徒1の場所は変わりません。印刷
席替えをしたら、先生のために印刷をしないと...
ということで、印刷用のCSSを用いて、印刷したときにもきれいになるようにしました。
方法は簡単で、「Ctrl + P」で印刷するだけ。このように、ヘッダー・フッターが消えた状態で印刷ができます。
先生用⇔生徒用の切り替え
実は、今見てる座席表は、生徒側から見た視点。
先生側から見ると、教卓が手前に来る形になります。そこで、右上の「先生用⇔生徒用」ボタン。
このボタンを押すと座席内の文字が180度回転します。この状態で印刷すると、あっという間に教卓に貼る先生用の座席表の完成です!
ファイル形式
アップロード・ダウンロードに使われるJSONファイルの中身については、GithubのREADMEに書いてあるので、Githubを見てください!
最後に
友達との軽い話から作ってみた「席替えWebアプリ」
Vue.jsを使って書いてみたのですが、スマートにかけてよかったです!
また、印刷用のCSSの存在も知れたので、個人的にはスキルアップにつながったかなぁと。よかったら席替えの時に使ってください!(Qiitaの記事読んでる人に学生がどのくらいいるかは置いといて...)
Githubに公開してるので、issue、PR、フォークなどご自由にどうぞ!(一応MITライセンスです)
- 投稿日:2020-11-20T12:24:59+09:00
GraphQLでAPIを叩き、cloudinaryでアクセス制御つきの画像登録を行う(Vue.js Express)
目次
- cloudinaryとは
- cloudinaryの設定を行う
- GraphQLのSchemaを定義する
- DBのカラム設定を変更する
- DBにデータを挿入する
- フロントからAPIを叩く
cloudinaryとは
引用すると、
Cloudinary is the market leader in providing a comprehensive cloud-based image and video management
引用元
Cloudinaryという画像変換サービスについて勉強してきたとのことで、大雑把な認識としては「画像や動画などをクラウド上に管理しておけるサービス」です。
今回やりたいこととしては、
ユーザー(ここではworker)が画像を登録する
→GraphQLを用いてAPIを叩き、それをDBに保存する
→プライバシーの度合いが高い画像なので、その際にアクセス制御つきで保存し、画像URLを暗号化するこんな感じです。
cloudinaryの設定を行う
cloudinaryの細かい使い方は他の記事で紹介されてますので今回は省きます。
ここでは引数にfileを指定してURLをbase64に変換したのち、登録する処理を書いています。
upload関数をupload変数に格納してexportすることで、他のファイルで呼び出せるようにしています。cloudinary.js'use strict'; const cloudinary = require('cloudinary'); const { promisify } = require('util'); const filetype = require('file-type'); const logger = require('./logger'); const fs = require('fs'); cloudinary.config({ cloud_name: process.env.CLOUDINARY_NAME || '設定時の名前', api_key: process.env.CLOUDINARY_API_KEY || 'API key', api_secret: process.env.CLOUDINARY_API_SECRET || 'シークレットキー' }); exports.upload = upload; async function upload(file) { try { const buf = Buffer.from(file); const fileType = filetype(buf); const base64 = buf.toString('base64'); const mime = fileType.mime.replace('vnd.', ''); const res = await promisify(cloudinary.v2.uploader.upload)(`data:${mime};base64,${base64}`, {type: "authenticated"}); ← ここ return res; } catch (err) { logger.error(err.toString().substr(0,10000)); throw err; } }注目すべきは、typeをauthenticatedに指定することです。
これを加えることによって、認証付きのURLを発行することができます。GlaphQLのSchema(スキーマ)を定義する
GraphQLを使うには、まずスキーマを定義してあげる必要があります。
これはAPIデータの型を定めるものです。schema.gqltype Mutation { uploadImage(worker_id: Int!, file: Upload!): Worker! } input WorkerInput { ~workerが入力する情報がここに入ります〜 image_id: String iamge_url: String ... } type Worker { ~worker情報~ image_id: String image_url: String }Mutationは登録、更新、削除などの時に使用し、Queryは取得の時に使います。
今回は登録なのでMutationの中に定義ですね。
schema.gql は Query や Mutationなど .gql に定義された型を全て管理する場所なので、実際には mutations.gql と types/getWorker.gql にも定義する必要があります。mutations.gqltype Mutation { uploadImage(worker_id: Int!, file: Upload!): Worker! ... } input WorkerInput { ~workerが入力する情報がここに入ります〜 image_id: String iamge_url: String ... }ちなみに、!←このマークは null を許容しない(必ずなんらかの値を持たせる)ことを意味します。
リストで値を返したい場合は、[Worker]など配列の中に型を入れると良いです。最後に、userの情報が型定義されているので、そこにも設定を追加してあげます。
types/worker.gqltype Worker { image_id: String iamge_url: String ... }これで全体としてのスキーマの設定は完了です。
DBのカラム設定を変更する
workers_create.sqlcreate table workers ( ...追加 image_id varchar(100) COMMENT 'cloudinaryで管理されるID', image_url varchar(255), ...実際にはALTER TABLE ~ で変更しましょう。割愛します。
DBにデータを挿入する
フロントからaxiosでAPIを叩いてpostのリクエストが来るので、それを受け取ってDBにinsertします。
server/src/graphql/resolvers/mutations/uploadImage.js'use strict'; const db = require('../../../util/mysql'); const logger = require('../../../util/logger'); const cloudinary = require('../../../util/cloudinary'); const worker = require('../queries/worker'); let sql = 'UPDATE カラム名 SET image_id = ?, image_url = ?, updated_at = Now() WHERE id = ?'; exports = module.exports = uploadImage; async function uploadImage(obj, args) { logger.info('mutation/uploadImage'); const { worker_id, file } = args; const res = await cloudinary.upload(file, false); let param = []; param.push(res.public_id); param.push(res.secure_url); param.push(worker_id); await db.query(sql, param); return await worker(null, { worker_id: worker_id }); } exports.sql = sql;res変数には先ほど設定したcloudinaryのupload関数を呼び出し、返ってきたid,セキュリティ化されたURLが格納されてますね。
パラメータとして配列にpublic_id,secure_url,worker_idを入れてあげて、query関数でDBにinsertします。
その後insertされた新たなデータをselectしてきたものを戻り値として返してあげます。(worker関数がその役割を果たしている)
フロントからAPIを叩く
ようやく準備が整ったので、フロントからAPIを叩いて終わりです。
言語化すると、とても長くなってしまいますね。疲れました。笑profileInput.vueimport axios from "~/plugins/axios"; import { BackendAPIURL } from "~/constant.js"; methods: { async uploadImage(file) { if (!file){ return }; const reader = new FileReader(); reader.onload = e => { this.execUploadImageFile(new Buffer(e.target.result)); }; reader.readAsArrayBuffer(file); }, async execUploadImageFile(file) { const query = ` mutation uploadImage($worker_id: Int!, $file: Upload!) { uploadImage(worker_id: $worker_id, file: $file) { id image_id image_url } } `; try { const res = await axios.post(BackendAPIURL, { query: query, variables: { worker_id: this.profile.id, file: file } }); this.profile.image_id = res.data.data.uploadImage.image_id; this.profile.image_url = res.data.data.uploadImage.image_url; this.submitRegister(this.profile); return res; } catch (err) { console.error(err.stack); } }, }data()やここで呼び出されている他のメソッドもあるのですが、あまりにもコードが長すぎて煩雑になってしまうので、省略します。
重要なのはexecUploadImage関数です。
workerが入力した画像をpostし、先ほどのバックエンドでの処理を行います。返ってきたレスポンスをプロフィールに登録しているのです。GraphQLを使ってみた感想
GraphQLの利点は、
- 必要なデータだけを指定してリクエストできるのでデータの取得過剰や取得不足が起こりにくい
- 処理が軽い
ことですね!
まだまだ使いこなせていないですが、とても好きな技術の1つです!
参考にした記事
- 投稿日:2020-11-20T12:11:35+09:00
jsを非同期であとで読み込む
Twitterを埋め込んだものをブログに表示したい。
まぁ、今回はVue.jsなんだが。では、どうすれば良いかというと
vue.blade.php
に直接書いても先にwidgets.js
が作動してしまい、うまく表示されない。ということで対応策として、
・記事が読み込まれる
・widgets.js を読み込むという流れにする。
axios.post('/article/view/', dataform).then(e => { this.res = e.data.res; //ここが味噌 let recaptchaScript = document.createElement('script'); recaptchaScript.setAttribute('src', 'https://platform.twitter.com/widgets.js'); document.head.appendChild(recaptchaScript);はい、以上です。
- 投稿日:2020-11-20T06:33:17+09:00
Vue.js WebSocket チュートリアル
目的
この記事ではVue.js WebSocket Tutorial の内容を参考に、Vue + Websocketを実現する方法をVue初心者向けに説明します。
Vue経験者であれば上記リンクを見て簡単にできてしまう内容ですが、自分と同じように「Vue初心者、なんならjsやHTMLも初心者だけどVueとwebsocketを使いたい!」という方がいると想定し、この記事を書いています。ソフト構成
今回使った環境のざっくり構成です。
Vue CLIで開発し、websocketのサンプルを動かします。
websocketサンプルを動かすだけであればビルドする必要は無いのですが、デプロイする際にはビルドしたものが必要となるため、ビルドして動かすところまでをゴールとしました。websocket
HTTPを使った双方向通信です。
【参考】今さら聞けないWebSocket~WebSocketとは~Vue CLI
Vueの開発環境を手早く進めることができるツールです。
手順
1.Vue CLI 環境構築
2.Vue + websocketの実装
3.ビルドして動かす1.Vue CLI 環境構築
Node.jsとVue CLIをインストールします。
【参考】VueCLIをインストール! ❏Vue.js❏コンソール$ vue --version @vue/cli 4.5.9プロジェクトを作成します。
コンソール$ vue create websocket-tutorial Vue CLI v4.5.9 ? Please pick a preset: Default ([Vue 2] babel, eslint)作成したプロジェクト内に移動して、開発用サーバーを立ててみる。
コンソール$ npm run serve DONE Compiled successfully in 2376ms 5:02:22 App running at: - Local: http://localhost:8080/ - Network: http://192.168.128.104:8080/2.Vue + websocketの実装
作成したプロジェクトのフォルダを見てみると、たくさんのファイルが生成されていますが、今回は
src
の中のApp.vue
のみ編集します。サンプルの中身を消して、以下をそのままコピペしてください。App.vue<template> <div id="app"> <h2>Vue.js WebSocket Tutorial</h2> <button v-on:click="sendMessage('hello')">Send Message</button> </div> </template> <script> export default { name: 'App', data: function() { return { connection: null } }, methods: { sendMessage: function(message) { console.log("Hello") console.log(this.connection); this.connection.send(message); } }, created: function() { console.log("Starting connection to WebSocket Server") this.connection = new WebSocket("wss://echo.websocket.org") this.connection.onmessage = function(event) { console.log(event); } this.connection.onopen = function(event) { console.log(event) console.log("Successfully connected to the echo websocket server...") } } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>ファイルを保存すると、開発サーバーのブラウザにWebSocket Tutorialの画面が表示されます。
Send Message
のボタンを押すと、WebSocketのデータがechoサーバーに送信されますが、画面上では確認できないため、コンソールで確認してみます。コンソール画面はF12
を押すことで開くことができます。
webscoketで”Hello”という文字列を送信して、echoサーバーから返ってきた”Hello”を受信していることが確認できます。やったあ!3.ビルドして動かす
Vueファイルはそのままの形ではデプロイすることができないため、ビルドしてデプロイできる形にします。
コンソール$ npm run build
dist
フォルダにindex.html
,css
,js
が作成されていることが確認できます。
ですが、このままhtmlをブラウザで表示しても真っ白になってしまいます。(絶対パスになっていることが原因のようです)
【参考】Vue CLIでbuildしたWebサイトを公開する方法対処法として、
vue.config.js
を作成して、以下をコピペしてビルドしてください。vue.config.jsmodule.exports = { publicPath: './' }無事、画面も表示されて、websocketの送受信ができることが確認できます!
- 投稿日:2020-11-20T01:14:31+09:00
Vueのバージョンアップとバージョンダウンを繰り返す
Vue2とVue4を行き来しなければならない
Vue2からVue4にする
# バージョン確認 test@test hoge % vue --version @vue/cli 4.2.2 # Vue2をアンインストール test@test hoge % npm uninstall vue-cli -g # Vue4をインストール test@test hoge % npm install -g @vue/cliVue4からVue2にする
# バージョン確認 test@test hoge % vue --version @vue/cli 4.2.2 # Vue2をアンインストール test@test hoge % npm uninstall -g @vue/cli # Vue4をインストール test@test hoge % npm install vue-cli -g
- 投稿日:2020-11-20T00:17:27+09:00
[Javascript]オブジェクトを定数に代入する
オブジェクトを定数に代入する
定数itemにオブジェクト内の値を代入する
console.logで出力するとオブジェクト内の値をまるっと表示させる事ができる
オブジェクトの値を取り出す
1行目は先ほどと同じようにitem定数に値を代入する
2行目でitem.nameでオブジェクト内のnameの値のみを出力する
name内の文字列のみ出力させる事ができる
const
constは定数なので上書きする事ができない。メリットとしてはコード量が多くなるほどconstで定義すれば可視化しやすい
let
letは変数なのでいくらでも上書きする事ができる。メリットとしては変数内の値を変えたい時に即座に変える事ができるが、本人しかわからないコードにならぬようコメントアウトしておいた方が吉
以上!
- 投稿日:2020-11-20T00:07:20+09:00
Vue.jsで無限スクロールをライブラリなしの自前で実装してみた(Laravel + Vue.js)
業務でとある予約システムを開発する機会があり、その中で無限スクロールを実装しましたが、
取得するデータが条件付きで単純な取得データの個数を一定にして行うことができなかったのでライブラリなしで実装しました?♂️ライブラリを使った記事は割とあるけど自前の方はあんまりないなーと思ったので自分のメモも含めて残します?
僕自身、vue.jsがかなり初心者だったため、こう書いたらもっといいよとかあればコメントで教えて下さい?♂️
本当はもう少し複雑なんですが、必要最低限のみ載せます?♂️
Seats.vue<template> <div class="seat-list"> <div v-for="seat in seats"> <div class="seat"> <div>{{ seat.value }}</div> </div> </div> <!--ロード中のアニメーション--> <div class="loader-wrap" v-show="loading"> <div class="text">取得中...</div> </div> </div> </template> <script> export default { props: { //予約枠のデータ propSeatsData: { type: Array, default: [], }, eventData: { type: Object, default: () => {}, }, initialStartDay: { type: String, default: '', }, endpoint: { type: String, default: '', }, }, data() { return { //ロード中のアニメーション loading: false //予約枠のデータ seats: this.propSeatsData, //イベントデータ event: this.eventData, //取得する開始日 startDay: this.initialStartDay, //非同期で取得中 通常: false, 通信中: true itemLoading: false, //まだ取得するデータが存在する: false, もう存在しない: true initialIsLastPage: this.propIsLastPage, }, }, mounted(){ window.onscroll = () => { //一定位置以上スクロールされればtrueを返す let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight >= document.documentElement.offsetHeight; //trueでデータ取得 if(bottomOfWindow) { //無限スクロールでデータ取得 this.getSeats(); } } }, methods: { //予約枠データ取得(無限スクロール) async getSeats () { if (!this.itemLoading) { //読込中は再読み込み防止 if (!this.isLastPage) { //取得データがもう存在しない場合は行わない const self = this //読込中 true this.itemLoading = true //ロード中のアニメーション表示 this.loading = true //非同期通信 axios.post(this.endpoint, { //送信するデータ date: this.startDay, event_id: this.event.id, }).then(function (responce) { //取得したデータを追加 self.seats.push(...responce.data.seats_data) //取得するスタート日を更新 self.startDay = responce.data.start_day //ローディングアニメーション非表示 self.loading = false //読込中 false self.itemLoading = false //まだ取得するデータが存在する: false, もう存在しない: true self.isLastPage = responce.data.is_last_page }) .catch(function (error) { //エラー出力 console.log(error) //ローディングアニメーション非表示 self.loading = false //読込中 false self.itemLoading = false }); } } }, },下記のコードの部分で、
通信の重複を回避するためにitemLoadingを通常はfalse、通信中はtrueにしてあげます。
データがまだ存在するかどうかはController側で工夫して真偽値を返してあげるといいですね?♂️Seats.vuedata() { return { //非同期で取得中 通常: false, 通信中: true itemLoading: false, //まだ取得するデータが存在する: false, もう存在しない: true isLastPage: this.initialIsLastPage, } },一番下までのスクロールを検知したい場合は
Seats.vuemounted() { window.onscroll = () => { //一定位置以上スクロールされればtrueを返す let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight >= document.documentElement.offsetHeight; if (bottomOfWindow) { 下までスクロールした時に実行したい処理を書く } } },もし、無限スクロール + selectとかで取得するデータをユーザーが自由に選べる場合は
optionタグ内に :disabled="disabled" を入れてあげて通信中はtrueにすればその間はユーザーはselectを選択できなくなります?♂️
通信の重複防止です?♂️Seats.vue<select @change="changeDate"> <option v-for="day in days" :disabled="disabled">{{ day }}</option> </select>Seats.vuedata() { return { disabled: false, } }メソッド内で⬇
Seats.vuemethods: { async getSeats() { //selectを選択させない this.disabled = true 処理... //解除 this.disabled = false } }とかしてあげれば良い感じに通信の重複は防げるかなと?
vue.jsはpropsで渡ってきたデータに、pushしてデータを追加すれば勝手に描画してくれるので
jqueryとかを使うよりもずっと簡単にできるなと実感しました?♂️おわり?
参考記事
【ライブラリ無し】Laravel + vue で無限スクロール(infinite scroll)を1から実装
https://inokawablog.org/laravel/laravel-vue-infinite-scroll-scratch/