Nuxt.js + Netlify

最高のDXの中の

つまづきポイント

 

@neripark

江面陽一 a.k.a ねり

Readyfor株式会社

フロントエンド+Rails

SIerから転職

本格的にWeb開発を始めて約1年半

@neripark

[like] Vue.js

[study] React、TypeScript

アジェンダ

  • 今回作ったものの紹介
    • 構成
    • 手順概要
  • つまづきポイント
    • Netlify Funtion
    • Netlify Form

今回作ったものの紹介

趣味でやっているバンドのサイト

要件

  • お問い合わせフォーム
    • ”ほぼ” 静的サイトで「よく出てくるけどできれば触りたくないWebサイトの機能」No.1(僕調べ)
    • そのためだけにWor○Press。。?
  • 問い合わせを受信したらLINEへ通知
    • メンバーは非Web業界。LINE > Slack
    • LINE@ は友だち登録のハードルを考慮し見送りたい
  • ライブ情報とニュースはCMSで
    • Netlify CMS なるものがある様子 (未実装)

そういう "チョイ足し機能" Netlifyでできますよ!

Netlify

Form

Netlify

Function

構成

Form

Function

push

通知

deploy

Nuxt.js 部分

  • 静的サイトジェネレータを使用
    • mode は "universal" (理由は後述)
  • Pug、SCSSを使用、TypeScriptはなし
  • あとは粛々とマークアップ&スタイリング...

GitHub

Netlify

GitHub

Netlify

かんたん

3step!!

Netlifyの良さ

  • masterにマージするだけで自動でビルド&公開
  • 独自ドメイン設定も超簡単
    • ただし取得は外部のDNSサービスを使う
  • GitHub上で Pull-Request を作るとPreview用URLを自動生成
    • master 1本でやっていたのは誰ですか?僕です。
  • ​Split Testing という機能を使ってABテスト
  • UIがわかりやすい
  • 速い
  • ドキュメント超充実

控えめに言って最高!

つまづきポイント(1)

Nuxt.js と Netlify の

仕様の競合

Netlify Form概要

<!--  contact.vue -->
<template lang="pug">
    form.contact-form(
      name="iyu-form"
      method="POST"
      data-netlify="true"
    )
      input( name="name" type="text")
</template>

指定通りにformを書くだけで、

管理画面で簡単にフォームを受け取れる!

Netlify公式ドキュメントは

Nuxt.jsのGenerateにおけるSPAとSSRの違い

// nuxt.config.js
{
  mode: 'spa'
}
// nuxt.config.js
{
  mode: 'universal'
}
<!-- dist/index.html -->
<body>
  <div id="__nuxt">
    <div id="nuxt-loading">
      <div>Loading...</div>
    </div>
  </div>
</body>
<!-- dist/index.html -->
<body>
  <div id="__nuxt">
    <div id="__layout">
        <!-- すべてのコンテンツが出力済み -->
    </div>
  </div>
</body>

SSRじゃないとだめだ。。

  • Netlifyの仕様で、デプロイされたHTMLファイルにform要素が記述されていないと認識されない
  • SPAモードだと最初のHTMLには要素が一切記述されていないので、formを使いたい場合は nuxt.config.js の mode を必ず "universal" にする!
  • [余談] そうすると Window と document が使えなくなるので動かないプラグインがある
    • no-ssr で対応(今回は割愛。。Nuxt公式に載っています)

SSRにしてみたが、動かなかった...

[nuxt] Error while initializing app DOMException:
Failed to execute 'appendChild' on 'Node': This node type does not support this method.
    at Object.appendChild

神がいた

要約すると

  • Nuxt.js SSRの仕様で、JS側で持っている情報とDOMに差分があるとDOMの紐付けを見つけられずにエラーを吐く
  • Netlifyは、デプロイされたHTMLファイルを解析して <input type="hidden"> を差し込んでくる → 必然的にDomに差分が生まれる。NetlifyとNuxt.jsの仕様の競合。

解決方法

  1. まったく同じ作りのformを別ページかなにかに静的に用意(今回は pages/dummy-form.vue を作成)
  2. いったんデプロイ → Netlifyにフォームが認識される。
  3. 1で作ったダミー用フォームを削除
  4. 本当のフォームに、Netlifyが差し込んでくる input[type="hidden"] を予めコーディング
  5. 3と4を同時にpush → これでNuxt.jsのDOMとJSの差分がなくなる。

めでたく解決。。

余談:

公式に追記されていた。SPAでもいける?

つまづきポイント(2)

Functionのロギングがつらい!

Netlify Function 概要

  • サーバーサイド側のプログラム( JS / GoLang) を書ける(裏側はAWS Lambda)
  • デプロイしたサイトのさまざまなイベントにフックさせて実行できる
    • formのsubmitにフックさせて、LINEに通知させたい!
  • 公開ソースと別のディレクトリにデプロイして使う
    • 直接commitしてもいいし、反対に gitignore しておいて Netlify 側でビルドさせるようにしてもいい
  • ローカル環境で動作させるためのnpmモジュールが公式から提供されている

Netlify Functionをlocalで動かす

  • npm install netlify-lambda --save-dev
  • npm scripts の定義
  "scripts": {
      "lambda-serve": "netlify-lambda serve lambda/",
  },
  • nuxt.config.js にローカル用設定を追加
  proxy: {
    '/.netlify': {
      target: 'http://localhost:9000',
      pathRewrite: {'^/.netlify/functions': ''},
    },
  },

これで npm run lambda-serve で動く!

LINE notify 概要

  • LINEの通知用botを使って、自分のLINEグループにメッセージを投稿させることができる
  • いつも使っているアカウントでLINE Notifyにログインし、トークンを取得、HTTPリクエストに乗せてPOSTするだけ
  • 使い方は全部公式に載っている

ローカルで動いたし、デプロイしよう!

動かない...

原因の調査

Netlify の管理画面でログを参照

1:01:37 PM: TypeError: Cannot read property 'name' of undefined
    at exports.handler (/var/task/submission-created.js:8:19979)

受け取ったオブジェクトの中からフォームでSubmitされたデータを取得するところでコケている

// lambda/submission-created.js
import querystring from 'querystring'
import axios from 'axios'

exports.handler = function(event, context, callback) {

  // const token = process.env.IYU_FORM_NOTIFY_TOKEN_TEST // test
  const token = process.env.IYU_FORM_NOTIFY_TOKEN_PROD // production

  const params = querystring.parse(decodeURIComponent(event.body))

  axios({
    method: 'post',
    url: 'https://notify-api.line.me/api/notify',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    data: querystring.stringify({
      message: getMsg(params)
    })
  })
    .then(res => {
      res.data.statusCode = 200
      res.data.body = 'ok'
      callback(null, res.data)
    })
    .catch(err => callback(err))
}
function getMsg(params) {
  const msg = `
webサイトからContactがありました!

--
[Name] ${params.name}
[Category] ${params.category}
[ReserveDate] ${params.reservedate}
[ReserveCount] ${params.reservecount}
[Email] ${params.email}
[Message]
${params.message}
  `

  return msg
}

公式ドキュメントは

  • event.body に、JSON形式で入ってくるとのこと

つらみ

  • ローカルでは event.body の中身は Query String 形式だった
    • でも公式は JSON String と書いてある。。
  • console.log を仕込んで push、しかしまさかの変更が反映しないことがある
    • console.logを3行仕込んでも1行しか出ない、エラー解消のための変更が反映しない、等
    • おそらくNetlify → AWS Lambdaへの連携時の時差
  • event.body の出力に成功、しかし長すぎて最初が見切れる
    • 愚直に出力可能な最大文字数を数え、splitして出力

なんとか突き止めた

// ローカル
const params = querystring.parse(decodeURIComponent(event.body))

// 本番
const params = JSON.parse(event.body).payload.data

学び

  • "netlify-lambda" は便利。Netlifyさんありがとう!
  • でも引数に関しては100%再現されていない
    • event.body の中身は、ローカルは Query String 形式、本番はJSON形式!
    • 中身は AWS Lambda だししゃあなしか。と察する心が必要
  • ローカルの修正が100%すぐ本番に反映するわけではない
  • ハマったときは、以上を念頭において粛々と開発😇

よかった

まとめ

なんとここまで

(一部制限はあるが)

すべて無料!

Netlify 最高!!

みんな使おう〜!

エンジニア・デザイナー積極採用中!

MV見てね〜!

ご清聴ありがとうございました!

Made with Slides.com