GAミント至上主義

Web Monomaniacal Developer.

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する(していた)組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が開発用Linuxサーバーのユーザー名に「yuyamazaki」が長いので勝手に作ってくれた。読み方わからないけどウヤマザク)

高校あたりからWEBサイトをやったり趣味の延長でWEB系を仕事にした感じの人間。

2020年1月から株式会社シニアジョブにジョイン。
まだ開発者の社員は2人、これからの会社なのでインフラからきれいな設計をしたい

アズールレーン@竹敷でモバイルのUI、UX研究中(初嫁ジャベリン)

GitHub: https://github.com/uyamazak/

これまでの主なプロジェクト

続きを読む

microCMSのリッチテキスト内のimgタグをamp-imgに変換する

7/1に公開したシニアタイムズでは、NuxtJSとamp-moduleを使ってAMP対応を行いました。

でもmicroCMSのリッチテキストで保存している記事本文は自分で対応する必要があります。

基本的に生成されるHTMLはきれいなのでほとんど問題ないのですが、imgタグだけはamp-imgに変換する必要がありました。

ドキュメント:<amp-img> - amp.dev

タグ名だけでよければ簡単ですが、難しいのが画像の高さを指定する必要があること。表示速度のためには仕方ない。

サムネイルに使うような画像でよければ大体横長で比率も固定しやすいですが、記事内なのでどんな画像が使われるかはわかりません。

今回はいろいろ試行錯誤の結果、画像APIで固定のサイズに変換し、余白は背景色で埋めるという対応をしました。

画像APIとは

HTMLで返ってくるので正規表現で置換するのが良さそうです。まずはimgタグを抽出し、中でsrcにパラメータをつける処理をしました。

で下記のように書いてみました。

ampページのvueファイル抜粋

<script lang="ts">
// 省略
  async asyncData({ $axios, error, params }) {
    // 省略
    // 記事データ取得
    const currentPost = await $axios.$get(API_URL + `magazines/${id}`)
    // imgをamp-imagに変換する heightの指定が必須なので200で固定する
    const ampBody = currentPost.body.replace(
      /<img([^>]*)>/gi,
      (_match, sub) => {
        sub = sub.replace(
          /src="(.+)"/,
          'src="$1?width=300&height=225&fit=fill&fill-color=fafafa"'
        )
        return `<amp-img ${sub} layout="intrinsic" width="300" height="225"></amp-img>`
      }
    return {
      ampBody,
    }
  },
</script>

layout属性については下記。
amp.dev


比率が違う画像を使うと余計な余白ができてしまうという暫定対応感は否めません。

きっちりやりたいのであれば画像APIでサイズなどのメタ情報を取得できるので、これを使って横幅を計算して入れれば良さそうです。
複雑さとAPIリクエスト数が増えるのが懸念事項でしょうか。

フォーマット

NuxtJSのAMPモジュールでamp-analyticsを使う方法

NuxtJS + AMPモジュールでGoogle Analyticsのタグを貼る際、ちょっと手コヅったのでメモ。

GitHub - nuxt-community/amp-module: AMP Module for Nuxt


このページを参考に
AMP のグローバル サイトタグ  |  グローバル サイトタグ(gtag.js)  |  Google Developers

普通にAMPのページテンプレートにタグを貼ると下記のようにエスケープされてしまいます。

<amp-analytics type="gtag" data-credentials="include"><script type="application/json">
      {
        &quot;vars&quot;: {
          &quot;gtag_id&quot;: &quot;UA-*******-1&quot;,
          &quot;config&quot;: {
            &quot;UA-********-1&quot;: { &quot;groups&quot;: &quot;default&quot; }
          }
        }
      }
</script></amp-analytics>

そのため、JSON部分をcomputedなどに書いて、

  computed: {
    gaJson(): string {
      return `{
          "vars": {
            "gtag_id": "${GOOGLE_ANALYTICS_TRACKING_ID}",
            "config": {
              "${GOOGLE_ANALYTICS_TRACKING_ID}": { "groups": "default" }
            }
          }
        }`
    },

v-htmlを使って表示すればエスケープされずに出すことができます。

    <amp-analytics type="gtag" data-credentials="include">
      <script type="application/json" v-html="gaJson"></script>
    </amp-analytics>

AMPモジュールはページ内で使ったamp-系のタグに応じて必要なタグを自動で読み込んでくれるので便利です。


タグの動作確認はGoogle謹製のChrome拡張、Tag Assistantが便利です。

chrome.google.com



NuxtJSとTypeScriptとmicroCMSとNetlifyとAlgoliaで自社メディアサイトつくった

2020/7/1に公開しました↓。いわゆるオウンドメディア的なもの。ここでは忘れないうちに技術的なことだけ書いておきます。

f:id:uyamazak:20200703174129p:plain
senior-job.co.jp

見た感じ普通のブログですが、裏側はタイトルどおり今どきな技術で攻められるだけ攻めきった感あります。

そのおかげで作るのも楽しく運用も楽、PageSpeed InsightsでPCだと100点!(調子がいいときに限る)

早いのでスマホでぜひ試してみてみて!

Google系のいろんなテスト結果 (2020/7/3頃)

PageSpeed Insights (PC)
f:id:uyamazak:20200703102553p:plain
AMP テスト
f:id:uyamazak:20200703161002p:plain

2020/7/17追記
モバイル優先で最適化した結果オールグリーン (90点以上)になりました!
Lighthouse (Mobile)
f:id:uyamazak:20200717113528p:plain

Lighthouse (PC)
f:id:uyamazak:20200703172835p:plain
構造化データ
f:id:uyamazak:20200706095005p:plain

主な機能

  • NextJS 2.13でfull static exportしたものを静的配信
  • レスポンシブ
  • 記事画像はwebp対応
  • 構造化データ対応 (パンくずと記事)
  • OGP系metaいろいろいれた
  • AMP対応(記事だけ、途中)
  • IE11でも動く(動くとはいっていない)


あとURL見れば分かる通り、既存サイトの/magazine/ディレクトリ配下で公開するためにNginxでプロキシしてますが省略。

技術スタック的なやつ

フロントエンド

NuxtJS + TypeScript
Nuxt.js - The Intuitive Vue Framework

full static exportについては公式ブログと、microCMSのブログがわかりやすいです。
Going Full Static - NuxtJS
Nuxt 2.13で導入されたFull Static Generationを試してみる

サーバーサイド

チーム体制

デザイナーの方が作ってくれた3ページ(トップ、一覧、個別)のHTML + CSSをもとに、
エンジニア職は2人なので、ざっくりフロント側が私、サーバー側がもう一人の方で分担しました。
microCMS、Algolia、Netlifyとマネージレス、サーバーレスなものを使ったので、ドキュメントや仕様書がほぼ無くてもスムースに進みました。

記事は広報の方SEOを考慮しながらバリバリ書いてくれています。
私が動いたのは公開まで約3週間でした(完了とはいっていない)。

NuxtJSについて

個人的にも会社的にもVueは使ったことありましたが、NuxtJSは初めてでした。
検索エンジン向けにSSRもしたいし、メディアみたいな典型的なWEBサイトであれば、NuxtJSがいいだろうということで決定。

最終的には開発中の2020/6/19にリリースされた2.13でnuxt generateから変わったnuxt build && nuxt exportをNetlify上で行ってデプロイしています。

デザイナーさんがつくったHTMLとCSSをもとにしたのでVuetify的なデザインフレームワークは使いませんでした。

NuxtJSでよかったこと

想像以上にこういうメディアサイトとしてよくやることは揃っているので非常に楽でした。サイトマップとかAMPとか構造化データとかフィードとか。

だいたいのページはasyncDataでmicroCMSやAlgoliaでデータを取ってきて終わりました。

なんだかんだいっても、こういうサイトをLaravelとかDjangoとかRails的なWEBフレームワークでまた作りたいかというともう絶対に戻りたくないので、今の所の最善解であると思っています。

NuxtJSでつらかったこと

つらかったのはNuxtJSの独自の機能、設定、処理の流れがかなりあるので、ちゃんとNuxtJSを使いこなすには、それを一気に理解する必要があること

ContextとかasycDataとか。
API: コンテキスト - NuxtJS

クエリパラメータの変更時に再レンダリングさせるときにはちょっとハマりました。専用の設定があった。

Vue.jsだとProgressiveと名乗るように、少しずつ覚えていけるので理解はしやすかった印象。

仕事でずっとさわり続けるにはいいけど、数ヶ月、下手したら数年たってから触るような開発時はつらいかも。

SSRでつらかったこと

今回はNodeサーバーは使っておらず、nuxt exportによる静的ファイル生成だけど、一応SSRということで。
SSRはやっぱ辛い。動くところがサーバーとブラウザで2箇所あり、それぞれがちょっと違うという・・・。createdは両方とかmountedはブラウザだけとか。


Vueのときはthisだけ考えてればいいけど、NuxtJSだと上記のContextもあり、共通処理をasyncDataで使いたいからプラグインでいれるかー思っていれたけど、あとでmountedとかでも使いたくなったり。Vueのときはそのコンポーネントでしか使わない細かい共通処理はmethodsとかcomputedに分けてたけどasyncDataでは使えないので沢山書くことになったり。

yarn devで起動する開発サーバーはホットリロードで快適ですが、本番相当の静的に書き出したものを確認したい場合は、何度もコマンド(nuxt build && nuxt export && nuxt serve)を実行する必要があり、時間もかかって面倒でした。

個人的にはSSRは過渡期の技術だと思っているので、ブラウザだけでなんとかなる日が来ると思うし、SEOを気にしないシンプルなWEBアプリであれば不要なことも多いと思ってます。そうであってほしい。

NetlifyでまだベータだけどPrerenderingは今度使ってみたい。
docs.netlify.com


また開発中にバージョン2.13が出たのでアップグレードしたのですが、残ってた.nuxtフォルダの影響かnuxtコマンドが失敗する状況にはまりました。 

Twitterで@sho_prodさんに助けられました。感謝。

個人開発ではNuxtJSは使わず、Vue.jsだけでなんとかできるしょぼいWEBアプリ作りたい。3も出るし

SSRでよかったこと

SEO的な問題を解決できたこと、速度的なメリットは当たり前ですが、トップページはカテゴリごととかいろんな条件でコンテンツをひっぱってくるのでAPIコールが多くなりがちなんですが、基本的に静的生成したものを配信するので、負荷をさほど気にしなくていいのはよかったです。

jQueryェ・・・

最初デザイン段階ではjQueryを使ってたんですが、NuxtJSとjQueryの共存はつらいし、将来的にもつらくなるのは見えていたので処理を移植して排除しました。

microCMS

管理画面作りたくないので、ヘッドレスCMSを書き手含めて社内でいろいろ検討してました。
WordPressAPIでもいいんですが、サーバーとか管理したくないし、今どきなんかなぁ・・・という感じからContentfulと国産のmicroCMSが候補に。

主に記事を書くことになる広報さんの意見と、触った感じもよかったのでmicroCMSを採用しました。日本語大事。

f:id:uyamazak:20200625191250p:plain

microCMSでよかったこと

日本語でわかりやすい

管理画面のわかりやすく単純なGETリクエストでJSONが返ってくるのでよくあるブログ的なものには十分でした。
スキーマを作ってくれたのは同僚でしたが、ドキュメントも不要で管理画面を見るだけで理解できました。

画像API

アップロードした画像のURLにクエリをつけるだけでリサイズや切り抜きなど必要なことは全部できました。webpへの変換もできるので使いました。
これ自分でやったり、他サービス使うの超面倒なので超助かりました。

microcms.io

microCMSでつらかったこと

並び順を指定できない

7/14 追記:7/10にリリースされました!
が、まだfiltersとの併用ができないとのことで使いたいところで使えなかった。

"{\"message\":\"'orders' and 'filters' can't be specified at the same time now. Coming soon!!\"}"

APIでソートができるようになりました


SQLでいうORDERのカラム指定、DESCができません。
ロードマップには入ってましたが、現状管理画面で表示される順でしか取得できません(ドラッグで並び替えできる)。
そのため公開日が古い順に並べたいとか、今の記事の一つ後の記事などを取得したい場合は全部取ってごにょごにょする必要があります。
あとAlgoliaとか検索エンジン使うのであればそちらで取得するという方法もあり。

ユニークキーはcontentIdだけ、作成時には指定不可

自分で作成した項目はユニークにできない。最初からついているcontentIdしかないので、URLのスラッグにはこれを使うしかなさそう。
でも新規作成時には指定できず、初期値がランダム文字列で入ってしまうので、自分で変更することでビルドが無駄に走ってしまったりするのでよろしくない。
UI的にも変えづらい。

filtersで複数idで取得できない(2020/7/9リリース)

追記:できるようになりました!
idを指定して複数のコンテンツを 一度に取得できるようになりました

contentIdを使って、SQLでいうwhete id in ('aaa', 'bbb', 'ccc')みたいな取得がしたかったけど、idの数分コールしてしまってます。
今回はピックアップ記事みたいのをidのリストを元に取りたかったけど、他にも使い所ありそう。

公式ライブラリSDK的なものがない

SDKで関数一つで結果が取得できるAlgoliaと比べてですが、自分でURLを作ってaxiosしなくてはいけないのはちょっと面倒だなぁと。

github.com

でもそんなクエリの種類もなかったので今のところ自分でライブラリをつくるまでもなく文字列連結で済んでます。

見出し(h[1-6]タグ)に自動で振られるidが被りまくる(すぐ解決済み)

ページ内に使うために振られてるidですが、被りまくるので使い物になりませんでしたが、既知のバグだったそうでTwitterでつぶやいたらすぐ直してくれました。
f:id:uyamazak:20200629140514p:plain

公開前々日にmicroCMSが全文検索に対応

開発ロードマップには入っていたのですが、公開直前にリリースされたのでまだ使ってません。
でもこれで十分ならAlgoliaが不要になるので更に便利になるなーと思いました。
試した感じ1-gram的な動きをしているので、精度的に今の所はAlgoliaの方がいいなという印象。

microcms.io

たまにページ生成時に502エラーを返す

nuxt export時にたまーに502エラーで取得に失敗することがありました。
これはビルドコマンドを

nuxt build && nuxt export --fail-on-error

とすることでNetlifyでコケさせてエラー通知を受け取るという方式で対処してます。
リトライ機構とか作ればいいんだろうけど今の所十分そう。

Netlifyについて

GitHub等を使っていればポチポチするだけでCI/CD環境ができてしまいます。設定ファイルもほとんど不要。
開発中にビルド時間をオーバーしたのといろんなSlack通知が便利なのでProプランを使用しています。(48ドル/月)

Netlifyでよかったこと

導入が超ラクなのに加えて、ビルド単位でURLを生成してくれるので、プルリクしたものを実際に確認したり、問題があったときに過去のビルドと比べたりできるのは超便利でした。
Firebaseでもできたらいいのにな。

Netlifyでつらかったこと

大きくハマったのが、URLに大文字が入っていると小文字にリダイレクトされてしまうこと。

Netlifyは大文字URLが使えない件とHexoを運用する上で確認すべき設定 - pixelog

microCMSのcontentIdをスラッグとしてURLに使っていましたが、そこで大文字を使ってしまうと再読み込みで小文字になってしまうので404になってしまいました。
ローカルサーバーでは再現できないし、nuxt-linkで飛ぶ場合は問題ないのに、再読み込みで404になることや、コンテンツによって起きたり起きなかったり、今回大文字だったのがI (アイ)だったため変化に気づきにくいなどが重なり、判明するまで時間がかかりました・・・。

今の所microCMSのcontentIdには大文字は使わないというルールの共有と、静的ビルド時に動的パスをチェックをして、大文字見つかったらthrowすることでビルドを失敗させ、Slack通知を受け取るというフローにしてます。

Algoliaについて

優秀な同僚がやってくれましたので正直よくわかってないんですが、超早い。SDKでめっちゃ楽。Elasticsearch立てなくてよかった。

microCMSからコンテンツ更新時などのWebhookでLamda + Serverlessで追加更新する関数を実行してます(という認識)。

1レコードあたり10kb制限があるので、全部入れると普通のブログ記事でもすぐオーバーします。
そのため記事を見出しごとに分割して入れるのはちょっと大変そうでした。
でもそれがスピードの秘訣っぽい。
一つ一つが短いほうがヒットした場所を示しやすいってメリットもあるけど、今の所実装はしてない。

これから

Lighthouseなど各種ツールで警告や、モバイルでのパフォーマンスの課題が残っているので、そういうのを潰していくのと、絶賛記事入稿中なので、アクセスが増えたら見えてくる問題もあると思うので対処していきます。
開発者の求人もしてるのでよろしくね。特にフロント側の人!Vueたくさん使います!