20200823のvue.jsに関する記事は6件です。

【Rails6】kaminariでページネーションしてVue.jsで無限スクロール(vue-infinite-loading)を導入する。

事前準備

バックエンドはRails6を使いフロントはVueを使いますので、
Ruby on Rails, Vue.js で始めるモダン WEB アプリケーション入門
こちらの方の記事を参考にVueを導入してあるのが前提とさせていただきます。
Vueの導入・基本的な書き方などは省かせていただきます。

またkaminariに関しては、
【rails】kaminariを使ってページネーションを作る
こちらの方の記事を参考にお願いいたします。

開発環境

  • Ruby on rails 6.0.3.2
  • vue 2.6.11
  • yarn 1.22.4
  • webpack 4.43.0

こちらの環境で動作確認しました。

詰まったポイント

Vue側でvue-infinite-loadingというプラグインを用いて実装していきます。
そこで詰まったところがrailsのモデル側でページネーションしておらず全データを渡してしまっていたため、
無限スクロールを行おうとしたら全データが表示されてしまい意味がない状態になってしまいました。
なのでrailsの便利Gemである「kaminari」でページネーションして少しずつデータをVueに送る訳です。

ルーティング

config/routes.rb
Rails.application.routes.draw do

#api用
  namespace :api, {format: 'json'} do
    namespace :v1 do
      resources :videos, only: [:index]
    end
  end
end

今回必要な部分のみ伐採しております。

kaminariをインストールする

gem 'kaminari'

kaminariをまずはGemfileに記述します。

$ bundle install

bundle installでインストールを実行する。

JSONをpagenationさせる

app/controllers/api/v1/videos_controller.rb
class Api::V1::VideosController < ApiController
  include Pagenation  #pagenation_controllerにて定義

  def index
    videos = Video.all.page(params[:page]).per(5)
    pagenation = resources_with_pagination(videos)  #pagenation_controllerにて定義
    @videos = videos.as_json
    object = { videos: @videos, kaminari: pagenation }  #{}でjsonを複数渡せる
    render json: object
  end

end

まずはコントローラーでJSON化する記述を書いていきます。
.page(params[:page]).per(5)で、URLにpage=1というオプションが与えられたら上から5つのデータを取り出すということになります。
また、page=2というオプションが与えられたら続きの5件のデータを取り出します etc...

pagenation = resources_with_pagination(videos)
こちらはkaminariの便利メソッドを呼び出すものでして次に説明します。

object = { videos: @videos, kaminari: pagenation }
{}に入れることによって@videosとkaminariの内容を同時にJSON化できます。

試しにhttp://localhost:3000/api/v1/videos?page=1 にアクセスしてみてください。

※初歩的なことですが僕がつまずいた注意点を書いておきます。
render json: @videosとした場合はVue側で

例1
  mounted () {
    axios
      .get('/api/v1/videos')
      .then(response => (this.videos = response.data))
    }

とやってaxiosからvueに@videosのデータを入れられます。

ただし、今回の場合は
object = { videos: @videos, kaminari: pagenation }
render json: object
オブジェクト{}の中に配列が2つ入っているので同じように@videosのデータを取り出す場合は

例2
  mounted () {
    axios
      .get('/api/v1/videos')
      .then(response => (this.videos = response.data.videos))
    }

response.data.videos と.videosを追加しなければならないことに注意してください。
逆にkaminariのデータが欲しい場合はresponse.data.kaminari とすればOKですね。

kaminariのメソッドを定義する

先程出てきたresources_with_pagination()を説明していきます。
無限スクロールをするときに
- 今は何ページなのか
- 今どこまで読み込んだのか
- 次のページは何ページか
- 全部で何ページなのか
などの情報がないと無限スクロール側が何を読み込めばいいのか、いつ止めればいいのかなどが分かりません。

それをkaminariがやってくれます。
まずは、app/controllers/pagenation.rbを作成します。

app/controllers/pagenation.rb
module Pagenation
  class Api::V1::VideosController < ApiController 
    def resources_with_pagination(resources)
      {
        pagenation: {
          current:  resources.current_page,
          previous: resources.prev_page,
          next:     resources.next_page,   
          limit_value: resources.limit_value,
          pages:    resources.total_pages,
          count:    resources.total_count
        }
      }
    end
  end
end

これでページネーションの情報をJSON化して渡すことができます。
RailsAPIでページネーションに対応したJSONを返す(kaminari使用)
こちらの記事が参考になりました。

ローカルホスト/api/v1/videos?page=1にアクセスしたときのJSON
{
"videos":[{"id":1,"title":"スターウォーズ"},{"id":2,"title":"プリズンブレイク"},{"id":3,"title":"ハリーポッター"},
{"id":4,"title":"アベンジャーズ"},{"id":5,"title":"バッドマン"}],
"kaminari":{"pagenation":{"current":1,"previous":null,"next":2,"limit_value":5,"pages":12,"count":56}}
}

http://localhost:3000/api/v1/videos?page=1 アクセスしてみると,
レコードが5個しかないので見事pagenationされていることが分かりますね!

  • currentは現在のページの番号です
  • previousは前のページ番号です。1ページ目で前は無いのでnullになっています。
  • nextは次のページ番号です。
  • limit_valueは読み込んだレコードの数です。
  • pagesはトータルで何ページあるのかです。これがあれば無限スクロール側がいつ終わればいいかわかりますね。
  • countは全てのレコードの数です。

仮にここまで行ったページネーションの設定をしていないと、http://localhost:3000/api/v1/videos?page=1 にアクセスしても、今回の場合は56個のレコードがJSON化されてしまいます。

ここまでいければ後はVue側の実装です!

vue-infinite-loadingをyarnでインストールする。

まずは無限スクロールを行うvue-infinite-loadingをVueにインストールします。
yarn add vue-infinite-loading
こちらのコマンドを叩きます。

次にVue側でimportするコードを書いていきます。

app/javascript/app.vue
import InfiniteLoading from 'vue-infinite-loading'

Vue.use(InfiniteLoading, {  //無限スクロール
 slots: {
     noMore: 'すべて読み込みました',
     noResults: '読み込み完了しています',
 },
});

importする場所はhello_vue.jsファイルなど何でもOkです。
これでvue-infinite-loadingのコンポーネントを使うことができます。

無限スクロールをさせたいコンポーネントに設定を書く

app/javascript/VideosIndex.vue
<template>
  <div id="app">
    <div v-for="video in videos" :key="video.id">
      <p>{{ video.title }}</p>
    </div>
    <infinite-loading @infinite="infiniteHandler"></infinite-loading>  <!-- 追加した部分 -->
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data: function () {
    return {
      videos: [],
      page: 1, //このpageの値によってpagenationされたJSONを取ってきます
    }
  },

  methods: {
    infiniteHandler($state) {  //追加した部分
        axios.get(`/api/v1/videos`, {
            params: {
                page: this.page, 
            },
        }).then(({ data }) => {
            //そのままだと読み込み時にカクつくので1500毎に読み込む
            setTimeout(() => {
                if (this.page <=data.kaminari.pagenation.pages) {
                    this.page += 1
                    this.videos.push(...data.videos)
                    $state.loaded()
                } else {
                    $state.complete()
                }
            }, 800)
        }).catch((err) => {
            $state.complete()
        })
    }
  }
}  //export default
</script>

全体はこんな感じで、「追加した部分」を書けば取り敢えず動作はします。
デフォルトでは画面を一番下にスクロールしたときに、メソッドinfiniteHandlerが呼び出されます。

app/javascript/VideosIndex.vue
axios.get(`/api/v1/videos`, {
  params: {
    page: this.page,
  },

このaxiosのgetにparams:{page: this.page}のオプションを付けてあげる部分が非常に重要です。
これはaxiosがrailsのAPIからJSONを取ってくる際のURLに?page=1というオプションをつけるということです。
https://〇〇/api/v1/videos?page=1
今回はdataでpage:1と定義しているので最初は1ページ目のレコードを取ってきます。
例えば、page:2とすると1が飛ばされ2から読み込まれていきます。

app/javascript/VideosIndex.vue
                if (this.page <=data.kaminari.pagenation.pages) {
                    this.page += 1
                    this.videos.push(...data.videos)
                    $state.loaded()
                } else {
                    $state.complete()
                }
            }, 800)
        }).catch((err) => {
            $state.complete()
        })

この部分ですが、if (this.page <=data.kaminari.pagenation.pages) で現在のpage数(this.page)がkaminariの最大ページ数より少なかったらという分岐です。
少ない場合はthis.page += 1 で現在のページ数を1多くします。
そして、this.videos.push(...data.videos) でページ数を1増やしたレコードを取ってきます。
これが最大ページ数に到達されるまで繰り返されるという仕組みです。

最後に

いかがだったでしょうか?
記事を書くのに慣れていなくて分かりづらくなってしまったかもしれませんが気付き次第修正をしていこうと思います。
僕は最初rails側でページネーションするという発想が全く無く'vue-infinite-loading'が勝手にデータの中から少しずつレコードを取ってきて無限スクロールしてくれるのかと思っていました。
rails+無限スクロールの記事は少なかったので、同じような悩みを抱えている方の助けになればとても嬉しいです!

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

プロジェクトに設定されているStorybook v5.3をv6.0系にアップデートする

Storybookのバージョン6.0がリリースされたので現行で設定されているもの(v5.3)をv6.0向けに変更してみました。
変更した際の手順をまとめました。

前提

  • vue-cliで作成したプロジェクト内に自動的に作成される、HelloWorld.vueを元にstory(サンドボックス環境)ファイルを作成します。
  • HelloWorld.vueにはmsgと言うstring型のpropsを用意して、ヘッダーの文字列を親コンポーネントから設定出来る様にしています。(Vue.jsのイメージ画像も削除しました。)

Storybook関係のパッケージのバージョン(変更前)

名前 バージョン
@storybook/addon-a11y 5.3.19
@storybook/addon-actions 5.3.19
@storybook/addon-backgrounds 5.3.19
@storybook/addon-knobs 5.3.19
@storybook/addon-notes 5.3.19
@storybook/vue 5.3.19

Storybook関係のパッケージのバージョン(変更後)

名前 バージョン
@storybook/addon-a11y 6.0.16
@storybook/addon-knobs 6.0.16
@storybook/addon-notes 5.3.19
@storybook/addon-essentials 6.0.16
@storybook/vue 6.0.16

参考

StorybookリポジトリのMIGRATION.md

Storybookのアップグレード

下記のコマンドでバージョンが更新されているパッケージの一覧が表示されるので「@storybook」の文言が入っているパッケージを全て選択します。

addon-notesはバージョンが5.3.19のままです。

$ yarn upgrade-interactive --latest
yarn upgrade-interactive v1.22.4
info Color legend : 
 "<red>"    : Major Update backward-incompatible updates 
 "<yellow>" : Minor Update backward-compatible features 
 "<green>"  : Patch Update backward-compatible bug fixes
? Choose which packages to update. 

アップグレード後、yarn storybookコマンドで下記のエラーが発生した場合はstyle-loaderをインストールしてください。

Module not found: Error: Can't resolve 'style-loader' in
$ yarn add --dev style-loader

上記の内容で、とりあえずv5.3系の設定を変更せずそのまま使うことが出来ます。(StoriesOfAPIも使えます。)

一方でコマンド実行した際に、下記の様なメッセージが表示されます。
このワーニングを削除する為に設定ファイルを修正します。

node:5664) DeprecationWarning: Configuration files such as "config", "presets" and "addons" are deprecated and will be removed in Storybook 7.0.
Read more about it in the migration guide: https://github.com/storybookjs/storybook/blob/master/MIGRATION.md

main.tsの作成

.storybookディレクトリ内には、addons.jsやconfig.jsなどを作成していましたが、これらを削除して新規にmain.js(ts)を作成します。

main.jsの記載内容は下記の通りです。
config.jsに記載していたstoryファイルの格納場所や拡張子の設定を記載します。

また、addons.jsに記載していたaddon設定もmain.jsに記載します。

Zero-config Storybookに記載されている様に、下記のaddonはaddon-essentialsで一括で設定出来る様になりました。

  • Docs
  • Controls
  • Actions
  • Viewport
  • Backgrouds
  • Toolbars
module.exports = {
  stories: ['../src/stories/**/*.story.@(ts|js)'],
  addons: ['@storybook/addon-essentials', '@storybook/addon-knobs/preset'],
};

storyファイルの修正

続いて、storyファイルを修正します。

前提で記載したHelloWorld.story.js(ts)の内容は下記の様になっています。

import { addDecorator, storiesOf } from '@storybook/vue'
import { withKnobs, text } from '@storybook/addon-knobs'
import HelloWorld from '../components/HelloWorld.vue'

addDecorator(withKnobs)

storiesOf('HelloWorld', module).add(
  'helloWorld',
  () => ({
    components: { HelloWorld },
    template: `
      <div>
        <HelloWorld
          :msg="msg"
        />
      </div>
    `,
    props: {
      msg: {
        type: String,
        default: text('msg', 'default text')
      }
    },
    data() {
      return {}
    },
    methods: {}
  }),
  {
    notes: `test note text.`
  }
)

後々使えなくなる予定の非推奨の設定もある為、HelloWorld.story.jsを下記の観点から修正をかけます。

  1. addDecoratorの削除
  2. withKnobsの設定の削除
  3. (画面上の)notesタブの削除し、Docsタブ内にnotesの設定を表示させる。
  4. storiesOfからCSF(Component Story Format)への書き換え
  5. (画面上で)センタリングをかける。

修正した結果は次の様になりました。

import { text } from '@storybook/addon-knobs'
import HelloWorld from '../components/HelloWorld.vue'
import markdown from './notes/sample.md'

export default {
  title: 'Test/HelloTest',
  parameters: {
    layout: 'centered',
    docs: {
      extractComponentDescription: (component, { notes }) => {
        if (notes) {
          return notes.markdown
        }
        return null
      }
    },
    notes: { markdown }
  }
}

export const HelloTest = () => ({
  components: { HelloWorld },
  template: `
    <div>
       <HelloWorld :msg="msg" />
    </div>
  `,
  props: {
    msg: {
      type: String,
      default: text('msg', 'default text')
    }
  },
  data() {
    return {}
  },
  methods: {}
})

1. addDecoratorの削除

main.jsにて、@storybook/addon-knobs/presetを設定していれば各storyファイル内で設定をする必要が無くなりました。
よって、非推奨となっているaddDecoratorは削除出来ます。

2. withKnobsの設定の削除

同上です。
text型のpropsを設定する為に@storybook/addon-knobs の設定はそのまま必要です。

3. (画面上の)notesタブの削除し、Docsタブ内にnotesの設定を表示させる。

notesに設定していたテキストはmarkdownファイルとしてimportする形式にしました。
直接記述すると、囲っているアポストロフィーにまでマークダウンが適用させる様になった為です。
export default内にてparametersとしてdocsとnotesの設定を記述すると、画面上のDocsタブに表示される様になります。

4. storiesOfからCSF(Component Story Format)への書き換え

storiesOfで記載した内容を分割するイメージです。
export default内にてパラメーター等の設定をして、export constでtemplateやpropsの設定を記述します。

5. (画面上で)センタリングをかける。

従来は@storybook/addon-centeredをインストールして設定していましたが、その必要は無くなりました。
parameters内に、「layout: 'centered'」と記述すればセンタリングされる様になりました。

上記を全て対応した上でyarn storybookコマンドを実行したらエラー無く画面を開くことが出来ました。

スクリーンショット 2020-08-23 17.14.00.png

これでいつも通りにStorybookを使えます。

ちなみに、v6.0に上げた後のaddon等のアップデートは下記の通りです。

$ npx sb@next upgrade 

他にもv6.0.0以降で出来る様になった機能があるので試して行きたいと思います。

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

vue.js 日付指定ライブラリ vuejs-datepickerで選択可能日付を制限する

本記事について

vuejs-datepicker のライブラリ取得方法等、基本的な情報については省略します。

今回はvuejs-datepickerで選択できる日付を制限する方法を記載します。

vuejs-datepickerで選択可能日付を制限する

テンプレートに、:disabled-dates="disabledDates"を設定

<template>
...
  <datepicker :disabled-dates="disabledDates"></datepicker>
...
</template>

script内で、disabledDatesを指定する

<script>
  data() {
    return {
     disabledDates: {
       to: new Date(),   // 本日以前の日付は選択不可
      from: new Date(),   // 本日以降の日付が選択不可
      days: [6, 0],   // 曜日指定(この場合、土曜日・日曜日選択不可)
      daysOfMonth: [29, 30, 31],   // 各月の29、30、31日が選択不可
      dates: [            // 選択不可の日付を複数指定
        new Date(2020, 8, 23),
        new Date(2020, 8, 24),
        new Date(2020, 8, 25)
      ],             
      ranges: [{                  // 選択不可の期間を指定
        from: new Date(2020, 9, 1),
        to: new Date(2020, 9, 15)
      }],
      }
    }
  }
</script> 

参照:https://www.npmjs.com/package/vuejs-datepicker

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

Vue CLI にreset.scssを入れた。

ビューが崩れるのでreset.scssを入れたい。

色々調べてみたが App.vue の style でimport するのが簡単。

他にもやり方はあるが、今回はこの方法でやってみる。

src に css フォルダーを作成し、reset.scss を入れる。

あとは App.vue の style タグで import するだけ。

src/App.vue
<style lang="scss">
@import "./css/reset.scss";     <!-- 左の記述を追加 -->
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;

}
</style>

変な所についていた margin が消えたので reset.scss が効いていることが確認できた。

他にも npm でインストールするやり方などあるので、次回はそちらを試してみたい。



参考記事:【Vue CLI】普通の外部CSSをcomponent全体に反映させたい

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

簡易CMSで無料ブログを公開、カスタム レイアウトを複数追加 #Vue #Vue-CLI #javascript

概要

事例的な内容となります。

CMSのアップデート内容で、
レイアウトを追加しました。

・デモサイトは、GithubPagesに設置して例になります。
・投稿データ等の、編集機能は前回と同じで。MD書式対応です


環境

Vue CLI
vue: 2.6.11
vue-router


前の関連のページ

前の CMS表示の内容

https://note.com/knaka0209/n/nc10d352550ed


画面 Top

・type2, グリッドレイアウト風の
 投稿データ表示となります。

ss-type2-0821a.png

■ 参考の、デモのページ 

https://kuc-arc-f.github.io/cms-sample-type2

■ 参考のコード

https://github.com/kuc-arc-f/vue_spa3cms3_type2


・type3 , 少し明るめ
ss-type3-0821b.png

■ 参考の、デモのページ 、type3

https://kuc-arc-f.github.io/cms-sample-type3

■ 参考のコード , type3

https://github.com/kuc-arc-f/vue_spa3cms3_type3


・type4
初版と、似ていますが、サイドに。twitterタイムラインを埋込可能な形です。
ss-type4-0822a.png

■ 参考の、デモのページ 、type4

https://kuc-arc-f.github.io/cms-sample-type4

■ 参考のコード 、type4

https://github.com/kuc-arc-f/vue_spa3cms3_type4


■ 補足

・初版レイアウトの、デモはこちら
https://kuc-arc-f.github.io/cms-sample


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

Nuxt + Vue + Cordovaでアプリ開発をしよう!

こんにちは、フロントエンドの初心者のYちゃんです。スマートフォン向けのアプリケーションを作るために、自分の使えるフレームワークであるVueやNuxtを使ってやろうと思っていろいろ調べていると、Cordovaというフレームワークでアプリケーションを作れるというのを知って、いろいろ試したんですが、いろいろ詰まったので備忘録的に書き記しておこうと思います。

開発環境

私はWindowsでもろもろをやる変態エンジニアなのでいろいろ詰まってた感はあります。

  • Windows 10 Pro Insider Preview 2004 Build 20190.1000(変態なのでInsider Previewerですがあまり気にしないでくださいw 普通のWindowsで十分だと思いますw)
  • Android Studio Ver 4.0.1
    • とりあえずインストールするだけでOK
  • Node.js v12.18.2
    • yarn 1.22.4
  • Git for Windows 2.27.0
    • Windows上で軽くUnixコマンドを扱うために入れています。package.json内なんかでUnixコマンドを使うことがあるためです。なお、これはインストール時の設定によって変わるので、使えるようにしている場合とそれ以外の場合とで二通り紹介します。

そもそもなぜNuxt + Vue + Cordova?

Vue + Cordovaでええやん、と思う人は結構いると思うんですが、私自身フロントエンド初心者で東京都 新型コロナウイルス感染症対策サイトからフロントエンドを触り始めたので、扱えるフレームワークがNuxtとVue(とVuetify)なのです。調べれば解決するでしょうがVue単体を切り離して使う方法がわからないですし、自分の中でVueを使うならNuxtとセットという固定概念が出来てしまっているということでNuxt + Vue + Cordovaという選択肢に行き着いたのです。

開発開始

Gradleのインストール(設置)

Gradleというオープンソースのビルド自動化ツールを導入しておく必要があります。Android Studioが入っている時点で導入されているというお話があったんですが、結局わからなかったのでマニュアルインストールします。
Gradleは6.6が最新版なんですが、2020/08/22現在、最新版をインストールしても後のビルドの段階では6.5を使っているようなので、別に6.5でいいと思います。なので、今回は6.5をインストールしていきます。
ここにアクセスすると直接6.5のバイナリが落とせます。落とせたら、zipなので普通に開いてあげます。
image.png
この、gradle-6.5をそのままCドライブ直下にコピーしちゃいましょう。(Gradleの公式ドキュメントにはそう書いてあったので別にいいはず)
パスを通さなければなりませんが、次の章でまとめてやります。

環境変数の設定

Windowsなので、環境変数がいろいろめんどくさいです(Linuxなんかと違ってexportなんかが使えないので)。とりあえず[スタートボタン]->[設定]->[システム]->[詳細情報]から関連設定の[システムの詳細設定]を開き、一番下の[環境変数]まで行きましょう。そこまでいけば環境変数を設定できます。わからなければ、ここら辺の記事はいろんなところにたくさんあるので、ここでは解説しません...
編集するのはユーザー環境変数です。

まずは先ほどCドライブ直下に配置したgradleのパスを通します。
ユーザー環境変数のPathの中に入り、未使用の部分をダブルクリックするか右上の新規ボタンよりPathを追加していきます。
C:\gradle-6.5\binを追加してあげればよいです。

つぎにJDKのパスを通します。Android Studioをインストールした時点でJDKも同時にインストールしてくれるので、特に何もなければそれを使いましょう。学生はOracleからJDKを落とすのが難しいのでありがたい限りです。
それではAndroid Studioの設定からJDKのパスを取得しましょう。何もプロジェクトを作っていなければ右下の[Configure]->[Default Project Structure...]から、
image.png
すでにプロジェクトを作っていて、上のような初期画面が開かない場合は[File]->[Other Settings]->[Default Project Structure...]から、
image.png
以下の画面にアクセスできます。
image.png
この中のJDK locationをコピーします。
Gradleの時と同じようPathを追加していきます。
ここで注意ですが、先ほどコピーしたものを貼り付けるだけでなく、"\bin"を付け加えてください。でなければちゃんとパスが通りません。
以下の図、した二つの欄のようにできていれば、OKを押し、大本の環境変数画面もOKを押しましょう。コマンドプロンプトなどから"gradle -v"コマンドと"java -version"コマンドを叩き、それぞれが動けば大丈夫です。
image.png

image.png

GradleとJDKのパスを通すだけではありません。一つ設定しなければならない環境変数があります。ANDROID_HOMEです。先ほどのAndroid StudioのDefault Project Stractureに戻りましょう。まずはここのAndroid SDK locationをコピーします。
image.png
そして今度はユーザーの環境変数枠の中の[新規]ボタンより、環境変数を設定していきましょう。以下のようにすれば大丈夫です。(今回はそのまま貼り付ければ大丈夫です)OKを押して設定しましょう。
image.png
これにて環境変数の設定は終了です。この環境変数が設定できているかを確認するのはCordovaが動くかどうかなので今のところは確認できません...一応環境変数に関するウィンドウとコマンドプロンプトなどはすべて閉じておきましょう。(反映されないことがあるので)

Nuxtプロジェクトの作成

では、Nuxtプロジェクトを作っていきます。あれ、Cordovaのプロジェクトじゃないの?と思うかもしれませんが、それは後で作ります。

yarn create nuxt-app <project name>

まぁここはNuxtの公式ドキュメントにも書いてありますし、特に困ることはなさそうですね。
そのあとの設定は自由でいいですが、一つだけ注意点があって、Rendering modeはSPAにしましょう。Universalにして、SSGでも動くんですが、SPAのほうが事故らないはずです(axiosとか使ってる場合は特に...?私は初心者なのでまだ事故ったことがまだない...)。まぁ、あとからnuxt.config.js内のmodeを書き換えれるので結局どっちを選んでもあんまり変わりませんが...w
今回はプロジェクトネームをcordova-testとしました。
image.png
無事に生成できれば、cdしてプロジェクトディレクトリに入りましょう。

nuxt.config.jsを手直しする

題の通りです。nuxt.config.jsに以下のものを足したり、書き換えたりします。

  1. headの中にscript: [{ src: "cordova.js" }]を足す
    linkの下にでも足しておきましょう。これによってcordovaで動作します。
    image.png

  2. buildの中にpublicPath: "/nuxt/"を足す
    cordovaが_nuxtというディレクトリを認識できない(正確には_を認識できない)ので、それに対応するために書き換えます。
    image.png

  3. routerを新設し、mode: "hash"を足す
    routerという項目がデフォルトではないので付け加えてあげます。この設定はよくわからないんですが、/index.htmlが開かれないようにするためのよう...?いずれにせよcordovaにとって悪影響を及ぼすから変えなければならないようです。buildの下にでも入れておいてあげましょう。
    image.png

  4. modespaにする
    プロジェクト作成の時点でSPAを選択している人は不要です。また、俺は/私はSSGで行くんや!って人は別にuniversalでもいいと思います。(確認した限りだと動くには動くようなので...どんな問題があるかは私にはわからないのでお勧めはしないです...)
    image.png

これで一通り手直しは終わりました。

package.jsonscriptを手直しする

generateコマンドの修正とcordova-run-androidのようなコマンドを作ります。
まずはgenerateの修正から。

"generate": "nuxt generate && rm app/www/* -r && cp dist/* app/www/ -r && replace '/nuxt/' 'nuxt/' app/www -r"

appディレクトリはCordovaのものです。後で作ります。なお、app以外のディレクトリで作成した場合はすべて別の名前に書き換えてください。

コマンドを解説していきます。nuxt generateはごく普通の、NuxtでHTMLやJSなんかを生成するコマンドです。
次のrm app/www/* -r&& cp dist/* app/www/ -rでは、cordovaプロジェクトのディレクトリにあるwwwというHTMLやCSS、JS、画像ファイルといったものを入れておくディレクトリの中身をいったん全削除し、先ほどのnuxt generatedistディレクトリ内に生成されたものと入れ替えています。
最後にreplace '/nuxt/' 'nuxt/' app/www -rでは、Cordovaはルートディレクトリの都合上、/nuxt/が使えないので、nuxt/に置き換えています。

なお、これはGit for WindowsでUnixコマンドを使えるようになっている人向けのコマンドです。Linuxなどでも使用可能な形になっています。では、次にWindows標準コマンドを使ったバージョンを貼っておきます。

"generate-by-windows": "nuxt generate && del /s /q app\\www\\* && xcopy /e dist\\ app\\www && replace '/nuxt/' 'nuxt/' app/www -r"

generate-by-windowsとしましたが、これを使う場合は普通のgenerateコマンドとは分けたほうがいいです。(windowsでしか使えないコマンドをいれているわけなので)
なお、どちらにも出てくるreplaceコマンドについてですが、これは

yarn add replace

でプロジェクト自体にくっつけるか、

yarn global add replace

でグローバルに埋め込むかすれば使えます。個人的にはプロジェクト自体にくっつけたほうが、ほかの環境に移った時なんかは楽だと思います。

次にcordova-run-androidコマンドです。
WindowsなのでAndroidのコマンドのみ追加しますが、Mac使ってiOS版もビルドするのであれば、androidの部分をiosに変えてあげればいいです。

"cordova-run-android": "cd app && cordova build android && cordova run android"

これを実行するだけでビルドから実機(orエミュレータ)での実行まで自動でできるようになります。
修正はここまでです、いよいよビルドに向かいます...!

Cordovaプロジェクトの作成

さて、ようやくCordovaプロジェクト作成の区域に入りました。
まずは、Cordovaをインストールしましょう。

yarn global add cordova

これでcordovaコマンドを実行できるようになりました。
次に、Nuxtプロジェクトのルートで以下を実行しましょう。

cordova create app <識別子> <application name>

識別子ってなに?って思うかもしれませんが、Androidアプリを日常的に作っている人とかならわかるかもしれません。逆引きドメイン スタイル識別子というやつです。もっと???となるかもしれませんが、com.example.helloworldみたいな、ドメインを逆にした感じのやつです。実在するものでなくてもいいようです。ストアにアプリ申請するときなんかに使われます(数百万とあるアプリを識別し、同一の識別子を持つことがないようにする...データ保管のディレクトリ名に使われたりするので...)
何も決まっていなければ適当でいい気がします。(あんまり適当なこと言えないけど...w)

ディレクトリは確実に指定しないといけないようですが、識別子やアプリ名は何も指定しないと、それぞれio.cordova.hellocordovaHelloCordovaになるようです。
今回は、ディレクトリはappで固定、識別子はcom.example.cordova_test、アプリ名はcordova-testにしてみます。(確か識別子に-は使えない...)
image.png
あっさりと終ります。それでは、appディレクトリにcdして、platformを追加しましょう。今回はAndroidのみやります。
image.png
これでビルド準備が整いました...!

いざ、ビルド...!

Nuxtプロジェクトのルートに戻ります。そこで、さっき設定を変更したyarn generate(or yarn generate-by-windows)とyarn cordova-run-androidを実行していきます。

実機で実行したい場合は、yarn cordova-run-androidを実行する前にでもデバッグモードをオンにしたスマホをUSBでつないであげるとよいでしょう。
image.png

image.png

上の画像がちょうどそうですが、runしてもエミュレータも実機もなかった場合は失敗して終わります(apkはあるのであとは実機に流し込むだけです)

まとめ

いかがでしょうか。ネット上で2つほど記事を見かけ、参考にして試してみようと思っていたところ、案外詰まった(javaとかgradleとかもろもろ)ので、自分が見て後で環境を再構築できるように書き残しました。ほかの方の参考にもなれば。
ところでNuxt + Vue + Cordovaの組み合わせ、かなり便利なのでちょっと推していきたいです。(開発環境ではNuxtで自動リロード/ビルド等があり、そしてCordovaでアプリ化できるというのがかなり良い)

おまけ

実際にアプリをインストールしてみました。Vuetify Logoは何があったんだろう...w
image.png

問題点(追記)

今のままの状態だとCordovaのプラグインを使用することができません。これを解決する必要があります。

参考文献

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