Slack
AWSLambda
PWA
Netlify
nuxt.js
PWADay 6

よく分かってなくてもNuxt.jsでPWAが作れた話

この記事はPWA Advent Calendar 2018の6日目の記事になります。

既視感のあるタイトルですが気にしないでください。

毎年何かしら自分のレベルに合わせて新技術に触れてみる・作ってみるみたいなのを課してるのですが、

今年個人的にチャレンジしてみようと思ったものの1つにPWAがあります。

そこで今回は大した知識が無くともPWAを作ることができた話をしようと思います。

内容として他の皆様と大したことやってないかもですが、こんなんでも形になったぞというのを知ってもらいたいのもあるので温かい目で見てくださいませ。


Reading…

Reading… Logo

Link: https://reading.yamanoku.net

GitHub: https://github.com/yamanoku/reading/

日頃自分が見ているニュースを集約してまとめてみたらどうなるだろうか、情報の蓄積・可視化みたいなのを考えておりそういうのができないかなと思ってそれを題材にPWAにしてみようと思いました。以下は経緯みたいなやつです。


情報収集ってどうしてる? - YAMALT vol.04



動作イメージ

Reading… iPhoneシュミレーターによる実動作イメージ図

自分が最近見た20件のニュース×5ページ分にした計100件を表示。

ページ間はページネーションで動きます。


使用技術

API
ホスティング
フレームワーク

Slack API
AWS API GateWay
Netlify
Nuxt.js

Nuxt.jsのプラグイン・モジュールは以下を使用



  • vue-paginate


    • ページネーション。asyncもあって複数で連携できたり、

    • 個人的には色々あるページネーションの中で導入が簡単(な印象)




  • nuxt-community/pwa-module


    • 皆様ご存知のNuxt.jsでPWAにするなら必要になる

    • PWAにしなくともキャッシュ高速化にも使える



あと当初はNuxt1.0で作成していましたが、今年の2.0の発表に合わせてアップデートしました


Nuxt 2でgenerateしたPWAサイトです

https://twitter.com/yamanoku/status/1043119076489318401



フローチャート

図です。

フローチャート 以下説明



  • 投稿自体はTwitter




  • DBは個人用Slack




  • AWSでAPI GatewayとLambda FunctionにてAPI変換


    • Slack APIから直接経由だと制約があってしんどかった


      • devtools使うとどのslackから持ってきてるのかとかがわかっちゃう

      • tokenを隠蔽してもnuxt generateしビルドしたJS内にtokenとかが見えると警告メールが来てAPI止められる(計4敗)

      • Image



    • 変えてよかったこと


      • tokenを完全隠蔽した

      • CORS対応したのでどこでも取得できる

      • gzip配信もできる

      • 今のところは自分用なのもあって無料枠で収まる内容になってる




    • Netlify Meetup Tokyo #2Netlify Functionsでもいけそうって話を@mottox2さんから聞けたのでNetlify内で完結できる方向に変えようかと考え中




  • Netlifyでホスティング



    • GitHubリポジトリと紐づけてる


    • nuxt generate & push-dir --dir=dist --branch=master --cleanup

    • 静的書き出ししたdistmasterブランチにプッシュ


    • masterブランチをホスティング


      • Image from Gyazo



    • SSL化やらカスタムドメイン可やらプレレンダリング(今回は未使用)やら無料でやってくれてすごい。

    • あとプライベートリポジトリも使える。




パフォーマンス


lighthouse


  • Device: Emulated Nexus 5X

  • Network throttling: 150 ms TCP RTT, 1,638.4 Kbps throughput (Simulated)

  • CPU throttling: 4x slowdown (Simulated)

上記設定で計測。Perfomance部分は変動ある感じですがだいたいこんな感じ


2018/9/6 計測

Perfomance 91, PWA 96, Accessibility 88, Best Practice 100, SEO 100


2018/12/2 計測

Perfomance 95, PWA 96, Accessibility 90, Best Practice 100, SEO 100


WebPageTest


  • From: Tokyo, Japan - EC2 - Chrome - Cable

上記設定で計測。


2018/9/6 計測

https://www.webpagetest.org/result/180905_90_60fd3b52c101b6aaeb61fda8ac192468/


2018/12/2 計測

https://www.webpagetest.org/result/181202_Q9_0b087ea9b135cf3ee5e8c790e07853a7/


Fixed & Updates

あんまりPWA要素と関係ないかもですが、更新したことなど。


ページネーションをクリックするとスクロール位置が保存されたままになってる


  • 中間くらいまでスクロールした状態で移動するとページ間でその位置のまま



    • position: fixedvhを使っているせい



  • ページが切り替わったときの制御にscrollTopをかませた


NewsList.vue

methods: {

onPageChange() {
document.getElementsByClassName('news-list')[0].scrollTop = 0;
}
}


NewsList.vue

<paginate-links

for="lists"
@change="onPageChange" <!-- ここ -->
:show-step-links="true"
:limit="2">
</paginate-links>


絵文字がパースされていない

🔥の絵文字が :fire: として出力されている

単純にパースしてあげればいいのかなと思ったので、

node-emoji を使いました。

🔥絵文字が適応された

👍👍👍👍👍


ナイトモード実装

21時〜翌6時までの間は自動的に適応するようにしています。

ただカラーをナイトモード用に自制したというよりかは filter: invert(100); を使って

色反転させただけという超シンプルナイトモードです。

ナイトモード適応イメージ:アニメーションで白から黒に色反転されている


defalut.vue

export default {

mounted() {
if((new Date()).getHours() >= 21 || (new Date()).getHours() < 6 ) {
document.body.className += "night-mode";
}
},
}


defalut.vue

.night-mode {

animation: night 2s ease 0s 1 normal;
animation-fill-mode: forwards;
background-color: #fff;
}
@keyframes night {
0% {filter:invert(0);}
100% {filter:invert(100);}
}

ちなみに、Vue.jsでhtmlとかbodyをマウントするのよくないらしいです。


与えられた要素は単にマウントするポイントとして機能します。Vue 1.x とは異なり、マウントされた要素は、全てのケースで Vue によって生成された DOM に置き換えられます。

従って、ルートインスタンスを<html>または<body>にマウントすることは推奨されません。

https://jp.vuejs.org/v2/api/index.html#el



ページネーションのボタンアクセシビリティ対応

今回ページネーションのライブラリで使用したvue-paginateですが、<a>タグのみでhrefで明確なリンク遷移が明示されていない、リンクとして未完成な状態のままでした。

また、tabindex指定もないのでタブキーでのフォーカスも効かない状態でした。

そこでページネーションのボタン部分をリンク要素としてではなくbuttonタグに変更して、意味あるタグを設置・タブにおけるフォーカスの両方を解消しようと思いました。

ただ、この内容についてIssueで報告する・プルリクエストを提出することを考えた時、個人での運用なのでいつ見てもらえるか・かつ受け入れられるかもわからないという不安がありました。

そこで、リポジトリをforkして自分専用用のモジュールを作ったほうが早いと感じたので、早速対応しました。

https://github.com/yamanoku/vue-paginate

aタグからbuttonタグに変更してタブキーのフォーカスが効くようになった

ただ、開閉時のaria-expandedほかWAI-ARIA部分などはまだまだ対応しきれていないので、今後も改良する余地はありそうです(自前実装になる?)。


今後の更新・TODOなど

以下Scrapboxのページにて順次手作業で更新予定です

https://scrapbox.io/yamanoku/Reading…


PWAを作ってみての感想


  • Nuxt.jsにおけるPWA導入が圧倒的にやりやすい・分かりやすいかなと思いました


    • Vue.js依存ですが…



  • PWAだけに限らないですが、何かしら動くものを作ってみると、新しいものがきたらそこから派生してみる・検証することができる

  • まだまだ改善の余地は大きい部分はあるが試行錯誤していろんなことが検証できるのが楽しい


    • こうしたらいいよ的なアドバイスお待ちしております(コメントでもTwitterでも)



  • 今後の派生として、妻を個人Slackに招待して、家族間でのURL共有みたいなのがやれたらいいかなと思っている


    • 妻が結構検索しまくって共有してくれる(育児・買い物・行きたいところ 等)

    • 夫婦間での共有を簡易・履歴として残すようにしたい



  • 自分でPWAを実装してみてAndroid実機で動かせるのがこれまでにない感覚で面白かった


    • ちなみにポートフォリオサイトもPWA化しています

    • GitHub Pagesと紐づけているので、配下のページ(リポジトリ)も自動的にPWA判定になっている?



      • Birthday-Countdown.js など

      • Service Workerがルートディレクトリで設定されているから?





  • 実際にPWAとして使えるものを使ってみたり検証したりしてみる


    • Service Workerがどういう感じで使われているか とか

    • 自分はTwitterはネイティブではなくTwitter Lite(PWA版)のを使うようにしています。



  • 企業の制作実体験記みたいなのが気になりだす(業務内でのノウハウや失敗など)




  • iOSマジお前...となる気持ちがよくわかる

以上になります。ご覧いただきありがとうございました。

明日(12/7)は@_lemon2003_さんになります。


【弊社アドベントカレンダーPR】

株式会社GEEK ロゴ

最後に宣伝になりますが、私が所属している株式会社GEEKでもアドベントカレンダーをやっております。良ければご覧になってみてください。

自分はこのアドベントカレンダーほか色んな所に出張執筆予定です。

GEEKアドベントカレンダーの次回担当はマークアップエンジニアの大房さんになります。