もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

phpで実装したファイルダウンロードが、chromeで変な挙動になる。

はじめに

こんばんは。

前回に続いて簡単なものです。

本日、phpで実装したファイルダウンロードがおかしくなるという話をききました。

現象としては、'donwload.csv' というように、シングルクォーテーションがついてきてしまうという現象でした。

実際に試してみると、自分が使っている最新のchromeでも確かにその現象が起きていました。

ちなみに試したのはmacchromeで、バージョン 72.0.3626.109 になります。

コード

上記の現象が起きたコードはこんな感じのコードでした。

public function download($fileSize, $contentType, $downloadName) {
    header("Content-Type: {$contentType}");
    header("Content-Length: {$fileSize}");
    header("Content-Disposition: attachment; filename='{$downloadName}'");
    readfile($filePath);
}

headerのコードのfilenameの部分をシングルクォーテーションで囲んでいる部分が問題のようでした。

該当のコードを

header("Content-Disposition: attachment; filename=\"{$downloadName}\"");

のようにダブルクォートで囲うようにするか、そもそもクォーテーションで囲わなければ同現象がおきているchromeでも問題なくdownloadできました。

chrome singlequote download とかでググると、同現象と同じ現象かな?と思える物が見つかりました。

こちらでは、とりあえずEdgeか古いバージョン使えと書いていました...

何か他に情報あれば教えてください。

Goとphpでbcryptでのパスワード検証を試してみた

はじめに

こんばんは。

先週ちょっと体調悪くて、ブログ上げるのが遅くなりました。

自分が現在作成中の簡単なサービスで、パスワード認証部分にbcryptを使ってハッシュ化しようとしています。

phpであれば、 password_hash($password, PASSWORD_BCRYPT); とかで簡単にできちゃうのですが、現在勉強中のGo言語でやるにはどうするのだろうと思って、調べてやってみました。

やってみた

今回試すパスワードは 1qaz2wsx3edc で統一しています。

各バージョンは以下です。

$ php -v
PHP 7.3.1 (cli) (built: Jan 21 2019 12:58:05) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.1, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.1, Copyright (c) 1999-2018, by Zend Technologies
    with Xdebug v2.7.0beta2-dev, Copyright (c) 2002-2018, by Derick Rethans
    
$ go version
go version go1.11 darwin/amd64

Go言語でパスワードハッシュ化

実装は、こちら のサイトを参考にさせていただきました。

実装
package main

import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

func main() {
    password := "1qaz2wsx3edc"
    hashedPassword, hashError := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    if hashError != nil {
        panic(hashError.Error())
    }

    fmt.Println(string(hashedPassword))
}
結果
$ go run main.go 
$2a$10$YDZISuUQVTcza4uIM71m/uAZeGToP7SSDcxMbmtIojP2hJWzGQFPO

こんな感じです。

これで検証は終わったんですが、ふと

Golangでハッシュ化したパスワードはphpで突き合わせできるのかな?」

と思ってやってみました。

phpでパスワード検証

実装
<?php
  
$password = "1qaz2wsx3edc";
$hashedPassword = $argv[1];
if (password_verify($password, $hashedPassword)) {
        echo "OK\n";
} else {
        echo "NG\n";
}
結果
$ php password_verify.php '$2a$10$YDZISuUQVTcza4uIM71m/uAZeGToP7SSDcxMbmtIojP2hJWzGQFPO'
OK

通りました。 まぁ同じアルゴリズムだから通るよねと、予想通りでした。

Go -> phpを試してみたので、 php -> Goも試してみました。

phpでパスワードハッシュ化

実装
<?php
  
$password = "1qaz2wsx3edc";
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
echo "{$hashedPassword}\n";
結果
$ php password_hash.php 
$2y$10$hooq151ubdNmdlihER7PEenSG53YIPylUdkjljEwkmEpJ2Hk16paW

出来上がりました。これを今度はGo言語で検証します。

Go言語でパスワード検証

実装
package main

import (
    "os"
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

func main() {
    password := "1qaz2wsx3edc"
    hashedPassword := os.Args[1]
    err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
    if err != nil {
        fmt.Println("NG")
    } else {
        fmt.Println("OK")
    }
}
結果
$ go run main.go '$2y$10$hooq151ubdNmdlihER7PEenSG53YIPylUdkjljEwkmEpJ2Hk16paW'
OK

通りました。こちらも想定通りです!

phpで作成した場合とgolangで作成した場合でハッシュ値が違うことに関しては、こちらに詳しく書かれていました。

暗号化のバージョン

上記のサイトで紹介されている「暗号化のバージョン」のところで、phpで作成した場合は 2ygolangの場合は 2a が少し気になったので調べたところ、こちらのサイトに詳しく書かれていました。

以下、引用です。

BCrypt を表すバージョンは、$2$ $2a$ $2b$ $2x$ $2y$ が存在するようです。

・$2$ (Provos・Mazières, 1999)による最初のBCrypt。

・$2a$$2b$ のいくつかの実装に存在するセキュリティ上の問題を修正したバージョン。

・$2x$ crypt_blowfish の $2a$ に存在していた問題を修正する前と同一のバージョン。

・$2y$ crypt_blowfish の $2a$ に存在していた問題を修正した後と同一のバージョン。

・$2b$ 公式の BCrypt の最も新しいバージョン。

Golangの場合は決め打ちで、2a になっているみたいです。

https://github.com/golang/crypto/blob/master/bcrypt/bcrypt.go#L57-L58

phpの場合は、参考サイトにも記載されているのですが、 php自体のバージョンによって 2a でも一部問題が発生するパターンがあるとのことで、php7など、最近のバージョンを使う場合は 2y を使うことで問題ないということでした。

終わりに

ちょっと気になって調べてるうちに、どこまで調べたらいいのかわからなくなってきたので、一旦やめました。

地味にGoのコマンドラインからの引数を受け取る flag パッケージも初めて使いました。

現場からは以上です。

追記

ありがたい指摘をいただきましたので、追記します。

自分は「コマンドラインからの引数をもらえる」ような認識でいたのですが、それは、取ろうと思えば取れるという感じで、実際の使い方とは違うようです。

github.com

golang.jp

今回の自分の使い方でいうと、osパッケージで問題ないということでした。

該当のコードも修正しておきました。

ありがたや。

やさしいLT会に参加してLTしてきた

はじめに

こんばんは。

今回品川で行われたやさしいLT会というイベントに参加してきました。

初心者にやさしいLT会&交流会 in品川

f:id:kojirooooocks:20190203212641p:plain

以下、順不同ですが、自分を含む発表された方のスライドになります。

(まとめたものは現状共有されているもののみになりますので、実際はもっと多くの方が発表されていました)

まとめ

1. FalconAPI開発にいいよ!

@shimakaze_softさん

2. イベント受付アプリとか作ってみた

@ayup_pgさん

3. 常駐先の無意味な当たり前をなくしたい

@kazuiti10さん

4. 5分じゃ無理だけど文書作成のコツを教えます

@lucky_7_nさん

5. 人にやりたいことを話せば話すほど機会がやってくる話

@naipakapakaさん

6. 駆け出しフロントエンドエンジニアの半年間

@ShpTomoyaさん

7. GitHubおすすめ検索ワード

@hyuraku1さん

https://docs.google.com/presentation/d/1DK0ETA-tuDk6VuVU--D5QchIDYrJEvUG9waRKWcxMZE/edit#slide=id.p

8. Slackチームを立ち上げて感じたこと

自分の資料

9. ラスト

こんな強者な方もいましたw

感想

前回の初LTでは10分という長めの時間を頂いていたのですが、今回はライトニングトークらしく5分という時間設定でした。

また目的としては、前回は初のためとにかく経験、2回目である今回は場数を踏むということでした。

評価点

  1. 時間を大体制限時間通りに収めることが出来た
  2. 資料作成スキルが向上していた(奥さんのお墨付き)
  3. 人数的には前回より多かったが、ガチガチにならず心地よい緊張感で臨むことが出来た
  4. ある程度皆さんの顔を見ながら発表することが出来た
  5. その後の懇親会でもいろいろな方に話に行くことが出来た

反省点

  1. @yoshitaku_jpさんに注意してもらっていたスライド修正点をマージし忘れていた...
    • まとめのスライド
    • 参考サイトのURL

感想

発表者の方々は、全体的にエンジニア経験浅めの方が多い印象でした。

空手の世界2位で現在エンジニアやっているというような、面白い経歴を持っている人もいました。

自分も結構面白い経歴を持っていると思っていましたがまだまだでした...w

自分含め基本的に発表者の方はLTは初心者のはずなのですが、全体の空気感もありとても発表しやすい空間ができあがっていました。

事前に考えていたものとは違い、ガッツリ技術系のLTをしている方はほんの一部で、全体的にエモい話 or 経験談が多かったです。

ただ、どれも面白いものばかりでした。

個人的には、@kazuiti10さんが発表された「常駐先の無意味な当たり前をなくしたい」がとても面白かったです。

前回のLTで自分が発表したレガシー環境に立ち向かう話に少し似ていたからだと思います。

ただ、@kazuiti10さんは、自分とは違いいきなり行動に移すのではなく、信頼を勝ち取っていくところから地道に始めていったということなので、自分よりもとても上手に立ち回っていたのだと思います。

自分の過去の行動を反省し、自分もさらに頑張らないとなと思わせてくれる良い発表でした。

次回の要望

LTが終わったあとの質問タイムがほしいなと思いました。

ポンポンと小気味よくLTを行っていくのはスピード感的に良かったのですが、気になったところを質問するのはラストの懇親会のときとなると、発表者が帰ってしまう可能性があるためです。

おまけ

作成したスライドで@kakakakakkuさんのTwitterのキャプチャ画像を使用しておりました。

キャプチャ画像の使用については事前にDMにて許可を頂いており、今回の発表後、再度DMにて資料の共有をさせていただきました。

イベントが休日だったこともあり

自分「ありがとうございました!休日に失礼いたしました!」

とDMを送ったところ

カ「毎日平日です」

という、怖い文章が返ってきました。

現場からは以上です。

phpでExcelファイルのダウンロード

こんにちは。

めちゃめちゃ簡単な話なのですが、自分の知識がアップデートされたのでメモ代わりに書いときます。

お仕事でめちゃんこ久しぶりにphpspreadsheetを使うことになりまして、テンプレートとなるエクセルファイルを管理画面からダウンロードさせるような機能を作りました。

そこで、いままでheaderで指定するContent-Typeを application/vnd.ms-excel としていたのですが、これは Excel2003以前のExcelである「xls」のものでした。

冒頭で話したとおりめちゃんこ久しぶりに触ったので、作るに当たって思い出すためにぐぐったら発見しました。

Excel2007以降のExcelのContent-Typeは application/vnd.openxmlformats-officedocument.spreadsheetml.sheet でした。

なげぇ。。。

コードだとこんな感じ。

$template_path = PROJECT_DIR . '/resource/template.xlsx';
$filename = 'template.xlsx';
$extension = pathinfo($template_path)['extension'];
$file_size = filesize($template_path);

header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
header("Content-Length: {$file_size}");
header("Content-Disposition: attachment; filename='{$filename}.{$extension}'");
readfile($template_path);

株式会社おかんに潜入して初LTをしてきた話

はじめに

こんばんは。

今回はこちらのイベントに参加してきました。

WEB系なんでもLT会 #2

場所を提供してくれたのは、株式会社おかん様でした。

内容

Multiple Dimension Spreadのご紹介

おおとやさんのLT

感想

開発中のOSSの紹介でした。

申し訳ないですが、早口だったのと自分の番来るなー!という緊張のせいで内容理解できてなかったです。

今回ブログを作るにあたり、再度上がっているスライドを確認したのですが、確認した上で未だによくわかりません。

とにかく大量データを柔軟に取り扱うためのツールという印象でした。

唯一理解できてすごくいいなと思ったのは序盤のデータレイクの話で、保存時はデータ形式をきにせずとにかく溜めることが出来て、取り出すときに形式を整えて取れるというところでした。

今すぐ適用しよう TLS1.2+

@ariakiさんのLT

感想

SSLの歴史からお話が始まります。

暗号化の方法や、その方法の脆弱性をついた有名な攻撃など、紹介してくれていました。

お話はすごく分かりやすいのだと思いますが、いかんせん理解できてない単語が多すぎて理解が出来ませんでした。。。

勉強不足で申し訳ないです。

皆さんご存知ですよね?的な流れだったので、焦りがやばかったです。

手を挙げなくてすいません!

レガシー感謝の日で発表してみたかった話し

@kojirock5260のLT ← 自分です

感想

終わりにまとめます。

Web開発を支えるドキュメンテーションのススメ

@zuckey_17さんのLT

スライドの共有がなく載せられませんでした。

もしアップされたら記事更新します。

感想

ドキュメントの書き方のtipsや、ドキュメント書くときのアンチパターンの話でした。

ドキュメントを書く場合、ドキュメントの書き方の型(フレーム)を決めてあげたほうがよいということです。

ただし、型にハメることに執着するよりも、臨機応変に手書きで書いてあとでドキュメントに起こすとかそういったアプローチもありかなということでした。

また、後半はPlantUMLアツいよ!という話でした。

vscodeでPlantUMLのプラグインがあるというのがすごくありがたい情報でした。

さっそくインストールしました!

5分で完全に理解するHTTP/3

@inductorさんのLT

スライドの共有がなく載せられませんでした。

もしアップされたら記事更新します。

感想

インフラよりな内容で、こちらも勉強不足であまり理解できませんでした... (ダメダメだな俺)

ただ、自分的にわかりやすかったのが、とりあえず今日覚えて帰ってもらいたいものという感じで、3個にまとめてくれていたのは助かりました。

とにかくそれだけに注目して聴くことが出来ました。

  • HTTP2

    => バイナリベースのプロトコルになって軽い

    => ヘッダーを圧縮するので軽い

    => 通信を多重化して同時に送れる

    => TLS強制

    まとめると「より早く、より安全に」

  • TLS1.3

    => サーバー ⇔ クライアント間の通信が行き来する順番(RTT: ラウンドトリップタイム)

    => TLS1.2は3RTTで、TLS1.3は0-1RTT(覚えていたらいきなり暗号化がはじまる: キャッシュ?)

    まとめると「より早く、より安全に」

  • QUIC on UDP

    => UDPの通信でTCPっぽい動きをアプリケーションレイヤで実装したのがQUIC

    まとめると「より早く、より安全に」

とにかく「より早く、より安全に」ということでした。

また、LT内でお話が出た、Qiitaの記事と、inductorさんが翻訳した本のリンクが以下になります。

終わりに

以下自分の初LTの感想です。

ぶっちゃけ自分の前に発表した方の内容がレベルが段違いのものだったので、もう逆に緊張しませんでした。

大体8:30〜9:00で終わりましたので、ある程度予行演習したとおりの時間で収めることが出来ました。

そして、想定外に好感触だったのですごく嬉しかったです。

良かった点・悪かった点をいろいろ出していただいたので、こちらに記載しておきたいと思います。

良かった点

  • 共感できるエモい話でよかった。
  • はじめてのLTなのに比較的進行がうまかった。
  • はじめてのLTなのにスライドが見やすかった。
  • ポジティブな視線を感じれる内容でよかった。

悪かった点

  • Esaの正式名称はesa(小文字)である。また、Githubの正式名称はGitHubである。製品名を出すときは正式名称とリンクもつけてあげたらツールの開発者的には嬉しいと感じる。

  • チームの規模感や、各エンジニアのレベル差をもっと詳細に伝えたほうが、もっとリアルな現状が伝わったと思う。

  • ネガティブな感情を持つ本質をみんなで考えるようなアプローチを行ってみるはどうか。

悪かった点の最後は、スライドの指摘というより、今後のチームの動きという感じで、そこまでアドバイスを頂けました。

とにかく、なんとか初LT終了しました。

ビビって去年できなかったLT経験を積ませてもらって、本当にありがとうございました。

やわらかく、とても鋭い指摘のマサカリばかりですごく勉強になりました。

今後も積極的にLTなど経験して、自分自身を高めていければと思います。

ありがとうございました。

現場からは以上です。

f:id:kojirooooocks:20190123032826j:plain

# yarn add ででた、error Extracting tar content of undefined failed, the file appears to be corrupt: "Unexpected end of data" の解決方法

こんばんは。

めちゃめちゃ短いですが、備忘録として残しておきます。

create-nuxt-appをglobalにインストールしようとしていたら、以下のようなエラーが発生してました。

$ yarn global add create-nuxt-app
yarn global v1.12.3
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
error https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.6.tgz: Extracting tar content of undefined failed, the file appears to be corrupt: "Unexpected end of data"
info Visit https://yarnpkg.com/en/docs/cli/global for documentation about this command.

ファイルが破損してるらしいんですが、今までyarnを使ってて出たことなかったので、調べていると以下のissueを見つけました。

https://github.com/eclipse/che/issues/12077

コメントで、大体yarn.lock消せって書いていたのですが、globalのyarn.lockってどこにあるのだろうと思って調べてると、丁寧にこのissueのコメントに書いてくれていました。

f:id:kojirooooocks:20190119020700p:plain

早速実行します。

$ yarn global dir
/path/to/.config/yarn/global

該当のディレクトリに行き調べるとたしかにありました。

$ cd /path/to/.config/yarn/global
$ ls
node_modules package.json yarn.lock

ここにある yarn.lock を削除して、再度、 yarn global add create-nuxt-app を実行します。

$ yarn global add create-nuxt-app
yarn global v1.12.3
[1/4] 🔍  Resolving packages...
warning @vue/cli > @vue/cli-ui > fkill > taskkill > execa > cross-spawn-async@2.2.5: cross-spawn no longer requires a build toolchain, use it instead
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "@vue/cli > @vue/cli-ui > graphql-type-json@0.2.1" has unmet peer dependency "graphql@>=0.8.0".
warning "@vue/cli > @vue/cli-ui > graphql-tag@2.10.1" has unmet peer dependency "graphql@^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0".
warning "@vue/cli > @vue/cli-ui > vue-cli-plugin-apollo > apollo-server-express > apollo-server-core > apollo-tracing@0.4.0" has incorrect peer dependency "graphql@0.10.x - 14.0.x".
warning " > babel-jest@22.0.4" has unmet peer dependency "babel-core@^6.0.0 || ^7.0.0-0".
warning " > react-test-renderer@16.0.0" has unmet peer dependency "react@^16.0.0-beta.5".
[4/4] 📃  Building fresh packages...
success Installed "create-nuxt-app@2.2.0" with binaries:
      - create-nuxt-app
✨  Done in 194.88s.

 $ which create-nuxt-app 
/usr/local/bin/create-nuxt-app

インストールできました。

調べる過程で、yarn自体のupgradeとかもしましたが特に意味はなかったです。

やはり、このyarn.lockの削除が解決方法でした。

急に使えなくなったのでビビりましたが、落ち着いて調べたら簡単でした。

以上。おやすみなさい。

Nuxt.jsビギナーズガイドを読んだ Vol.3

はじめに

こんにちは。

めちゃめちゃ時間空きましたが、進めていきます。

Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

今回は、3章を勉強しました。

やってみた

1. ライフサイクル

nuxtにはvueのライフサイクル以外に、nuxtのライフサイクルが存在します。

nuxtのライフサイクルが終わったあと、vueのライフサイクルが実行されます。

f:id:kojirooooocks:20190114004650p:plain

  • nuxtServerInit

    • ストア内にこのアクションが定義されているときに自動で呼び出される。
    • ライフサイクル的に一番最初に来るので、ユーザー認証のデータ保存や、必ずセットしたいデータがある時などで使用できる。
  • middleware

    • ページコンポーネント内で、middlewareが定義されている際に呼び出される。
    • 他に依存がない専門的な機能などを提供する。(認証がとっていなければリダイレクトするなど?)
  • validate()

    • ページコンポネント内で、validateが定義されている際に呼び出される。
    • 動的ルーティングのバリデーション処理として使用される。
  • asyncData() & fetch()

    • ライフサイクルの一番最後に実行される。
    • asyncData()とfetch()の違いは、fetch()はコンポーネントのdataへのセットが行われない

2. middlewareの使い方

本書では、ミドルウェアの使用方法の紹介として、認証を実装しています。

今回は本書と同じ構成を真似してみました。

エンドポイント 認証の振り分け
http://localhost:3000/ 誰でもアクセス可能
http://localhost:3000/login 未ログインユーザーのみアクセス可能。既ログインユーザーは「/」へ移動
http://localhost:3000/users 既ログインユーザーのみアクセス可能。未ログインユーザーは「/login」へ移動
1. 認証が必要なページ(/users)
<template>
  <div>
    <p> user/index </p>
  </div>
</template>

<script>
export default {
  middleware: 'auth'
}
</script>
2. ログインページ(/login)
<template>
  <div>
    <h2>ログインページ</h2>
    <p>
      <button 
        type="button" 
        @click="login">ログイン</button><br >
      <nuxt-link to="/">トップページへ戻る</nuxt-link>
    </p>
  </div>
</template>

<script>
import Cookies from 'universal-cookie'
export default {
  middleware: 'auth',
  methods: {
    login() {
      const cookies = new Cookies()
      cookies.set('credential', 'true', { maxAge: 90 })
      this.$router.push('/')
    }
  }
}
</script>
3. indexページ(/)
<template>
  <div>
    <h2>index page</h2>
    <ul>
      <li>
        <nuxt-link to="/login">ログインページへ</nuxt-link>
      </li>
      <li>
        <nuxt-link to="/users/">認証が必要なページへ</nuxt-link>
      </li>
    </ul>
  </div>
</template>
4. 認証ミドルウェアjs(auth.js)
import Cookies from 'universal-cookie'

export default ({ req, route, redirect }) => {
  if (['/'].includes(route.path)) {
    return
  }

  const cookies = req ? new Cookies(req.headers.cookie) : new Cookies()
  const credential = cookies.get('credential')
  if (credential && route.path === '/login') {
    return redirect('/')
  }

  if (!credential && route.path !== '/login') {
    return redirect('/login')
  }
}

f:id:kojirooooocks:20190114004707g:plain

ルーティングに処理を差し込むことができ、状態によってredirectさせることが出来ました。

便利に見えるミドルウェアですが、本書でも注意点として書かれておりますが、柔軟な機能が作成できる反面、責務をまたぐコードが作成される可能性があります。

便利な反面、使い所は気をつけたほうが良さそうです。

3. pluginの使い方

本書では、VueRouterのbeforeEachフックを利用して、ページ遷移が行われるたびにルーティングのパスを記録するlogger.jsプラグインを実装しています。

今回は本書の説明を見ながら、あるルートにきた際に、slackへ通知するようなpluginを作ってみようと思います。

1. ライブラリインストール(slack-notify)
$ yarn add slack
2. .envrcでslack tokenを追加
export SLACK_TOKEN="xxxxxxxxxxxxxxxxxxxxxxx"
3. .envrc読み込み
direnv allow
direnv: loading .envrc
direnv: export +SLACK_TOKEN
4. nuxt.configのenvプロパティに追加
  env: {
    SLACK_TOKEN: process.env.SLACK_TOKEN
  }
5. plugin追加
import Slack from 'slack'
export default ({ app, env }) => {
  app.router.beforeEach((to, from, next) => {
    if (to.fullPath === '/purchased') {
      const slack = new Slack()
      slack.chat.postMessage(
        {
          token: process.env.SLACK_TOKEN,
          channel: 'general',
          text: '購入されました!'
        },
        () => {
          next()
        }
      )
    } else {
      next()
    }
  })
}
6. 確認ページ
<template>
  <div>
    <ul>
      <li>
        <nuxt-link to="/purchased">購入する</nuxt-link>
      </li>
    </ul>
  </div>
</template>
7. 購入完了ページ
<template>
  <div>
    <h2>購入完了ページ</h2>
    <p>購入ありがとうございました。</p>
  </div>
</template>

f:id:kojirooooocks:20190114004802g:plain

できましたー!

実際には、もっと粒度の細かい使い方がpluginだと思いますが、今回は本書のrouterフックを勉強するために作ってみました。

4. Vuexモジュールモードの使い方

以前使用していたクラシックモードでは、以下のようにVuexをコード内で読み出してストアを作成していました。

import Vuex from 'vuex'

export default () =>
  new Vuex.Store({
    state: {
      weatherData: ''
    },

    getters: {
      weatherData: state => state.weatherData
    },

    mutations: {
      setWeatherData(state, weatherData) {
        state.weatherData = weatherData
      }
    },

    actions: {
      async fetchWeatherData({ commit }, { pref }) {
        const url = `https://api.openweathermap.org/data/2.5/weather?q=${pref}&APPID=${
          process.env.OPENWEATHERMAP_API_KEY
        }`
        const response = await this.$axios.$get(url)
        commit('setWeatherData', response.weather[0].main)
      }
    }
  })

モジュールモードでは、こちらのような書き方になります。

export const state = () => ({
  weatherData: ''
})
export const getters = {
  weatherData: state => state.weatherData
}
export const mutations = {
  setWeatherData(state, weatherData) {
    state.weatherData = weatherData
  }
}
export const actions = {
  async fetchWeatherData({ commit }, { pref }) {
    const url = `https://api.openweathermap.org/data/2.5/weather?q=${pref}&APPID=${
      process.env.OPENWEATHERMAP_API_KEY
    }`
    const response = await this.$axios.$get(url)
    commit('setWeatherData', response.weather[0].main)
  }
}

ロジックのみ抜き出しているような形です。

終わりに

アドベントカレンダーからだいぶ時間空きましたが、やっと勉強テンションになってきたので、今からまたやってきます!

お疲れ様でした。