- 投稿日:2020-04-26T23:24:25+09:00
【Vuex, Nuxt.js】子コンポーネントのアクションを実行しても状態が反映されない
最近Nuxt.jsを使ってサービスの開発をしてみてます。
フロントエンドのフレームワークの経験がなく慣れておらず、
状態管理でハマったところがあったので備忘録として投稿。
個人的にはてなブログにかいてましたが
技術系はQiitaに書こうと決めたので内容は下記の書き直しとなります。
https://moritomo7315.hatenablog.com/entry/vuex/state_manage/1ちなみにログイン認証等は今回の記事の本質とは離れてるので割愛してます。
何にハマったか
userのログイン・ログアウトをVuexで
// login loginStatus = true // logout loginStatus = falseのように状態管理しようとしたわけですが、下記のような現象が起きてしまいました。
- ログアウトする場合:loginStatusがtrue->falseに変化
- ログインする場合:loginStatusがfalseのまま
ソースコード
store
index.jsimport Vuex from 'vuex' import user from './modules/user' export default () => new Vuex.Store({ modules: { user } })user.jsconst state = { user: null, loginStatus: false } const getters = { user: (state) => state.user, isLogin: (state) => state.loginStatus } const mutations = { setUser(state, { user }) { state.user = user }, login(state) { state.loginStatus = true }, logout(state) { state.loginStatus = false state.user = null } } const actions = { fetchUser({ commit }, user) { commit('setUser', {user}) }, login({ commit }) { commit('login') }, logout({ commit }) { commit('logout') } } export default { namespaced: true, state, getters, actions, mutations }header コンポーネント
header.vue<template> <div> <v-app-bar color="primary" > <v-toolbar-title> <nuxt-link to='/'> <img src="~/static/weblogo.png"> </nuxt-link> </v-toolbar-title> <v-spacer></v-spacer> <v-menu offset-y> <template v-slot:activator="{ on }"> <v-btn icon color="transparent" v-on="on" > <v-app-bar-nav-icon></v-app-bar-nav-icon> </v-btn> </template> <v-list v-if="loginStatus"> <v-list-item nuxt to='#' > <v-list-item-title>マイページ</v-list-item-title> </v-list-item> <v-list-item @click="logout" nuxt to='/' inactive > <v-list-item-title>ログアウト</v-list-item-title> </v-list-item> </v-list> <v-list v-else> <v-list-item nuxt to='/login' > <v-list-item-title>ログイン</v-list-item-title> </v-list-item> <v-list-item nuxt to='/signup' > <v-list-item-title>会員登録</v-list-item-title> </v-list-item> </v-list> </v-menu> </v-app-bar> </div> </template> <script> import { mapState, mapGetters, mapActions } from 'vuex'; export default { name: "Header", computed: { ...mapState({ user: state => state.user.user, loginStatus: state => state.user.loginStatus }) }, methods: { ...mapActions('user',[ "logout" ]) } } </script>login page
login.vue<!-- EmailSigninコンポーネントを呼び出してるだけ。今後Twitterログイン等も入れたいので、コンポーネントに分けている --> <template> <EmailSignin /> </template> <script> import EmailSignin from '~/components/emailSignin' export default { components: { EmailSignin } } </script>emailSignin.vue<template> <div class="userpage"> <h1>ログイン</h1> <div class="userform"> <v-form> <v-text-field v-model="email" label="E-mail" outlined required ></v-text-field> <v-text-field v-model="password" label="password" outlined type="password" required ></v-text-field> <v-btn color="secondary" class="mr-4" @click="login" x-large nuxt to="/" > ログイン </v-btn> <nuxt-link to="#" > パスワードを忘れた方はこちら... </nuxt-link> </v-form> </div> </div> </template> <script> import { mapState, mapGetters, mapActions } from "vuex"; export default { name: "EmailSignin", data() { return { email: '', password: '', } }, computed: { ...mapState({ user: state => state.user.user, loginStatus: state => state.user.loginStatus }) }, methods: { ...mapActions('user',[ "login" ]), passworLogin () { this.login() } } } </script>何が問題だったか
結論から言うと、
@click
の使い方が原因でした。ソースコードでいうと、storeの記述に関しては問題なしです。
なぜログアウトはできて、ログインができなかったか
なぜログアウトはできるのに、
ログインはできないといった現象が起きてしまっていたかと言うと、これは親子コンポーネントとイベントの関係があるみたいです。
僕のソースコードで実装されてるログアウトとログインの違いは
- headerコンポーネントがコンポーネント内のアクション
logout
を呼びイベント発生- loginコンポーネントがemailSigninコンポーネント内の
login
を呼びイベント発生となっております。
どちらも
@click="methodName
としていますが、ログインだけ動かないのはコンポーネントの親子関係が原因でした。
親コンポーネントに子コンポーネントのアクションイベントを実行させるには、
@click.native="methodName
と指定する必要があるみたいです。
まとめ
コンポーネントの親子関係を意識して、
そのコンポーネントが親か子に応じて
@click
か@click.native
を指定するで解決しました。まだvuexの概念を理解しきっていないので、
説明に使用してる言葉があやしいところもあるかもしれませんが、
無事解決できました。
- 投稿日:2020-04-26T22:44:39+09:00
vue-cliでsocket.ioを使う方法(socket.io-client)
はじめに
基本的なことだからなのか書いている人があまりいなくて入れるのに少し困ったので書きました。
1. socket.io-clientをインストール
$ npm install socket.io-client2. socket.ioを使いたいコンポーネントに以下を記述
~ </template> <script> import io from 'socket.io-client'; //追加 export default { name: 'name', data: function(){ return { socket : io('localhost:4000') //生成 } }, }おわりに
思いのほか簡単でした!
参考
- 投稿日:2020-04-26T20:32:14+09:00
ミニマムなVue.jsコンポーネントプログラミングその2(ルータ実装)
前回、"Hello, vue.js"を静的にレンダリングするだけのミニマムなシングルコンポーネントを持ったプロジェクトを作成しましたので、それをベースに拡張していきたいと思います。今回の趣旨はミニマムなルータ制御です。SPA的なクライアントアプリを組むためにはルータ機能が必要です。
やるべきことは、以下です。
- App.vueは(ほぼ)抽象的なアプリケーションコンポーネント(画面の入れ物)とする。
- 唯一の画面として、Home画面(コンポーネント)を追加する。
- vue-routerでHome画面を表示させる。
Home画面作成
h1要素を持つだけのシンプルなコンポーネントです。vue-cliが自動生成してくれるようなフォルダ構成でちまちまちだちま作っていきます。
src/vies/Home.vue
<template> <div> <h1>これはほーむがめんです</h1> </div> </template> <script> export default { name: 'Home' } </script>ルータ追加
画面遷移をコントロールするルータを追加します。
(1)ルータライブラリインストール
npm install vue-router(2)ルータ実装(src/router/index.js)
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home } ] const router = new VueRouter({ routes }) export default routerルート('/')とHome画面(コンポーネント)を連結させただけの
ミニマムなルータです。(3) Vueインスタンスにルータ追加(src/main.js)
import Vue from 'vue' import App from './App.vue' import router from './router' <== ココ Vue.config.productionTip = false new Vue({ router, <== ココ render: h => h(App), }).$mount('#app')こうやって、Vueインスタンスにいろんな機能モジュール(コンポーネント)を突っ込んでおくと、Vueインスタンスに対してフレームワークがいろいろよしなにさばいてくれるんですねぇ。
こうしてながめると、router/index.jsで作ったルータも一種のコンポーネントとしてとらえたほうがいいかな。
(4) Appコンポーネントにルータ(によるView)を埋め込む(src/App.vue)
<template> <div> <router-view/> <== ココ </div> </template> <script> export default { name: 'App' } </script>npm run serveして、localhost:8081すると、
補足
今回のやったことはつまり、
- 抽象的なアプリケーションコンポーネントを用意する。 (複数の画面を持つアプリケーションとして、Appコンポーネントを定義する)
- HOME画面を用意する。
- '/'をHOME画面と接続したルータ用意。
- アプリケーションにルータをジョイント。
って感じです。
次はサブコンポーネントを扱っていきたいと思います。
- 投稿日:2020-04-26T20:32:14+09:00
ミニマムなコンポーネントプログラミングその2
前回、Hello, Vue.jsを静的にレンダリングするだけのミニマムなコンポーネントを持ったプロジェクトを作成しましたので、それをベースに拡張していきたいと思います。今回の趣旨はミニマムなルータ制御です。
- Home画面を用意する。
- vue-routerでHome画面を表示させる。
以上2点です。
Home画面作成
とりあえず前回のAppコンポーネントのように、h1要素を持つだけのシンプルなコンポーネントです。vue-cliがやってるようなフォルダ構成でちまちまちだちま作っていきます。
src/vies/Home.vue
<template> <div> <h1>これはほーむがめんです</h1> </div> </template> <script> export default { name: 'Home' } </script>ルータ追加
(1)ルータライブラリインストール
npm install vue-router(2)ルータ実装(src/router/index.js)
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home } ] const router = new VueRouter({ routes }) export default routerルート('/')とHome画面(コンポーネント)を連結させただけの
ミニマムなルータです。(3) Vueインスタンスにルータ追加(src/main.js)
import Vue from 'vue' import App from './App.vue' import router from './router' <== ココ Vue.config.productionTip = false new Vue({ router, <== ココ render: h => h(App), }).$mount('#app')こうやって、Vueインスタンスにいろんな機能モジュール(コンポーネント)を突っ込んでおくと、Vueインスタンスに対してフレームワークがいろいろよしなにさばいてくれるんですねぇ。
こうしてながめると、router/index.jsで作ったルータも一種のコンポーネントとしてとらえたほうがいいかな。
(4) Appコンポーネントにルータ(によるView)を埋め込む(src/App.vue)
<template> <div> <router-view/> <== ココ </div> </template> <script> export default { name: 'App' } </script>npm run serveして、localhost:8081すると、
補足
今回のやったことはつまり、
- 抽象的なアプリケーションコンポーネントを用意する。 (複数の画面を持つアプリケーションとして、Appコンポーネントを定義する)
- HOME画面を用意する。
- '/'をHOME画面と接続したルータ用意。
- アプリケーションにルータをジョイント。
って感じです。
次はサブコンポーネントを扱っていきたいと思います。
- 投稿日:2020-04-26T19:20:19+09:00
ミニマムなコンポーネントプログラミング
Vue.jsを使うと、コンポーネントプログラムというものの楽しさを体感できると思います。(正確に言うと、vue-cliを使ってコンポーネントプログラムを構成すると、かな?)
Web Componentsという概念も最近知ったところですが、MVCという概念に沿って、
- データモデル定義(M)
- UI構造(V)
- データモデルとUI構造の制御インタフェースであるコントローラ(C)
みたいな感じにフォルダをバラバラに分けて管理するのはもう古い、ということなのかも。(適材適所はあるでしょうが)
環境
vue-cli 4.3.1
プロジェクト作成
vue create vue_sample ? Please pick a preset: VueSample (typescript, router) default (babel, eslint) ❯ Manually select featuresまずはミニマム構成を用意したいので、マニュアルを指定。
? Check the features needed for your project: ❯◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◯ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ◯ Unit Testing ◯ E2E TestingJavaScriptの新しい記法は使いたいので、Babelを選択し、後はオフ。後の設定はデフォルトとして、プロジェクトを作成。
コンポーネント構造をミニマム化
公開するindex.htmlは、
<div id="app"></div>となっており、すでにミニマムです。
エントリポイントであるmain.jsは、
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')となっており、vueの基幹ライブラリのみimport、
唯一Appコンポーネントのみマウントしており、
こちらもすでににミニマムです。App.vueを以下のように書き直します。
<template> <div> <h1>Hello, Vue.js</h1> </div> </template> <script> export default { name: 'App' } </script>UIの構造をテンプレート定義し、データモデルとコントローラを内包可能なVueインスタンスを定義/生成します。
- UIの構造は見出しのみ(h1)
- コンポーネントも名称のみ定義(name: 'App')
以上により、h1要素のみ備えたAppコンポーネント(Vueインスタンス)をレンダリングするだけのミニマムなプロジェクトが完成します。
npm run serveして、localhost:8081とかにアクセスすると、
超シンプルな画面がレンダリングされます。vue.jsを使った'hello world'と、
vue-cliによるコンポーネントプログラミングとしての'hello world'は別物だと思いますし、今回は後者を体験してみました。次回はこのミニマムなコンポーネントの上に、ミニマムな画面遷移(vue-router)を乗せていこうと思います。
- 投稿日:2020-04-26T19:20:19+09:00
ミニマムなVue.jsコンポーネントプログラミングその1(シングルコンポーネント)
Vue.jsを使うと、コンポーネントプログラムというものの楽しさを体感できると思います。(正確に言うと、vue-cliを使ってコンポーネントプログラムを構成すると、かな?)
Web Componentsという概念も最近知ったところですが、MVCという概念に沿って、
- データモデル定義(M)
- UI構造(V)
- データモデルとUI構造の制御インタフェースであるコントローラ(C)
みたいな感じにフォルダをバラバラに分けて管理するのはもう古い、ということなのかも。(適材適所はあるでしょうが)
最終的にやりたいのは、SPAライクなクライアントアプリです。
従って、
ルータを内包し、
サーバサイドレンダリングはせず、
データはResutFul APIサーバを立ててアクセスし、
アプリ自体は部品(コンポーネント)を組み合わせてつくる、
そんなテーマで進めたいと思います。そして応用編まで行けたら、Typescriptと組み合わせたモダンな記法とか、やってみたいところです。まずはVue.jsネイティブの理解が必要ですけれども。
今回はシングルコンポーネントだけで構成されたVue.jsアプリを組んでみます。
環境
vue-cli 4.3.1
プロジェクト作成
vue create vue_sample ? Please pick a preset: VueSample (typescript, router) default (babel, eslint) ❯ Manually select featuresまずはミニマム構成を用意したいので、マニュアルを指定。
? Check the features needed for your project: ❯◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◯ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ◯ Unit Testing ◯ E2E TestingJavaScriptの新しい記法は使いたいので、Babelを選択し、後はオフ。後の設定はデフォルトとして、プロジェクトを作成。
コンポーネント構造をミニマム化
公開するindex.htmlは、
<div id="app"></div>となっており、すでにミニマムです。
エントリポイントであるmain.jsは、
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')となっており、vueの基幹ライブラリのみimport、
唯一Appコンポーネントのみマウントしており、
こちらもすでににミニマムです。App.vueを以下のように書き直します。
<template> <div> <h1>Hello, Vue.js</h1> </div> </template> <script> export default { name: 'App' } </script>UIの構造をテンプレート定義し、データモデルとコントローラを内包可能なVueインスタンスを定義/生成します。
- UIの構造は見出しのみ(h1)
- コンポーネントも名称のみ定義(name: 'App')
以上により、h1要素のみ備えたAppコンポーネント(Vueインスタンス)をレンダリングするだけのミニマムなプロジェクトが完成します。
npm run serveして、localhost:8081とかにアクセスすると、
超シンプルな画面がレンダリングされました。補足
Vue.jsの長所の一つに、「プログレッシブなフレームワーク」という文言がありますが、つまりこれは、
- 便利なデータバインディングのみ使う
- 再利用性の高いコンポーネント設計を導入する
等々、シーンに応じた導入ができるフレキシブルさを指していると思います。SPAにしたければ内部ルータを埋め込んだり。
今回のようなシンプルなケースでは、データバインディングのみ使った
非コンポーネントVue.jsプログラミングで全然間に合いますが、ヘッダ、フッタ等、画面が増えていけばどうしても部品を組み合わせたくなってくるのでコンポーネント設計を取り組んでみます。次回
次回はこのミニマムなコンポーネントの上に、ミニマムな画面遷移(vue-router)を乗せていこうと思います。
- 投稿日:2020-04-26T19:20:19+09:00
ミニマムなコンポーネントプログラミング1
Vue.jsを使うと、コンポーネントプログラムというものの楽しさを体感できると思います。(正確に言うと、vue-cliを使ってコンポーネントプログラムを構成すると、かな?)
Web Componentsという概念も最近知ったところですが、MVCという概念に沿って、
- データモデル定義(M)
- UI構造(V)
- データモデルとUI構造の制御インタフェースであるコントローラ(C)
みたいな感じにフォルダをバラバラに分けて管理するのはもう古い、ということなのかも。(適材適所はあるでしょうが)
環境
vue-cli 4.3.1
プロジェクト作成
vue create vue_sample ? Please pick a preset: VueSample (typescript, router) default (babel, eslint) ❯ Manually select featuresまずはミニマム構成を用意したいので、マニュアルを指定。
? Check the features needed for your project: ❯◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◯ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ◯ Unit Testing ◯ E2E TestingJavaScriptの新しい記法は使いたいので、Babelを選択し、後はオフ。後の設定はデフォルトとして、プロジェクトを作成。
コンポーネント構造をミニマム化
公開するindex.htmlは、
<div id="app"></div>となっており、すでにミニマムです。
エントリポイントであるmain.jsは、
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')となっており、vueの基幹ライブラリのみimport、
唯一Appコンポーネントのみマウントしており、
こちらもすでににミニマムです。App.vueを以下のように書き直します。
<template> <div> <h1>Hello, Vue.js</h1> </div> </template> <script> export default { name: 'App' } </script>UIの構造をテンプレート定義し、データモデルとコントローラを内包可能なVueインスタンスを定義/生成します。
- UIの構造は見出しのみ(h1)
- コンポーネントも名称のみ定義(name: 'App')
以上により、h1要素のみ備えたAppコンポーネント(Vueインスタンス)をレンダリングするだけのミニマムなプロジェクトが完成します。
npm run serveして、localhost:8081とかにアクセスすると、
超シンプルな画面がレンダリングされました。補足
Vue.jsの長所の一つに、「プログレッシブなフレームワーク」という文言がありますが、つまりこれは、
- 便利なデータバインディングのみ使う
- 再利用性の高いコンポーネント設計を導入する
等々、シーンに応じた導入ができるフレキシブルさを指していると思います。SPAにしたければ内部ルータを埋め込んだり。
今回のようなシンプルなケースでは、データバインディングのみ使った
非コンポーネントVue.jsプログラミングで全然間に合いますが、ヘッダ、フッタ等、画面が増えていけばどうしても部品を組み合わせたくなってくるのでコンポーネント設計を取り組んでみます。次回
次回はこのミニマムなコンポーネントの上に、ミニマムな画面遷移(vue-router)を乗せていこうと思います。
- 投稿日:2020-04-26T19:20:19+09:00
ミニマムなコンポーネントプログラミングその1
Vue.jsを使うと、コンポーネントプログラムというものの楽しさを体感できると思います。(正確に言うと、vue-cliを使ってコンポーネントプログラムを構成すると、かな?)
Web Componentsという概念も最近知ったところですが、MVCという概念に沿って、
- データモデル定義(M)
- UI構造(V)
- データモデルとUI構造の制御インタフェースであるコントローラ(C)
みたいな感じにフォルダをバラバラに分けて管理するのはもう古い、ということなのかも。(適材適所はあるでしょうが)
環境
vue-cli 4.3.1
プロジェクト作成
vue create vue_sample ? Please pick a preset: VueSample (typescript, router) default (babel, eslint) ❯ Manually select featuresまずはミニマム構成を用意したいので、マニュアルを指定。
? Check the features needed for your project: ❯◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◯ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ◯ Unit Testing ◯ E2E TestingJavaScriptの新しい記法は使いたいので、Babelを選択し、後はオフ。後の設定はデフォルトとして、プロジェクトを作成。
コンポーネント構造をミニマム化
公開するindex.htmlは、
<div id="app"></div>となっており、すでにミニマムです。
エントリポイントであるmain.jsは、
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')となっており、vueの基幹ライブラリのみimport、
唯一Appコンポーネントのみマウントしており、
こちらもすでににミニマムです。App.vueを以下のように書き直します。
<template> <div> <h1>Hello, Vue.js</h1> </div> </template> <script> export default { name: 'App' } </script>UIの構造をテンプレート定義し、データモデルとコントローラを内包可能なVueインスタンスを定義/生成します。
- UIの構造は見出しのみ(h1)
- コンポーネントも名称のみ定義(name: 'App')
以上により、h1要素のみ備えたAppコンポーネント(Vueインスタンス)をレンダリングするだけのミニマムなプロジェクトが完成します。
npm run serveして、localhost:8081とかにアクセスすると、
超シンプルな画面がレンダリングされました。補足
Vue.jsの長所の一つに、「プログレッシブなフレームワーク」という文言がありますが、つまりこれは、
- 便利なデータバインディングのみ使う
- 再利用性の高いコンポーネント設計を導入する
等々、シーンに応じた導入ができるフレキシブルさを指していると思います。SPAにしたければ内部ルータを埋め込んだり。
今回のようなシンプルなケースでは、データバインディングのみ使った
非コンポーネントVue.jsプログラミングで全然間に合いますが、ヘッダ、フッタ等、画面が増えていけばどうしても部品を組み合わせたくなってくるのでコンポーネント設計を取り組んでみます。次回
次回はこのミニマムなコンポーネントの上に、ミニマムな画面遷移(vue-router)を乗せていこうと思います。
- 投稿日:2020-04-26T18:16:47+09:00
Unknown custom element: <TagName> - did you register the component correctly?
上記のエラー、いくつか原因があるそうです。以下の記事で気づくことができました。助かりました。
自分の場合、循環参照で問題が起こっていました。解決策は、以下のように非同期 import (asynchronous import) させることでです。
// 誤 import Component from './Component.vue' export default { components: { Component }, }// 正 export default { components: { Component: () => import('./Component.vue') }, }他にも require を使う方法も記載されていました。以下、公式ドキュメントになります。
自分の場合はさらに
Content
と言うコンポーネント名を使おうとしていました。これがcontent
と言う非推奨になっているタグとバッティングしていたらしく、さきに非推奨のcontent
が適用されてさらによくわからない状態になっていました... orz
- 投稿日:2020-04-26T14:14:05+09:00
Vue.jsとWordPressを共存させる(OGPにも対応)
地元のグルメ情報を紹介するWebアプリをVue.jsで作り、WordPressで運営しているサイトのサブディレクトリー上に公開しました。また。WebアプリのURLがSNSでシェアされた時に写真が展開されるよう、OGPに対応させています。
この記事ではWordPressを使っているサイトにVue.jsのアプリを同居させ、プリレンダリングを使ってOGPに対応させる方法について紹介します。WordPressは https://example.com で運営され、そこにVueアプリを https://example.com/app-name/ で公開するものとします。
前提とする環境
Vue CLIを使ってアプリを開発しています。
$ npm install -g @vue/cli $ vue --version @vue/cli 4.2.3ルート以外でVueアプリを公開する
開発したアプリはルートディレクトリ https://example.com/ で公開されるように作られています。これを、サブディレクトリ https://example.com/app-name/ で公開されるように変更します。
WordPressをサーバーのディレクトリー
/
にインストールしている場合、その下に新しく/app-name
ディレクトリーを作り、Vueアプリの成果物をアップロードします。最初はWordPressの動作をよく理解していなかったので、
- /app-name/ でアクセスできるページが必要?
- それなら「固定ページ」で app-name を作ればいいのでは?
と誤解していたのですが、そんなことは不要でした。
vue.config.js を変更する
vue.config.js に publicPath [公式]、outputDir [公式] を追記します。
vue.config.jsmodule.exports = { publicPath: '/app-name/', outputDir: 'dist/app-name/', ... }まず、開発用サーバーで正しく動作するかを確認します。
$ npm run serve ... DONE Compiled successfully in XXXXms App running at: - Local: http://localhost:8080/app-name/ - Network: http://XXX.XXX.XXX.XXX:8080/app-name/ Note that the development build is not optimized. To create a production build, run npm run build.Webブラウザーで http://localhost:8080/app-name/ にアクセスして、正しく動作することを確認します。
public ディレクトリーに.htaccessを作成してビルド
WordPressはWebブラウザーが
/app-name
にアクセスした時に/app-name/index.php
を読み込むように設定されています。その設定はサーバーの/.htaccess
に書いてあります。
Vue.jsでは/app-name
にアクセスした時に(index.phpではなく)/app-name/index.html
を読み込むことを期待します。そこで、/app-name/.htaccess
を準備して/app-name
にアクセスしたときの動作を変更します。ここで作成する.htaccessファイルは開発マシンのpublicディレクトリーに作ります。public/.htaccess<IfModule mod_rewrite.c> RewriteEngine On RewriteBase /app-name/ RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.html [L] </IfModule>参考:WordPressのサブディレクトリでVueアプリを動かす
次にビルドを実行して、dist/app-name/ に成果物が出力されることを確認します。なお、vue uiでbuildタスクを動かす場合、vue.config.js の outputDir が無視されます。UIからoutputを設定してください。
参考:outputDir config not working #2639
$npm run build ... DONE Build complete. The dist/gourmet directory is ready to be deployed. INFO Check out deployment instructions at https://cli.vuejs.org/guide/deployment.htmlWordPressの.htaccessを書き換える
生成された dist/app-name をサーバーの
/app-name
としてアップロードします。ここで https://example.com/app-name にアクセスしても、まだVueアプリは動作しません。サーバー側の/.htaccess
を修正して、/app-name
へのアクセスだけは/app-name/.htaccess
に書いたルールで動作させる必要があります。修正を間違えるとWordPressが正しく動作しなくなるため、.htaccessファイルのコピーを別名で作ってから作業してください。
/.htaccess# BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !(^/app-name/) # この行を追加 RewriteRule . /index.php [L] </IfModule> # END WordPress参考:WordPressのサブディレクトリでVueアプリを動かす
https://example.com/app-name にアクセスしてVueアプリが正しく動作することを確認してください。
VueアプリをOGPに対応させる
OGPではシェアしたときに表示したい写真のURLなどをHTMLの
タグにあるタグに記述します。そのページのURLがSNSでシェアされた時に、SNSのボットがそのURLにアクセスし、metaタグを解析することでSNSに表示する情報を決めています。VueアプリのトップページをOGPに対応させる
public/index.html
を編集して、OGPに対応させます。このファイルはビルドによりdist/app-name/index.html
として出力されるのでそのままサーバーにアップロードして公開します。public/index.html<!DOCTYPE html> <html lang="ja"> <head prefix="og:http://ogp.me/ns# fb:http://ogp.me/ns/fb# website:http://ogp.me/ns/website#"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="/favicon.ico"> <title>[アプリ名]</title> <meta name="description" content="[アプリの説明]" /> <meta property="og:type" content="website" /> <meta property="og:site_name" content="[サイト名]" /> <meta property="og:title" content="[アプリ名]" /> <meta property="og:url" content="https://example.com/app-name/" /> <meta property="og:image" content="https://example.com/app-name/img/app-name.jpg" /> <meta property="og:description" content="[アプリの説明]" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:site" content="@[アプリのTwitterアカウント名]" /> <meta name="twitter:player" content="@[アプリのTwitterアカウント名]" /> </head> <body> <noscript> <strong>We're sorry but vue_app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>公開後、OGPの情報が正しく設定されているかを確認するため、以下のツールにアプリのURLを入力して動作を確かめます。
Vue Routerで作ったページをOGPに対応させる
OGP対応で問題になるのが、Vue Routerを使ったアプリです。
OGPの情報を収集するFacebookやTwitterのボットはJavaScriptに対応していません。なので、Vue Routerで生成したURLにボットがアクセスしてもOGPの情報を収集することがでず、そのURLをSNSでシェアしても、写真などが展開されることはありません。
そこでビルドの際にヘッドレスブラウザーPuppeteerを使ってプリレンダリングを実施し、Vue RouterのURLごとにindex.htmlを成果物として作成します。出来上がったindex.htmlをそのままサーバーにアップロードして公開すれば、ボットがOGPの情報を収集できるようになります。
vue-meta を使ってURLごとにOGPを設定する
Nuxt.jsに含まれるvue-metaを使えば、URLごとに異なるOGP情報を設定できます。
vue-metaを使う時に気をつける必要があるのは、変更するタグには data-vmid 属性を付与することです。
ここでは、
description
,og:title
,og:url
,og:image
,og:description
の5つを変更するため、public/index.html
を次のように修正します。public/index.html... <meta data-vmid="description" name="description" content="[アプリの説明]" /> <meta data-vmid="og:title" property="og:title" content="[アプリ名]" /> <meta data-vmid="og:url" property="og:url" content="https://example.com/app-name/" /> <meta data-vmid="og:image" property="og:image" content="https://example.com/app-name/img/app-name.jpg" /> <meta data-vmid="og:description" property="og:description" content="[アプリの説明]" /> ...その上で、vue-meta をインストールして、URLに応じてタグを書き換えるために metaInfo() を記述します。
$ npm install --save vue-metaSpot.vue... <script> ... data: { return: function() { id: '...', titie: '...', desc: '...' } }, metaInfo() { return { title, meta: [ { vmid: 'description', name: 'description', content: desc }, { vmid: 'og:title', property: 'og:title', content: title }, { vmid: 'og:description', property: 'og:description', content: desc }, { vmid: 'og:url', property: 'og:url', content: `https://example.com/app-name/spots/${this.id}/` }, { vmid: 'og:image', property: 'og:image', content: `https://example.com/app-name/img/${this.id}.jpg` } ] } } } </script>prerender-spa-plugin を使ってビルドの際にプリレンダリングを実施する
prerender-spa-pluginを使ってビルド時に開発用サーバーを起動し、PupeteerでVueアプリにアクセスすることでHTMLを生成していきます。
ここではVueアプリのURLとして /app-name/spots/00001/ から /app-name/spots/00100/ が存在するとします。
$ npm install --save prerender-spa-plugin
staticDir
は app-name なし、indexPath
は app-name ありで index.html まで指定することに気をつけてください。なお、アクセスする先の /app-name/spots/:id は、アクセスしてから
<div id="spot" />
が表示されるまでに少し時間がかかるので、Puppeteer にそのタグが出るまで待たせるようにしています。皆さんのアプリには不要なので削除してご利用ください。vue.config.jsconst path = require('path') const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer module.exports = { publicPath: '/app-name/', outputDir: 'dist/app-name/', configureWebpack: () => { if (process.env.NODE_ENV === 'production') { const routes = [] for (let i = 1; i <= 100; i++) { let id = String(i).padStart(5, '0') routes.push(`/app-name/spots/${id}/`) } return { plugins: [ new PrerenderSPAPlugin({ staticDir: path.join(__dirname, 'dist'), indexPath: path.join(__dirname, 'dist/app-name/index.html'), routes, renderer: new Renderer({ renderAfterElementExists: '#spot' }), }) ] } } } }あとはビルドを実行して、
dist/app-name/spots
の下に 00001/index.html, ..., 00100/index.html がそれぞれ出力されて、タグに期待通りの情報が記述されていることを確認してください。参考:プリレンダリングを用いてVue.jsのSPAをビルドする導入から設定まで
問題がなければ
dist/app-name
をサーバーにアップロードし、Facebook デバッガーなどで https://example.com/app-name/spots/00001/ などが期待通りに動作するか確認してください。
- 投稿日:2020-04-26T14:07:12+09:00
Vue.jsとWordPressを共存させる(OGPにも対応)
地元のグルメ情報を紹介するWebアプリをVue.jsで作り、WordPressで運営しているサイトのサブディレクトリー上に公開しました。また。WebアプリのURLがSNSでシェアされた時に写真が展開されるよう、OGPに対応させています。
この記事ではWordPressを使っているサイトにVue.jsのアプリを同居させ、プリレンダリングを使ってOGPに対応させる方法について紹介します。WordPressは https://example.com で運営され、そこにVueアプリを https://example.com/app-name/ で公開するものとします。
前提とする環境
Vue CLIを使ってアプリを開発しています。
$ npm install -g @vue/cli $ vue --version @vue/cli 4.2.3ルート以外でVueアプリを公開する
開発したアプリはルートディレクトリ https://example.com/ で公開されるように作られています。これを、サブディレクトリ https://example.com/app-name/ で公開されるように変更します。
WordPressをサーバーのディレクトリー
/
にインストールした場合、/app-name
ディレクトリーを作ってその下にVueアプリの成果物を配置します。vue.config.js を変更する
vue.config.js に publicPath [公式]、outputDir [公式] を追記します。
vue.config.jsmodule.exports = { publicPath: '/app-name/', outputDir: 'dist/app-name/', ... }まず、開発用サーバーで正しく動作するかを確認します。
$ npm run serve ... DONE Compiled successfully in XXXXms App running at: - Local: http://localhost:8080/app-name/ - Network: http://XXX.XXX.XXX.XXX:8080/app-name/ Note that the development build is not optimized. To create a production build, run npm run build.Webブラウザーで http://localhost:8080/app-name/ にアクセスして、正しく動作することを確認します。
public ディレクトリーに.htaccessを作成してビルド
WordPressはWebブラウザーが
/app-name
にアクセスした時に/app-name/index.php
を読み込むように設定されています。その設定はサーバーの/.htaccess
に書いてあります。
Vue.jsでは/app-name
にアクセスした時に(index.phpではなく)/app-name/index.html
を読み込むことを期待します。そこで、/app-name/.htaccess
を準備して/app-name
にアクセスしたときの動作を変更します。ここで作成する.htaccessファイルは開発マシンのpublicディレクトリーに作ります。public/.htaccess<IfModule mod_rewrite.c> RewriteEngine On RewriteBase /app-name/ RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.html [L] </IfModule>参考:WordPressのサブディレクトリでVueアプリを動かす
次にビルドを実行して、dist/app-name/ に成果物が出力されることを確認します。なお、vue uiでbuildタスクを動かす場合、vue.config.js の outputDir が無視されます。UIからoutputを設定してください。
参考:outputDir config not working #2639
$npm run build ... DONE Build complete. The dist/gourmet directory is ready to be deployed. INFO Check out deployment instructions at https://cli.vuejs.org/guide/deployment.htmlWordPressの.htaccessを書き換える
生成された dist/app-name をサーバーの
/app-name
としてアップロードします。ここで https://example.com/app-name にアクセスしても、まだVueアプリは動作しません。サーバー側の/.htaccess
を修正して、/app-name
へのアクセスだけは/app-name/.htaccess
に書いたルールで動作させる必要があります。修正を間違えるとWordPressが正しく動作しなくなるため、.htaccessファイルのコピーを別名で作ってから作業してください。
/.htaccess# BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !(^/app-name/) # この行を追加 RewriteRule . /index.php [L] </IfModule> # END WordPress参考:WordPressのサブディレクトリでVueアプリを動かす
https://example.com/app-name にアクセスしてVueアプリが正しく動作することを確認してください。
VueアプリをOGPに対応させる
OGPではシェアしたときに表示したい写真のURLなどをHTMLの
タグにあるタグに記述します。そのページのURLがSNSでシェアされた時に、SNSのボットがそのURLにアクセスし、metaタグを解析することでSNSに表示する情報を決めています。VueアプリのトップページをOGPに対応させる
public/index.html
を編集して、OGPに対応させます。このファイルはビルドによりdist/app-name/index.html
として出力されるのでそのままサーバーにアップロードして公開します。public/index.html<!DOCTYPE html> <html lang="ja"> <head prefix="og:http://ogp.me/ns# fb:http://ogp.me/ns/fb# website:http://ogp.me/ns/website#"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="/favicon.ico"> <title>[アプリ名]</title> <meta name="description" content="[アプリの説明]" /> <meta property="og:type" content="website" /> <meta property="og:site_name" content="[サイト名]" /> <meta property="og:title" content="[アプリ名]" /> <meta property="og:url" content="https://example.com/app-name/" /> <meta property="og:image" content="https://example.com/app-name/img/app-name.jpg" /> <meta property="og:description" content="[アプリの説明]" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:site" content="@[アプリのTwitterアカウント名]" /> <meta name="twitter:player" content="@[アプリのTwitterアカウント名]" /> </head> <body> <noscript> <strong>We're sorry but vue_app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>公開後、OGPの情報が正しく設定されているかを確認するため、以下のツールにアプリのURLを入力して動作を確かめます。
Vue Routerで作ったページをOGPに対応させる
OGP対応で問題になるのが、Vue Routerを使ったアプリです。
OGPの情報を収集するFacebookやTwitterのボットはJavaScriptに対応していません。なので、Vue Routerで生成したURLにボットがアクセスしてもOGPの情報を収集することがでず、そのURLをSNSでシェアしても、写真などが展開されることはありません。
そこでビルドの際にヘッドレスブラウザーPuppeteerを使ってプリレンダリングを実施し、Vue RouterのURLごとにindex.htmlを成果物として作成します。出来上がったindex.htmlをそのままサーバーにアップロードして公開すれば、ボットがOGPの情報を収集できるようになります。
vue-meta を使ってURLごとにOGPを設定する
Nuxt.jsに含まれるvue-metaを使えば、URLごとに異なるOGP情報を設定できます。
vue-metaを使う時に気をつける必要があるのは、変更するタグには data-vmid 属性を付与することです。
ここでは、
description
,og:title
,og:url
,og:image
,og:description
の5つを変更するため、public/index.html
を次のように修正します。public/index.html... <meta data-vmid="description" name="description" content="[アプリの説明]" /> <meta data-vmid="og:title" property="og:title" content="[アプリ名]" /> <meta data-vmid="og:url" property="og:url" content="https://example.com/app-name/" /> <meta data-vmid="og:image" property="og:image" content="https://example.com/app-name/img/app-name.jpg" /> <meta data-vmid="og:description" property="og:description" content="[アプリの説明]" /> ...その上で、vue-meta をインストールして、URLに応じてタグを書き換えるために metaInfo() を記述します。
$ npm install --save vue-metaSpot.vue... <script> ... data: { return: function() { id: '...', titie: '...', desc: '...' } }, metaInfo() { return { title, meta: [ { vmid: 'description', name: 'description', content: desc }, { vmid: 'og:title', property: 'og:title', content: title }, { vmid: 'og:description', property: 'og:description', content: desc }, { vmid: 'og:url', property: 'og:url', content: `https://example.com/app-name/spots/${this.id}/` }, { vmid: 'og:image', property: 'og:image', content: `https://example.com/app-name/img/${this.id}.jpg` } ] } } } </script>prerender-spa-plugin を使ってビルドの際にプリレンダリングを実施する
prerender-spa-pluginを使ってビルド時に開発用サーバーを起動し、PupeteerでVueアプリにアクセスすることでHTMLを生成していきます。
ここではVueアプリのURLとして /app-name/spots/00001/ から /app-name/spots/00100/ が存在するとします。
$ npm install --save prerender-spa-plugin
staticDir
は app-name なし、indexPath
は app-name ありで index.html まで指定することに気をつけてください。なお、アクセスする先の /app-name/spots/:id は、アクセスしてから
<div id="spot" />
が表示されるまでに少し時間がかかるので、Puppeteer にそのタグが出るまで待たせるようにしています。皆さんのアプリには不要なので削除してご利用ください。vue.config.jsconst path = require('path') const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer module.exports = { publicPath: '/app-name/', outputDir: 'dist/app-name/', configureWebpack: () => { if (process.env.NODE_ENV === 'production') { const routes = [] for (let i = 1; i <= 100; i++) { let id = String(i).padStart(5, '0') routes.push(`/app-name/spots/${id}/`) } return { plugins: [ new PrerenderSPAPlugin({ staticDir: path.join(__dirname, 'dist'), indexPath: path.join(__dirname, 'dist/app-name/index.html'), routes, renderer: new Renderer({ renderAfterElementExists: '#spot' }), }) ] } } } }あとはビルドを実行して、
dist/app-name/spots
の下に 00001/index.html, ..., 00100/index.html がそれぞれ出力されて、タグに期待通りの情報が記述されていることを確認してください。参考:プリレンダリングを用いてVue.jsのSPAをビルドする導入から設定まで
問題がなければ
dist/app-name
をサーバーにアップロードし、Facebook デバッガーなどで https://example.com/app-name/spots/00001/ などが期待通りに動作するか確認してください。
- 投稿日:2020-04-26T10:41:32+09:00
vuexのmutationsの動きを理解する
vuex
画像は、Vue.js + Vuexでデータが循環する全体像を図解してみた - Qiitaを引用させていただきました。
この流れに沿って、本の詳細を表示するsetBookInfoメソッドの動きを見てみます。
①→コンポーネント側のsetBookInfoをコミットすることで、Vuex側のsetBookInfoが発火する。
②、③→発火された結果、mutationsからstateに値が渡される。④→computedプロパティで、コンポーネントからvuexストアを参照することができる。参照した結果、template側で値を使うことができる。
※そのコンポーネントだけでVuexの値を参照したい場合は、computedで参照するのが望ましいが、複数のコンポーネントからVuexの値を参照したい場合は、gettersを使用する。非同期処理や、actions,gettersについては、使い所などまだ理解できていないので、わかり次第また更新します。
理解間違っていましたら、ご指摘いただけると幸いです。getters:複数コンポーネントで、Vuexストアから値を参照するときに使う。
state:値の状態
mutations:stateを変更するためのメソッドのようなもの。同期処理でなければならない。
actions:mutationsをコミットするためのもの。非同期処理でなければならない。