メンチカツ

ロースカツが好きです

docker-composeのNuxt.jsからAPIをコールしたら「[HPM] Error occurred while trying to proxy request」

f:id:easy-breezy:20191025170443p:plain

こういう構成のdocker-composeで、frontendコンテナ(Nuxt.js:localhost:3000)から

backendコンテナ(Laravel:localhost:8000)にAPIリクエストをしたら

[HPM] Error occurred while trying to proxy request /api/hoge from localhost:3000 to http://localhost:8000/

という504エラーが返ってきて、backendまで届いていない。。

これは フロントエンドの nuxt.config.js のproxy設定が間違っていて、

proxy: {
  '/api': {
    target: 'http://localhost:8000/api',
    pathRewrite: {
      '^/api/': '/'
    }
  }
},

を以下のように修正してリスタートしたらなおった。

proxy: {
  '/api': {
    target: 'http://backend:8000/api', # コンテナ名に変更!
    pathRewrite: {
      '^/api/': '/'
    }
  }
},

[HPM] Error occurred while trying to proxy request 、webフロントエンド作るたびに言われてる気がする。

【メモ】docker-composeで起動したNuxt.jsアプリに繋がらない

コンテナでは起動を確認できる( docker-compose exec [コンテナ名] bash で、 curl localhost:8080 とすると起動してればhtmlが返る )のに、ホスト側で見れない。

package.json を以下のように書き換えたら見られるようになった。

# これを
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },

# こうじゃ!
  "scripts": {
    "dev": "HOST=0.0.0.0 PORT=8080 nuxt", # ココを変えた!
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },

【メモ】create-nuxt-appで失敗する

throw new SAOError(`Failed to install ${packageName} in ${cwd}`)

とか出て進まない。メッセージ違うけど、前もあったなこれ・・・

今回はESListを外したら成功しました。成功した組み合わせ置いておきます。

? Choose the package manager Yarn
? Choose UI framework Element
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools Prettier
? Choose test framework None
? Choose rendering mode Single Page App

【メモ】Nuxt.js: axiosでオフライン状態(NetworkError)を捕捉する

axiosでAPIにアクセスするとき、エラーレスポンスをハンドリングすることは出来るんだけど、接続エラーなどの、サーバーからレスポンスが何も帰ってこないエラー(ネットワークエラー)は onError にも onResponse にも入ってこない。

// @/plugins/axios.js

export default function({ $axios }) {
  $axios.onRequest(req => {
    // リクエスト前に呼び出されるコード
  })
  $axios.onResponse(res => {
    // 成功レスポンスを受け取った時に呼び出されるコード
    // 接続エラーはここにこない(レスポンス自体ないから)
  })
  $axios.onError(res => {
    // エラーレスポンスを受け取った時に呼び出されるコード
    // 接続エラーはここにこない(レスポンス自体ないから)
  })
}

一日考えた挙句、そうかリクエスト前にチェックすれば良いんだわということに気が付いた(遅)

window.navigator.onLine で、オンライン状態を確認できるので、onRequest にこのチェックを追加。

developer.mozilla.org

// @/plugins/axios.js
import Vue from 'vue'

export default function({ $axios }) {
  $axios.onRequest(req => {
    if (!window.navigator.onLine) {
      Vue.toasted.error( // オフライン時にAPIリクエストがあったときにtoastを表示するようにした
        '現在オフラインです。'
      )
      return Promise.reject(req)
    }
    // リクエスト前に呼び出されるコード
  })
  $axios.onResponse(res => {
    // 成功レスポンスを受け取った時に呼び出されるコード
  })
  $axios.onError(res => {
    // エラーレスポンスを受け取った時に呼び出されるコード
  })
}

無事捕捉することが出来ました。

【メモ】chromeアップデートしたら Unchecked runtime.lastError: The message port closed before a response was received.

Vue.js 開発環境で、chromeアップデートしたら

Unchecked runtime.lastError: The message port closed before a response was received.

がコンソールに出るようになった。

f:id:easy-breezy:20190807114217p:plain

ぐぐる拡張機能のどれかが影響して出ているらしい。

でも、環境によって犯人が異なるみたい。

なので、インストール済みの拡張機能を少しずつオンオフしていくと、私の環境では、犯人は

 SkyChromeExtension

だった。

f:id:easy-breezy:20190807114219p:plain

速攻オフって平安を取り戻した。

Vue Test Utilsでコンポーネントのdata()を評価したい

なぜかうまくいかず時間がかかったのでメモ

f:id:easy-breezy:20190801171844p:plain

こういうページネーションのコンポーネントのテストを書くとき、

コンポーネントのpropsには以下のような値(現在のページ,ページあたりの件数,トータル件数)を設定できるとして

<Pagination :config="{ current: 1, limit: 20, total: 100 }" @movePage="load" />

propsに値を設定したことを受け、watch に定義したメソッドで、data() の最終ページ lastPage の値を算出したい。

watch: {
  config: function(payload) {
    // ここではわかりやすく「5ページ >>」とか変数にいれてる
    this.lastPage = Math.ceil(payload.total / payload.limit) + 'ページ >>' // 上記例の設定値だと「5ページ >>」になる
  }
}

この「5ページ >>」を検査したかったのだけど、テストコードで propsData をセットしてもなぜかwatchが発火しない。

<template>
  ...
  <a
    class="pagination-link last-page"
    :disabled="currentPage === lastPage"
    aria-label="Goto last page"
    @click="moveLast"
  >
    {{ lastPage }} <!-- 「5ページ >>」 -->
  </a>
  ...
</template>
describe('components/Pagination.vue', () => {
  let wrapper
  describe('template', () => {
    beforeEach(() => {
      wrapper = mount(Pagination, {
        propsData: { config: { nextPage: 2, limit: 20, total: 100 } } // propsにセットしたのだからwatch発動してほしい!
      })
    })
    test('最終ページは5ページ目であること', () => {
      const page = wrapper.find('.last-page')
      expect(page.text()).toBe('5ページ >>') // 失敗。got:null
    })
  })
})

props を2回セットしたら思った通り動くようになった。

describe('components/Pagination.vue', () => {
  let wrapper
  describe('template', () => {
    beforeEach(() => {
      wrapper = mount(Pagination, {
        propsData: { config: { nextPage: 2, limit: 20, total: 100 } }
      })
    })
    test('最終ページは5ページ目であること', () => {
      wrapper.setProps({ config: { nextPage: 2, limit: 20, total: 100 } }) // これを足した。
      const page = wrapper.find('.last-page')
      expect(page.text()).toBe('5ページ >>') // 成功した!
    })
  })
})

2度setPropsしていてなんかモヤるけど、やりたいことはできた。

【メモ】Nuxt.js/BulmaのNavbarでdropdownが残ってしまう(:focus-withinが効きっぱなし)

よくあるSPAのドロップダウン。

f:id:easy-breezy:20190712172643p:plain
ドロップダウンだ

<n-link class="navbar-link" to="/members">
  <span class="icon is-medium">
    <i class="fas fa-lg fa-user"></i>
  </span>
  <span>メンバー管理</span>
</n-link>
<div class="navbar-dropdown">
  <n-link class="navbar-item" to="/members">メンバー一覧</n-link>
  <n-link class="navbar-item" to="/members/new">メンバー登録</n-link>
</div>

これ、なぜか n-link click後にフォーカス状態が残ってしまい、閉じてくれない。

これは @click.native イベントに一行追加することで、閉じてくれるようになりました(手元のChromeFirefoxで確認)。

<n-link class="navbar-link" to="/members" @click.native="hideMenu">
  <span class="icon is-medium">
    <i class="fas fa-lg fa-user"></i>
  </span>
  <span>メンバー管理</span>
</n-link>
<div class="navbar-dropdown">
  <n-link class="navbar-item" to="/members" @click.native="hideMenu">メンバー一覧</n-link>
  <n-link class="navbar-item" to="/members/new" @click.native="hideMenu">メンバー登録</n-link>
</div>

んで、このメソッドを追加。

<script>
export default {
  methods: {
    hideMenu(e) {
      e.target.blur()
    }
  }
}
</script>

以上です。

この一行を追加するまで、謎にえらい時間を費やしてしまったので、メモとして残します。