20210502のvue.jsに関する記事は11件です。

Nuxt.jsの基本的な使い方【storeを使ったデータの状態管理編】

プロローグ Nuxt.jsを触り始めて三ヶ月弱くらいですが、 色々と使い方を覚えてきたので、メモとして記録を残します。 不備不足だったり、理解が浅くて説明不足なところがあるかと思いますが、 勉強して更新していきたいとは思っています。 この記事で何ができるようになるか storeを使ってデータを扱うにはこうするんだ〜程度のことがわかるようになるかとは思います。 というのも、まだ1回教えてもらってなんとか使えるようになった程度なので、詰めが甘いところがあるかと思います。 storeとは storeを使うことで、アプリケーションの状態(state)やデータを保持することができます。 emitやpropを使ってdataを共有することもできますが、ブラウザ上にデータを保持することがストアではできるので、一旦ブラウザを閉じてもstoreに保持したデータは残ります。 使い方 今回はクラシックモードでストアを使うので、Vuex インスタンスを返す関数がエクスポートされている store/index.js ファイルを作ります。 しかし、この機能(クラシックモード)は Nuxt 3 で廃止し、削除される予定です・・。 クラシックモード この機能は Nuxt 3 で廃止し、削除される予定です。 クラシックモードでストアを使うには、Vuex インスタンスを返す関数がエクスポートされている >store/index.js ファイルを作る必要があります。 参照:Nuxt.jsドキュメント Vuexストア store/index.jsを解説 保持されたデータを扱うために、下記の4つを使ってデータを扱います。 リンク先はそれぞれのドキュメントに記載されている説明になります。 用語 state データの情報源(信頼できる唯一の情報源 ) getter 変更された値を保持する(ゲッターの結果はその依存関係に基づいて計算され、依存関係の一部が変更されたときにのみ再評価される ) mutation データの情報源を更新する(実際に状態の変更を行う) action 非同期処理や外部APIとの通信を行い、最終的にmutationを呼び出す為に使われます。(引用:【Vuex】ストアの4つの概念まとめ【唯一の情報源】) 今回は、state / mutation / actionを解説していきたいと思います(getterはまだ使ったことないので割愛させていただきます)。 今回保持したいデータとして、ブログなどで月別での記事を検索するとします。 下記画像のように、「4月」をクリックしたら「4月」の記事一覧画面へ遷移するとともに、「4月」というデータを保持したい場合を想定します。 「4月」の記事一覧画面へ遷移すると同時に、選択された「4月」というデータを保持する。 store/index.jsでの解説を行っていきます(コメントにて説明)。 store/index.js export const state = () => ({ selectedDay: null }); //stateでデータを設定します。月別での記事を検索する前は月は選ばれていないのでnullと設定しました。 export const mutations = { setSelectedDay(state, selectedDay) { state.selectedDay = selectedDay } } //setSelectedDay(state, selectedDay) の第1引数で状態(state)を取得します。 //第2引数でselectedDayの値をstate(情報源)に渡します。ここでは例えばselectedDayを4月とします。 //すると、setSelectedDay(state, selectedDay) {}の中身では //selectedDayがnullだったものを4月とデータを更新していることになります(state.selectedDay = selectedDayがそれ)。 export const actions = { setSelectedDay({ commit }, selectedDay) { commit('setSelectedDay', selectedDay) } //setSelectedDay({ commit }, selectedDay)の第1引数でmutationsとcommitします。 //Actions は、状態は変更せず、 Mutations を commitし、Mutations を commit することで状態(State)を更新します。:引用(参考記事/[Vue.js] Vuexの使い方を知る) //commit('setSelectedDay', selectedDay)では、第1引数に呼び出す関数の名前を入れてます。 //第2引数では該当mutationsで使用する値(selectedDay)をいれています。 } actionsで非同期処理や外部APIとの通信を行い変更されたデータがsetSelectedDayに入ります。 setSelectedDayに入ったデータはmutationsのstate.selectedDay = selectedDayで更新されます。 更新されたデータはstateのselectedDayに保持されてるというながれになります。 actions(データ受け取り) -> mutations(データ更新) -> state(データ保持) という流れになります。 pages/index.vueを解説 保持されたデータを使いたいときは下記のように書きます。 こうすることで、保持されたデータがdayに入ったので、使い回すことができます。 index.vue const day = this.$store.state.selectedDay stateのデータを更新したいときは下記のように書きます。 ('setSelectedDate', "5月")の第1引数はactionsで定義した関数の名前です。 第2引数は更新したい値をいれます。 index.vue this.$store.dispatch('setSelectedDate', "5月") //または this.$store.dispatch('setSelectedDate', this.lastDay) //dataで定めた値を入れてもいい おまけ 説明してきたように、vueでstoreの状態を呼び出したり(this.$store.state.selectedDay)、更新させたり(this.$store.dispatch('setSelectedDate', this.lastDay))することができます。 そのときに便利なヘルパーがあるのでそちらの記事を引用して終わりにします。 ・【Vuex】mapState, mapGetters, mapMutations, mapActionsの最低限の使い方まとめ 参考記事 ・Vuexとは。まずはこれを読んでVuexの概要を理解しよう。 ・Nuxt.jsをはじめよう - Vuexを使ってデータを渡す ・Vue.js、Nuxt.jsでのVuex(store)の使いドコロ ・Nuxt.js で Vuex を使う ・【Vuex】ストアの4つの概念まとめ【唯一の情報源】 ・[Vue.js] Vuexの使い方を知る 一言 数週間前にやったものですが、1度書いてみただけだったので、記事を書くにあたって調べ直したり、理解が浅すぎるところがありました・・。何回も何回も同じ状態に遭遇して、やらないと覚えきれないなと痛感しました。 なので、再度不備不足や間違っているところがないのかを勉強し直して更新していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】動的ルーティングまでを実装してみた!

ルーティングを実際にやってみましょう! ルーティングは、Vue.jsやNuxt.jsを使用する際には切ってもきれないのでしっかり理解しておきましょう。 では始めます。 ルーティングって何? まず、ルーティングって何って方に解説していきます。 ルーティングとは、大まかに言うとURLとファイルを紐付けることです。 下記のようにURLを変更すると、、、 localhost:8888 ⇨ localhost8888/users ルーティングを用いることで、同じhtml内でjavascriptが処理をしてくれる構造となっています。 聞いたことがあると思いますが、これがSPA(シングルページアプリケーション)です。 これだけだとわかりにくいので従来のWebアプリケーションで実装した時と何が違うのか解説します。 【従来のWebアプリケーション】 1.ユーザーが何かしらのアクション(クリックなど)を行う 2.それを受けてサーバに通信(リクエスト) 3.サーバ側でHTMLを生成し、ブラウザに返す(レスポンス) 4.HTMLに反映 【SPAを導入したWebアプリケーション】 1.ユーザーが何かしらのアクション(クリックなど)を行う 2.そのアクションに必要な部分のデータだけをサーバに要求 3.返ってきたデータをJavaScriptで処理 4.処理HTMLに反映 上記のような違いがあります。 SPAの処理速度の方が早い理由が分かりますね。 説明だけ聞いても分かりにくいので、手を動かした方が想像できると思うので実際にやってみましょう!! 静的ルーティング まずは、静的ルーティングからやっていきます! まずは、下記のような構造でページを作成してみましょう。 pages/ --| users/ -----| index.vue -----| about.vue --| index.vue 下記の構造で作成すると、router.jsは、 router.js routes: [{ path: "/users", component: _899cc86c, name: "users" }, { path: "/users/about", component: _7757a308, name: "users-about" }, { path: "/", component: _43aa7c51, name: "index" }], このように、nuxt.jsでは自動生成されます! 確認してみてください。 まず、index.vueに記載していきます。 pages/index.vue <template> <div style="text-align: center"> <router-link to="/users">users</router-link> <router-link to="users/about">about</router-link> </div> </template> すると画面には、、、 次に users/index.vue users/about.vue に記載していきます。 users/index.vue <template> <h1>users/index.vueです!</h1> </template> users/about.vue <template> <h1>users/about.vueです!</h1> </template> では、index.vueから、 ・users/index.vue ・users/about.vue 上記のページへ遷移してみましょう。 pages/index.vueのusersをクリックしてみてください!! すると画面が、、、 URLも、 http://localhost:3000/users  に切り替わっています。 同様に、pages/index.vueのaboutをクリックしてみてください!! すると画面が、、、 URLも、 http://localhost:3000/users/about  に切り替わっています。 これで静的ルーティングは終わります。 理解できましたでしょうか? 次にいきましょう!! 動的ルーティング 次は動的ルーティングです! /users/11 のようにid名などで動的にリンクを作成する場合ファイル名、ディレクトリ名にアンダースコアをつけることで可能に なります。 pages/ --| users/ -----| _id.vue 上記のような階層でファイルを作成したら、router.jsは、 router.js routes: [{ path: "/users/:id?", component: _352808b2, name: "users-id" },] このように生成されます。 では、users/_id.vueに記載していきます。 /users/_id.vue <template> <div style="margin: 50px"> <p>{{ message }}</p> <h1>id:{{ this.$route.params.id }}</h1> </div> </template> <script> export default { data (){ return { message:'/user/_id.vueを表示中' } } } </script> URLを http://localhost:3000/users/11 に変更してみましょう!! すると画面には、、、、、 このようにuserのidが表示されます。 ちなみに、 {{ this.$route.params.id }} でパラメータの値を引っ張ることができます。こちらも使うことがあると思うので覚えていきましょう!! 以上で動的ルーティングを終了します。 おまけ 最後におまけです! 先程のidに数字のみしか入れられないように、バリデーションを入れていきましょう。 では、users/_id.vueに追加で記載します。 /users/_id.vue <div style="margin: 50px"> <p>{{ message }}</p> <h1>id:{{ this.$route.params.id }}</h1> </div> </template> <script> export default { data (){ return { message:'/user/_id.vueを表示中' } }, validate ({ params }){ return /^\d+$/.test(params.id) } } 上記の正規表現で数字のみ可能と指定をすることで、 idやページなどを動的に処理する際には入れて便利なので使ってみてください!! 正規表現やtestメソッドなどが気になる方はご自身で調べてみてください。 最後に 最後までお付き合いいただきまして、ありがとうございます。 ルーティングについて手を動かして実践してみてどうでしたか? 難しかった方は、もう一度見ずにできるかどうか実践してみると記憶に定着していいかもです! この記事が良かったと思う方は、LGTMをお願いします! では楽しいエンジニアライフを〜〜〜!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nuxtにcomposition-apiを導入する

インストール $ yarn add @nuxtjs/composition-api nuxt.config.jsonに加筆 { buildModules: [ '@nuxtjs/composition-api/module' ] }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VuePressサイトをAuth0で認証すーる

はじめに 前回はAzure Static Web AppsでVuePressサイトを作成しました。 今回はAuth0で認証してみたいと思います。 参考にしたのはこれ https://auth0.com/blog/vuepress-authentication/ https://auth0.com/blog/complete-guide-to-vue-user-authentication/ 開発環境 Auth0 Azure Node.js GitHub Windows 10 PC 導入 1.前回を参考にVuePressサイトを作る .github .gitignore 以外は全部消してOK、最初からやっていく 2.Node.jsのコマンドプロンプトから初期化、vuepress、auth0プラグインのインストール cd vuepress-static-app npm init -y npm install --save-dev vuepress npm install @auth0/auth0-spa-js 3.package.jsonの編集 package.json { "name": "vuepress-sample", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "vuepress build", "dev": "vuepress dev", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/SatoshiRobatoFujimoto/vuepress-static-app.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { "url": "https://github.com/SatoshiRobatoFujimoto/vuepress-static-app/issues" }, "homepage": "https://github.com/SatoshiRobatoFujimoto/vuepress-static-app#readme", "devDependencies": { "vuepress": "^1.8.2" }, "dependencies": { "@auth0/auth0-spa-js": "^1.15.0" } } 4.ファイル構成は次のようになっている 5..vuepress/config.jsを作成 config.js module.exports = { title: "My Documentation Site", description: "This is going to be awesome!", themeConfig: { nav: [ { text: "Home", link: "/" }, { text: "About", link: "/about/" } ] } }; 6.Auth0アプリケーションを作成 Auth0に登録、ログイン ダッシュボードのActivityからCREATE APPLICATIONボタンをクリック アプリ名を入れて、Single Page Web Applicationsを選び、CREATEする アプリのSettingsからDomain、Client IDをメモしておく。あとで使う。 Allowed Callback URLs, Allowed Logout URLs, Allowed Web Origins, Allowed Origins (CORS) に http://localhost:8080 を入力する。本番でやるときはAzure Static Web Appsで発行されたアプリのURLにする。 SAVE CHANGESで保存 6..vuepress/auth_config.jsを作成 auth_config.js const config = { domain: "YOUR_AUTH0_DOMAIN", clientId: "YOUR_CLIENT_ID" }; export default config; YOUR_AUTH0_DOMAINとYOUR_CLIENT_IDには先ほどメモしたものを入れる。GitHubにアップロードするのでリポジトリはPrivateにしておく。本来は環境変数とかで設定した方がいいと思う。(.gitignoreでpushしないように設定もしたほうがいい) 7..vuepress/index.jsを作成 index.js import config from "./auth_config"; import createAuth0Client from "@auth0/auth0-spa-js"; async function createClient() { let auth0Client = await createAuth0Client({ domain: config.domain, client_id: config.clientId }); return auth0Client; } async function loginWithPopup(client, options) { try { await client.loginWithPopup(options); } catch (e) { // eslint-disable-next-line console.error(e); } } function logout(client) { return client.logout(); } const auth = { createClient, loginWithPopup, logout }; export default auth; ※実行時に、createAuth0Clientがないと怒られたので、@auth0/auth0-spa-jsプラグインのインストールとインポートが必要だった 8..vuepress/enhanceApp.jsを作成 enhanceApp.js if (typeof window !== "undefined") window.global = window; ※実行時に ReferenceError: global is not defined vuepress というエラーがでたので対応( https://github.com/vuejs/vuepress/issues/1434 ) 9..vuepress/components/LoginButton.vueを作成 LoginButton.vue <template> <button @click="login()">Login</button> </template> <script> import auth from "../"; export default { props : ['client'], methods : { async login () { await auth.loginWithPopup(this.client); this.$emit('login-complete'); } } } </script> 10..vuepress/components/LogoutButton.vueを作成 LogoutButton.vue <template> <button @click="logout()">Log Out</button> </template> <script> import auth from "../"; export default { props : ['client'], methods : { async logout () { await auth.logout(this.client); } } } </script> 11.README.mdを作成 README.md --- home: true footer: Made by Auth0 User with ❤️ --- <template> <div class="main-content"> <div v-if="user"> <p align="center"> Hi {{user.email}}, Welcome to the Vuepress Blog </p> <p align="center"> <LogoutButton :client="auth0client" /> </p> </div> <div v-else> <p align="center"> You are currently not logged-in to the Application. Please use the login button below to sign in </p> <p align="center"> <LoginButton :client="auth0client" @login-complete="getUser()" /> </p> </div> </div> </template> <script> import auth from "./.vuepress"; import LoginButton from "./.vuepress/components/LoginButton"; import LogoutButton from "./.vuepress/components/LogoutButton"; export default { data() { return { auth0client : null, user : null } }, async mounted(){ this.auth0client = await auth.createClient(); this.user = await this.auth0client.getUser(); }, methods : { async login () { await auth.loginWithPopup(this.auth0client); }, async getUser(){ this.user = await this.auth0client.getUser(); } } } </script> 12.npm run devでアプリを起動、 http://localhost:8080 を開き、確認 13.ログインボタンを押すとAuth0のログイン画面が出てくる。ここから誰でもSign Up、Log Inすることが可能。きたこれ 14.ログインが完了すると、ログインした人しか見れないページになっている 15.サインアップさせたくない場合は、Auth0のAuthenticationからDisable Sign Upsをオンにする するとサインアップが消えている!すごい! サインアップできないようにしたら、Auth0のUsers画面からCREATE USERで追加したり、削除ができる 16.ApplicationsのConnectionsタブから、DatabaseもしくはSocialからのログインをオンオフできる すべてオフ Socialのみ Databaseのみ 社内ドキュメントに使う予定なのでDatabaseのみとした 17.ログインできるメールアドレスの制限はAuth PiplineのRulesからCREATE、Access ControlのWhitelistを選択する Ruleを編集し、whitelistにログインを許可するメールアドレスを指定する function userWhitelist(user, context, callback) { // Access should only be granted to verified users. if (!user.email || !user.email_verified) { return callback(new UnauthorizedError('Access denied.')); } const whitelist = ['YOUR EMAIL ADDRESS']; //authorized users const userHasAccess = whitelist.some(function (email) { return email === user.email; }); if (!userHasAccess) { return callback(new UnauthorizedError('Access denied.')); } callback(null, user, context); } 18.GitHubにpush git status git add . git commit -m "add Auth0" git push 19.Azure Static Web Appsで発行されたURLでアプリの確認ができたら完成(Allowed Callback URLs, Allowed Logout URLs, Allowed Web Origins, Allowed Origins (CORS)の設定を忘れずに!) VuePressで特定ユーザーのみログイン可能な社内ドキュメントサイト完成!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自己紹介ポートフォリオを作りたい #2

半年さぼりました vue-cliを入れてよく見る「Welcome to Your Vue.js App」を表示させます。 引き続きとっても簡単なところです。 これまでの過程 自己紹介ポートフォリオを作りたい #0 - 背景 自己紹介ポートフォリオを作りたい #1 - 作業場を作る(Git周り) 今回の内容 必要なものをインストール プロジェクトにVue.jsのテンプレートを入れてみる 必要なものをインストール vueでの実装をするにはNodeとnpmとvue-cliが必要になります。 確認してみたところ3つともインストールされてました。 $ node -v v12.13.1 $ npm -v 6.12.1 $ vue --version 2.9.6 完璧ですね! なければインストールが必要になります。 インストールについて node, npmのインストール Node.jsの公式HP] vue-cliのインストール $ npm i -g vue-cli $ vue --version でバージョンが出ればOK! プロジェクトにVue.jsのテンプレートを入れてみる 前回 portfolio という作業フォルダをGitHubからクローンしてきたのでその中にインストールします。 フォルダに移動して下記を実行します。 フォルダへの移動は cd でもいいですがSourcetreeの右上にあるターミナルで開くと簡単です。 $ vue init webpack-simple ? Generate project in current directory? (Y/n) → y ? Project name (portfolio) → そのままenter ? Project description (A Vue.js project) → そのままenter ? Author (tanu <55673996+tanu-7@users.noreply.github.com>) → そのままenter ? License (MIT) → そのままenter ? Use sass? (y/N) → y vue-cli ・ Generated "portfolio". To get started: npm install npm run dev npm install と npm run dev で始められるよーとあるのでこのまま通します。 $ npm install $ npm run dev http://localhost:8080/ で↓のページが自動で開きます。 これでOKです、Vueが使えるのが確認できました! $ npm run dev を実行中じゃないと画面が確認できないのでご注意ください。 次回 (今度こそ)Firebaseにデプロイしてみる! 読んでいただきありがとうございました
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js、FireBaseで読書管理アプリを作ってみた

はじめに はじめまして。閲覧いただきありがとうございます。 今回、Vue.jsとFirebaseを用いてポートフォリオを作成したのでご説明します。 アプリの概要 読書管理アプリ「SHALIBO」を作成しました。 ・読んだ本をアウトプット ・おすすめしたい本を投稿 ・要約された本をユーザー同士でシェア ポートフォリオの制作背景 本の内容が要約された読書管理アプリです。 購入前に『どんな本だったか知りたい』、『本の内容をアウトプットしたい』、『要約された本の内容を知りたい』、『読む時間を短縮したい』、『本を持ち歩かなくても本の内容をいつでも振り返りたい』と思った背景から開発しました。 機能一覧 認証機能 ユーザー登録 ログイン機能 ログアウト機能 アカウント削除機能 CRUD 機能 本のタイトル検索 本の投稿機能 投稿内容編集機能 本の削除機能 詳細一覧機能 使用技術 フロントエンド Vue.js VueRouter Vuex Vuetify バックエンド Firebase(Authentication) GoogleBookAPI / WebStorage(LocalStorage) サーバー Netlify ポートフォリオのURL GitHub: https://github.com/oga0927/Book_Library_Vue.js URL: https://shalibo.netlify.app UserName: test E-mail: shalibo@test.com Password: password 何ができるのか 1. ユーザー登録 ヘッダーのユーザー登録を押して、フォームにUserName、Email、Passwordを入力して登録。 (ユーザーネームを入力しない場合はゲストログイン名として表示されます) 2. ログイン アカウント登録済みの場合はフォームにEmailとPasswordを入力してログイン。 3. トップページ 最初にアクセスするとトップページの画面が描画されます。ヘッダーにログイン、 ユーザー登録を配置して、router-linkでフォームを描画しています。 4. ユーザー認証 ・ユーザー登録と同時にユーザー情報をfirebaseのAuthenticationに保存しています。 ・ログインするとヘッダーにホーム、投稿する、マイページ、ログアウトのボタンが表示されます。 ・firebaseのAuthenticationからuseridを取得し、storeに格納、stateからuseridを呼び出し、 投稿するときにlocalstorageのuseridと紐づけてv-forで一覧表示。 ・トップページでは投稿した本の一覧画面が描画されます。v-ifで認証状態を判別し、 『おすすめの一冊を投稿ボタン』を表示させています。 5. 検索 『 おすすめの一冊を投稿 』ボタンを押すと本の検索画面に遷移します。 本のキーワードを入力して検索ボタンを押すと、非同期処理でGoogleBookApiからキーワードと一致した本を取得します。 最大40件表示され、検索結果から投稿したい本を選択できます。 6. 投稿 本の【+ボタン】を押すと『読んだ日付』、『この本にはどんな情報が書かれているか』、『自分はこの本から何を学んだか』、『この本がなぜ重要なのか』、『テーマに対しどのような事例を出しているか』、『この本が他の本と似ている所、違う要素は何か』を入力して投稿ボタンを押すと投稿できます。 投稿した本は、他のユーザーが書き込めないようにログイン中のuseridとlocalstorageのuseridと紐づいた本のみ削除と投稿ボタンの表示させています。 投稿した本の削除ボタンを押すと、アラートでメッセージが表示され、OKボタンを押すと localstorageから該当する本のデータが削除されます。 ログイン中はlocalstorageのuseridとfirebaseのuseridと紐づいている本のみ削除ボタンが表示。 ヘッダーのロゴ(SHALIBO)を押すとトップページへリダイレクト。 7. マイページ 投稿した本の一覧を表示。 編集ボタンで投稿した本の内容を修正。 削除ボタンを押すとlocalstorageに保存されているデータが削除され、トップページ、マイページからも削除されます。 アカウント削除ボタンをクリックすると、firebaseのAuthenticationからuseridが削除されます。 8. レスポンシブ対応 Vuetifyを使用してスマートフォンからでも使用可能 デバイスによってハンバーガーメニューを実装 9. 工夫したところ(機能面) ①フォームにバリデーションを実装 ・目のマークをクリックで入力したパスワードを確認。 ・登録済みのアドレスはアラートでお知らせ Vue.js <template> <v-form ref="form" v-model="valid" lazy-validation> <v-main> <v-card width="500" class="mx-auto mt-5"> <v-toolbar dark color="primary"> <v-card-title>ユーザー登録</v-card-title> </v-toolbar> <v-card-text> <v-text-field name="username" label="UserName" type="text" v-model="userName" prepend-icon="mdi-account" required data-cy="userNameField" /> <v-text-field name="email" label="Email" type="email" v-model="email" :rules="emailRules" prepend-icon="mdi-email" required data-cy="registerEmailField" /> <v-text-field name="password" label="Password" :type="showPassword ? 'text' : 'password'" v-model="password" :rules="passwordRules" prepend-icon="mdi-lock" :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'" @click:append="showPassword = !showPassword" data-cy="registerPasswordField" required /> </v-card-text> <v-divider></v-divider> <v-card-actions> <v-spacer></v-spacer> <p mr-4>既にアカウントをお持ちですか? <router-link to="/login">ログインはこちらから</router-link> </p> <v-spacer></v-spacer> <v-btn color="error" @click="submit" :disabled="!valid" data-cy="registerSubmitBtn" >登録</v-btn > </v-card-actions> </v-card> </v-main> </v-form> </template> <script> export default { name: 'Register', data() { return { valid: false, showPassword: false, userName: '', email: '', password: '', emailRules: [ v => !!v || "メールアドレスを入力してください", v => /.+@.+/.test(v) || "正しいメールアドレスを入力してください" ], passwordRules: [ v => !!v || "パスワードを入力してください", v => v.length >= 6 || "パスワードは6文字以上で入力してください" ] } }, methods: { submit() { if (this.$refs.form.validate()) { this.$store.dispatch("userRegister", { userName: this.userName, email: this.email, password: this.password }); } }, }, } </script> <style scoped> p a{ text-decoration: none; } </style> ② 質問テンプレートについて ・読んだ本が記憶に残る読書術、読書ビジュアライジング法をアプリに取り入れました。 (本来は質問事項がもっと多い、一部抜粋) 10. 工夫したところ(実装面) ①データの永続化 ・再描画でlocalStorageのデータが初期化されないよう設定 ・vuex-persistedstateをインストールでVuexのステート情報を保存 ・vuex-persistedstateの設定をVuexのStoreのpluginsに設定 store.js import createPersistedState from 'vuex-persistedstate'; export default new Vuex.Store({ }, state: { }, mutations: { }, actions: { }, getters: { }, plugins: [ createPersistedState({     key: 'example',  storage: window.sessionStorage })] }); ② マイページでユーザー名の表示、本の編集、削除 ・アカウント登録時に設定したユーザー名が表示され、登録時に設定していない場合は『ゲストログインさん』と表示。v-ifで認証状態を判別してユーザー名を表示させています。 ・マイページから投稿した本の内容を振り返れます。 Profile.vue <template> <div> <v-row> <v-col cols="12"> <p v-if="getStateUserName">こんにちは! {{ getStateUserName }}さん </p> <p v-else>こんにちは!ゲストユーザーさん</p> <p> <v-btn color="orange lighten-1" to="/search" > 本を投稿する </v-btn> </p> <p> <v-btn color="error" class="delete-btn" @click="deleteUser" > アカウントを削除 </v-btn> </p> <v-col cols="12" sm="6" md="6" v-for="(book, index) in books" :key="index" > <!-- 自分が投稿した本の一覧 --> <!-- 投稿した本のuseIdとログイン中のuserIdが同じのを表示 --> <v-card v-if="book.userId === $store.state.userId" class="mb-8"> <v-row> <v-col cols="3"> <!-- 画像が表示される --> <v-img :src="book.image"></v-img> </v-col> <v-col cols="9"> <v-card-title >{{ book.title }}</v-card-title> <v-spacer></v-spacer> <v-card-actions> <!-- 書き込み --> <v-btn :to="{name: 'BookEdit', params: {id: index}}" color="primary" class="mx-1" > 編集する </v-btn> <v-spacer></v-spacer> <v-btn color="error" @click="deliteLocalStorage(index)" > 削除 </v-btn> </v-card-actions> </v-col> </v-row> </v-card> </v-col> </v-col> </v-row> </div> </template> <script> const STORAGE_KEY = 'books' export default { props: { books: Array, }, name: 'Profile', data() { return { user: this.$store.getters.getStateUser, userName: '' } }, methods: { deleteUser() { this.$store.dispatch("userDelete"); }, saveBooks() { const parsed = JSON.stringify(this.books); localStorage.setItem(STORAGE_KEY, parsed); }, deliteLocalStorage(index) { const isDeleted = 'データを削除してもいいですか?' if(window.confirm(isDeleted)) { this.books.splice(index, 1) this.saveBooks(); this.books = [] window.location.reload() } }, }, computed: { getStateUser() { return this.$store.getters.getStateUser; }, getStateUserName() { return this.$store.getters.getUserName; }, isAuthenticated() { return this.$store.getters.isAuthenticated; } }, } </script> 13. ポートフォリオの課題 ・localStorageへ保存しているデータをfirebaseに移行。 (一時的にデータを保存する場合はlocalStorageが適してるが、データの保存先としては同期処理のため大きなデータを扱うと処理が止まる) ・いいね機能、ソート機能を用いていいね数が多い本を表示 ・デバイスによってサイドナビゲーションが予期せぬ表示をする ・ユーザーが投稿した本を検索する機能 14. 最後に ここまで読んでいただきありがとうございます。 ソースコードでご指摘等ございましたらご教授いただけると幸いです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuexについてざっくりまとめてみた

Vuexとは? JavascriptのフレームワークであるVue.jsを使ったアプリケーションで用いられる状態管理ライブラリ。 つまり、Vueのプロジェクト内で使われているデータをより扱いやすくするためのの管理システム。 なんのためにある? アプリケーション内にあるデータのやりとりの利便性や可読性を上げるため。 アプリケーション全体で使用するデータをコンポーネント毎ではなく、一つの場所でまとめて管理するという考え方。 Vuexを使用せずに、図内左端のコンポーネントにあるデータを右端のコンポーネントで使用する場合、propsや$emitが繰り返され、コードの可読性が低下する(データが追いにくくなる)。 Vuex内でのデータの動き
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsエコシステムのVue 3対応状況

(フロントエンド強化月間の参加記事です。) Vue 3がリリースされてからしばらく経ち、周辺ライブラリも徐々にVue 3対応が進んできている。Vue.jsエコシステムのVue 3対応について、2021年5月2日時点での最新状況を整理した。 概要としては、Vue RouterやVuexなどコア部分のライブラリは対応が進んでいるが、UIフレームワーク等はまだ追いついていない、という状況となっている。また、近々にVue 3にVue 2互換モードが実装されるようで、これで当面の互換性を維持することも選択肢となりそうである。 Vue 3対応状況(2021年5月2日時点) Vue 3対応 最新版 Vue 3対応 stableリリース 参考リンク Vue 3.0.11 2020/09/19 Vue CLI 4.5.12 2020/10/07 *1 Vue Router 4.0.6 2020/12/07 Vuex 4.0.0 2021/02/02 Vue Devtools 6.0.0 beta 8 不明 Vue.js Migration Guide Vetur 0.33.1 不明 Vue.js Migration Guide Vuetify 未対応 2021 Q3 (予定) Roadmap Boostrap Vue 未対応 未定 Vue 3 support #5196 Nuxt.js 未対応 (2121 Q2 Public Beta) Vue 3 Support #5708State of Nuxt {2,3} *1 Vue 3.0.0をインストールするようになった正確なバージョンは不明 参考 Vue.js Migration Guide - Supporting Libraries Vue.jsの主要UIフレームワークのVue3対応状況を見てみる(2020年12月版) Vue3 compatibility status of central vue libraries #3544 (2020年11月最終更新) Vue Roadmap (2020年9月以降更新なし)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モナコインで投げ銭できる機能をつけたQiitaみたいなサービス開発してみた

モナレッジというサービスを開発しました モナレッジはqiitaやzennのようにmarkdownで記事を書いて投稿できるサービスです。 一番の特徴は、良い記事を書いた著者に対してMONACOINを投げつけることができることです。将来的にはmonapartyを使ったトークンとかも扱えるようにしたいと思っています。 開発経緯 qiitaで記事を書いてくださっている皆さんの情報発信のありがたさは常日頃感じていますが、その感謝を送る方法として「いいね」や「シェア」くらいしかできないことにもどかしさを感じていました。 インターネット上でお金を扱う方法として、bitcoinに代表されるような、公開鍵暗号の方式を利用したUTXO構造でお金を動かすというのは結構よくできた仕組みだなぁと思っています(もちろん、すべて自己責任なのでGOXとかしたときとかに何もすることができないデメリットもありますが)。 とりわけ、投げ銭などの使い道っていうことだと、モナコインとかの方がクレカ登録して投げ銭とかよりもお気軽だし、有事の際のリスクも低いんじゃないのかなぁとおもっています。 ちょっと動かす用のウォレットを作成して(これはめっちゃ簡単にできる。BIP32以降の仕組みによって、リカバリーフレーズさえちゃんと管理すれば、複数の秘密鍵の管理もそんなに苦ではない)、そこに数MONA入れたじょうたいで投げ銭をするというのも良いだろうし、 まだMONAを持っていない人は、bitbankとかの取引所で購入するか、記事を書いてMONAをもらったりするのが良いかも。 開発の思い 僕が、いわゆる暗号資産にかんするところで違和感を感じているのが、投機の対象にばかり注目が集まりがちなところです。 まあ、ボラティリティが大きくなるような仕組みなのでその手のものが好きな人は好きなんでしょうが、個人的にはこの技術を使ってどんな新しいサービスができるのかなぁというところの方が気になるところではあります。 このサービスではサインアップの際に、パスワードが必要ありません。どうしてそんなことができるかというとデジタル署名を利用しているからです。 このサービスではログインをする代わりに、記事の投稿時などユーザの識別が必要なタイミングではデジタル署名を求めるようにしています。 デジタル署名に関してわからない方は、これを機に公開鍵暗号などと合わせて一度勉強してみてください。暗号資産を扱う上で知っておいた方がいいと思います。 ユーザ認証のタイミングで署名を求め、バックエンドで署名を検証することでユーザを認証する仕組みをとっています。 そんなこんなでweb3.0っぽいけど、おそらくピュアな3.0じゃないので2.5くらいなのかな? 機能 記事を書く人はmdで記事を書けます 読んだ人は、良いなと思った記事に投げ銭をすることができます。 開発技術 ここからはエンジニアが興味ありそうな部分 フロント : Vue, Nuxt バックエンド : Node.js, Express, postgres 基本的には、フロントで署名を作成し、バックエンドでリクエストの検証をデジタル署名の検証で行うという流れになります。 なので、いわゆる一般的なwebサービスなどとは一味違ったUXになっていると思います。 その辺は使って感じてみてください 大事なのは, mpurseという拡張機能。 Ethereumのmetamaskに相当するものですね。 これが、送金、署名作成(厳密にはこれが送金の大部分でもあるんだけど、そこは割愛)などをフロントのロジックでやってくれます。 これにより、バックエンド側にユーザの機密情報(カード情報、パスワード、etc)を持たずとも、ユーザを認証し、送金機能を持たせることが可能になります。 これは開発者にとってはかなり嬉しいことで、 お金とユーザ情報を扱うサービスなのに、サーバ側にその情報を保存しておかなくて良いので、とても開発しやすいです。 これは、公開鍵暗号の仕組みがベースになっていて、 暗号を利用したアプリケーションの台頭で、クライアントが秘密鍵を持ち始める動きが進んことで、なし得ることができる設計です。 まだまだ、この分野は開拓途中なので、みんなで盛り上がって 楽しんでいきましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】個人開発したサービスを半年ほど運用して気づいたこと【お絵描きアプリ】

はじめに こんにちわ! 3年目ペーペーエンジニアです。 今回は、去年の11月にリリースした超本格なお絵描きサービスの話です。 まず、言いたいのが、 実際にリリースしたサービスが過疎りすぎてヤバい まじで過疎ってます。 個人開発である以上はある程度は仕方ないと思っていますが、 プライベートな時間を削って半年以上かけた開発だったのでそれはそれで悲しいのです。 作ったときの詳細の話は、前回の記事をご覧ください。 実際のアプリは以下のリンクから 『お絵描き道場(旧名:PaintMonitor)』は、 Qiitaでそれなりの方がみてくださったおかげで リリース当初何人かの利用者がいたもののあまりの過疎具合に、 もはや利用者が自分のみになってしまいました。 そこで、個人開発したサービスを運用してみて感じたポエムをつらつらと話していこうかと思います。 まずは、リリースしてよかった事から。 リリースしてよかった事 開発能力が向上した 開発力を上げるのに、個人はすごいおすすめです。 私は、2年前に他業種からITエンジニアに転職したのですが、 正直2年前までは、DBすら何かもわかっていないお粗末な技術力でしたが、 今回の個人開発を通して、DB設計からサーバーのデプロイ作業など、 開発の0から1をすべて行ったおかげで、とても技術力が向上しました。 Ubuntuのコマンド操作や、APサーバー、WEBサーバーの概念、AWSなどのクラウドサービスの利用など インフラ周りの理解も深まります。 また、今回挑戦したVue.jsを使ったSPA開発経験のおかげで、 ほぼ初めてみるReactをかなり扱えるようになってたりと、自身の成長に少し感動できます。 機能追加等のアップデート作業が純粋に楽しい 追加開発して自分の思うようにより良いサービスにしていくのはかなり楽しかったです。 リリース当初から、毎週のようにアップデートを行ったおかげでかなり機能が増えました。 配信機能やコース機能など、『こういう機能があったらいいなぁ』を自分本位で追加していけるのでこれぞ個人開発の醍醐味といったものを感じました。 ベースとなるサービスの根幹はできているので、追加開発自体は結構楽です。 自身の実績として残る いつかフリーランスになったときなど、仕事をもらう上で 『こういったことが出来ます!』といった1つの指標になります。 実際にサービスを運用して、ブログや記事を書いてから、そこそこヘッドハンティングが来るようになりました。 仮に仕事がなくなっても食っていけるかもしれないという自信につながります。 リリースした後の反省点 リリース当初は思った以上にバグる 個人開発のため、いい意味で大胆になれます。 バグなんて見つけたらその都度直せばいいでしょの精神でリリースしてみたら、 時間が世界時間になっているわ、せっかく描いた絵が消えるわ、小さなバグから致命的なバグまで結構見つかりました。 小さなバグの場合はいいですが、せっかく数時間かけて描いた絵が一瞬で消えた絶望感は半端じゃなかったです。 サービスがニッチすぎる 作ったアプリは本格的なペイントツールを利用した画力向上を目的としたアプリです。 私は絵を描くことが好きですし得意なので、このようなサービスを作ろうと思ったのですが、 実際にこのサービスを求めてる人はどれくらいいるだろうと考えると、 このサービスを利用するターゲットは、絵を描く人であり、絵を効率よく練習したく、さらにITに強い人、となります。 さらにこのような客層は、アニメ系の絵を描く方にはウケといいのですが、 私が個人的にアニメ絵以外のアーティストに向けたサービスを作りたかった旨もあって、 サイトのテイストが小洒落た油絵チックなテイストになってしまい、完全にターゲット層が消失しました。。笑 まとめ 個人開発したサービスは流行らせようとしたらダメだ!!!!! 気が向いたら、ぜひ一度、利用してみてください! 私も時々配信しています(笑) お絵描き道場 ではまた!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】modelプロパティでコンポーネントを疎結合する

はじめに 社内システムのフロントエンドにVue.jsを利用しています。 コンポーネント間でデータのやり取りをpropsやemitでやっていたのですが、 あまりにもそれらの量が多くなりコンポーネントどうしが密結合してしまいました。 状態はVuexで管理するとして、 「ダイアログの開閉動作もなんとか分離できないか?」 ということで、modelプロパティを利用して疎結合にしました。 やること modelプロパティを使う場合と使わない場合の、ダイアログの開閉動作の実装の違いを見て、 同プロパティが疎結合に寄与していることを確認します。 ダイアログは、VuetifyのVDialogを利用し独自に定義したコンポーネントを使うといった想定です。 ※前提知識として、Vue.jsでは親の値を子が直接変更すると、動きはするものの怒られるということを知っておいてください。 環境 今回実行した環境は以下です。 Vue.js 2.6.12 Vuetify 2.4.3 ソースコード 1. modelプロパティを使わない場合 Parent.vue <template> <div> <v-btn depressed @click="openDialog"> Open </v-btn> <dialog :value="value" @close="close"> </div> </template> <script lang="ts"> import Vue from 'vue'; import dialog from '@/dialog'; export default Vue.extend({ name: "Parent", data: { value: false }, methods: { openDialog() { this.value = true; }, close() { this.value = false; } } }) </script> Dialog.vue <template> <div> <v-dialog v-model="value"> <v-card tile> hoge </v-card> </v-dialog> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ name: "Dialog", props: { value: { type: Boolean, default: false } }, methods: { close() { this.$emit("close", false); } } }) </script> 2. modelプロパティを使う場合 Parent.vue <template> <div> <v-btn depressed @click="openDialog"> Open </v-btn> <dialog v-model="value"> </div> </template> <script lang="ts"> import Vue from 'vue'; import dialog from '@/dialog'; export default Vue.extend({ name: "Parent", data() { value: false }, methods: { openDialog() { this.value= true; } } }) </script> Dialog.vue <template> <div> <v-dialog :value="value" @input="(val) => val || close()" > <v-card tile> hoge </v-card> </v-dialog> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ name: "Dialog", model: { prop: "value", event: "change-dialog" }, props: { value: { type: Boolean, default: false } }, methods: { close() { this.$emit("change-dialog", false); } } }) </script> 何が変わったか 1. modelプロパティを使わない場合 やっていること propsでvalue変数(ダイアログの開閉状態)を渡している エラーを避けるため、ダイアログを閉じる処理はParent.vueが担っている 良くない点 処理がParent.vueに依存する部分があるため、コンポーネントが密結合している 閉じるためにモーダル部分をクリックすると、Dialog.vueがParent.vueのvalue変数を直接書き換えるためエラーとなってしまう Dialogコンポーネントを使いまわそうとすると、そのたびに親にダイアログを閉じる処理を書かないといけない 2. modelプロパティを使う場合 やっていること v-modelを経由して、ダイアログの開閉状態をDialog.vueに渡している v-modelで渡した状態をmodelプロパティ(とprops)で受け取っている Dialog.vueの@inputイベントでダイアログの開閉状態を監視している 良い点 処理がDialog.vueで完結しているため、1に比べてコンポーネントが疎結合になっている モーダル部分をクリックして閉じたとしても、エラーとならない 親コンポーネントが簡素になる 最後に modelプロパティを使うことでコンポーネントを疎結合にすることが出来ました。 子コンポーネントにmodelプロパティとprops両方を書かないといけず、記述量が増えるのが難点ですが 疎結合になることで使いまわすことが容易になりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む