20200514のvue.jsに関する記事は8件です。

【Vue】v-ifと$refsによるTypeErrorの対処法

v-ifを設定したコンポーネントに対してthis.$refsで要素を取得しようとするとunderfinedになります。

以下、具体例と対処法です。
開発環境はVue.js 2.6.11です。

コード

例は適当です。最低限の部分しか書いていません。

<template>
  <div>
    <!-- 編集部分 -->
    <status-edit
      v-if="isEditing"
      ref="editArea"
    />
    <!-- ビュー部分 -->
    <status-view v-if="!isEditing" />
    <!-- ボタン -->
    <button v-if="!isEditing" @click="pushEdit">編集</button>
  </div>
</template>

<script>
import statusView from "./StatusView"
import statusEdit from "./StatusEdit"

export default {
  components: {
    statusView,
    statusEdit
  },
  data() {
    return {
      isEditing: false
    }
  },
  methods: {
    pushEdit() {
      // isEditingをtrueにすることで編集画面/表示画面を切り替える
      this.isEditing = true
      // statusEditコンポーネントの中のfetchDateメソッドを実行
      this.$refs.editArea.fetchData()
    }
  }
}
</script>

エラー

[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'fetchData' of undefined"

vue.runtime.esm.js:1927 TypeError: Cannot read property 'fetchData' of undefined

「編集」ボタンを押すと、このようなエラーが出ます。
this.$refs.editAreaがunderfinedになっているから、fetchDateなんてプロパティは読み込めないよ」という意味です。

原因

v-if="false"のとき、statusEditコンポーネントはページ上に存在していません。
そして、メソッド内の処理は非同期に行われます。
つまり、コンポーネントが描画されていない状態でthis.$refs.editAreaを取得しようとするので、当然underfinedになってしまうのです。

解決法

async/awaitを使う

methods: {
  async pushEdit() {
    await (this.isEditing = true)
    this.$refs.editArea.fetchData()
  }
}

async/awaitでコンポーネントが描画された後、$refsでコンポーネントを取得しメソッドを実行する。

v-ifをv-showに変える

<status-edit
 v-show="isEditing"
 ref="editArea"
/>

v-showdisplay: none;によって見えていないだけで描画はされているので、コンポーネント内のメソッドを実行することができる。

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】v-ifと$refsによるTypeErrorの対処法

v-ifを設定したコンポーネントに対してthis.$refsで要素を取得しようとするとunderfinedになります。

以下、具体例と対処法です。
開発環境はVue.js 2.6.11です。

コード

例は適当です。最低限の部分しか書いていません。

<template>
  <div>
    <!-- 編集部分 -->
    <status-edit
      v-if="isEditing"
      ref="editArea"
    />
    <!-- ビュー部分 -->
    <status-view v-if="!isEditing" />
    <!-- ボタン -->
    <button v-if="!isEditing" @click="pushEdit">編集</button>
  </div>
</template>

<script>
import statusView from "./StatusView"
import statusEdit from "./StatusEdit"

export default {
  components: {
    statusView,
    statusEdit
  },
  data() {
    return {
      isEditing: false
    }
  },
  methods: {
    pushEdit() {
      // isEditingをtrueにすることで編集画面/表示画面を切り替える
      this.isEditing = true
      // statusEditコンポーネントの中のfetchDateメソッドを実行
      this.$refs.editArea.fetchData()
    }
  }
}
</script>

エラー

[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'fetchData' of undefined"

vue.runtime.esm.js:1927 TypeError: Cannot read property 'fetchData' of undefined

「編集」ボタンを押すと、このようなエラーが出ます。
this.$refs.editAreaがunderfinedになっているから、fetchDataなんてプロパティは読み込めないよ」という意味です。

原因

v-if="false"のとき、statusEditコンポーネントはページ上に存在していません。
そして、メソッド内の処理は非同期に行われます。
つまり、コンポーネントが描画されていない状態でthis.$refs.editAreaを取得しようとするので、当然underfinedになってしまうのです。

解決法

async/awaitを使う

methods: {
  async pushEdit() {
    await (this.isEditing = true)
    this.$refs.editArea.fetchData()
  }
}

async/awaitでコンポーネントが描画されるのを待ちます。その後、$refsでコンポーネントを取得してメソッドを実行します。

v-ifをv-showに変える

<status-edit
 v-show="isEditing"
 ref="editArea"
/>

v-showdisplay: none;によって見えていないだけで描画はされているので、コンポーネント内のメソッドを実行することができます。

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue(Vue-CLI)とTypeScriptでhighchartsのグラフ表示

Vue(Vue-CLI)とTypeScriptでhighchartsのグラフを表示させます。

プロジェクトの作成

Vue CLI UIでプロジェクトを作成します。パッケージマネージャーとしてyarnを選択しています。
スクリーンショット (28).png

手動を選択し、TypeScriptを有効化します。
スクリーンショット (27).png

package.jsonにhighchartsの追加

package.jsonのdependenciesにhighchartshighcharts-vueを追加します。

package.json
  "dependencies": {
    "core-js": "^3.6.4",
    "highcharts": "^8.1.0",
    "highcharts-vue": "^1.3.5",
    "vue": "^2.6.11",
    "vue-class-component": "^7.2.3",
    "vue-property-decorator": "^8.4.1",
    "vue-router": "^3.1.6"
  }

追加後、yarnコマンドを実行し、追加します。

yarn

グラフの作成

2019年の平均気温を表示するグラフを作成します。

./src/components/Graph.vue
<template>
    <div>
        <highcharts :options="graph"></highcharts>
    </div>
</template>

<script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    import { Chart } from 'highcharts-vue';

    export type DataType = {
        graph: any;
    }

    @Component ({
        components: {
            highcharts: Chart 
        },
    })
    export default class Graph extends Vue {
        data (): DataType {
            return {
                graph: {
                    title: {
                        text: '2019年の平均気温'
                    },
                    subtitle: {
                        text: '引用:<a href="http://www.data.jma.go.jp/obd/stats/etrn/view/monthly_s1.php?prec_no=44&block_no=47662&year=2019&month=&day=&view=">気象庁</a>'
                    },
                    xAxis: {
                        categories: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
                        crosshair: true
                    },
                    yAxis: {
                        title: false,
                        labels: {
                            format: '{value} ℃'
                        },
                        opposite: false,
                    },
                    credits: {
                        enabled: false
                    },
                    tooltip: {
                        pointFormat: '{series.name}:{point.y:.1f} ℃'
                    },
                    series: [{
                        name: '平均気温',
                        type: 'column',
                        data: [5.6, 7.2, 10.6, 13.6, 20.0, 21.8, 24.1, 28.4, 25.1, 19.4, 13.1, 8.5],
                        marker: {
                            enabled: true
                        },
                    }],
                }
            }
        }
    }
</script>

グラフの読み込み

App.vueに上記で作成したグラフを読み込ませます。

./src/App.vue
<template>
    <div id="app">
        <Graph></Graph>
    </div>
</template>

<script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    import Graph from '@/components/Graph.vue';

    @Component ({
        components: {
            Graph,
        },
    })
    export default class App extends Vue {
    }
</script>

<style>
</style>

グラフの表示

グラフが表示されます。
スクリーンショット (30).png

参考

こちらの記事を参考にさせていただきました。
Vue.js+TypeScript+Nuxt.js環境で、highcharts-vueを使うためのtips

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】vue-js-modalの導入方法

概要

Nuxtにvue-js-modalを導入するのに少しハマった部分があったので、導入方法を残しておきます。

導入

インストール

yarnを使ってインストール

$ yarn add vue-js-modal

vue-js-modal.jsを作成

pluginsディレクトリ内にvue-js-modal.jsファイルを作成します。

plugins/vue-js-modal.js
import Vue from "vue"
import VModal from "vue-js-modal"

Vue.use(VModal)

nuxt.config.js

以下ハマり部分。

nuxt.config.jsを変更していきます。

nuxt.config.js
export default {
  plugins: [
    "~/plugins/vue-js-modal",
  ]
}

とやれば導入出来るはずだったのですが、私の環境では動かず、以下のようにすることで動きました。

nuxt.config.js
export default {
  plugins: [
    { src: "~/plugins/vue-js-modal", ssr: false },
  ]
}

明示的にssrをfalseにすることでコンポーネント内でmodalを使用することが出来ました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】MIDIデバイスで音楽を配信できるWebサービスを作った

はじめに

なんか色々あってWebサービスをリリースすることになったのでついでに記事を書くことにしました。
何ができるのか、どんな感じに実装したか等を説明したいと思います。

作ったもの

サービス名は、midiesです。友達に考えてもらいました。
アイコンはMIDI端子とWi-Fiを組み合わせた感じで、まあ悪くはないかなと思ってます。

このサービスの特徴は、MIDIデバイス(仮想でも可)の信号をブラウザだけでライブ配信できることです。
配信機能はパソコンやAndroid端末の一部ブラウザのみ対応しており、受信と再生は多分全てのブラウザが対応してます。
ただし、IEだけは動作の保証をしません(一応再生はできたっぽい)。
送受信者のネットワークが安定していれば1秒未満の遅延で配信が可能です。
スクリーンショット 2020-05-13 11.44.53.png
スクリーンショット 2020-05-13 12.28.45.png

配信と再生画面のスクショです。パソコンのみですがスマホにもちゃんと対応しています。

技術的な話

通信部分の解説(というか開発の流れ)

最初は、NETDUETTOを真似できないかなとか思って作り始めました。
とりあえず何回も使ったことのある、WebSocketを使って実装しました。
東京都内同士での通信で遅延が大体40~100 msくらいでセッションはできなくもないって感じです。
欠点は短時間に大量のデータを送信するとパケットは詰まってというか、一気にデータが流れたりしたんですね(要はラグい)。
多少和音を弾くくらいだと問題はなかったですが不安定な感じでした。

次に、WebRTCに初めて挑戦してみました。まあ色々頑張ってVuexで状態管理できる感じのを実装しました。
データチャネルを使って通信したんですがこちらはWebSocketよりラグが酷かったですね。
なんか設定をミスったのか知りませんが諦めました。

最後に、FirebaseのRealtime Databaseを使いました。遅延は150~200 msって感じで安定性はWebSocketと変わらない感じでした。
まあ、通信にWebSocketを使ってるので当たり前ですね。そこで、セッションはもう諦めてライブ配信を実装することにしました。
また、サーバー使うの面倒だし全部Firebaseで実装することにしました(この時点で完成したら公開しようと思った)。
ラグの軽減のために色々試しましたが最終的に採用したのは以下の手法です。

送信側は、大量のパケットを送信しないためにまずデータを50 ms間スタックしてから送信します。
受信側は、それをsetTimeoutを使って(1000+Δt-Δt')ms後に再生するようにします。
ここでΔtは、データの前後の送信時間の差、Δt'は、データの前後の受信時間の差です。
こうすることにより、再生時のラグを緩和することが可能です(間違ってたら教えてください)。
ちなみに、送信側のスタック時間が50 msなのは30、50、100、500、1000 msの中で一番安定した気がするからです。
30 ms以上の時点で和音を1回で送信できるのでかなり効率的でした。
時間が長くなると不安定になる原因はわかりませんでした。データサイズが大きいからというわけではない気がします。
受信側の1000 msは500 ms以上で再生に問題がある場合、ネットワーク環境に問題がある気がしたので
なんとなく倍の1000 msをデフォルトに設定しました。500 msを用意したのは全体の遅延が1秒以内だと宣伝したかったからです。

この手法を実装したあとは、ユーザー、投稿等の機能を頑張って実装しました。
細かい解説はしませんが何を使って何を実装したかは説明します。

フロントエンド

みんな大好き...かは知りませんがVueを使ってます。
UIフレームワークはVuetifyを使っていてシンプルな感じに作ったつもりです。
PWAにも一応対応していますがWorkboxデフォルトのPrecacheだけですね。

音の再生にはhowler.jsを使っていてVuexで管理してます。
PWA対応したのでVuexは要らないと言えば要らないですが、それでもロードに時間がかかるのでありだと思いました(Vuexからのロードは一瞬なので)。
他にもMIDI.jsというものがありますが今回は多分使わなくていいかなと思いました、というか途中で変えたくなかった。
それと音源ファイルを後ほど説明するFirebaseのStorageで管理しようかと思ったんですがなんとなくやめました。

肝心のMIDIデバイスへのアクセスは、Web MIDI APIを使っています。
ブラウザでMIDIデバイスを操作できるって何を目指してるんですかね?
まあ、おかげでこういうサービスが作れたんですけど。

バックエンド

有名なmBaaSであるFirebaseを使っています。
実際に使うのは初めてだったので、間違った使い方もあると思いますが何をどう使ったのか軽く説明します。

Hosting

Vueでビルドしたファイルをホスティングしてます。特に言うことはないです。

Authentication

ツイッター認証を使用しています。こちらも特に言うことはないです。

Firestore

ユーザー情報と投稿詳細が保存されます。
データ構造はユーザー削除をしやすいようにするため、サブコレクションを選択しました。
ユーザードキュメントに投稿とお気に入り一覧のサブコレクションがある感じです。
こちらをかなり参考にしました。具体的な実装方法が気になる方は見てみてください。

Realtime Database

投稿のMIDIデータが保存されます。Firestoreも考えましたが遅延時間がかなり不安定だったので断念しました。
もし、このサービスが人気になればすぐストレージが足りなくなるので、ライブ配信が終了したら自動でFirestoreに移すなり方法を考える必要があります(Realtime Databaseはストレージ料金が高いので)。
また、ライブ配信中のホスト状態を共有するためにオンラインであるか、配信が終了したかのフラグも保存してます。
これらは、次に説明するFunctionsのトリガーに使用しています。

Functions

以下の4つの関数を作成しました。

  1. Realtime Databaseでオフラインになったら1分後にオンラインに復帰したか確認して、してなかったら終了フラグを立ててFirestoreの投稿詳細に再生時間を追加する。
  2. Firestoreで投稿が削除されたらRealtime Databaseも削除する。それと同時に、Firestoreで全ユーザーのお気に入り一覧からその投稿を削除する。
  3. ユーザーのお気に入り一覧に投稿が追加または削除されたら、その投稿のお気に入り数を変化させる。
  4. Authenticationでユーザーが削除されたらFirestoreでサブコレクションを含む全てのデータを削除する。

改善点

アップロード機能の実装
これはそんなに複雑ではないですが、とりあえずユーザーが増えたりしたら実装します。

フォロー機能の実装
お気に入り機能とたいして変わらないですが面倒なので、こちらもユーザーが増えたりしたら実装します。

MIDIファイルの対応
インポートやエクスポートができると便利そうですよね。これにより、投稿データの保存をStorageに任せることができるようになるのでユーザーが増えれば節約にもなります(JSONでも可能ではある)。また、リアルタイムでの楽譜表示なんかもできるようになります。ただ、MIDIファイル自体よくわからなかったので諦めました。バイナリはちょっと...

検索機能の実装
Firestoreって全文検索できないんですよね。N-gramでの実装も考えましたがとりあえず今後も実装の予定はありません。ただ、どこかのデータベースから曲名だけ引っ張ってきてタグみたいにするのはありかなと記事を書きながら思いました。これはいつか実装するかも知れないです。

タイトルの自動生成(実装しました)
SSRは無理なのでFunctions使うしかないと思います。シェアする時にタイトルがわからないのは不便なのでまあユーザーが増えたら実装します。mountedでの自動生成でも問題なく検索で表示されるみたいです。やったことあるのでそのうち対応します。

終わりに

今回初めて自分が1から作ったWebサービスを公開することになりました。
もともとは軽く作ってGitHubに公開する程度のつもりでしたが、楽しくなっちゃって1ヶ月以上かけてここまで作ってしまいました...
今までも学校のイベントで使うウェブサイトだったり、友達が運営してるSNSのフロントエンドだけだったりと色々作ってきましたが、それに比べると早く完成しました。1年以上色々作ってるしもういい加減慣れてきた感じなんですかね。
FirebaseはNoSQLに戸惑ったくらいで使いやすかったです。ただ、SQLは本当に便利なんだなとも思いました。

今のところは既存の動画投稿サイト等と比べると遅延がかなり少ないことと、音源を変更可能であるというメリットしかありませんが少しでも使っていただけると嬉しいです。また、音源の種類は増やしたいと思っているのでオススメとかあったら教えてください。
最後まで読んでいただきありがとうございました。

参考文献

setIntervalとsetTimeoutを調べた結果余分なことになった
WebRTCの簡易シグナリング
WebRTCのデータチャネル解説
Firebaseドキュメント
Cloud Firestoreで「いいね」機能を実装するときの勘所
CloudFunctionsを使ってFirestoreのサブコレクションを削除する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelとVueでSPAを作るときにTypescriptとpugを使う設定をする

LaravelVue.jsTypescriptpugSPAを作ったときのメモ。

docker --version
Docker version 19.03.8, build afacb8b

扱ったDockerのバージョンは19.03.8。

Fig.1
project
├── db
├── web
├── Dockerfile
├── docker-compose.yml
└── .env

以下の手順で Fig.1 のような構成のレポジトリを作り、webディレクトリの中にLaravelをインストールする。

レポジトリと各ファイルを作成

mkdir project_name && cd $_ && touch {docker-compose.yml,Dockerfile,.env}

プロジェクトのディレクトリを作成し( mkdir project_name )、作成したディレクトリに移動し( cd $_ ) 1docker-compose.yml, Dockerfile, .env ファイルを作成する( touch {docker-compose.yml,Dockerfile,.env} )。

この時点で Fig.2 の構成になる。

Fig.2
project_name
├── Dockerfile
├── docker-compose.yml
└── .env

各ファイルは以下の内容で書く。

docker-compose.yml

docker-compose.yml
version: '3'
services:
  web:
    build: .
    container_name: ${PROJECT}-web
    ports:
      - 80:80
      - 3000:3000
    volumes:
      - ./web:/var/www/html/${PROJECT}
    depends_on:
      - db
  db:
    image: mysql:8
    container_name: ${PROJECT}-mysql
    restart: always
    environment:
      MYSQL_DATABASE: ${PROJECT}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      TZ: ${TZ}
    ports:
      - 3306:3306
    volumes:
      - ./db:/var/lib/mysql

Dockerfile

Dockerfile
FROM php:7.4-fpm

COPY --from=node:12
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN apt-get update \
  && apt-get install -y wget git zip unzip vim libpq-dev \
  && : 'Install PHP Extensions' \
  && docker-php-ext-install pdo_mysql pdo_pgsql \

WORKDIR /var/www/html/project_name

マルチステージビルド2Node.jsComposerDocker公式イメージ を利用する。

.env

.env
PROJECT=project_name
DB_USER=user_name
DB_PASSWORD=password
TZ=Asia/Tokyo

コンテナを起動

docker-compose up -d

この時点で Fig.1 の構成になる。

コンテナに入る

docker-compose exec web bash

コンテナの中ではcomposernpmが使える。

# composer --version
Composer version 1.10.6 2020-05-06 10:28:10

# node --version
v12.16.3

# npm --version
6.14.5

Laravelをインストール

# composer create-project --prefer-dist laravel/laravel .

// バージョンを指定する場合 (e.g. Laravel6 を指定する場合)
# composer create-project --prefer-dist laravel/laravel . "6.*"

Laravelの設定

web/config/app.php

app.php
<?php

return [

    // 他の設定

    'timezone' => 'Asia/Tokyo',
    'locale' => 'ja',

    // 他の設定
];

設定値を変更。

web/.env

.env
// 他の設定
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=project_name
DB_USERNAME=user_name
DB_PASSWORD=password
// 他の設定

データベースに接続するための情報を入れる。
MySQLに接続する場合はDB_CONNECTION=mysql
DB_HOSTdocker-compose.ymlservicesで設定したdbとする。
他の設定も .env で設定したものと同じ。

パッケージをインストール

# npm i
# npm i vue vue-router pug pug-plain-loader --save-dev  
# npm i -g typescript
# tsc --init

typescriptをグローバルにインストールするとtscコマンドが使えるようになり、

# tsc --version
Version 3.8.3

tsc --initとするとtsconfig.jsonが生成される。3
tsc --initgit init と混同して tsc init としないように注意。4

ファイルを編集

web/webpack.mix.js

webpack.mix.js
const mix = require('laravel-mix')

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix
.webpackConfig({
  module: {
    rules: [{
      test: /\.pug$/,
      oneOf: [
        {
          resourceQuery: /^\?vue/,
          use: ['pug-plain-loader']
        },
        {
          use: ['raw-loader', 'pug-plain-loader']
        }
      ]
    }]
  }
})
.browserSync({
  proxy: '0.0.0.0:80',
  open: false,
  files: [
    'resources/**/*',
    'public/**/*'
  ]
})
.ts('resources/ts/app.ts', 'public/js/app.js')
.sass('resources/sass/app.scss', 'public/css')
.version()

pugを扱うための設定も記載する。5
BrowserSyncの設定を記入。6 7
mix.js() と書かれていた部分を mix.ts() 変えるだけ。8 あとは扱うファイルの変更に従って引数のファイルもjsからtsに変更する。

web/resources/ts/app.ts

app.ts
import Vue from 'vue'
import router from './router'
import App from './App.vue'
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App />'
})

web/resources/js/app.jsweb/resources/ts/app.ts に書き換える。
ここで App.vuerouter.ts を読み込む。

web/resources/ts/App.vue

App.vue
<template lang="pug">
div
  main
    RouterView
</template>

ルートコンポーネント。
ここで Vue Router が提供する RouterView9 を使う。

web/resources/ts/router.ts

router.ts
import Vue from 'vue'
import VueRouter from 'vue-router'
import Foo from './pages/Foo.vue'
import Bar from './pages/Bar.vue'

Vue.use(VueRouter)

const routes: any = [{
    path: '/foo',
    component: Foo
  },
  {
    path: '/bar',
    component: Bar
  }
}]

const router = new VueRouter({
  mode: 'history',
  routes
})

export default router

ルーティングの定義の設定。
History モード 10にすることでURLがハッシュなしで設定できる。

web/resources/ts/pages/Foo.vue

Foo.vue
<template lang="pug">
  .foo
    h1 Foo
</template>

web/resources/ts/pages/Bar.vue

Bar.vue
<template lang="pug">
  .bar
    h1 Bar
</template>

web/routes/web.php

web.php
<?php

Route::get('/{any?}', function () {
    return view('index');
})->where('any', '.+');

web/resources/views/index.blade.php

index.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>project_name</title>
    <script src="{{ mix('js/app.js') }}" defer></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

web/vue-shim.d.ts

vue-shim.d.ts
declare module "*.vue" {
    import Vue from "vue";
    export default Vue;
}

Typescript: IDE reports TS2307: Cannot find module error for Vue components imports とエラーが出たので作成する。11

サーバーを起動

# php artisan serve --host 0.0.0.0 --port 80

ビルド

# npm run watch-poll

http://localhost:3000 にアクセスしてブラウザで確認する。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuexでストア内容の永続化を部分的に行う

方法

ストアの永続化にはvuex-persistedstateを使い、ストアのインスタンス生成時のplugins指定でcreatePersistedState()にオプションとしてpathsを付ける。
https://github.com/robinvdvleuten/vuex-persistedstate

import createPersistedState from "vuex-persistedstate";

const store = new Vuex.Store({
  // ...
  plugins: [createPersistedState({
    paths: ['※パス']
    ...
  })],
});

※ パスはステート内のパスになります。
例:namespaced: trueでモジュールとしてUser.jsを作成した場合は'User'

オプションの詳細はAPI説明で確認ください。

参考にしたページ

https://stackoverflow.com/questions/55319006/making-only-one-module-persistent-with-vuex-persistedstate

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-cliで環境ごとに異なる静的ファイルをコピーする

 最近地味に困ってあれこれ調べたことの記録です。

public配下においた静的ファイルをビルド対象によって切り替えたい

 vue-cliのプロジェクトで静的ファイルをpublic配下に置いていたのですが、productionで切り替えたい事案が発生しました。具体的にはvue-routerでhistoryモードを使っていたがために.htaccessを置いていたのですが、開発環境ではサブディレクトリでデプロイ先が切られていたために、ルートディレクトリを指定したかったのです。

public/.htaccess
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /test/ [L] <-- 本番では / にしたい…
</IfModule>

 最初ここに<%= PUBLIC_PATH %>とか書いてみたのですが、そのまま文字列として出力されてしまいました。どのみちAPIのスタブJSONとかも置いていたので、これらが出力されるのも困るのでこの方法は早々に諦めました。手動でビルドしたファイルを毎回編集したり削除したりするのは死んでも嫌だったので、vue-cli先生にはそのくらいの気は利かせてくれるようにしたいと思います。

 「そもそもここに.htaccess置くな」と言われれば最もだとは思います。スタブもjsonじゃなくてexpressとかでやった方がスマートではあるのですが、まぁそれはそれとして…。

ビルドモードごとに異なる静的ファイルを参照させる

 public配下に静的ファイルを置くのではなくて、ビルド時に環境ごとに用意したディレクトリから静的ファイルを配置するように変えてみます。

ディレクトリを用意する

 特に指定された箇所があるわけではないので、自分で src/static/ディレクトリを作成し、その中で環境ごとにそれぞれサブディレクトリを掘ります。今回は本番環境とそれ以外の環境で分けたかったので、src配下に以下のようにディレクトリを切りました(不要なところは省略)。

% tree -d  
.
├── static
│   ├── develop
│   └── production

 各ディレクトリに、それぞれビルド時にコピーしたいファイルを配置していきます。

プロダクションと開発環境のビルドを分ける

 npm run buildだとproductionでビルドされるので、develop用のビルドコマンドを追記してそれぞれにビルド出来るようにします。

package.json(抜粋)
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "build:develop": "vue-cli-service build --mode develop", <--追加
    "lint": "vue-cli-service lint"
  },

 .envもそれぞれ用意しておきます。

.env.production(抜粋)
NODE_ENV=production
.env.develop(抜粋)
NODE_ENV=development

 これでnpm run build:developを打ったときには、developmentでビルドしてくれるようになっているかと思います。

ビルドモードに応じたディレクトリ内容をdistへコピーする

 vue-cliはデフォルトでpublicからコピーするようになっているようですが、先程追加したディレクトリからビルドモードに応じたファイルをコピーするように追記します。前述のようにproductionかそれ以外かで分けられれば今回はOKだったので、以下のように追記しました。

vue.config.js(抜粋)
const isProduction = process.env.NODE_ENV === "production"; // <-- productionビルドかどうかを判定
const path = require("path"); // <-- ディレクトリ操作をしてなければ追記

module.exports = {
  assetsDir: "assets",
  lintOnSave: false,
  chainWebpack: config => {
    // 静的ファイルのコピー処理
    config.plugin("copy").tap(args => {
      args[0].push({
        from: path.resolve(
          __dirname,
          isProduction ? "src/static/production" : "src/static/develop" <--先程作成したディレクトリをそれぞれ指定
        ),
        to: path.resolve(__dirname, "dist"),
        toType: "dir",
        ignore: [".DS_Store"]
      });
      return args;
    });
  },
};

コピー処理は以下を参照して記述しました。
https://stackoverflow.com/questions/60304092/how-to-copy-assets-with-vue-cli-service-build-command

ビルドしてみる

 npm run build時にはsrc/static/production内が出力され、npm run build:developではsrc/static/developが出力されるようになっているのが確認出来ればOKです。今回はproductionか否かしか判定していませんが、環境がもっと増えた場合は判定を追加すればコピー元ディレクトリもそれぞれに用意出来るかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む