- 投稿日:2020-11-26T23:25:51+09:00
Vue + axios + Spring BootでCORSでクマった時の対処法
CORSエラーで絶対に躓かないために
CORSとは何か
正式名称は"Cross Origin Resource Sharing"
セキュリティの観点から、リソースを制限するものだと思います。
詳しくは、こちらの記事がより丁寧に説明してくれています。?
https://qiita.com/att55/items/2154a8aad8bf1409db2b実際の実装
axios側の設定?
originには、postのparamsが含まれています。
import service from '../utils/service' export const api = { async getRoute(origin) { return await service .post('http://localhost:8080/getRoute', origin, { withCredentials: true, }) .then(result => { return result.data }) .catch(result => {}) }, }serviceでは、axiosの設定そしています。
import axios from 'axios' import { api } from '../config/api' const service = axios.create({ timeout: 30000, headers: { 'Content-Type': 'application/json;charset=UTF-8', }, emulateJSON: true, withCredentials: true, })Vue側の設定?
methods: { async searchRoute() { const params = { origin: this.markers.marker[0].position, direction: this.direction, } const result = await api.getRoute(params) },Vueのmethodsのところで呼び出します。
Spring Boot側での設定
私は、Springfilterを使ってnetworkのconfigを設定しました。
Vueはlocalhost:9000で動かしているので、9000を許可します。CorsConfig.java
@Configuration public class corsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(new ArrayList<>(Arrays.asList("http://localhost:9000"))); config.setAllowCredentials(true); config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.addExposedHeader("Set-Cookie"); UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); return new CorsFilter(configSource); } }Controller.java
@CrossOrigin(origins = {"http://localhost:9000"}) @RestController public class RouteController { @Autowired RouteManager routeManager; @PostMapping("/getRoute") public DirectionsResult sendGetRouteReq (@RequestBody GetRouteReq req) throws InterruptedException, ApiException, IOException { DirectionsResult result = routeManager.getRoute(req.getOrigin(), req.getDirection()); return result; } }こんな感じで実装しました。
参考になりましたら幸いです。
- 投稿日:2020-11-26T17:24:26+09:00
Windows10のPCにVueCLIを使用してVue.jsの環境を構築
〈下準備〉
Node.js、IDE環境(今回はVSCode)のインストールを行います。①Node.jsのインストール
下記のURLよりNode.jsのインストールを行います。
https://nodejs.org/ja/※VueCLSを使用するにはNode.jsのバージョンは8.9以上がインストールされている必要があります。
すでにNode.jsはインストールされているがバージョンが8.9未満の場合はインストールを行いましょう。【確認方法】
コマンドプロンプトで「node --version」のコマンドを実行実行すると画像のようにバージョンが表示される
②VSCodeのインストール
下記のURLよりインストールを行います。
https://azure.microsoft.com/ja-jp/products/visual-studio-code//-----------------------------------------------------------------------------------------------------/
ここからが実際のインストールです。
ここからのコマンドはVSCode上のターミナルからすべて実行します。【ターミナルの表示方法】
VSCodeメニューの「View」→「Terminal」と選択してください。①Vue CLIのnpmパッケージをインストール
次のコマンドを実行します。
npm install -g @vue/cli処理が走るので、一通り終わったところで下記のコマンドを実行します。
vue -Vコマンド実行後にバージョン情報が表示されたらインストールは成功です。
次はプロジェクトの作成方法について書きます。
- 投稿日:2020-11-26T17:05:59+09:00
Vue-CLI + Vuexでポケモンのパーティを構築するSPAをつくる。
概要
ポケモンのパーティを構築して遊べるSPAです。
VueでSPAを作りたいなーと思って作り始めた。
加えて、listの高速なフィルター機能も使いたい。
実装はGitHubPagesを使ってフロントエンドのみ。技術・ライブラリ
- Vue-CLI
- Vuex
- lodash
- moji
- axios
- ityped
- babel-polyfill
- reset-css
- progressbar.js
つくったもの
パーティをつくる
ポケモンずかんからポケモンをピックアップし、パーティを構築する。
パーティはVuexのstoreで管理し、てもち画面で確認できる。
ポケモンの詳細をみる
vue-routerを利用して詳細画面を生成。
ポケモンのステータスを確認できる。詳細設計
ポケモンのステータス、画像
axiosで外部APIから取得。
フォント
検索機能
computed()
でフィルター、ソートを実現。
早くてかっこいい。(全データを取得してるだけだから数が大きくなると問題だけど。)ソート
- なまえ
- ばんごう
フィルター
- なまえ検索
- タイプ
どれもjsで操作しているだけなのでレスポンスが早い。
さらになまえ検索は日本語入力未決定前でも検索できるようにしている。早い。vue<input @input="handleInput"></input>jshandleInput(event) { this.wd = event.target.value //未決定の値を取得 },タイプフィルターはチェックボックスから選ぶ。
おわり
細かい実装やその方針などは省略していますが、素人ながらかなり手こずりました。
良い勉強になりました。
- 投稿日:2020-11-26T15:46:45+09:00
【Nuxt.js】Vuex基礎編②stateを複数使ってみよう
? この記事はWP専用です
https://wp.me/pc9NHC-en前置き
今回は前回の基礎編に続き、
stateが複数ある場合の書き方です✍️
基礎編でVuexの基本的な解説はしています!
また基礎編のコードに追記するので
そちらを確認しながらやってみてください?Vuex基礎編はこちら
https://note.com/aliz/n/n497914c981c8やりたいこと
基礎編のカウンターを2つに増やします!
これだけ!!!NGパターン
まずはNGパターンから。
まずはstateにsubCounterを追加。
mutationsなどにも同様に
subCounterについて追記します✍️が!
これだと後述したsubCounterに
全てがまとまってしまいます。。。? 続きはWPでご覧ください?
https://wp.me/pc9NHC-en
- 投稿日:2020-11-26T13:57:59+09:00
Vue-CLIを使ってVue.js開発環境をお手軽に実装する
はじめに
Vue-CLIをつかって最速でVueアプリケーションの雛形を作成します。今回はnpmを使用するので、あらかじめnode.jsがインストールされている前提で進めていきます。
参考環境
- apple MacBook (Retina, 12-inch, Early 2016)
- macOS Catalina 10.15.4
Vue-CLIとは
Vue-CLIは、Vue.jsでアプリケーションを構築する際に、開発環境をセットアップしてくれたりする公式のコマンドラインツールです。今回解説するプロジェクトの作成以外にもいろいろできるみたいです。
Vue-CLIのインストール
早速Vue-CLIをインストールしていきます。Macの場合はターミナルを、Windowsの場合はコマンドプロンプトを開いて下記のコマンドを実行すると最新バージョンがインストールされます。(記事作成時の最新は4.5.9)
npm install -g @vue/cli vue --version #バージョンの確認プロジェクトの作成
次にプロジェクトを作成したいディレクトリに移動して
vue create myproject #プロジェクト名実行すると
Vue CLI v4.5.9 ❯ Default ([Vue 2] babel, eslint) Manually select featuresと表示されます。特定のライブラリを使用する予定がない場合は
Default ([Vue 2] babel, eslint)
かDefault (Vue 3 Preview) ([Vue 3] babel, eslint)
を選択してエンターするだけでプロジェクトが作成されます。これらを選択した場合も後からライブラリの追加ができるようです。プロジェクトをマニュアルで作成する
Manually select features
を選択すると下記のように表示されます。? Check the features needed for your project: ❯◉ Choose Vue version ◯ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◉ Router ◉ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ◯ Unit Testing ◯ E2E Testingカーソルキーで移動、スペースキーで選択ができます。今回はRouterとVuexを選択しました。選択した状態でエンターするとVueのバージョンと各ライブラリの設定を行うことができます。
プロジェクトの動作確認
cdコマンドでプロジェクトのディレクトリに移動し、npmでサーバー(http-server)を起動して動作を確認します。
npm run serveApp running at: - Local: http://localhost:8080/ - Network: http://192.168.10.30:8080/ Note that the development build is not optimized. To create a production build, run npm run build.サーバーの起動に成功すると上のように表示されるので、ブラウザでhttp://localhost:8080/にアクセスします。
このように表示されていればプロジェクトの作成は完了です!追記
Vue-CLIを使用して作ったプロジェクトで認証機能を作成しました!よかったらこちらもご覧ください。
- 投稿日:2020-11-26T13:45:06+09:00
[vue.js] クリックすると編集可能に切り替え、入力した後、input value を取得する(v-if/v-show $refs)
参考先「1」でinput実装したけど、更新されたinput値はどうやって取得するかは苦戦した!
v-ifを使う時に、
$refs
でinput valueなどを取得したいのに、undefined になっていることがよくあります!($refs.input
まで値出ているけど、$refs.input.XXX
にすると、undefinedになった)色々調べて、原因はわかった、参考先「2」より
ref の登録タイミングに関する重要な注意事項として、参照自体は、render 関数の結果として作成されているため、最初の描画においてそれらにアクセスすることができません。それらはまだ存在しておらず、$refs はリアクティブではなく、従ってデータバインディングのためにテンプレートでそれを使用すべきではありません。
のためです!
下記の方法で一応、編集モードからテキストモードに切り替えると、更新されたinput値を取得できた!*** IDを付与して、document.getElementById('post_wgt').innerTextでvalueをgetします ***
<template> <div id="app"> <div class="text" id="post_wgt" v-if="!edit" v-text="value" v-on:click="edit = true"></div> <b-inform-input v-if="edit" type="text" ref="input" v-model="value" v-on:blur="edit = false" v-focus></div> <b-button @click="onSave()" variant="outline-info" >保存</b-button> </template> <script> export default { data () { return { edit: false } }, directives: { focus: { inserted: function (el) { el.focus() } } }, methods: { onSave () { this.$nextTick(() => { console.log(document.getElementById('post_wgt').innerText) )} } } } </script>
- 投稿日:2020-11-26T00:20:54+09:00
Vue.jsで上手く初期表示が出来なかったときの謎を再レンダリングで無理やり解決した
Vue.jsとFirebaseを利用して、Cloud Firestore からデータを読み込んで初期表示させたかったが、上手く初期表示できず苦しんだのでメモ。
事象
データベース(Firebase)から取得したデータを、Vue.jsの"Test[0]"と"Test[1]"に入れて表示させたかったが、何故か表示がされなかった。コンソールで見るとちゃんとデータは取得出来ているのに、、謎。
取り敢えずv-ifのレンダリングで無理やり解決させた。データベースに保存した内容が表示される。
v-ifの再レンダリングで無理やり解決したコード
無理やり解決させた時の全体コードを書いておく。(あまり良くない解決の仕方ではあると思う。)
.html<!DOCTYPE html> <html lang="jp" > <head> <meta charset="UTF-8"> <title>Vuejs Rerender</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <!-- 全体をVue.js有効にする --> <div id="app" v-if="Show === 1"> <input v-model="Test[0]"> <input v-model="Test[1]"> </div> <script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/firebase/8.0.1/firebase.js'></script> <script> // Your web app's Firebase configuration var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "xxxx.firebaseapp.com", databaseURL: "https://xxxx.firebaseio.com", projectId: "xxxx", storageBucket: "xxxx.appspot.com", messagingSenderId: "999999999999", appId: "1:999999999999:web:xxxxxxxxxxxxxxxxxxxxxx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); const db = firebase.firestore(); const app = new Vue({ el: '#app', // Vueが管理する一番外側のDOM要素 data: { // Vue内部で利用する変数定義 Test: ["0000","1111"], Show: 0, // 再レンダリング用 }, created: async function (){ // Firebaseから初期値を読み込み await this.FirebaseSet(); console.log(`Test[0]:${this.Test[0]},Test[1]:${this.Test[1]}`); // 上手いこと初期表示できないから無理やりレンダリング this.Show = 1; }, methods: { // 関数はココに記述 FirebaseSet: async function() { // CloudFirestoreからデータ取得 let docRef = db.collection("hpcaccede").doc("User01"); let me = this; // thisを関数内で使えないので変数に代入 await docRef.get().then(function(doc) { if (doc.exists) { me.Test[0] = doc.data().test1; me.Test[1] = doc.data().test2; } else { console.log("No such Cloud Firestore document!"); } }); }, }, }); </script> </body> </html>ポイント
全体に対して、v-ifで再レンダリングさせる。
.html<div id="app" v-if="Show === 1">"Show"変数の初期値は"0"として非表示。
.jsconst app = new Vue({ el: '#app', data: { Test: ["0000","1111"], Show: 0, // 再レンダリング用 },});"created"の最後に"this.Show"を"1"に更新して、再レンダリングさせる。
.jscreated: async function (){ await this.FirebaseSet(); console.log(`Test[0]:${this.Test[0]},Test[1]:${this.Test[1]}`); // 上手いこと初期表示できないから無理やりレンダリング this.Show = 1; },原因と思われる個所を特定した
色々切り分けていくと、配列が悪さをしているっぽいことが判明。
vue.jsのルールなのか、私の書き方が悪いのかは知らんけど、Test配列を止めたら再レンダリングせずに上手く表示できた。(body要素内だけ記述しておく。).html<body> <!-- 全体をVue.js有効にする --> <div id="app"> <input v-model="Test0"> <input v-model="Test1"> </div> <script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/firebase/8.0.1/firebase.js'></script> <script> // Your web app's Firebase configuration var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "xxxx.firebaseapp.com", databaseURL: "https://xxxx.firebaseio.com", projectId: "xxxx", storageBucket: "xxxx.appspot.com", messagingSenderId: "999999999999", appId: "1:999999999999:web:xxxxxxxxxxxxxxxxxxxxxx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); const db = firebase.firestore(); const app = new Vue({ el: '#app', // Vueが管理する一番外側のDOM要素 data: { // Vue内部で利用する変数定義 Test0: "0000", Test1: "1111", //Show: 0, // 再レンダリング用 }, created: async function (){ // Firebaseから初期値を読み込み await this.FirebaseSet(); //console.log(`Test[0]:${this.Test[0]},Test[1]:${this.Test[1]}`); console.log(`Test0:${this.Test0},Test1:${this.Test1}`); // 上手いこと初期表示できないから無理やりレンダリング // this.Show = 1; }, methods: { // 関数はココに記述 FirebaseSet: async function() { // CloudFirestoreからデータ取得 let docRef = db.collection("hpcaccede").doc("User01"); let me = this; // thisを関数内で使えないので変数に代入 await docRef.get().then(function(doc) { if (doc.exists) { // me.Test[0] = doc.data().test1; // me.Test[1] = doc.data().test2; me.Test0 = doc.data().test1; me.Test1 = doc.data().test2; } else { console.log("No such Cloud Firestore document!"); } }); }, }, }); </script> </body>
- 投稿日:2020-11-26T00:14:12+09:00
Vue.js、emitで動かすかcallbackで動かすか
はじめに
Vue.jsでコンポーネント間のやり取りを記述する場合、親はプロパティ(props)を利用してデータを受け渡し、イベント(event)を利用して子コンポーネントの動作を購読する形式が一般的かと思います。
こうした中、TypeScript x Vue.jsでの実装をプロダクト開発で行っていく際に、propsにコールバック関数を渡したほうが型安全に記述でき、ランタイムエラーを減らせるのではないか?という意見が開発チーム内で挙がったため、従来のevent駆動形式とpropsにコールバック関数を渡す形式をそれぞれで比較し、どちらが型安全に開発ができそうか比較してみます。
前提
こうしたemit vs props-callback論争?はすでにいくつか意見があるようでした
vue, emitting vs passing function as props
「propで子に渡してeventで親に伝える」が基本理念というのは正しいようですが、callbackでも同じことは実現できるというようですね。
検証
ボタンをクリックするとconsoleにmessageを渡すような部品を作ってみて検証してみます。
実装したサンプルはこちらのGithubリポジトリに公開していますので、ご自由に参照ください。まずはevent駆動のボタン部品ですが、以下のように
発火するイベントで渡す値をEmitButtonClickEventValue
という型で指定する形です。
このボタンだけで見れば、クリックしたことを親に伝えることだけを意識すれば良く、その先を意識することはありません。EmitButton.vue<template> <button @click="emitClick">イベント駆動ボタン</button> </template> <script lang="ts"> import Vue from "vue"; import { Component, Emit } from "vue-property-decorator"; export type EmitButtonClickEventValue = { message: string; }; @Component export default class EmitButton extends Vue { @Emit("click") emitClick(): EmitButtonClickEventValue { return { message: "myEmitMessage" }; } } </script>一方で、callback駆動のボタン部品です。
こちらはevent駆動のものとは違い、CallbackButtonOnClickCallback
という名前でコールバック関数の型を定義しています。
また、executeClickCallback
関数内に記述している通り、親から渡されたコールバック関数が存在するかを意識する必要が出てきます。CallbackButton.vue<template> <button @click="executeClickCallback">コールバック駆動ボタン</button> </template> <script lang="ts"> import Vue from "vue"; import { Component, Prop } from "vue-property-decorator"; export type CallbackButtonOnClickCallback = (value: { message: string; }) => void; @Component export default class CallbackButton extends Vue { @Prop({ type: Function, required: false }) onClick?: CallbackButtonOnClickCallback; executeClickCallback() { if (!this.onClick) return; this.onClick({ message: "myCallbackMessage" }); } } </script>続いて、上記2つのボタンを利用する親のコンポーネントを見ていきましょう。
ここで、イベント駆動型ボタン部品と連動しているonEmitButtonClick
では、EmitButton.vue
のclick
イベントで渡される引数の型EmitButtonClickEventValue
をimportして利用する必要があります。
一方で、コールバック駆動型ボタン部品と連動しているonCallbackButtonClick
では、CallbackButton.vue
のpropに定義したコールバック関数の型CallbackButtonOnClickCallback
をimportして利用する必要があります。App.vue<template> <div id="app"> <emit-button @click="onEmitButtonClick" /> <callback-button :on-click="onCallbackButtonClick" /> </div> </template> <script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import EmitButton, { EmitButtonClickEventValue } from "./components/EmitButton.vue"; import CallbackButton, { CallbackButtonOnClickCallback } from "./components/CallbackButton.vue"; @Component({ components: { EmitButton, CallbackButton } }) export default class App extends Vue { // イベント駆動の場合、イベントから渡される値の型を知っている必要がある onEmitButtonClick(value: EmitButtonClickEventValue) { console.log(value.message); // -> 'myEmitMessage' } // コールバック駆動の場合、コールバック関数のインターフェースを知っている必要がある onCallbackButtonClick: CallbackButtonOnClickCallback = value => { console.log(value.message); // -> 'myCallbackMessage' }; } </script>どちらで実装すべきか
では、上記の2つの実装方式どちらを選択すべきでしょうか?
いくつかの観点から見てみます。型安全の観点
先ほど実装したコンポーネントの型が変化した際、壊れやすいのはどちらでしょうか?
改めてそれぞれの実装形式で定義した型をまとめると、それぞれexport type EmitButtonClickEventValue = { message: string; }; export type CallbackButtonOnClickCallback = (value: { message: string; }) => void;ですが、messageを配列で渡すような修正を行い、
export type EmitButtonClickEventValue = { messages: string[]; }; export type CallbackButtonOnClickCallback = (value: { messages: string[]; }) => void;に変更したとしましょう。
この場合、利用元のApp.vue
ではtype errorが発生し、変更を静的に検知することが出来ます。
そのため、既存の定義済の型を変更する分にはどちらの実装形式も特に変わりないといえます。一方、今回のevent vs callbackそのものには関係ないのですが、現在Vue.jsの制約として型テンプレート内と、eventで渡される型そのもの静的型推論は効かない(はず)なので、以下のようにそもそもの型を変更するような修正をした場合は親側でimportしている型を変更する必要があります。
EmitButton.vue<template> <button @click="emitClick">イベント駆動ボタン</button> </template> <script lang="ts"> import Vue from "vue"; import { Component, Emit } from "vue-property-decorator"; // 元の型を残したまま export type EmitButtonClickEventValue = { message: string; }; // 新しい型を作って export type NewEmitButtonClickEventValue = { message: number[]; }; @Component export default class EmitButton extends Vue { @Emit("click") emitClick(): NewEmitButtonClickEventValue { return { message: 12345 }; } } </script>Vue.jsの基本理念
冒頭にも記述した通り、Vue.jsのコンポーネントの基本理念はpropsとeventで親子のやり取りを行うことだと思います。
そのため、prop-eventパターンで記述できるのであればそちらを選択するのが安牌だと考えています。
いべn
またcallback形式を選択した場合、例えば以下のようにネイティブのHTMLElementと組み合わせた際にイベント駆動とコールバック駆動の記述が混在してしまう恐れや、sample.vue<template> <div> <input type="text" :value="myTextValue" @input="onMyTextValueChange"/> <callback-button :on-click="onClickCallback"/> </div> </template>コールバック関数用のpropの命名規則を固めないと、親から見た際にコールバック関数を渡しているのかプリミティブな値を渡しているのか判別しにくいという問題もあるとは思います。
sample.vue<template> <my-sample-component :name="myName" :my-value="myValue" :on-click="onClickCallback" :context-menu="contextMenuFunction" /> </template>結論
結論ですが、プロジェクトのメンバーと意見を擦り合わせつつ、好きなほうを選択すればいいと思います(最後に投げやりですみません)。
上記で見た通り、型推論の観点でもどちらも大差ないと思えますし、アプリケーションの要件はどちらの実装方式でも満たすことが出来ると思います。私はprop-eventに長く触れてきており、これからもこの形式を推していきますが、プロジェクトの状況・メンバーや、Vue.jsが進化していく中で常に考え、検証を行い、堅牢なアプリケーションを開発していくことが大事かなーと思いました。
最後まで読んでくれた方がいれば、駄文にお付き合いいただきありがとうございました。
うちはこうやってるよー、とか、もっとこうした方が良いのでは?等意見がありましたら頂けると嬉しいです。