- 投稿日:2019-11-12T23:57:56+09:00
laravelのbladeをVue.jsで書き換える【Form編】
はじめに
プログラミング学習をはじめ、10カ月です。
現在はLravelとVue.jsを学習しています。この組み合わせだと世間的には、SPAと決まっているのですか?
Vueも少しずつわかってきたので、laravelのbladeをVueに書き替えて、MPAを作り始めました。
基本のCRUD処理をと思いましたが、さっそくつまずきました。
register画面を作ろう
laravelやVue.jsの基本は理解している前提で進めていきます。
ポイントを押さえながら解説します
まずは基本となるregister.phpです。
register.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta name="csrf-token" content="{{ csrf_token() }}"> //① //中略(cssなど) <title>title</title> </head> <body> <div id="app"> <registerform-component :old="{{ json_encode(Session::getOldInput()) }}" //② :errors= "{{ $errors }}"> //③ </registerform-component> </div> <script src="{{ mix('js/app.js') }}"></script> </body>htmlではポイントは3つ
①laravelではcsrf対策を取らないといけません。
headにトークンを仕込んでおきます②フォームで重要なのは入力ミスがあった場合、元のフォームにリダイレクトされます。入力した内容は残って欲しいですよね。
laravelのbladeでは、oldヘルパーが使えたのですが、Vueでは使えませんでした。
ここはあとで解説します。③入力ミスがあった場合、何が間違っているのか、バリデーションによるエラーメッセージが表示されます。
laravelでは$errorsでどこからでも拾ってこれるそうですね。\$errors変数はwebミドルウェアグループに所属する、Illuminate\View\Middleware\ShareErrorsFromSessionミドルウェアによりビューに結合されます。このミドルウェアが適用される場合は、いつでもビューの中で\$errors変数が使えます。$errors変数はいつでも定義済みであると想定でき、安心して使えます。
https://readouble.com/laravel/5.5/ja/validation.htmlしかし、これもVueの中では素直に使えませんでした。
Componentを作ろう
今回はRegisterForm.Vueを作成しました。
予め使えるようコンポーネントの登録はしておいてください。RegisterForm.Vue<template> <form action="/register" method="POST"> //④ <input type="hidden" name="_token" :value="csrf"> //⑤ <div class="form-group"> <label>名前</label> <strong class="error" v-for="value in error.name">{{ value }}</strong> //⑥ <input class="form-control" name="name" type="text" v-model="name"> //⑦ </div> <div class="form-group"> <label>Email</label> <strong class="error" v-for="value in error.email">{{ value }}</strong> <input class="form-control" name="email" type="text" v-model="email"> </div> <div class="form-group"> <label>パスワード</label> <strong class="error" v-for="value in error.password">{{ value }}</strong> <input class="form-control" name="password" type="password" v-model="password" autocomplete="off"> </div> <div class="form-group"> <label>再入力</label> <input class="form-control" name="password_confirmation" type="password" v-model="password_confirmation" autocomplete="off"> </div> <input type="submit" class="button" value="登録"> //⑧ </form> </template> <script> //⑨ export default { props:[ 'old', 'errors' ], data:function(){ return{ csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'), name: this.old.name, email:this.old.email, password:'', password_confirmation:'', error:{ name:this.errors.name, email:this.errors.email, password:this.errors.password, } } }, }コンポーネントはこんな感じにしました。
④register登録は/registerにPOSTします。
⑤laravelではPOSTデータにcsrfトークンを仕込まないと、弾かれてしまいます。
scriptタグで取得したトークンをもたせます。
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content')
これは、このコンポーネントが入るregister.phpのheadに記述したものをとってきています。
⑥ここはエラー文ですね。後述します。
⑦はVueの基本の双方向バインディングですね。
Vーmodelでinputが変更されると、dataも変更されます。⑧でサブミットされますね。
先述の
<registerform-component
:old="{{ json_encode(Session::getOldInput()) }}" //②
:errors= "{{ $errors }}"> //③
</registerform-component>
の③で
コンポーネントに変数$errorsを渡します。
コンポーネント内ではpropで使用することができます。同様に②で入力したデータはセッションに保存されています。
これをgetOldInput()で取得できます。
これをjson_encode()でjsonデータに直してコンポーネントに渡しています。propで受け取ったデータは、thisで使用できます。
dataの初期値にthisで取得したデータを入れることで、入力したデータやエラーメッセージを表示させることができます。まとめ
こうして、無事にフォームができたのでした。
かなり時間かかりましたが、なんとかなりました。
コンポーネントは複雑化させていきますので、
次はvuexを導入します。
- 投稿日:2019-11-12T22:34:28+09:00
vue-metaを用いたOGPやJSON-LD用のメタタグ生成
vue-meta
https://github.com/nuxt/vue-meta・Title and Description
・OGP
・JSON-LD
を生成するにはvue-metaが便利。
弊社ではNuxtを使わず、Vue.jsで作っています。
Vue-metaはNuxtプロジェクトの中で開発しているのでVue.jsでは使えないと思っていたのですが、Vue.jsでも使えました?この記事を書いたときのバージョン
"vue": "^2.6.10",
"vue-meta": "^2.3.1",
インストール
npm install vue-meta --save
コードの変更箇所
// src/main.ts import VueMeta from 'vue-meta'; Vue.use(VueMeta);// Page.vue import {metatag} from ‘@/metatag/metatag.ts' export default Vue.extend({ data() { return { this.initialized = false; this.param = {hoge: ‘bar'} } }, mounted() { // ここでAPI通信などの非同期処理を行う。 this.initialized = true; }, metaInfo() { return this.initialized ? metatag(this.param) : null; // this.initializedが変更されると検知してくれる? }, });// src/metatag/metatag.ts const TITLE = ’サイト名’; export function metatag(params: any) { const description = `descriptionを書くよ ${JSON.stringify(params)}`; return { title: TITLE, meta: [ {name: 'description', content: description}, {property: 'twitter:card', content: 'summary'}, {property: 'twitter:title', content: document.title}, {property: 'twitter:site', content: '@anonymous'}, {property: 'twitter:creator', content: '@anonymous'}, {property: 'twitter:description', content: description}, { property: 'twitter:image', content: 'https://FQDN/favicon256.jpg', }, {property: 'og:title', content: document.title}, {property: 'og:description', content: description}, {property: 'og:type', content: 'website'}, {property: 'og:url', content: location.href}, { property: 'og:image', content: 'https://FQDN/favicon256.jpg', }, ], script: [ { type: 'application/ld+json', innerHTML: JSON.stringify( [{ '@context': 'http://schema.org', '@type': 'Organization', 'url': location.href, 'logo': 'https://FQDN/favicon256.jpg', }, { '@context': 'http://schema.org', '@type': 'WebSite', 'name': document.title, 'alternateName': description, 'url': location.href, }, ], null, 2, ), }, ], }; }vue-headという類似ライブラリを使っていたんですけど、
this.$emit(‘updateHead’);
というメソッドを非同期通信完了のタイミングで実行しなければならなかったのが辛かった・・・
また、JSON-LDの書き方がすごくスッキリしました。これで最低限のOGPとGoogle対策ができました。
とはいえ、Twitter CardやGoogleに完全対応するには、DynamicRenderingやSSRが必要です。
Vue.jsで開発しているとSSR対応は難しいので、HeaderlessChrome(puppeteer)を使ったDynamicRenderingをやることにしました。
DynamicRenderingに関しては結構長い記事なので次回更新します。
- 投稿日:2019-11-12T16:31:16+09:00
Vue.js + webpack で 数字をドラムロールする
はじめに
数字をドラムロールするアニメーションはすでに、Vue.jsのドキュメント にあるけど、これをwebpackで使いたい。
あと、フォームに入力してカウントアップするやり方ではなく、ボタンを押したら10ずつ増えるという簡単なコードを書きました。HTMLを書く
ボタンと数字を表示する
<div id="app"> <button v-on:click="countup"> 10ずつカウントアップ </button> <p>{{ animatedNumber }}</p> </div>GSAPをインストール
GSAPのTweenLiteを使うため、GSAPをwebpackにインストールします。
npm install gsap
コードを書きます。
import Vue from 'vue' import TweenLite from 'gsap' // GSAPのTweenLiteを使います。 new Vue({ el: '#app', data: { tweenedNumber: 0 }, computed: { animatedNumber: function() { return this.tweenedNumber.toFixed(0) //小数点を削除するため toFixedを使用 } }, methods: { countup: function() { const newValue = this.tweenedNumber + 10 TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue }) } } })といった感じです。
これができると色々応用がきくと思います。
わたしは、検索結果の件数をリアルタイムで出す際にこれを使ってます。
- 投稿日:2019-11-12T12:18:01+09:00
Vue.js+TypeScript (Vue.ts) へ、Vue Test Utils+Jestを入れた際に発生したエラーを解消した軌跡
Vue.jsでTypeScriptと一緒にVue Test Utils+Jestを使う
公式の日本語ドキュメントの
に記載されている手順通り順番に導入していくと案外すんなりといかなかったので、その際に試した解決方法をここに残しておきます。
(ちなみに上から順番に解決していったのでもしかしたら余計なプラグインが入っているかもしれません)なおここで扱うテストコード(
HelloWorld.spec.ts
)とテスト対象のコード(HelloWorld.vue
)は公式のリポジトリにあるコードと同じ想定です。
src\components\HelloWorld.vue
src\components\HelloWorld.vue<template> <div class="hello"> <h1>{{ msg }}</h1> <p> For guide and recipes on how to configure / customize this project,<br> check out the <a href="https://cli.vuejs.org" target="_blank">vue-cli documentation</a>. </p> <h3>Installed CLI Plugins</h3> <ul> <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank">typescript</a></li> </ul> <h3>Essential Links</h3> <ul> <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li> <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li> <li><a href="https://chat.vuejs.org" target="_blank">Community Chat</a></li> <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li> <li><a href="https://news.vuejs.org" target="_blank">News</a></li> </ul> <h3>Ecosystem</h3> <ul> <li><a href="https://router.vuejs.org" target="_blank">vue-router</a></li> <li><a href="https://vuex.vuejs.org" target="_blank">vuex</a></li> <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank">vue-devtools</a></li> <li><a href="https://vue-loader.vuejs.org" target="_blank">vue-loader</a></li> <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li> </ul> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ name: 'HelloWorld', props: { msg: String, }, }); </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
src\components_tests_\HelloWorld.spec.ts
src\components\__tests__\HelloWorld.spec.tsimport 'jest'; import { shallowMount } from '@vue/test-utils' import HelloWorld from '../HelloWorld.vue' describe('HelloWorld.vue', () => { test('renders props.msg when passed', () => { const msg = 'new message' const wrapper = shallowMount(HelloWorld, { propsData: { msg } }) expect(wrapper.text()).toMatch(msg) }) })①、VSCode上で
Cannot find name 'describe'.
と警告されるVisual Studio Codeでテストコードを記述した際に以下のように警告されました
Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha` and then add `jest` or `mocha` to the types field in your tsconfig.ts(2593)①解決方法
jest
をインストールし、tsconfig.json
に以下の記述を追加tsconfig.json{ "compilerOptions": { // .. "types": [ "webpack-env", "jest" // typesにjestを追加 ], // .. }②、テスト実行時に
spawn jest ENOENT
というエラーが発生①を解消後、テストを実行すると以下のエラーが発生。
Error while running task C:\work\project\vue:test:unit with message 'spawn jest ENOENT'
(VueUIでのエラーログ)
以下詳細なエラー
Error: spawn jest ENOENT at notFoundError (C:\Users\sola\AppData\Roaming\npm\node_modules\@vue\cli\node_modules\cross-spawn\lib\enoent.js:6:26) at verifyENOENT (C:\Users\sola\AppData\Roaming\npm\node_modules\@vue\cli\node_modules\cross-spawn\lib\enoent.js:40:16) at ChildProcess.cp.emit (C:\Users\sola\AppData\Roaming\npm\node_modules\@vue\cli\node_modules\cross-spawn\lib\enoent.js:27:25) at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12) { code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn jest', path: 'jest', spawnargs: [] }②解決方法
グローバルに
jest
とts-jest
をインストールnpm i jest ts-jest -g
i
・・・install
の省略記法以下実行結果例
C:\Users\sola>npm i jest ts-jest -g npm WARN deprecated left-pad@1.3.0: use String.prototype.padStart() // 略 + ts-jest@24.1.0 + jest@24.9.0 added 476 packages from 358 contributors in 19.261s③、テスト実行時に
Cannot find module 'babel-core'
とエラーが発生②を解消後、再度テストを実行。以下のように別エラーが発生
Cannot find module 'babel-core'
③解決方法
プラグイン
babel-core
をインストールする ※npm i babel-coreプラグイン詳細:https://www.npmjs.com/package/babel-core
※しかしながらこの際、
babel-core
の最新(安定)版をインストールすると以下のエラーが発生しました。Plugin 1 specified in "C:\\work\\project\\vue\\node_modules\\@vue\\cli-plugin-babel\\preset.js" provided an invalid property of "default" (While processing preset: "C:\\work\\project\\vue\\node_modules\\@vue\\cli-plugin-babel\\preset.js")調べてみるとどうやらバージョン
7.0.0-bridge.0
を固定して入れるとエラーが解消されるみたいなので、
もし上記のエラーが発生した場合はバージョンを以下のように固定して再インストールしたほうがいいのかもしれません。package.json{ "dependencies": { "babel-core": "7.0.0-bridge.0", }, }参考:Cannot find module 'babel-core' · Issue #4891 · facebook/jest
おわり
- フロントエンド初心者なんでなにか間違いありましたら指摘ください
参考URL
- 投稿日:2019-11-12T11:27:32+09:00
データの監視
watch
やcomputed
を使ってデータを監視するときにいろいろ大変だったのでメモ。
vueではdata
やcomputed
にあるデータを監視して、変更を検知して何かしらfunctionを起こしたりとかが出来る。今回やりたいこと
props[quote]
の変更を検知して、quote_controllerにPOSTしたい。
ただし、quote
の中にはquote.choices
とquote.room
がある。元の状態
watch: { quote: { handler: function(newValue) { this.$http.post('//localhost:3000/quote', { choices: this.choices, room: this.room }).then((res) => { this.quote.sections = res.data }) }, deep: true } }何が問題なの?
handler
をつけると、配列の要素そのものが変更されたことを検知する。つまり、this.room = room1 => this.room = room2
とか、this.choicesの要素の一つの内容だけ変わったとかも検知してくれる。deep: true
がないと、配列の要素の増減は検知してくれるけれど内容が変わったことは検知しない。今回、
room
に関しては内容の変更も検知してもらう必要がある。でないとroomに入っているのはいつも一つの要素だけだから永遠に検知されない。
でも、choices
に関してはpattern
を変更すると一気に要素が入れ替わる為、その時に要素が変わったのをいちいち検知して永遠にPOSTされてしまった。
しかし、choices
とroom
は同時にPOSTする必要があるので別々にwatchしてPOSTアクションを起こすことは避けたい…解決??
これで一旦正常に動いているように見える。
watch: { choices: function(newVal, oldVal) { this.change = true console.log("heyyyyyy") }, room: { handler: function(newVal, oldVal) { this.change = true console.log("wowowowowowo") }, deep: true }, change: { handler: function(newVal, oldVal) { if(this.change) { this.$http.post('//localhost:3000/quote', { choices: this.choices, room: this.room }).then((res) => { this.quote.sections = res.data }) console.log("www") this.change = false } } } }まず。data
change
を置く。
room
だけdeep: true
の状態にして、choices
とroom
の変更が検知されたらchange=true
としてchange
自体の変更もwatchする。
- 投稿日:2019-11-12T10:13:39+09:00
Amazon S3でSPAをサクッと公開する
はじめに
ストレージサービスとして有名&優秀なAmazon S3ですが、実は「静的ウェブサイトホスティング」という機能を使うことで、Vue.jsやReactで作ったSPAを簡単に公開することができます。また、AWS CLI を使用することでコマンド一発でサクッとデプロイすることができます。
herokuやfirebaseなどのPaaSが充実している昨今、あまりS3でやるメリットない気もしますが。今回はその手順についてまとめてみました。(ちなみに私がVue.jsをよく使うのでちょくちょくVue.jsが登場しますが、Reactでも同様の操作ができるはずなので、適宜読み替えていただければと思います。)
お値段
まず一番大事なお金の話から。
S3の料金は「利用したストレージの容量」「リクエスト件数」「リクエストに対するデータ送信量」の3軸で計算されます。
ストレージ料金 最初の 50 TB/月 0.025USD/GB
リクエスト料金 PUT、COPY、POST、または LIST リクエスト リクエスト 1,000 件あたり 0.0047USD GET、SELECT および他のすべてのリクエスト リクエスト 1,000 件あたり 0.00037USD
データ転送料金 1 GB まで/月 0.00USD/GB 次の 9.999 TB/月 0.114USD/GB ※料金 - Amazon S3 | AWSより一部抜粋
例えば、配信するコンテンツのデータサイズが1MB、リクエスト件数が100件/日の場合、
ストレージ料金:0.000025USD
リクエスト料金:0.00111USD
データ転送料金:0.228USD
で、合計約 0.229USD/月 (日本円で約 25.19円/月)です。ばちくそ安いですね。アカウント開設から1年以内の無料枠を利用すればほぼ0円に抑えられると思います。
※参考 - AWS クラウド無料利用枠SPAの準備
各フレームワークでのビルドを実行し、
index.html
と各フォルダ(css/js/img等)が揃っている状態にしましょう。
ちなみにVue.jsでのプロジェクト開始方法およびビルド方法はこちらの記事(Vue CLI スタートガイド)にまとめてありますので、フロントフレームワークを全く触ったことないという方はこちらを参考にしてみてください。
S3用のIAMユーザ作成
AWSルートアカウントの作成
AWSを初めて使うという方はAWSルートアカウントを作成しましょう。
こちらを参考にすると良いと思います。
AWS アカウント作成の流れ | AWSS3用のIAMユーザの作成
ルートアカウントは全権限アカウントのため全てのAWSリソースへのアクセスができてしまいます。このルートアカウントで作業を続けることはセキュリティ上よろしくないので、S3のみ使用可能なIAMユーザを別途作成し、今後の作業はこのS3用IAMユーザで行います。
まずルートアカウントでログインし、マネジメントコンソールからIAMへ移動します(検索バーで「iam」と打てば出てきます)。
「ユーザー」メニューを選択すると、作成したIAMユーザの一覧が表示されます。
今回は新規でユーザを作成するので、青色ボタンの「ユーザーを追加」をクリックしましょう。
IAMユーザを作成するための設定画面が表示されます。
- ユーザー名は任意の名前で構いません(公開しようとしているSPA用のIAMユーザであることが分かるネーミングだと良いです)
それ以外はデフォルトの設定で問題ありません。
IAMユーザの作成が完了すると一覧に表示されるので、問題なく作成されているか確認しましょう。
IAMユーザの作成が完了したら、早速ログインしてみましょう。
右上にIAMユーザ名 @ アカウント名
と表示されていれば問題なくログインできています。
S3以外のサービスへのアクセスがブロックされているか確認するために、試しにIAMを開いてみましょう。
先ほどIAMユーザを作成した画面に移動しても「アクセス権限が必要です」と表示され、ユーザを新規作成できないようになっているはずです。
このように、利用用途ごとにIAMユーザを作成してAWSサービスへの権限を切り分け、意図しない操作が実行されないようにしましょう。
S3でSPAを公開
先ほどログインしたS3用IAMユーザで作業を進めます。
バケットの作成
S3の画面へ移動し、青色の「+バケットを作成する」ボタンをクリックしましょう。
バケット作成に際しての設定画面が表示されるので、情報を入力しましょう。
- バケット名:任意の文字で構いません。ここで設定したバケット名が後ほど静的ホスティングする際のURLの一部として使われるので、それっぽい名前を付けましょう。
- リージョン:これも任意で構いませんが、「アジアパシフィック(東京)」を選択するのが無難です。
![]()
それ以外の設定は一旦デフォルトのままで問題ないです。
バケットが作成されるとバケット一覧に表示されるようになります。
コンテンツのアップロード
バケット名をクリックするとバケットの詳細画面に遷移することができます。
青色の「アップロード」ボタンをクリックし、公開したいSPAの各ファイル(index.htmlとcss/js/imgフォルダ等)をローカルからアップロードしましょう。
初回アップロード時にバケットの設定について色々と聞かれますが、全てデフォルトで問題ないです。
無事アップロードが完了するとこのような画面になると思います。
静的ウェブサイトホスティング機能の設定
「プロパティ」タブへ移動し、「Static website hosting」の設定を行います。
上記の設定が完了したら「保存」ボタンをクリックしましょう。
なお、この画面で表示されている「エンドポイント」のURLがウェブサイトへのアクセス用URLになります。が、今の状態でこのURLにアクセスしようとしても403エラーが返ってきてしまいます。URLでのアクセスを許可するために、次の章で説明するバケットポリシーを設定しましょう。
バケットポリシーの設定
S3に格納したオブジェクトが不用意にネットに晒されないよう、デフォルトでは外部からS3バケットへのアクセスは全て拒否するようになっています。先ほど403エラーが返ってきたのもそのためです。正しくWebページを表示させるためにはURLによる外部からのリクエストを明示的に許可する必要があります。
アクセス制御に関することは「アクセス制御」タブで行います。
まず、「ブロックパブリックアクセス」を開きます。「編集」をクリックし、以下の設定を行います。
- 「パブリックアクセスをすべてブロック」のチェックを外します
- 下から2つ目、「新しいパブリックバケットポリシーを介して...」のチェックを外します
- 一番下、「任意のパブリックバケットポリシーを介して...」のチェックを外します
![]()
これでバケットポリシーによるアクセス許可設定が有効になります。ここの設定を行わないと、いくらバケットポリシーで許可の設定を行ってもブロックされてしまうので注意しましょう。
次に「バケットポリシー」を開き、エディタ欄に以下のJSONをバケット名を置き換えて貼り付けましょう。
このJSONはsample-hosting-kiyokiyo
バケットへのGetリクエストを許可するバケットポリシーです。AWS公式チュートリアルのものをそのまま抜粋しました。バケット名の部分のみ、自分が作成したバケット名に置き換えるのを忘れないようにしましょう。{ "Version":"2012-10-17", "Statement":[{ "Sid":"PublicReadForGetBucketObjects", "Effect":"Allow", "Principal": "*", "Action":["s3:GetObject"], "Resource":["arn:aws:s3:::sample-hosting-kiyokiyo/*" ] } ] }これで外部からS3に格納したファイルを取得できるようになりました。
先ほど403エラーが返ってきたURLでアクセスし直すと、今度は正しくWebページが表示されるようになっているはずです。
S3へのデプロイコマンドを作る
上記の手順でWebページの公開はできるようになりましたが、Webページを更新するたびにIAMユーザでログインし、S3バケットに格納してあるファイルを削除して、ローカルにある新しいファイルをアップロードし直す、というのはかなり面倒です。Vue.jsでは
npm run serve
でローカルサーバーを起動し、
npm run build
でビルドを行うことができます。
それと同じノリで、
npm run deploy
で、S3へのデプロイができるよう設定を組みましょう。AWS CLI を使用する
AWS CLI を使うと、ターミナル等のコマンドラインツールからAWSサービスを操作できるようになります。これを利用して、S3上の対象バケットにあるファイルを削除し、ローカルにある新規ファイルをアップロードするスクリプトを組みます。
こちらの記事でAWS CLI を使うための手順がまとめられているので参考にしてみてください。
【初心者向け】MacユーザがAWS CLIを最速で試す方法 | Developers.IOかいつまんで説明しますと、
1:まずpipをインストールします(Python3.4以降であればPythonのインストールと同時に使えるそうです)。
pip -V
と打ってバージョンが表示されれば問題ないです。$ pip -V pip 19.3.1 from /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip (python 3.7)2:次にAWS CLI をインストールします
$ pip install awscli3: ルートアカウントでS3用IAMユーザのアクセスキーとシークレットアクセスキーを生成・取得します。IAMメニューで先ほど作成したS3用IAMユーザを選択し、「認証情報」タブの「アクセスキーの作成」ボタンをクリックします。アクセスキーとシークレットアクセスキーが表示されるので、手元に控えておきましょう。
4:ターミナルに
aws configure
と入力しS3を操作するための設定を行います。Access Key ID
とSecret Access Key
に先ほど取得した情報を入力しましょう。Default region
はS3バケットで指定したリージョン(アジアパシフィック(東京)の場合はap-northeast-1)を入力しましょう。Default output format
はとりあえずtextで問題ありません。$ aws configure AWS Access Key ID [None]: XXXX AWS Secret Access Key [None]: XXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]: text動作確認として、
aws s3 ls
でS3に登録しているバケットが一覧表示されればOKです。$ aws s3 ls 2019-11-11 22:45:22 sample-hosting-kiyokiyoデプロイ用のシェルスクリプトを組む
index.html等が格納されているディレクトリを
dist
とします。
distと同じ階層にデプロイ用のスクリプトを記載したdeploy-s3.sh
を配置します。
ディレクトリ構造のイメージはこんな感じです。(any directory) ├dist/ │ ├css/ │ ├img/ │ ├js/ │ └index.html └deploy-s3.sh
deploy-s3.sh
の中身はこんな感じで書きます。deploy-s3.sh#!/bin/sh aws s3 rm s3://sample-hosting-kiyokiyo/ --recursive aws s3 cp dist s3://sample-hosting-kiyokiyo/ --recursive1行目はシェルスクリプトを走らせるためのおまじないです。詳しく知りたい方はこちらの記事(#!/bin/sh は ただのコメントじゃないよ! Shebangだよ!)とかが参考になると思います。
2行目ではsample-hosting-kiyokiyoバケットの中身を再帰的(--recursive)に削除(rm)しています。
3行目ではdistディレクトリの中身をsample-hosting-kiyokiyoバケットにコピー(cp)しています。AWS CLI でできることはこちらのAWS CLI Command Referenceにまとまっているので、他のスクリプトを走らせたい方は調べてみてください。
デプロイ用コマンドを作る
最後に、
npm run deploy
と入力したら先ほど作成したdeploy-s3.sh
が呼び出されるようにします。
npm runコマンドはpackage.json
のscripts
ブロックで設定できます。
"deploy": "bash deploy-s3.sh"
をscriptsブロック内に追加しましょう。package.json{ (省略) "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint", "deploy": "bash deploy-s3.sh" }, (省略) }これで作業としては完了です。
試しにローカルでの変更をS3にデプロイできるか確かめてみましょう。まず、ローカルで適当に変更を行います。
サンプルとして今回はとりあえずApp.vueのHelloWorldタグを以下のように変えてみます。(変更前) <HelloWorld msg="Welcome to Your Vue.js App"/> (変更後) <HelloWorld msg="Hi! My name is Kiyokiyo! Nice to meet you!"/>ビルドコマンドを走らせます。
$ npm run build
これでローカルのdistディレクトリ以下に必要なファイルが揃いました。
最後にデプロイコマンドを走らせます。
$ npm run deploy
S3上のファイルがdeleteされ、ローカルのファイルがS3にアップロードされたことが、ターミナルの出力からも分かると思います。
WebページのURLにアクセスするとしっかり変更が反映されていますね。
おわりに
これでローカルで作成していた静的ウェブサイトを公開できるようになりました。
デプロイもコマンド一発で簡単にできるようになったので、開発速度もかなり向上したんじゃないでしょうか。個人的な今後としては、LambdaやDynamoDBを利用したサーバーレスAPIとの通信にチャレンジしてみたいと思います。
- 投稿日:2019-11-12T07:38:41+09:00
Vue.js + axios でヘッダーにログイン情報を毎回セットする
axios の create() と interceptors() を使ってラップした http plugin を作ると良いかもしれません。
src/plugins/http.tsimport _Vue from 'vue'; import axios from 'axios'; export default { install(Vue: typeof _Vue): void { const http = axios.create({ // URL は環境変数とかで変えられるにする baseURL: 'http://localhost:3000/', timeout: 10000, }); http.interceptors.request.use((config: any) => { // $stores.auth.show に認証情報が入っているとする config.headers = Vue.prototype.$stores.auth.show; return config; }); Vue.prototype.$http = http; }, };main.ts のどこかに下記を書きます。
すると、plugin が使えるようになります。src/main.ts// 略 import http from '@/plugins/http'; Vue.use(http);認証情報の store はこんなイメージです。
vuex-module-decorators を使っています。src/stores/AuthStore.tsimport Vue from 'vue'; import { Module, Mutation, VuexModule } from 'vuex-module-decorators'; import LoginInterface from '@/interfaces/LoginInterface'; import { AuthStoreInterface } from '@/interfaces/StoresInterface'; import { AuthResponseInterface, ErrorResponseInterface } from '@/interfaces/ResponseInterface'; @Module({ name: 'auth' }) export default class AuthStore extends VuexModule implements AuthStoreInterface { public token = ''; public user_id = 0; public get show(): object { return { Authorization: `Token ${this.token}`, 'User-Id': this.user_id, }; } @Action public create(users: LoginInterface): void { Vue.prototype.$http.post('/users/auth', { user: users }) .then((res: AuthResponseInterface): void => { this.set_token(res.data.token); this.set_user_id(res.data.id); }) .catch((err: ErrorResponseInterface): void => { // エラー処理 }); } @Mutation private set_token(token: string): void { this.token = token; } @Mutation private set_user_id(user_id: number): void { this.user_id = user_id; } }そうすると Store 内で
Vue.prototype.$http.get()
やVue.prototype.$http.post()
すると AuthStore のログイン情報を毎回見に行ってくれます。
vue ファイルから呼び出す際はthis.$http.get()
やthis.$http.post()
でリクエストできます。もし vuex-persist を使っていれば、リロードしても消えません。
- 投稿日:2019-11-12T07:38:41+09:00
Vue.js + axios でヘッダーにログイン情報を毎回セットする。
plugin を自作して axios の create() と interceptors() を使ってラップした http plugin を作ると良いかもしれません。
src/plugins/http.tsimport _Vue from 'vue'; import axios from 'axios'; export default { install(Vue: typeof _Vue): void { const http = axios.create({ // URL は環境変数とかで変えられるにする baseURL: 'http://localhost:3000/', timeout: 10000, }); http.interceptors.request.use((config: any) => { // $stores.auth.show に認証情報が入っているとする config.headers = Vue.prototype.$stores.auth.show; return config; }); Vue.prototype.$http = http; }, };Typescript で書いているため、上記 plugin の型ファイルを *.d.ts という拡張子にしてどこかに置きます。
src/types/http.d.tsimport Vue from 'vue'; import { AxiosStatic } from 'axios'; declare module 'vue/types/vue' { interface Vue { $http: AxiosStatic; } }あとは main.ts のどこかに下記を書きます。
すると、自作 plugin が使えるようになります。src/main.ts// 略 import http from '@/plugins/http'; Vue.use(http);認証情報の store はこんなイメージです。
vuex-module-decorators を使っています。src/stores/AuthStore.tsimport Vue from 'vue'; import { Module, Mutation, VuexModule } from 'vuex-module-decorators'; import LoginInterface from '@/interfaces/LoginInterface'; import { AuthStoreInterface } from '@/interfaces/StoresInterface'; import { AuthResponseInterface, ErrorResponseInterface } from '@/interfaces/ResponseInterface'; @Module({ name: 'auth' }) export default class AuthStore extends VuexModule implements AuthStoreInterface { public token = ''; public user_id = 0; public get show(): object { return { Authorization: `Token ${this.token}`, 'User-Id': this.user_id, }; } @Action public create(users: LoginInterface): void { Vue.prototype.$http.post('/users/auth', { user: users }) .then((res: AuthResponseInterface): void => { this.set_token(res.data.token); this.set_user_id(res.data.id); }) .catch((err: ErrorResponseInterface): void => { // エラー処理 }); } @Mutation private set_token(token: string): void { this.token = token; } @Mutation private set_user_id(user_id: number): void { this.user_id = user_id; } }そうすると Store 内で
Vue.prototype.$http.get()
やVue.prototype.$http.post()
すると AuthStore のログイン情報を毎回見に行ってくれます。
vue ファイルから呼び出す際はthis.$http.get()
やthis.$http.post()
でリクエストできます。もし vuex-persist を使っていれば、リロードしても消えません。
- 投稿日:2019-11-12T00:09:16+09:00
VeturにTypeScript3.7のOptional Chainingを適用してみる
TL;DR
settings.json{ // Use dependencies from workspace. Currently only for TypeScript. "vetur.useWorkspaceDependencies": true, }概要
TypeScript 3.7のOptional Chainingは最高!!
いつも「なんで?.
で書けないんだ」って思っていたけど、ついに来た!ただ...VueというかVeturで使うには...
Veturは固定のTypeScriptバージョンに依存している1。
最新サポートしているバージョンはVetur0.22.3のTypeScript3.6.3。
このままTypeScript 3.7のOptional Chainingを書いたら、Veturに怒られる!
トランスパイルしているのはあくまでtsc
なので、実害があるわけじゃないが、Intelligenceが効かなくなるのは相当な痛手だ。解決策
探してみたらちゃんとあった、
Allow using workspace typescript version#682
how can i use optionalChaining?#1438Vetur 0.17.0以降、
"vetur.useWorkspaceDependencies"
のオプションを設定できるらしい。
字面通りの意味なら、この設定をしたら、ワークスペースのTypeScriptバージョンを使ってくれるみたい!ワークスペースの
TypeScript
のバージョンを3.7.2に上げて、$ npm install typescript@3.7.2 --save-devESLintのparserの
@typescript-eslint/parser
を最新バージョンの2.7.0に更新する。(3.7.2対応のparserにしないと表示が色々おかしくなる)$ npm install @typescript-eslint/parser@2.7.0 --save-devVSCodeの
settings.json
にsettings.json{ // Use dependencies from workspace. Currently only for TypeScript. "vetur.useWorkspaceDependencies": true, }を追加したら、Optional Chainをちゃんと認識してくれるようになった!(VSCodeの再起動必須)
使用環境
TypeScript 3.7.2
VSCode 1.40.0
Vetur 0.22.6
ESLint 5.16.0ESLint 設定
.eslintrc.jsmodule.exports = { parserOptions: { parser: '@typescript-eslint/parser', }, extends: [ // ... ], };
how can i use optionalChaining?#1438 Comment https://github.com/vuejs/vetur/issues/1438#issuecomment-533952010 ↩