- 投稿日:2019-04-15T22:39:13+09:00
Nuxt.jsでどうしても直接DOMを操作したい
背景
大前提として、DOMを直接操作する前にまず打てる手を色々と考えていただきたいです
さて、Nuxt.jsを使っていて、どうしてもDOMを直接操作したい場合があります。
たとえば、ページ遷移直後に、遷移先の内部リンク(/hoge#id
)に自動でスクロールしたい場合などが考えられます。
このような場合、ライフサイクルイベントと直接DOM操作を駆使して動かす以外に方法がないのでは?と思い、実際にやってみました。tl;dr
mountedとnextTickを使います。
mountedはスクロールするためにDOMの操作が必要になるので使用します。
ただし、mountedは子コンポーネントのマウントを保証しないので、nextTickを使い、レンダリングが終わるまで(操作対象のDOMがあることを保証して)コールバック関数を実行しないようにすることが重要です。もし内部リンクとなるidを指定したコンポーネント内からさらにコンポーネントを呼び出している場合は、子コンポーネントの中でmountedとnextTickを使ってください。
動かしてみる
記法はvue-class-componentに従っているのでよしなに脳内変換をお願いします
test.vue<template> <div id="id"> <p>Please scroll</p> </div> </template> <script lang="ts"> import { Component } from "vue-property-decorator"; @Component export default class Child extends Vue { private mounted() { this.$nextTick(function() { const element = document.getElementById('id') if (!element) { return } // elementの位置座標を取得 const rect = element.getBoundingClientRect() window.scrollTo(rect.left, rect.top) }) } } </script> <style> </style>内部リンクとなるidがない場合はスクロールはせず終了します。
さらに
上記のままだと、決まったidにしか反応できないので、以下のようにすれば内部リンクが増えたときも便利です。
test.vue<template> <div id="id"> <p>Please scroll</p> </div> </template> <script lang="ts"> import { Component } from "vue-property-decorator"; @Component export default class Child extends Vue { private mounted() { this.$nextTick(function() { // url内のhash(#id)を取得する const hash = this.$route.hash if (hash.length === 0) { return } // hash.slice(1)でhashから「#」を取り除いておく const element = document.getElementById(hash.slice(1)) if (!element) { return } const rect = element.getBoundingClientRect() window.scrollTo(rect.left, rect.top) }) } } </script> <style> </style>vue-routerの
$route.hash
を使えばurl内にあるhashを取得してくれるので、hashの動的な変更に合わせて内部リンクへのスクロールが可能です。最後に
しつこいようですが、直接DOMを操作する前になにかできることはないか考えてみるのがベストだと思います。少しでもお役に立てれば幸いです
- 投稿日:2019-04-15T20:28:25+09:00
RailsプロジェクトへのVue.js導入
Rails × Vueのセットアップ
導入方法は以下の4つが考えられそうだが今回は1.で導入
- デフォルトのRailsプロジェクトの中にVue.jsを導入
- APIモードのRailsプロジェクトの中にVue.jsを導入
- RailsコンテナとVueのコンテナに分けてコンテナ同士で接続
- Rails用サーバーとVue用のサーバーでそれぞれ構築して、エンドポイントで接続
Gemfilegem "webpacker"# gemのインストール bundle install # webpackerインストール bin/rails webpacker:install # コンパイル bin/webpack # vueインストール bin/rails webpacker:install:vue # Top作成 rails g controller Home index --no-assets --no-helper --no-test-framework # vue読み込み sed -ie '10i \ <%= javascript_pack_tag "hello_vue" %>' app/views/layouts/application.html.erb
- routeの書き換え
config/routes.rbRails.application.routes.draw do root to: 'home#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
- localhostにアクセスしてhello worldを確認
雑感
何も開発していないのに最初から色々1つのプロジェクトにたくさん入っちゃっている感
- 投稿日:2019-04-15T19:52:06+09:00
Vue.js初学者でも[確実に]ToDoアプリを動かしてVueコンポーネント間データ渡しを学ぶ
はじめに
前回のおさらいと今回すること
前回の記事ではVue.jsのHelloWorldを動かして基本事項を学びました。またコンポーネントという考えに触れ、実際に改良しました。
今回は、簡単なToDoアプリケーションを通して、複数のコンポーネントに分割してwebアプリの機能を作ることを初学者の状態から学びます。またその過程で求められるコンポーネント間のデータ渡しについて理解し、実装できるようになります。前回の状態の続きから実装を始めるので、前回の記事を読んでいたら[確実に]動かせるようになります。コードは更新のたびに、Githubにあげています。todoapp
Vuetify適応版なのでご注意を一連の連載で[確実に]理解すること一覧
Vue.jsを使ってHelloWorldを出力し、プロジェクト内のファイルの役割や仕組みを理解する 前回の記事
コンポーネントとは何かを理解し、HelloWorldを改良してページのQRコードを表示してみる。前回の記事
コンポーネント間の値の受け渡しの基礎を導入し、Vuexの意義を理解した上で導入する[本記事]
WebAPIを導入してフロントエンドとバックエンドでデータをやり取りし、実践的なサイトを作り上げる
ToDo管理アプリを実装してみよう
アプリの機能と構成について
ToDo管理アプリについて考えてみましょう。必要な機能は
ToDoタスクリストとして表示すること (=> タスクビュワーコンポーネント)
ToDoタスクの終了が管理できること (=> タスクリビュワーコンポーネント)
ToDoタスクの追加ができること (=> インプットタスクコンポーネント)
としましょう。今回はこの機能を実現するために上記のTaskViewコンポーネントとInputTaskコンポーネントの二つを実装します。前回同様にこれらのコンポーネントを貼り付けるためのキャンバスの役割を担うApp.vueも利用します。
コンポーネントが何の機能を持つのかは上述しましたが、これらのコンポーネント間でのデータのやり取りはどうなっているでしょうか?ここで一点だけ、全タスクデータは、App.vueが保持するという制約を加えます。それを踏まえて、ここではデータの移動を以下のように考えました。
タスクビュワーはタスクリストを受け取り、表示する
タスクビュワーは各タスクを終了状態にすることができるボタンを持ち、それを押すとタスクリスト中の該当タスクが終了状態になる
インプットタスクは入力フォームを持ち、入力されたタスクがタスクリストに追加される
コンポーネントの概念図は以下の関係になります。概念図のトップのApp.vueは二つのコンポーネントを内部に持ち、配置等を制御するためのコンポーネントであり、親コンポーネントと呼びます。
一方で、下の二つのコンポーネントは親から呼ばれたコンポーネントで子コンポーネントと呼びます。
TaskViewコンポーネントを実装してみよう。
子コンポーネントは親から値をもらって、表示しなければいけません。その橋渡しをする変数がPropsです。親は子供のProps変数に値を渡します。子はそれを受け取ります。
子コンポーネントの役割はもう一つあります。それはタスクの終了制御です。子供はタスクが終わった場合、親コンポーネントになんらかのアクションで通知します。親コンポーネントはボタンが押されたことを検知します。この橋渡しをする変数がEventです。PropsもEventも共通することとして親コンポーネントから呼び出すときに親と子で値の受け渡しがあることを宣言しておく必要があるということです。では実際に親から子コンポーネントを呼び出すコードを見てみましょう。
<taskView v-bind:tasks='tasks' v-on:child-event="TaskFinished"/>
この一文が親コンポーネント内のTemplate内に記述したコンポーネント呼び出しであることは前半の記事を読んだ方ならば分かるかと思います。前半のv-bind:tasks='tasks'
について
v-bind:'Props名'='データ名'
と書くことで「親は、'データ名'を'Props名'という名前で子に渡しますよ」という宣言です。後半の
v-on:child-event="TaskFinished"
については
v-on:'Event名'='親が呼び出すメソッド名'
ということで、「子から'Event名'のイベントが発火されたら、親は'親が呼び出すメソッドを' 実行しますよ」という宣言です。では実際のコードで見てみましょう。なお、HelloWorldコンポーネントの部分は今回は削除しましたが、あっても動きます。src/components/TaskView.vue<template> <div class='task'> <table> <!-- テーブル形式にして表示することにします。 <tr> <th>Task Name</th> <th>Status</th> </tr> <tr v-for='(task, index) in tasks' :key='index'> <!-- tasksの配列の要素をループして表示。 <td>{{ task.name }}</td> <!-- buttonアクション(v-on:click)にclickというメソッドを発火 <td v-if=task.flag ><button v-on:click='click(task.name)'> Done </button></td> <td v-else ><button v-on:click='click(task.name)'>Not Yet</button></td> <!--if文でflagを参照してボタンに表示名を変えている。 </tr> </table> </div> </template> <script> export default { name: 'TaskView', props: { tasks: Array // Array型のPropsを親から受け取ります 宣言 }, methods: { // template の部分のbuttonアクションで書いたものの実態はこれ。$emitで親に'child-event'というイベント名でイベントが起きたことを通知します。第二引数を指定すると、親に値を渡すことができます。 click: function (msg) { this.$emit('child-event', msg) } } } </script> <style scoped> </style>src/App.vue<template> <div id="app"> <img src="./assets/logo.png"> <taskView v-bind:tasks='tasks' v-on:child-event="TaskFinished"/> <!-- コンポーネントの作成文。 <!-- 前半は子のtasksというProps名に親のtasksというデータを渡す。 <!-- 後半は子がchild-eventという名前でイベントを投げたら、親はTaskFinishedというメソッドを発火する宣言 </div> </template> <script> import TaskView from './components/TaskView' export default { name: 'App', components: { TaskView }, data () { // ここでtaskの配列を与える。今回は{name(タスク名): String, flag(終了フラグ): Boolean} return { tasks: [ { name: 'No1', flag: false }, { name: 'No2', flag: true }, { name: 'No3', flag: false } ] } }, methods: { // 子からイベント名(event-child)を受け取って発火する関数。具体的に作り込んでいく部分。 TaskFinished: function (msg) { this.tasks.forEach(value => { if (value.name === msg) { if (value.flag === true) { value.flag = false } else if (value.flag === false) { value.flag = true } } }) } } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>表示は以下のようになっていて、ボタンを押して'Done'と'NotYet'が切り替われば成功です。
InputTaskを実装してみよう
タスクの登録では、親のもつタスクリストの配列を操作することになります。つまりTaskViewで出てきたようなEvent通知処理でタスク追加を通知し、受け取った親側で配列にタスクを追加します。といっても今回タスクは{タスク名,状態}しか保持しない上に初期状態では状態もfalseであることを考えると、タスク名のみを通知すればよいことがわかるでしょう。
src/components/InputTask.vue<template> <div class='InputTask'> <form v-on:submit.prevent='addtask(taskname)'> <!-- formタグ内に書いていきます。v-on:submitとv-on:clickという2パターンのイベントを書いていますが、発火する関数はどちらもaddtaskです。 <input type='text' v-model='taskname' placeholder='input the task'/> <!-- コンポーネントがもつtasknameというdataにinputの入力値を即時反映させるためにv-modelというオプションにtasknameを指定。 <button v-on:click.prevent='addtask(taskname)'>Submit</button> </form> </div> </template> <script> export default { name: 'InputTask', data () { return { taskname: '' } }, methods: { // v-onで定義したアクションから呼ばれる関数です。v-on:clickもv-on:submitもどちらもchild-eventという名前で親に通知します。親からしたらエンターキーで追加されたか、submitボタン押で追加されたかはどうでもよく、イベント発火のみを判断して処理したいからです。 addtask: function (msg) { this.$emit('child-event', msg) this.taskname = '' // イベントを通知したら一応、初期化します。 } } } </script> <style scoped> .input{ width: 130pt; height:30pt; } .button { display: block; position: relative; margin: 0 auto; width: 70pt; border: solid 1px silver; border-radius: 0.5rem 0.5rem; padding: 0.5rem 1.5rem; margin-top: 1rem; text-decoration: none; } </style>App.vue<template> <div id="app"> <img src="./assets/logo.png"> <InputTask v-on:child-event="TaskAdded"/> <!-- コンポーネントを作成。先ほど同様イベント通知を受ける宣言をここに。 受けたときに呼ぶ関数は'TaskAdded'と指定。 <taskView v-bind:tasks='tasks' v-on:child-event="TaskFinished"/> </div> </template> <script> import TaskView from './components/TaskView' import InputTask from './components/InputTask' export default { name: 'App', components: { TaskView, InputTask }, data () { return { tasks: [ { name: 'No1', flag: false }, { name: 'No2', flag: true }, { name: 'No3', flag: false } ] } }, methods: { TaskFinished: function (msg) { this.tasks.forEach(value => { if (value.name === msg) { if (value.flag === true) { value.flag = false } else if (value.flag === false) { value.flag = true } } }) }, TaskAdded: function (msg) { // InputTaskのイベントを受けて発火する関数。 // {受け取ったタスクの名前と初期状態false}でデータ配列の末尾に追加。デバッグ用にコンソール出力も console.log(msg) this.tasks.push({ name: msg, flag: false }) } } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>以下のような画面になり、エンターでもSubmitボタンを押すことでもタスクが追加できたら成功です。
終わりに
今回はコンポーネント間でのデータのやり取りの必要性を理解し、実際に親子間でのデータやりとりをToDoアプリを作成することで実践しました。では、例えばコンポーネントが20~30個のアプリになったり親子関係がさらに深くなったり、、、と複雑化していった場合に、全ての変数を今回のようなやり方でやり取りするのが適切でしょうか?
各コンポーネントから参照可能なデータストアがあれば楽なのにと感じることも出てくるかもしれません。それこそがQiitaでもたくさんの記事が書かれているVuex(ReactユーザならばRedux)の生まれた経緯です。次回はこのVuexを今回作成したToDoアプリに導入してみましょう。一連の連載
- 投稿日:2019-04-15T17:18:31+09:00
Webpack + TypeScript + Vue環境でJestを最低限使えるようにする
はじめに
事前にTypeScript+Vueの環境構築はできているものとします。
今回は下記環境でJestを使えるようにします。
- webpack: 4.29.6
- vue: 2.6.10
- typescript: 3.4.1
インストール
yarn add -D @vue/test-utils jest ts-jest vue-jest設定
package.json{ // 省略 "scripts": { // 省略 "test": "jest" }, "jest": { "moduleFileExtensions": [ "js", "ts", "json", "vue" ], "transform": { ".*\\.(vue)$": "vue-jest", "^.+\\.tsx?$": "ts-jest" } }, }テストファイル配置
ソースファイルを配置しているディレクトリ配下に「__tests__」ディレクトリを作成します。
__tests__/ChatMessage.spec.tsimport { shallowMount } from "@vue/test-utils"; import ChatMessage from "../components/ChatMessage.vue"; describe("ChatMessage.vue", () => { test("renders props.message when passed", () => { const message = "new message"; const wrapper = shallowMount(ChatMessage, { propsData: { message } }); expect(wrapper.find(".text-box").text()).toMatch(msg); }); });実行
yarn test問題なければテストが実行されると思います。
参考
https://vue-test-utils.vuejs.org/ja/guides/using-with-typescript.html
https://jestjs.io/docs/ja/configuration#transform-object-string-string
- 投稿日:2019-04-15T13:44:16+09:00
FontAwesomeを使おうとしたら、Could not find one or more icon(s) というエラーが出た話
概要
FontAwesomeをVue.jsに入れようとした。
他のアイコンでも使えるか試したところ、Could not find one or more icon(s) というエラーが出た。FontAwesomeの入れ方
入れ方は、こちらの記事を参照
https://fontawesome.com/how-to-use/on-the-web/using-with/vuejsエラーの詳細
解決策
prefixを変更する。
Before
font-awesome-icon(icon='envelope' )After
font-awesome-icon(:icon="['far', 'envelope']" )解説
FontAwesomeのアイコンには、複数のスタイルが用意されている。
それぞれ、違うprefixを指定する必要がある。例:
Prefix Style fas Solid far Regular fal Light fab Brand 参考:https://fontawesome.com/how-to-use/on-the-web/setup/getting-started
- 投稿日:2019-04-15T13:43:36+09:00
jQueryからVue.jsへ乗り換えてみた
はじめに
この記事でQiitaデビュー?ですが、文書書くの下手くそなので、読みにくかったらすみません。
JavaScriptとjQueryで個人的に作りたかったマネーカウンター(コインカウンター)を題材に、仕事と趣味半々で作ってました。でも複雑怪奇になったのでVue.jsを使えば多分楽になると思って、移植しました。
なお、PWAとして まねかん と、 Cordovaでラッピングして Androidアプリ「まねかん」 がありますので、参考まで。 ソースは GitHub においてあります。「まねかん」制作のアイデア
個人的にとある集会の会計をやっていて、小銭勘定を常に行う必要があったので、Excelで計算してたのですが、必ず手元にExcelがあるわけがないので、スマホアプリを作ろうと考えました。大元のExcelはこんな感じ。
ただし、スマホアプリを一から設計するだけの余裕と技量が無いので、Webアプリの作り方をググっていくと、PWA
とか便利な技術があることに気づきました。これならできる
と早速作成を開始しました。jQueryの頃
考え方は単純なので、感覚としては
<!DOCTYPE html> <html lang="jp"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> </head> <body> <p> <span>1円:</span> <input class="count" type="number" value="0"> <span class="subtotal s1">0円</span> </p> <p> <span>5円:</span> <input class="count" type="number" value="0"> <span class="subtotal s5">0円</span> </p> <p> <span>10円:</span> <input class="count" type="number" value="0"> <span class="subtotal s10">0円</span> </p> <p> <span>50円:</span> <input class="count" type="number" value="0"> <span class="subtotal s50">0円</span> </p> <p> <span>100円:</span> <input class="count" type="number" value="0"> <span class="subtotal s100">0円</span> </p> <p> <span>500円:</span> <input class="count" type="number" value="0"> <span class="subtotal s500">0円</span> </p> <p> <span>千円:</span> <input class="count" type="number" value="0"> <span class="subtotal s1000">0円</span> </p> <p> <span>弐千円:</span> <input class="count" type="number" value="0"> <span class="subtotal s2000">0円</span> </p> <p> <span>五千円:</span> <input class="count" type="number" value="0"> <span class="subtotal s5000">0円</span> </p> <p> <span>壱万円:</span> <input class="count" type="number" value="0"> <span class="subtotal s10000">0円</span> </p> <p> <span class="total">0円</span> </p> <script> let items = [ {type: 's1', callName: '1円', unit: 1}, {type: 's5', callName: '5円', unit: 5}, {type: 's10', callName: '10円', unit: 10}, {type: 's50', callName: '50円', unit: 50}, {type: 's100', callName: '100円', unit: 100}, {type: 's500', callName: '500円', unit: 500}, {type: 's1000', callName: '千円', unit: 1000}, {type: 's2000', callName: '弐千円', unit: 2000}, {type: 's5000', callName: '五千円', unit: 5000}, {type: 's10000', callName: '壱万円', unit: 10000} ]; $(function() { $('input').on('change', function () { let total = 0; for (let i = 0; i < $('.count').length; i++) { let c = Number($('input').eq(i).val()); let u = items[i].unit; let subtotal = u * c; total += subtotal; $('.subtotal.' + items[i].type).text(`${Number(subtotal).toLocaleString()}円`); } $('.total').text(`合計:${Number(total).toLocaleString()}円`); }); }); </script> </body> </html>とデータを用意して、あとはinputタグだけで計算させてました。
あとはjQueryでイベント拾って計算させるだけという簡素な物でしたが、いろんなjQueryコンポーネントで装飾していくと、いろいろイベント処理とかが複雑になってきました。で、Vue.js
をつかうと楽になる的な情報を聞きました。Vue.jsに移植
Vue.jsは設計思想的には非常に合理的で、
フォームの値が変わったら計算項目はすべて自動計算される
のが特徴です。jQueryだとDOMにイベントを割り当てて、関数を定義しなければいけませんが、基本の書き方をすれば、全部計算してくれます。<!DOCTYPE html> <html lang="jp"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> <div id="app"> <p v-for="(item, index) in items"> <span>{{ item.callName }}</span><input type="number" v-model.number="item.counts"><span class="subtotal"> {{ subtotalMoney[index] }} 円</span> </p> <p> <span>合計: {{ totalMoney }} </span> </p> </div> <script> const app = new Vue({ el: '#app', data: { items: [ {callName: '1円', unit: 1, counts: 0}, {callName: '5円', unit: 5, counts: 0}, {callName: '10円', unit: 10, counts: 0}, {callName: '50円', unit: 50, counts: 0}, {callName: '100円', unit: 100, counts: 0}, {callName: '500円', unit: 500, counts: 0}, {callName: '千円', unit: 1000, counts: 0}, {callName: '弐千円', unit: 2000, counts: 0}, {callName: '五千円', unit: 5000, counts: 0}, {callName: '壱万円', unit: 10000, counts: 0} ] }, computed: { totalMoney: function () { let total = 0; for (let i = 0; i < this.items.length; i++) { total += this.items[i].unit * this.items[i].counts; } return Number(total).toLocaleString(); }, subtotalMoney: function () { let subtotals = []; for (let i = 0; i < this.items.length; i++) { subtotals.push(Number(this.items[i].unit * this.items[i].counts).toLocaleString()); } return subtotals; } } }); </script> </body> </html>Vue.jsは見た目に難しいようですが意外と単純で、
computed:
節に計算する関数を書けば、いちいち関数をコールする必要もありません。
jQueryだけだとイベントを一つ一つ書かなければいけませんが、すっきりします。雑感
何で先に Vue.js を覚えなかったのか?と思うほど動作を単純化できます。もちろん見栄えを良くするにはBootstrapとかで装飾したりでjQueryも必要ですけれども…
そのうちVueコンポーネントの書き方でも書こうかなと思ってます。
続き:Vue.jsのコンポーネントの作り方(たぶん初級編)参考
Vue.js 公式サイト(日本語) https://jp.vuejs.org/index.html
jQuery 公式サイト(英語) http://jquery.com/
- 投稿日:2019-04-15T11:47:02+09:00
Nuxt.jsでCookieを使って閲覧したデータを取得する方法
Nuxt.js専用のモジュールのcookie-universal-nuxtを使用
Nuxt.js用のCookieを扱うためのモジュールがあるので、こちらを利用します。
導入
対象のプロジェクト下で
yarn add cookie-universal-nuxt or npm install --save cookie-universal-nuxtnuxt.config.jsの設定
nuxt.config.jsmodules: [ ['cookie-universal-nuxt', { parseJSON: false }] ],これで設定は完了
取得するデータをキャッシュに保持(set)
今回は取得するデータがあるページへアクセスしたら、キャッシュに記録されるというもの
cookie.vueexport default { created() { const setCached = { title: 'こんにちは!タイトルをキャッシュします', content: 'こんにちは!内容をキャッシュします。' } this.$cookies.set('article01', setCached, { path: '/', maxAge: 60 * 60 * 24 * 7 }) } }setCachedにページ内のデータをセットする。
今回は直接入力しているが、apiから取得したデータ等をセットしたり、dataプロパティと紐づけてもよいthis.$cookie.setでクッキーに入るNameをarticle01に指定し、valueをセットする。
cookieを保持するpathと時間を指定する
これでcookieにセットされます。cookieに保持されているデータを取得(get)
今度は違うページで取得できるようにしてみる。
index.vueexport default { data () { return { article01: this.$cookies.get('article01') } }, computed: { article() { let post if (this.article01) { post = JSON.parse(this.article01) } return post } } }取得したいページのdataプロパティへgetした値をセットする
this.$cookie.get('CookieにセットしたName')で値が取得できる先ほどオブジェクトでValueをセットしたので、parseしてデータを扱えるようにする。
index.vue<template> <div> <article v-if="article"> <h1>{{ article.title }}</h1> <p>{{ article.content }}</p> </article> </div> </template>あとはデータがないときには表示されないようにv-ifを設定し、
値を表示するこれでデータをCookieへsetしgetするまでができるようになります。
値は配列にしてもいいし、そのままStringでわたしてもいいし、それぞれ使いやすい形式でいれてください
まとめ
cookie周りの設定が結構簡単にできるので、導入しやすいと思います。
あとはデータの取得周りでエラーがでないように条件分岐等を使っていくといいかなと思います。
数を指定して例えば3つとかを閲覧したページ順に入れつつ更新とかもできるので、直近の閲覧履歴とかデータベースにいれなくても履歴にのこすこともできる点がいいと思った。
- 投稿日:2019-04-15T11:42:52+09:00
Nuxt.js(v2.6.x)導入時の基本的な設定:その1/3|create-nuxt-app〜各種プラグインの導入まで。
個人的な独断と偏見でまとめています。気になることやご指摘事項があればコメントいただけると嬉しいです。
最近の3プロジェクトで同じようなことを調べていたので導入時の基本的な設定をまとめていきます。本稿はv2.6.x系で動作を確認しています。Nuxt.jsは良くも悪くもアップデート頻度が高いので、違うバージョンを使ってる場合は地道に調べるか、質問でも投げてください。(体力残ってれば解答します。)
ということで本文スタート。
「Nuxt.js導入時の基本的な設定:その1/3」として、サイト制作やアプリ開発などに入る前にやっておきたい設定をまとめた。「その2/3」では、Workbox、Manifest、Meta、Icon、Ogp(OneSignalはサーバーが関わるの本稿ではやらない)などのSEOやPWAに係る部分を記載予定。「その3/3」になって、やっとコンテンツ部分に入ってコンポーネントとか。
とりあえず、以下「その1/3」の設定をすれば、中規模程度までの開発に困ることは少ない…はず。フレームワークは何かと便利なBuefy(Bulma)を利用しています。開発も活発なのでBulma良いんじゃないかな。うん、ブルマはよいものです。
さて、楽しんで学んでいきませう。
creat-nuxt-appインストール時の設定
create-nuxt-app
でインストールするときの設定は以下。? Project name amiTemplate-nuxt ? Project description Amiten's tremendous Nuxt.js project ? Use a custom server framework express ? Choose features to install Progressive Web App (PWA) Support, Linter / Formatter, Axios ? Use a custom UI framework buefy ? Use a custom test framework jest ? Choose rendering mode Universal ? Author name Amiten ? Choose a package manager npm大事なことなので最初に…
rendering modeは「Universal」を選ぶ。
rendering modeは「Universal」を選ぶ。特別な事情がなければSPAは選ばない。generate(静的書き出し)前提でも「Universal」を選ぶ。
他設定は以下
name & description & Author name
案件にあわわせて適当に。server&test framework
案件に合わせて適当に…決まっていなければ利用者の多いexpressとjestを選ぶ。(初期設定をインストール時にしてくれるので最初に入れておくと手間が減る。使わなければ後で削除すればいい。)UI framework
案件に合わせて適当に…決まっていなければBuefyを選ぶ。カスタマイズがしやすく軽量で~且つブルマと合法的に連呼できるので~おすすめ。features
案件に合わせて適当に…ESLintは基本として入れたいので、Linter / Formatterを選択。また、他決まってなければ、PWA、axiosあたりを入れておく。(初期設定をインストール時にしてくれるので最初に入れておくと手間が減る。使わなければ後で削除すればいいが、なんだかんだで使うと思う。)prettierは好みがかなり分かれるので外す。package manager
案件に合わせて適当に…でも基本はnpmで統一。yarnも優秀で個人的には好きだけど、初学者向けにnpm選んどいたほうが良い(新しい人が入ってくる度に説明や設定頼まれると意外と時間取られるため)。Nuxt.jsの初回インストールが済んだら、一度commitをしておく。
既存npmパッケージのアップデート
npm-check-updates(v3)を利用してpackage.jsonのアップデートを行う。
v2以前のバージョンをグローバルに入れている人を結構見かけるので、念の為、最新をプロジェクト用にインストールしてからアップデートをかける。npm i -D npm-check-updates
npx ncu -u
npm-check-updates(v3)からbeta版などは対象に含まれないため、もしbeta版を利用したい場合は個別で書き換える。まずは、
npm outdated
で更新確認。npm outdated 結果 ↓ Package Current Wanted Latest Location @nuxtjs/pwa 2.6.0 2.6.0 3.0.0-beta.14 amiTemplate-nuxt babel-core 7.0.0-bridge.0 7.0.0-bridge.0 6.26.3 amiTemplate-nuxt babel-eslint 8.2.6 8.2.6 10.0.1 amiTemplate-nuxt babel-jest 23.6.0 23.6.0 24.7.1 amiTemplate-nuxt jest 23.6.0 23.6.0 24.7.1 amiTemplate-nuxt今回は、既存バージョンだとエラーを吐き出す@nuxtjs/pwaを更新する。package.jsonから該当の項目を削除。
package.json{ "dependencies": { "@nuxtjs/pwa": "^2.6.0" <-これを削除 }, ... }そして、再度インストール。
npm i @nuxtjs/pwa
npm-check-updates
をかけて問題なければOK。npx ncu 結果 ↓ All dependencies match the latest package versions :)まだ続く…
きれいになったpackage.jsonに基づいて、npmパッケージをインストールを再度実行する。
はじめにpackage-lock.jsonを削除rm package-lock.json次に、node_modulesも削除
rm node_modulesnpmパッケージのインストール
npm i動作確認
npm run devOK。一度commit。
プラグインなどのnpmパッケージを追加でインストールする
レッツ、一括インストール!
↓npm i pug pug-loader pug-plain-loader node-sass sass-loader @nuxtjs/style-resources lodash csvtojson moment @nuxtjs/google-analytics @nuxtjs/google-tag-manager @nuxtjs/sitemap vee-validate vue-mq vue-scrollto vue2-perfect-scrollbar vue-rellax aosインストールしたら一旦commit。後で各種設定をしていくので、各npmパッケージの概略を確認。
フレームワーク系
カスタマイズがしやすく軽量なbuefyをベースにする。好みだとおもうけど、じゃまにならない程度に便利で
ブルマと合法的に連呼できるのでおすすめ。
- nuxt-buefy(create-nuxt-app時にインストール済み)
言語系
htmlの記述量を減らすためpug(旧jade)を採用。肥大化しがちなvueファイルをコンパクトに、学習コストもそれほど高くないのでおすすめ。また、cssはscssを採用。単純に仕様者が多く学習コストが低いため。合わせて、style-resocesをインストールしておくことで共通のvaliables(scss変数)をnuxt内で使い回せるようにしておく。
- pug
- pug
- pug-loader
- pug-plain-loader
- scss
- node-sass
- sass-loader
- @nuxtjs/style-resources
データー整形・処理系
データー整形や簡易的なデーター処理に仕込んでおくと非常に助かる3種の神器。使わなければコメントアウトしておけばいい。中規模以上の開発をしたいのであれば覚えておいて損はない。LPマスターには必要ないと思う。
- lodash
- csvtojson
- moment
@nuxtjsチーム系
axios、pwaは
create-nuxt-app
時にインストール済み。使うかどうかは案件次第。goole-analyticsやtag-managerは運用チームに確認しつつ選び、サイト制作であればsitemapぐらいは、初めから入れておきたい。
- @nuxtjs/axios(create-nuxt-app時にインストール済み)
- @nuxtjs/pwa(create-nuxt-app時にインストール済み)
- @nuxtjs/google-analytics
- @nuxtjs/google-tag-manager
- @nuxtjs/sitemap
便利系
ほぼ100%出番があるvue用plugin。説明は以下。
vee-validate
フォームのフロントバリデートに使う。buefyとの相性がよく、柔軟性もそこそこ。覚えておいて損はない。vue-mq
media-query用にプラグイン。v-ifと連携させることが多い。cssフレームワークと命名規則やbreakpointsをあわせておくと気持ちいい。ssr対応でnuxtで使いやすくしてくれているのもポイントが高い。vue-scrollto
スムーズスクロール系で一番使いやすいんじゃないだろうか。nuxtにも対応していることもポイントが高い。vue2-perfect-scrollbar
みんな大好き、perfect-scrollbarのvueプラグイン版。これ無しでレスポンシブ対応とかスクロール調整関連で地獄を見る。パララックス系
パララックスというか…スクロールに合わせたDOMのアニメーション用。個人的には多様したくないが未だに要望が多い。どうせやるなら初めから入れておき、デザイナーやクライアントと仲良く作業したい。
- vue-rellax
- aos
.gitignoreに追加しておきたい項目
基本ディフォルトで問題ない。Nuxtありがとう。ただ、更新毎にハッシュで出力されるデーターは含めたくないので、@nuxtjs/pwa及び@nuxtjs/sitemapで自動生成されるファイルを
.gitignore
ファイルに追加しておく。また、generate(静的書き出し)利用の場合はdistディレクトリ比較とかするので、dist_がつくディレクトリは含まれないようにしておく。package-lock.jsonはお好みで。
# Nuxt generate dist dist_*/ # @nuxtjs/pwa sw.js # @nuxtjs/sitemap sitemap.xml # package-lock.json package-lock.jsonついでに、除外対象を事前にcommitしちゃった場合の対応を載せておく。
git rm --cached -r . git add -A . git commit -m ".gitignore 除外指定を変更" git pushESLintの設定
ディフォルトのESLintの設定は、JavaScript Standard Styleをベースに、Vue用に一部カスタマイズされている。
↓で、修正箇所を表示して。
npm run lint↓で自動で修正してくれる。
npm run lint -- --fixNuxtありがとう。ほとんどいじることが無いのだけど、ちょっとだけ
.eslintrc.js
に追加して個人好みにしている。eslintrc.jsmodule.exports = { // ↓lodashがESLintに引っかからないようにする。 globals: { "_": true }, ... rules: { // ↓devlopmentのときだけconsole.logを許可する 'no-console': process.env.NODE_ENV === 'production' ? 2 : 0, // ↓末尾のセミコロンを許容する。 'comma-dangle': ['error', 'only-multiline'], // ↓空白行に対してwarnのみ出るようにする。 'no-multiple-empty-lines': ['warn', {max: 1}] } }IE11対応
対応したくないけど、しなければならない。そんな憂鬱なあなたに…
package.jsonにIE11対応を明示する。
package.jsonにIE11対応を明示しておく。ディフォルトでは
last 2 version
が設定されている。なので、現状は何もしなくてもいいんだけど指定しておくと3年後の自分が喜ぶ。package.json{ ... "browserslist": [ "last 2 version", "android >= 4.4", "IE 11" ], ... }以下で対応しているブラウザリストを一覧できる。どのブラウザに対応してるの?とか聞かれたら、出力されたブラウザリストを提示してあげよう。
npx browserslist
nuxt.config.jsを修正してautoprefixerを有効化。
nuxt.config.jsを修正してpostcssのプラグイン「autoprefixer」を有効することで、cssで自動にプリフィックスがつくようにする。他postcssのプラグインを使いたいのであればここに追加していくが、今回はscss利用前提にするので使わない。
nuxt.config.js{ ... build: { // ↓Build内に以下を追加 postcss: { plugins: { 'postcss-preset-env': { // Doc: https://github.com/postcss/autoprefixer autoprefixer: { grid: true } }, }, }, ... } }細かい設定に入る前にディレクトリ構成を変更・整理する
各プラグイン・コンポーネント・SEO周りの細々したこと調整する前に、いらないファイルの削除や、作業がよく行われるディレクトリをまとめていく。
README.mdファイルを削除
各ディレクトリに含まれているREADME.mdはいらない。
rm ./store/README.md ./static/README.md ./plugins/README.md ./pages/README.md ./middleware/README.md ./layouts/README.md ./components/README.md ./assets/README.mdNuxt.jsの基本作業ファイルを一つのディレクトリ(srcディレクトリ)にまとめる。
ディレクトリ数が多いと軽微なミス(ファイル格納場所間違いなど)が発生する。また、初学者は知らないディレクトリが並んでいることに拒否反応が起きることもあるので、Nuxt.jsの基本作業ファイルを一つのディレクトリ(srcディレクトリ)にまとめる。
規模が大きくなってからの対応が厳しいので、プロジェクト立ち上げ時から対応しておくと良い。nuxt.config.jsに作業ファイルの変更内容を追記
nuxt.config.jsmodule.exports = { mode: 'universal', // ↓以下追加 srcDir: 'src/', ... }作業フォルダをまとめる
mkdir src mv assets components layouts pages plugins static store middleware src/
動作確認
npm run devOK。commit。
Buefyをカスタマイズする。
Buefyは「Bulma(CSSフレームワーク)」「Material Desgin Icons」「Buefyのコンポーネントセット」の3つで成り立っている。そのうちのBulmaの部分をプロジェクトに合わせてカスタマイズできるようにする。
オプションでCSSをfalseに。
nuxt.config.jsに記載されているnuxt-buefyの設定に、
css:false
を追加してBulmaをOFFにする。nuxt.config.js{ modules: [ // Doc: https://buefy.github.io/#/documentation ['nuxt-buefy', { css: false }], ... ], }Bulmaのstyleをutilitiesとその他(core)に分ける。
utilities.scssには、Bulmaのベースとなるユーティリティ(変数等)を入れる。基本、こちらを変更・追記する(正確には、Bulmaで予め登録されている変数を更新していく。)
assets/css/bulma/utilities.scss// Bulmaのベースユーティリティ(変数等) @import "~bulma/sass/utilities/initial-variables"; @import "~bulma/sass/utilities/functions"; @import "~bulma/sass/utilities/derived-variables"; @import "~bulma/sass/utilities/animations"; @import "~bulma/sass/utilities/mixins"; @import "~bulma/sass/utilities/controls";core.scssには、カスタマイズ予定のutilities.scss+その他のsassを読み込む。基本修正しなくていい。使わないものはコメントアウトする。Buefy独自のcssを忘れがちなので必ず入れる。
assets/css/bulma/core.scss/*! bulma.io v0.7.4 | MIT License | github.com/jgthms/bulma */ // 制作したutilities.scssを読み込み @import "utilities"; // @import "~bulma/sass/base/_all" @import "~bulma/sass/base/minireset"; @import "~bulma/sass/base/generic"; @import "~bulma/sass/base/helpers"; // @import "~bulma/sass/elements/_all" @import "~bulma/sass/elements/box"; @import "~bulma/sass/elements/button"; @import "~bulma/sass/elements/container"; @import "~bulma/sass/elements/content"; @import "~bulma/sass/elements/form"; @import "~bulma/sass/elements/icon"; @import "~bulma/sass/elements/image"; @import "~bulma/sass/elements/notification"; @import "~bulma/sass/elements/progress"; @import "~bulma/sass/elements/table"; @import "~bulma/sass/elements/tag"; @import "~bulma/sass/elements/title"; @import "~bulma/sass/elements/other"; // @import "~bulma/sass/components/_all" @import "~bulma/sass/components/breadcrumb"; @import "~bulma/sass/components/card"; @import "~bulma/sass/components/dropdown"; @import "~bulma/sass/components/level"; @import "~bulma/sass/components/list"; @import "~bulma/sass/components/media"; @import "~bulma/sass/components/menu"; @import "~bulma/sass/components/message"; @import "~bulma/sass/components/modal"; @import "~bulma/sass/components/navbar"; @import "~bulma/sass/components/pagination"; @import "~bulma/sass/components/panel"; @import "~bulma/sass/components/tabs"; // @import "~bulma/sass/grid/_all" @import "~bulma/sass/grid/columns"; @import "~bulma/sass/grid/tiles"; // @import "~bulma/sass/layout/_all" @import "~bulma/sass/layout/hero"; @import "~bulma/sass/layout/section"; @import "~bulma/sass/layout/footer"; // buefy用CSS @import "~buefy/src/scss/buefy";core.scssを共通のcssとして登録する。
nuxt.config.jsにcore.scssを共通のcssとして登録する。
nuxt.config.js{ css: [ // Bulma Doc: https://bulma.io/documentation/ '~/assets/css/bulma/core.scss', ], }utilities.scssの変数やMixinをコンポーネントでも使えるようにする。
nuxt.config.jsにひと手間加えて、
@nuxtjs/style-resources
モジュール経由で、utilities.scssの変数やMixinをコンポーネントでも使えるようにする。nuxt.config.js{ // ↓modules内で@nuxtjs/style-resourcesを読み込む。 modules: [ ... // Doc: https://github.com/nuxt-community/style-resources-module '@nuxtjs/style-resources' ], // styleResources内でutilities.scssを読み込む。 styleResources: { sass: [ '~/assets/css/bulma/utilities.scss', ] }, }これでカスタマイズの準備完了。まだ準備段階か…とくじけない。
utilities.scssを修正
Buety公式の「layout&element」に書いてあるものは、Bulmaのドキュメントを参照。それ以外はBuetyのドキュメントを参照。
修正例を載せておく。
assets/css/bulma/utilities.scss// Bulmaのベースユーティリティ(変数等) @import "~bulma/sass/utilities/initial-variables"; @import "~bulma/sass/utilities/functions"; @import "~bulma/sass/utilities/derived-variables"; @import "~bulma/sass/utilities/animations"; @import "~bulma/sass/utilities/mixins"; @import "~bulma/sass/utilities/controls"; // この下に上書きしたいベースユーティリティ(変数等)を記載していく。 // Typography ------------------------- // ↓追加フォントなどの外部CSSは、core.scssかnuxt.config.jsで読み込む // YakuHanMP: https://cdn.jsdelivr.net/npm/yakuhanjp@3.0.0/dist/css/yakuhanmp.min.css // Noto Serif JP: https://fonts.googleapis.com/css?family=Noto+Serif+JP:400,700&subset=japanese // 日本語用設定。sans-serifしかないので、serif書体も追加しておく。 $family-sans-serif: sans-serif; $family-serif: "YakuHanMP", "Noto Serif JP", "Yu Mincho", "YuMincho", serif; // ベースのfont-familyを登録 $family-primary: $family-sans-serif; $family-secondary: $family-serif; //フォントサイズを登録 ------------------------- // 16pxを基準にremで調整(Doc:http://www.standardista.com/px-to-rem-conversion-if-root-font-size-is-16px/) $body-size: 16px !default; // 日本語でよく使われるサイズに変更。 $size-1: 2.5rem; // 40px $size-2: 2rem; // 32px $size-3: 1.5rem; // 24px $size-4: 1.25rem; //20px $size-5: 1rem; // 16px $size-6: 0.875rem; // 14px $size-7: 0.75rem; // 12px // 基本のフォントサイズを設定 $size-small: $size-7; $size-normal: $size-5; $size-medium: $size-4; $size-large: $size-3; // line-heightが1.5なので、日本語に合わせて広めに変更 $body-line-height: 1.6; // brakepoints ------------------------------- // 個人的に好きなbreakpoints $tablet: 667px; //LG G3など、5.5incタイプの大型スマートフォン $desktop: 970px; //ipad縦 ipad横 $widescreen: 1360px; //MackBook 12/Pro 13/Surface2/3/Pro/Nexus10 $widescreen-enabled: true !default; $fullhd: 1344px + (2 * $gap) !default; $fullhd-enabled: true !default; // breakpointsメモ // @include mobile {} //until 666px // @include tablet {} //from 667px // @include tablet-only {} //from 667px and until 969px // @include touch {} //until 969px // @include desktop {} //from 970px // @include desktop-only {} //from 970px and until 1359px // @include widescreen {} //from 1360px // @include widescreen-only {} //from 1360px and until 1407px // @include fullhd {} /from 1408px // add Mixin @mixin wrapper { box-sizing: border-box; position: relative; width: 100%; padding-right: $bm * 2; padding-left: $bm * 2; @include tablet { padding-right: $bm * 3; padding-left: $bm * 3; } @include desktop { margin-right: auto; margin-left: auto; max-width: $widescreen; padding-right: $bm * 4; padding-left: $bm * 4; } @include fullhd { max-width: $fullhd - 2 * $gap; width: $fullhd - 2 * $gap; } } // color-variable ------------------------------------ // secondary、thirdlyを追加して、カスタムカラーの幅を増やしています。 // 明るい色・暗い色 $light: #efefef; $light-lighten: lighten($light, 15%); $light-darken: darken($light, 5%); $dark: #3C3C48; $dark-lighten: lighten($dark, 10%); $dark-darken: darken($dark, 10%); // primary (ブランドカラー) $primary: #0A428C; $primary-invert: findColorInvert($primary); $primary-lighten: #205aa2; $primary-darken: darken($primary, 5%); // secondary(ポイントカラー ※カスタム) $secondary: #FA4988; $secondary-invert: findColorInvert($secondary); $secondary-lighten: lighten($secondary, 15%); $secondary-darken: darken($secondary, 5%); // thirdary(※カスタム) $thirdly: #DEE5EE; $thirdly-invert: findColorInvert($thirdly); $thirdly-lighten: #EBF0F4; $thirdly-darken: darken($thirdly, 5%); //snsカラー(※カスタム) $line: #1dcd00; $line-invert: findColorInvert($line); $facebook: #3B5998; $facebook-invert: findColorInvert($facebook); $twitter: #55acee; $twitter-invert: findColorInvert($twitter); $youtube: #EC3324; $youtube-invert: findColorInvert($youtube); $instagram: #CDA98A; $instagram-invert: findColorInvert($instagram); // 上記カラーを使えるようにする $custom-colors: ( "secondary": ($secondary, $secondary-invert), "thirdly": ($thirdly, $thirdly-invert), "line": ($line, $line-invert), "facebook": ($facebook, $facebook-invert), "twitter": ($twitter, $twitter-invert), "youtube": ($youtube, $youtube-invert), "instagram": ($instagram, $instagram-invert) ); $colors: mergeColorMaps(( "white": ($white, $black), "black": ($black, $white), "light": ($light, $light-invert), "dark": ($dark, $dark-invert), "primary": ($primary, $primary-invert), "link": ($link, $link-invert), "info": ($info, $info-invert), "success": ($success, $success-invert), "warning": ($warning, $warning-invert), "danger": ($danger, $danger-invert) ),$custom-colors); // グラデーション(※カスタマイズ) @mixin gradient { background: $primary; background: linear-gradient(to right, #35B8CB 10%,$primary 90%); } // color-text ------------------------------------ // text color $text: #666666; $text-strong: $secondary; // Links $link: $primary; $link-hover: $primary-darken; $link-invert: $primary-invert; $link-focus-border: $primary; // etc ------------------------- // その他自由に登録していく。 // base margin(※オリジナル) $bm: 8px; // radius $radius-small: 2px; $radius: 3px; $radius-large: 8px; // radiusを使えるようにする $control-radius: $radius; $control-radius-small: $radius-small;これだけカスタマイズできれば困らないはず。もしダメだったら素直に自作して追加する。
pluginの設定をしていく。
プラグインの設定は大きく分けて二通りある。
- nuxt.config.jsに直接記載していく。
- pluginsにファイルを作成して、nuxt.config.jsに読み込む。
1のパターンは既出していてnuxt-buefyや@nuxtjs/style-resourcesが該当する。@nuxtjsがついてればだいたい該当する。2のパターンは…まぁあれだ、これからやるので覚えてほしい。vue用プラグインが利用可能になるので制作の幅が広がる。
また、lodashやvue-scrolltoなんかは代表例なんだけど、1でも2でもどちらでもOKな場合もある。自分で調べて好きな方にすればいい。
確認までに上記を意識してもう一度設定していくプラグインを書き出す。
細かいオプションの設定は省いて基本の使い方のみ掲載する。
1のパターン
- @nuxtjs/axios
- @nuxtjs/pwa
- @nuxtjs/google-analytics
- @nuxtjs/google-tag-manager
- @nuxtjs/sitemap
- lodash
2のパターン
- vue-scrollto
- vue-mq
- vee-validate
- vue2-perfect-scrollbar
- vue-rellax
- aos
- csvtojson
- moment
@nuxtjs/axios
何もしなくて良い。create-nuxt-app時にnuxt.config.jsに組み込まれている。もしかしたら近い将来nuxt/httpに置き換わるかもしれないということだけ、心のノートにメモっておく。
nuxt.config.jsに記載したオプションを引き継いで使う場合は、
$axios
を使う。オプション詳細は公式サイトを参照のことasyncDataasync asyncData({ $axios }) { const ip = await $axios.$get('http://icanhazip.com') return { ip } }methods/created/mounted/etcmethods: { async fetchSomething() { const ip = await this.$axios.$get('http://icanhazip.com') this.ip = ip } }@nuxtjs/pwa
何もしなくても最低限のことをやってくれる。create-nuxt-app時にnuxt.config.jsに組み込まれている…が、PWAは複数の技術の集まりであることを忘れてはならない。
PWA対応のアプリだよ!と言い切るには、nuxt.config.jsにオプションを丁寧に設定し、PWA用の画像を用意し、PWA時に使いやすいサイト・アプリ設計をしなければならないと心のノートにメモっておく。しかし、@nuxtjs/pwaが入れておくだけでもキャッシュ戦略が大きく楽になるので「わざと入れない」という選択肢はない。
オプションは公式サイトを参照のこと。
@nuxtjs/google-analytics or @nuxtjs/google-tag-manager
GAとGTMどっちを使うか、または両方使うかは運用チームに確認すること。すり合わせ、データー取得テストを事前にしておかないと「とれない!どうなってんだ!」とリリース後の疲れた身体には少々辛いお言葉が飛び交う。
事前に確認しろといっただろ使い方は各GitHub参照のこと。
nuxt.config.jsexport default { modules: [ ['@nuxtjs/google-analytics', { id: 'UA-XXXXXX-X' }], // Doc: https://github.com/nuxt-community/modules/tree/master/packages/google-tag-manager ['@nuxtjs/google-tag-manager', { id: 'GTM-XXXXXXX' }], ... ], }@nuxtjs/sitemap
Nuxtは巷にあるsitemapジェネレーターでxmlを生成できないので、事前に仕込んでおく。google検索の初回反映に大きく影響が出るので、オプションも丁寧に設定しておき。リリース後にサイトマップの送信を必ずやるぞ!と心のノートにメモっておく。
オプションはGitHubを参照のこと。
nuxt.config.jsexport default { modules: [ ... // Doc: https://github.com/nuxt-community/sitemap-module '@nuxtjs/sitemap' ], sitemap: { path: '/sitemap.xml', hostname: 'https://example.com', cacheTime: 1000 * 60 * 15, gzip: true, // exclude: [ // 含めたくないディレクトリがあれば追加 // ], // routes: [ // 自動生成ページがあれば追加 // ] }, }lodash
10kとか軽くするために必要な関数のみimportする風習があるけど、それ以前にやることがいっぱいある。とりあえず全入れでいいと思うのは自分だけ?
Nuxt公式サイトのWebpack プラグインを追加するには?を参考にして導入する。webpackをimportすること、既出のESlintで
_
を許可すること、の2点を忘れずに。nuxt.config.jsconst webpack = require('webpack') export default { ... build: { plugins: [ new webpack.ProvidePlugin({ '_': 'lodash' }) ], } }vue-scrollto
ここから、pluginsの中にファイルを作って、nuxt.config.js内のpluginに登録していく。まずは、vue用プラグインで感覚を覚える。ちなみにvue-scrolltoはnuxtに正式に対応しているので、nuxt.config.jsにモジュールとしてオプションを記載することもできる。素晴らしい。
が、今回は、plaugins経由で読み込んで見る。
ファイル作成
touch src/plugins/vue-scrollto.jsvue-scrollto.jsに必要なこと記載する。
plugins/vue-scrollto.jsimport Vue from 'vue' import VueScrollTo from 'vue-scrollto' Vue.use(VueScrollTo, { // ↓以下オプション Doc: https://vue-scrollto.netlify.com/ duration: 700, easing: [0, 0, 0.1, 1], offset: -150 })nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue-scrollto', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div nuxt-link(to="" v-scroll-to="{ el: '#test' }") click! .test#test スクロールした! </template> <style lang="scss" scoped> .test { margin-top: 200vh; } </style>OK。
vue-mq
ブレークポイントを設定してくれるナウいやつ。今回は、Bulmaで変更したブレークポイントと合わせて修正をしてみる。だけじゃつまらないので、mixinを追加して使い勝手を良くしてみる。
ファイル作成
touch src/plugins/vue-mq.jsvue-mq.jsに必要なこと記載する。
plugins/vue-mq.jsimport Vue from 'vue' import VueMq from 'vue-mq' Vue.use(VueMq, { breakpoints: { mobile: 666, tablet: 969, desktop: Infinity, // fullhd: Infinity, }, defaultBreakpoint: 'mobile' }) Vue.mixin({ data() { return { mobile: false, // until 666px tabletOnly: false, // from 667px and until 969px tablet: false, // from 667px desktop: false, // from 970px } }, watch: { $mq() { this.mobile = this.$mq === 'mobile' this.tabletOnly = this.$mq === 'tablet' this.tablet = this.$mq === 'tablet' || this.$mq === 'desktop' this.desktop = this.$mq === 'desktop' } }, mounted() { this.mobile = this.$mq === 'mobile' this.tabletOnly = this.$mq === 'tablet' this.tablet = this.$mq === 'tablet' || this.$mq === 'desktop' this.desktop = this.$mq === 'desktop' } })nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue-mq', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div div {{ $mq }}サイズだよ div(v-if="mobile") mobileサイズだよ! div(v-else) 他のサイズだよ </template>OK。
vee-validate
buefyに公式で対応している上に、日本語対応も簡単なのでおすすめのフロントバリデート。自作する余力があるならバリデートの制度を高めることに時間を使ったほうがいいと思う。
ファイル作成
touch src/plugins/vee-validate.jsvee-validate.jsに必要なこと記載する。合わせて日本語対応をする。
plugins/vee-validate.jsimport Vue from 'vue' import VeeValidate from 'vee-validate' import VeeValidateJa from 'vee-validate/dist/locale/ja' // 「VeeValidate」標準の日本語ローカル情報を使用しています // vee-validateの日本語 Vue.use(VeeValidate, { locale: 'ja', dictionary: { ja: VeeValidateJa }, // events: '' })nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vee-validate', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div.content form table.table tbody tr th Email td b-field( :type="{'is-danger': errors.has('email')}" :message="errors.first('email')" ) b-input(v-model="form.email.value" placeholder="例) @kurilon.co.jp" name="email" v-validate="'required|email'" :data-vv-as="'Email'") button.button(@click.prevent="validateBeforeSubmit()") 送信! </template> <script> export default { data() { return { form: { email: { name: 'Email', value: null, }, } } }, methods: { validateBeforeSubmit() { this.$validator.validateAll().then((result) => { if (result) { alert('OKだよ!') return } this.$toast.open({ message: '赤字で記載された項目を確認してください。', type: 'is-danger', position: 'is-top' }) }) }, } } </script>それっぽいものが悩まず作れる…OK。
vue2-perfect-scrollbar
scrollをコントロールしたいときに必ずお世話になるperfect-scrollbarのvueプラグイン。自作しようとすると実機テストが必須になるところ、だいたいカバーしてくれるナウいやつ。
ファイル作成
touch src/plugins/vue2-perfect-scrollbar.jsvue2-perfect-scrollbar.jsに必要なこと記載する。今回はCSSも追加する。
plugins/vue2-perfect-scrollbar.jsimport Vue from 'vue' import PerfectScrollbar from 'vue2-perfect-scrollbar' import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css' Vue.use(PerfectScrollbar)nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue2-perfect-scrollbar', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div .test-area perfect-scrollbar div.test つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)つれづれなるまゝに、日暮らし(100文字) </template> <style lang="scss" scoped> .test-area { width: 300px; } .test { width: 600px; background-color: $secondary; } </style>OK。
vue-rellax
vue-rellaxは、簡易的なパララックスを表現してくれるrellax.jsのvueプラグイン。工夫をすればサイト全体の雰囲気をコントロールすることも可能なので、デザイナーやクライアントとのやり取りをスムーズにするためにも、はじめから導入しておく。
ファイル作成
touch src/plugins/vue-rellax.jsvue-rellax.jsに必要なこと記載する。
plugins/vue-rellax.jsimport Vue from 'vue' import VueRellax from 'vue-rellax' Vue.use(VueRellax)nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue-rellax', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div .test .test-text div(v-rellax) ぱらららっくすぅ </template> <style lang="scss" scoped> .test { height: 200vh; margin-top: 50vh; } .test-text { width: 100%; background-color: $light; } </style>OK。
aos
aosはスクロールアクション系プラグイン。見出しなどのワンポイント導入にはもってこいなので、初めから入れておく。
ファイル作成
touch src/plugins/aos.jsaos.jsに必要なこと記載する。ESLintに引っかかるため、eslintの設定を一部無視する記述を追加しておく。また、ページ推移した際に一度AOSを解除する記述をMixinで追加しておくと幸せになれる。
plugins/aos.jsimport Vue from 'vue' import AOS from 'aos' import 'aos/dist/aos.css' // ↓eslintの設定を一部無視 /* eslint new-cap: 0 */ export default ({ app }) => { app.AOS = new AOS.init({ // disable: 'phone' duration: 800 }) } Vue.mixin({ watch: { '$route.path': function () { setTimeout(() => { AOS.refresh() }, 500) } } })nuxt.config.jsのplugins内に登録…だけど、aosはSSRに対応していないためオプションを追加する。
nuxt.config.jsmodule.exports = { plugins: [ { src: '~/plugins/aos', ssr: false }, ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div h1(data-aos="zoom-in") 見出し1 .test h2(data-aos="zoom-in") 見出し2 </template> <style lang="scss" scoped> .test { padding-top: 120vh; padding-bottom: 50vh; } </style>OK。
csvtojson
最後は、csvtojsonをベースとした関数をどこでも使えるようにしてみる。プラグインを自作できるとやっとエンジニアっぽい仕事ができてる感があって楽しい。
ファイル作成
touch src/plugins/csvtojson.jsinjectを使って、
$csvToJson
でどこでも関数を使えるようにする。plugins/csvtojson.jsimport csvtojson from 'csvtojson' export default ({ app }, inject) => { inject('csvToJson', (csv) => { // ↓以下処理はお好みで const nl2br = (str) => { return str.replace(/\n/g, '<br>') } return new Promise((resolve) => { csvtojson() .fromString(csv) .then((data) => { _.each(data, (row, index) => { _.each(row, (v, key) => { data[index][key] = nl2br(v) }) }) resolve(data) }) }) }) }nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/csvtojson', ... ], }動作テスト1(asyncData内で描画前に処理)
src/pages/index.vue<template lang="pug"> div pre {{ json }} </template> <script> export default { async asyncData({ app }) { const csv = await app.$axios.$get('test.csv') const json = await app.$csvToJson(csv) return { json: json } }, } </script>動作テスト2(mounted内で描画後に処理)
src/pages/index.vue<template lang="pug"> div pre {{ json }} </template> <script> export default { data() { return { json: null } }, async mounted() { const csv = await this.$axios.$get('test.csv') const json = await this.$csvToJson(csv) this.json = json }, } </script>axiosと同じように書ける。こういうの気持ちいい。OK。
moment
あれ?momentは??
何かとエラーの起きやすい日付関連をよしなにしてくれるmoment。頻出は多くないので、pages内で利用する方法を記載しておく。
src/pages/index.vue<template lang="pug"> div p {{ day }} </template> <script> import moment from 'moment' export default { data() { return { day: null } }, mounted() { const date = new Date() const day = moment(date).format('YYYY.MM.DD') this.day = day } } </script>このように、コンポーネントに個別で呼び出しても使える。そいうことも覚えておくとよい。
これで、やっと基本的な設定完了。「その2/3」では、Workbox、Manifest、Meta、Icon、Ogp(OneSignalはサーバーが関わるの本稿ではやらない)などのSEOやPWAに係る部分を記載予定。「その3/3」になって、やっとコンテンツ部分に入ってコンポーネントとか。
さて、時間が取れるだろうか…。
- 投稿日:2019-04-15T11:42:52+09:00
Nuxt.js(v2.6.x)やっておきたい設定:その1/3|create-nuxt-app〜各種プラグインの導入まで。
気になることやご指摘事項があればコメントいただけると嬉しいです。
最近の3プロジェクトで同じようなことを調べていたので導入時の基本的な設定をまとめていきます。本稿はv2.6.x系で動作を確認しています。Nuxt.jsは良くも悪くもアップデート頻度が高いので、違うバージョンを使ってる場合は地道に調べるか、質問でも投げてください。(体力残ってれば解答します。)
ということで本文スタート。
「Nuxt.jsやっておきたい設定:その1/3」として、サイト制作やアプリ開発などに入る前にやっておきたい設定をまとめた。「その2/3」では、Workbox、Manifest、Meta、Icon、Ogp(OneSignalはサーバーが関わるの本稿ではやらない)などのSEOやPWAに係る部分を記載。「その3/3」になって、やっとコンテンツ部分に入ってコンポーネントとか。
とりあえず、以下「その1/3」の設定をすれば、中規模程度までの開発に困ることは少ない…はず。フレームワークは何かと便利なBuefy(Bulma)を利用しています。開発も活発なのでBulma良いんじゃないかな。うん、ブルマはよいものです。
さて、楽しんで学んでいきませう。
creat-nuxt-appインストール時の設定
create-nuxt-app
でインストールするときの設定は以下。? Project name amiTemplate-nuxt ? Project description Amiten's tremendous Nuxt.js project ? Use a custom server framework express ? Choose features to install Progressive Web App (PWA) Support, Linter / Formatter, Axios ? Use a custom UI framework buefy ? Use a custom test framework jest ? Choose rendering mode Universal ? Author name Amiten ? Choose a package manager npm大事なことなので最初に…
rendering modeは「Universal」を選ぶ。
rendering modeは「Universal」を選ぶ。特別な事情がなければSPAは選ばない。generate(静的書き出し)前提でも「Universal」を選ぶ。
他設定
name & description & Author name
案件にあわわせて適当に。server&test framework
案件に合わせて適当に…決まっていなければ利用者の多いexpressとjestを選ぶ。(初期設定をインストール時にしてくれるので最初に入れておくと手間が減る。使わなければ後で削除すればいい。)UI framework
案件に合わせて適当に…決まっていなければBuefyを選ぶ。カスタマイズがしやすく軽量で且つブルマと合法的に連呼できるのでおすすめ。features
案件に合わせて適当に…ESLintは基本として入れたいので、Linter / Formatterを選択。また、他決まってなければ、PWA、axiosあたりを入れておく。(初期設定をインストール時にしてくれるので最初に入れておくと手間が減る。使わなければ後で削除すればいいが、なんだかんだで使うと思う。)prettierは好みがかなり分かれるので外す。package manager
案件に合わせて適当に…でも基本はnpmで統一。yarnも優秀で個人的には好きだけど、初学者向けにnpm選んどいたほうが良い(新しい人が入ってくる度に説明や設定頼まれると意外と時間取られるため)。Nuxt.jsの初回インストールが済んだら、一度commitをしておく。
既存npmパッケージのアップデート
npm-check-updates(v3)を利用してpackage.jsonのアップデートを行う。
v2以前のバージョンをグローバルに入れている人を結構見かけるので、念の為、最新をプロジェクト用にインストールしてからアップデートをかける。npm i -D npm-check-updates
npx ncu -u
npm-check-updates(v3)からbeta版などは対象に含まれないため、もしbeta版を利用したい場合は個別で書き換える。まずは、
npm outdated
で更新確認。npm outdated 結果 ↓ Package Current Wanted Latest Location @nuxtjs/pwa 2.6.0 2.6.0 3.0.0-beta.14 amiTemplate-nuxt babel-core 7.0.0-bridge.0 7.0.0-bridge.0 6.26.3 amiTemplate-nuxt babel-eslint 8.2.6 8.2.6 10.0.1 amiTemplate-nuxt babel-jest 23.6.0 23.6.0 24.7.1 amiTemplate-nuxt jest 23.6.0 23.6.0 24.7.1 amiTemplate-nuxt今回は、既存バージョンだとエラーを吐き出す@nuxtjs/pwaを更新する。package.jsonから該当の項目を削除。
package.json{ "dependencies": { "@nuxtjs/pwa": "^2.6.0" <-これを削除 }, ... }そして、再度インストール。
npm i @nuxtjs/pwa
npm-check-updates
をかけて問題なければOK。npx ncu 結果 ↓ All dependencies match the latest package versions :)まだ続く…
きれいになったpackage.jsonに基づいて、npmパッケージをインストールを再度実行する。
はじめにpackage-lock.jsonを削除rm package-lock.json次に、node_modulesも削除
rm node_modulesnpmパッケージのインストール
npm i動作確認
npm run devOK。一度commit。
プラグインなどのnpmパッケージを追加でインストールする
レッツ、一括インストール!
↓npm i pug pug-loader pug-plain-loader node-sass sass-loader @nuxtjs/style-resources lodash csvtojson moment @nuxtjs/google-analytics @nuxtjs/google-tag-manager @nuxtjs/sitemap vee-validate vue-mq vue-scrollto vue2-perfect-scrollbar vue-rellax aosインストールしたら一旦commit。後で各種設定をしていくので、各npmパッケージの概略を確認。
フレームワーク系
カスタマイズがしやすく軽量なbuefyをベースにする。好みだとおもうけど、じゃまにならない程度に便利で
ブルマと合法的に連呼できるのでおすすめ。
- nuxt-buefy(create-nuxt-app時にインストール済み)
言語系
htmlの記述量を減らすためpug(旧jade)を採用。肥大化しがちなvueファイルをコンパクトに、学習コストもそれほど高くないのでおすすめ。また、cssはscssを採用。単純に仕様者が多く学習コストが低いため。合わせて、style-resocesをインストールしておくことで共通のvaliables(scss変数)をnuxt内で使い回せるようにしておく。
- pug
- pug
- pug-loader
- pug-plain-loader
- scss
- node-sass
- sass-loader
- @nuxtjs/style-resources
データー整形・処理系
データー整形や簡易的なデーター処理に仕込んでおくと非常に助かる3種の神器。使わなければコメントアウトしておけばいい。中規模以上の開発をしたいのであれば覚えておいて損はない。LPマスターには必要ないと思う。
- lodash
- csvtojson
- moment
@nuxtjsチーム系
axios、pwaは
create-nuxt-app
時にインストール済み。使うかどうかは案件次第。goole-analyticsやtag-managerは運用チームに確認しつつ選び、サイト制作であればsitemapぐらいは、初めから入れておきたい。
- @nuxtjs/axios(create-nuxt-app時にインストール済み)
- @nuxtjs/pwa(create-nuxt-app時にインストール済み)
- @nuxtjs/google-analytics
- @nuxtjs/google-tag-manager
- @nuxtjs/sitemap
便利系
ほぼ100%出番があるvue用plugin。説明は以下。
vee-validate
フォームのフロントバリデートに使う。buefyとの相性がよく、柔軟性もそこそこ。覚えておいて損はない。vue-mq
media-query用にプラグイン。v-ifと連携させることが多い。cssフレームワークと命名規則やbreakpointsをあわせておくと気持ちいい。ssr対応でnuxtで使いやすくしてくれているのもポイントが高い。vue-scrollto
スムーズスクロール系で一番使いやすいんじゃないだろうか。nuxtにも対応していることもポイントが高い。vue2-perfect-scrollbar
みんな大好き、perfect-scrollbarのvueプラグイン版。これ無しでレスポンシブ対応とかスクロール調整関連で地獄を見る。パララックス系
パララックスというか…スクロールに合わせたDOMのアニメーション用。個人的には多様したくないが未だに要望が多い。どうせやるなら初めから入れておき、デザイナーやクライアントと仲良く作業したい。
- vue-rellax
- aos
.gitignoreに追加しておきたい項目
基本ディフォルトで問題ない。Nuxtありがとう。ただ、更新毎にハッシュで出力されるデーターは含めたくないので、@nuxtjs/pwa及び@nuxtjs/sitemapで自動生成されるファイルを
.gitignore
ファイルに追加しておく。また、generate(静的書き出し)利用の場合はdistディレクトリ比較とかするので、dist_がつくディレクトリは含まれないようにしておく。package-lock.jsonはお好みで。
# Nuxt generate dist dist_*/ # Service worker sw.* # @nuxtjs/sitemap sitemap.xml sitemap.xml.gz # package-lock.json package-lock.jsonついでに、除外対象を事前にcommitしちゃった場合の対応を載せておく。
git rm --cached -r . git add -A . git commit -m ".gitignore 除外指定を変更" git pushESLintの設定
ディフォルトのESLintの設定は、JavaScript Standard Styleをベースに、Vue用に一部カスタマイズされている。
↓で、修正箇所を表示して。
npm run lint↓で自動で修正してくれる。
npm run lint -- --fixNuxtありがとう。ほとんどいじることが無いのだけど、ちょっとだけ
.eslintrc.js
に追加して個人好みにしている。eslintrc.jsmodule.exports = { // ↓lodashがESLintに引っかからないようにする。 globals: { "_": true }, ... rules: { // ↓devlopmentのときだけconsole.logを許可する 'no-console': process.env.NODE_ENV === 'production' ? 2 : 0, // ↓末尾のセミコロンを許容する。 'comma-dangle': ['error', 'only-multiline'], // ↓空白行に対してwarnのみ出るようにする。 'no-multiple-empty-lines': ['warn', {max: 1}] } }IE11対応
対応したくないけど、しなければならない。そんな憂鬱なあなたに…
package.jsonにIE11対応を明示する。
package.jsonにIE11対応を明示しておく。ディフォルトでは
last 2 version
が設定されている。なので、現状は何もしなくてもいいんだけど指定しておくと3年後の自分が喜ぶ。package.json{ ... "browserslist": [ "last 2 version", "android >= 4.4", "IE 11" ], ... }以下で対応しているブラウザリストを一覧できる。どのブラウザに対応してるの?とか聞かれたら、出力されたブラウザリストを提示してあげよう。
npx browserslist
nuxt.config.jsを修正してautoprefixerを有効化。
nuxt.config.jsを修正してpostcssのプラグイン「autoprefixer」を有効することで、cssで自動にプリフィックスがつくようにする。他postcssのプラグインを使いたいのであればここに追加していくが、今回はscss利用前提にするので使わない。
nuxt.config.js{ ... build: { // ↓Build内に以下を追加 postcss: { plugins: { 'postcss-preset-env': { // Doc: https://github.com/postcss/autoprefixer autoprefixer: { grid: true } }, }, }, ... } }細かい設定に入る前にディレクトリ構成を変更・整理する
各プラグイン・コンポーネント・SEO周りの細々したこと調整する前に、いらないファイルの削除や、作業がよく行われるディレクトリをまとめていく。
README.mdファイルを削除
各ディレクトリに含まれているREADME.mdはいらない。
rm ./store/README.md ./static/README.md ./plugins/README.md ./pages/README.md ./middleware/README.md ./layouts/README.md ./components/README.md ./assets/README.mdNuxt.jsの基本作業ファイルを一つのディレクトリ(srcディレクトリ)にまとめる。
ディレクトリ数が多いと軽微なミス(ファイル格納場所間違いなど)が発生する。また、初学者は知らないディレクトリが並んでいることに拒否反応が起きることもあるので、Nuxt.jsの基本作業ファイルを一つのディレクトリ(srcディレクトリ)にまとめる。
規模が大きくなってからの対応が厳しいので、プロジェクト立ち上げ時から対応しておくと良い。nuxt.config.jsに作業ファイルの変更内容を追記
nuxt.config.jsmodule.exports = { mode: 'universal', // ↓以下追加 srcDir: 'src/', ... }作業フォルダをまとめる
mkdir src mv assets components layouts pages plugins static store middleware src/
動作確認
npm run devOK。commit。
Buefyをカスタマイズする。
Buefyは「Bulma(CSSフレームワーク)」「Material Desgin Icons」「Buefyのコンポーネントセット」の3つで成り立っている。そのうちのBulmaの部分をプロジェクトに合わせてカスタマイズできるようにする。
オプションでCSSをfalseに。
nuxt.config.jsに記載されているnuxt-buefyの設定に、
css:false
を追加してBulmaをOFFにする。nuxt.config.js{ modules: [ // Doc: https://buefy.github.io/#/documentation ['nuxt-buefy', { css: false }], ... ], }Bulmaのstyleをutilitiesとその他(core)に分ける。
utilities.scssには、Bulmaのベースとなるユーティリティ(変数等)を入れる。基本、こちらを変更・追記する(正確には、Bulmaで予め登録されている変数を更新していく。)
assets/css/bulma/utilities.scss// Bulmaのベースユーティリティ(変数等) @import "~bulma/sass/utilities/initial-variables"; @import "~bulma/sass/utilities/functions"; @import "~bulma/sass/utilities/derived-variables"; @import "~bulma/sass/utilities/animations"; @import "~bulma/sass/utilities/mixins"; @import "~bulma/sass/utilities/controls";core.scssには、カスタマイズ予定のutilities.scss+その他のsassを読み込む。基本修正しなくていい。使わないものはコメントアウトする。Buefy独自のcssを忘れがちなので必ず入れる。
assets/css/bulma/core.scss/*! bulma.io v0.7.4 | MIT License | github.com/jgthms/bulma */ // 制作したutilities.scssを読み込み @import "utilities"; // @import "~bulma/sass/base/_all" @import "~bulma/sass/base/minireset"; @import "~bulma/sass/base/generic"; @import "~bulma/sass/base/helpers"; // @import "~bulma/sass/elements/_all" @import "~bulma/sass/elements/box"; @import "~bulma/sass/elements/button"; @import "~bulma/sass/elements/container"; @import "~bulma/sass/elements/content"; @import "~bulma/sass/elements/form"; @import "~bulma/sass/elements/icon"; @import "~bulma/sass/elements/image"; @import "~bulma/sass/elements/notification"; @import "~bulma/sass/elements/progress"; @import "~bulma/sass/elements/table"; @import "~bulma/sass/elements/tag"; @import "~bulma/sass/elements/title"; @import "~bulma/sass/elements/other"; // @import "~bulma/sass/components/_all" @import "~bulma/sass/components/breadcrumb"; @import "~bulma/sass/components/card"; @import "~bulma/sass/components/dropdown"; @import "~bulma/sass/components/level"; @import "~bulma/sass/components/list"; @import "~bulma/sass/components/media"; @import "~bulma/sass/components/menu"; @import "~bulma/sass/components/message"; @import "~bulma/sass/components/modal"; @import "~bulma/sass/components/navbar"; @import "~bulma/sass/components/pagination"; @import "~bulma/sass/components/panel"; @import "~bulma/sass/components/tabs"; // @import "~bulma/sass/grid/_all" @import "~bulma/sass/grid/columns"; @import "~bulma/sass/grid/tiles"; // @import "~bulma/sass/layout/_all" @import "~bulma/sass/layout/hero"; @import "~bulma/sass/layout/section"; @import "~bulma/sass/layout/footer"; // buefy用CSS @import "~buefy/src/scss/buefy";core.scssを共通のcssとして登録する。
nuxt.config.jsにcore.scssを共通のcssとして登録する。
nuxt.config.js{ css: [ // Bulma Doc: https://bulma.io/documentation/ '~/assets/css/bulma/core.scss', ], }utilities.scssの変数やMixinをコンポーネントでも使えるようにする。
nuxt.config.jsにひと手間加えて、
@nuxtjs/style-resources
モジュール経由で、utilities.scssの変数やMixinをコンポーネントでも使えるようにする。nuxt.config.js{ // ↓modules内で@nuxtjs/style-resourcesを読み込む。 modules: [ ... // Doc: https://github.com/nuxt-community/style-resources-module '@nuxtjs/style-resources' ], // styleResources内でutilities.scssを読み込む。 styleResources: { sass: [ '~/assets/css/bulma/utilities.scss', ] }, }これでカスタマイズの準備完了。まだ準備段階か…とくじけない。
utilities.scssを修正
Buety公式の「layout&element」に書いてあるものは、Bulmaのドキュメントを参照。それ以外はBuetyのドキュメントを参照。
修正例を載せておく。
assets/css/bulma/utilities.scss// Bulmaのベースユーティリティ(変数等) @import "~bulma/sass/utilities/initial-variables"; @import "~bulma/sass/utilities/functions"; @import "~bulma/sass/utilities/derived-variables"; @import "~bulma/sass/utilities/animations"; @import "~bulma/sass/utilities/mixins"; @import "~bulma/sass/utilities/controls"; // この下に上書きしたいベースユーティリティ(変数等)を記載していく。 // Typography ------------------------- // ↓追加フォントなどの外部CSSは、core.scssかnuxt.config.jsで読み込む // YakuHanMP: https://cdn.jsdelivr.net/npm/yakuhanjp@3.0.0/dist/css/yakuhanmp.min.css // Noto Serif JP: https://fonts.googleapis.com/css?family=Noto+Serif+JP:400,700&subset=japanese // 日本語用設定。sans-serifしかないので、serif書体も追加しておく。 $family-sans-serif: sans-serif; $family-serif: "YakuHanMP", "Noto Serif JP", "Yu Mincho", "YuMincho", serif; // ベースのfont-familyを登録 $family-primary: $family-sans-serif; $family-secondary: $family-serif; //フォントサイズを登録 ------------------------- // 16pxを基準にremで調整(Doc:http://www.standardista.com/px-to-rem-conversion-if-root-font-size-is-16px/) $body-size: 16px !default; // 日本語でよく使われるサイズに変更。 $size-1: 2.5rem; // 40px $size-2: 2rem; // 32px $size-3: 1.5rem; // 24px $size-4: 1.25rem; //20px $size-5: 1rem; // 16px $size-6: 0.875rem; // 14px $size-7: 0.75rem; // 12px // 基本のフォントサイズを設定 $size-small: $size-7; $size-normal: $size-5; $size-medium: $size-4; $size-large: $size-3; // line-heightが1.5なので、日本語に合わせて広めに変更 $body-line-height: 1.6; // brakepoints ------------------------------- // 個人的に好きなbreakpoints $tablet: 667px; //LG G3など、5.5incタイプの大型スマートフォン $desktop: 970px; //ipad縦 ipad横 $widescreen: 1360px; //MackBook 12/Pro 13/Surface2/3/Pro/Nexus10 $widescreen-enabled: true !default; $fullhd: 1344px + (2 * $gap) !default; $fullhd-enabled: true !default; // breakpointsメモ // @include mobile {} //until 666px // @include tablet {} //from 667px // @include tablet-only {} //from 667px and until 969px // @include touch {} //until 969px // @include desktop {} //from 970px // @include desktop-only {} //from 970px and until 1359px // @include widescreen {} //from 1360px // @include widescreen-only {} //from 1360px and until 1407px // @include fullhd {} /from 1408px // add Mixin @mixin wrapper { box-sizing: border-box; position: relative; width: 100%; padding-right: $bm * 2; padding-left: $bm * 2; @include tablet { padding-right: $bm * 3; padding-left: $bm * 3; } @include desktop { margin-right: auto; margin-left: auto; max-width: $widescreen; padding-right: $bm * 4; padding-left: $bm * 4; } @include fullhd { max-width: $fullhd - 2 * $gap; width: $fullhd - 2 * $gap; } } // color-variable ------------------------------------ // secondary、thirdlyを追加して、カスタムカラーの幅を増やしています。 // 明るい色・暗い色 $light: #efefef; $light-lighten: lighten($light, 15%); $light-darken: darken($light, 5%); $dark: #3C3C48; $dark-lighten: lighten($dark, 10%); $dark-darken: darken($dark, 10%); // primary (ブランドカラー) $primary: #0A428C; $primary-invert: findColorInvert($primary); $primary-lighten: #205aa2; $primary-darken: darken($primary, 5%); // secondary(ポイントカラー ※カスタム) $secondary: #FA4988; $secondary-invert: findColorInvert($secondary); $secondary-lighten: lighten($secondary, 15%); $secondary-darken: darken($secondary, 5%); // thirdary(※カスタム) $thirdly: #DEE5EE; $thirdly-invert: findColorInvert($thirdly); $thirdly-lighten: #EBF0F4; $thirdly-darken: darken($thirdly, 5%); //snsカラー(※カスタム) $line: #1dcd00; $line-invert: findColorInvert($line); $facebook: #3B5998; $facebook-invert: findColorInvert($facebook); $twitter: #55acee; $twitter-invert: findColorInvert($twitter); $youtube: #EC3324; $youtube-invert: findColorInvert($youtube); $instagram: #CDA98A; $instagram-invert: findColorInvert($instagram); // 上記カラーを使えるようにする $custom-colors: ( "secondary": ($secondary, $secondary-invert), "thirdly": ($thirdly, $thirdly-invert), "line": ($line, $line-invert), "facebook": ($facebook, $facebook-invert), "twitter": ($twitter, $twitter-invert), "youtube": ($youtube, $youtube-invert), "instagram": ($instagram, $instagram-invert) ); $colors: mergeColorMaps(( "white": ($white, $black), "black": ($black, $white), "light": ($light, $light-invert), "dark": ($dark, $dark-invert), "primary": ($primary, $primary-invert), "link": ($link, $link-invert), "info": ($info, $info-invert), "success": ($success, $success-invert), "warning": ($warning, $warning-invert), "danger": ($danger, $danger-invert) ),$custom-colors); // グラデーション(※カスタマイズ) @mixin gradient { background: $primary; background: linear-gradient(to right, #35B8CB 10%,$primary 90%); } // color-text ------------------------------------ // text color $text: #666666; $text-strong: $secondary; // Links $link: $primary; $link-hover: $primary-darken; $link-invert: $primary-invert; $link-focus-border: $primary; // etc ------------------------- // その他自由に登録していく。 // base margin(※オリジナル) $bm: 8px; // radius $radius-small: 2px; $radius: 3px; $radius-large: 8px; // radiusを使えるようにする $control-radius: $radius; $control-radius-small: $radius-small;これだけカスタマイズできれば困らないはず。もしダメだったら素直に自作して追加する。
pluginの設定をしていく。
プラグインの設定は大きく分けて二通りある。
- nuxt.config.jsに直接記載していく。
- pluginsにファイルを作成して、nuxt.config.jsに読み込む。
1のパターンは既出していてnuxt-buefyや@nuxtjs/style-resourcesが該当する。@nuxtjsがついてればだいたい該当する。2のパターンは…まぁあれだ、これからやるので覚えてほしい。vue用プラグインが利用可能になるので制作の幅が広がる。
また、lodashやvue-scrolltoなんかは代表例なんだけど、1でも2でもどちらでもOKな場合もある。自分で調べて好きな方にすればいい。
確認までに上記を意識してもう一度設定していくプラグインを書き出す。
細かいオプションの設定は省いて基本の使い方のみ掲載する。
1のパターン
- @nuxtjs/axios
- @nuxtjs/pwa
- @nuxtjs/google-analytics
- @nuxtjs/google-tag-manager
- @nuxtjs/sitemap
- lodash
2のパターン
- vue-scrollto
- vue-mq
- vee-validate
- vue2-perfect-scrollbar
- vue-rellax
- aos
- csvtojson
- moment
@nuxtjs/axios
何もしなくて良い。create-nuxt-app時にnuxt.config.jsに組み込まれている。もしかしたら近い将来nuxt/httpに置き換わるかもしれないということだけ、心のノートにメモっておく。
nuxt.config.jsに記載したオプションを引き継いで使う場合は、
$axios
を使う。オプション詳細は公式サイトを参照のことasyncDataasync asyncData({ $axios }) { const ip = await $axios.$get('http://icanhazip.com') return { ip } }methods/created/mounted/etcmethods: { async fetchSomething() { const ip = await this.$axios.$get('http://icanhazip.com') this.ip = ip } }@nuxtjs/pwa
何もしなくても最低限のことをやってくれる。create-nuxt-app時にnuxt.config.jsに組み込まれている…が、何も設定しない場合は、icon機能をfalseにしましょう。気づかずにnuxtのお山アイコンがmanifestに登録されていることも。
nuxt.config.jsexport default { modules: [ // Doc: https://pwa.nuxtjs.org ['@nuxtjs/pwa', { icon: false }], ... ], }また、PWAは複数の技術の集まりであることを忘れてはならない。
PWA対応のアプリだよ!と言い切るには、オプションを設定し、PWA用のアイコンなど画像を用意し、PWA用のサイト・アプリ設計をしなければならない…と心のノートにメモっておく。しかし、@nuxtjs/pwaが入れておくだけでもキャッシュ戦略が大きく楽になるので「わざと入れない」という選択肢はない。
オプションは公式サイトを参照のこと。
@nuxtjs/google-analytics or @nuxtjs/google-tag-manager
GAとGTMどっちを使うか、または両方使うかは運用チームに確認すること。すり合わせ、データー取得テストを事前にしておかないと「とれない!どうなってんだ!」とリリース後の疲れた身体には少々辛いお言葉が飛び交う。
事前に確認しろといっただろ使い方は各GitHub参照のこと。
nuxt.config.jsexport default { modules: [ ['@nuxtjs/google-analytics', { id: 'UA-XXXXXX-X' }], // Doc: https://github.com/nuxt-community/modules/tree/master/packages/google-tag-manager ['@nuxtjs/google-tag-manager', { id: 'GTM-XXXXXXX' }], ... ], }@nuxtjs/sitemap
Nuxtは巷にあるsitemapジェネレーターでxmlを生成できないので、事前に仕込んでおく。google検索の初回反映に大きく影響が出るので、オプションも丁寧に設定しておき。リリース後にサイトマップの送信を必ずやるぞ!と心のノートにメモっておく。
オプションはGitHubを参照のこと。
nuxt.config.jsexport default { modules: [ ... // Doc: https://github.com/nuxt-community/sitemap-module '@nuxtjs/sitemap' ], sitemap: { hostname: 'https://example.com', cacheTime: 1000 * 60 * 15, // gzip: true, // exclude: [ // 含めたくないディレクトリがあれば追加 // ], // routes: [ // 自動生成ページがあれば追加 // ] }, }lodash
10kとか軽くするために必要な関数のみimportする風習があるけど、それ以前にやることがいっぱいある。とりあえず全入れでいいと思うのは自分だけ?
Nuxt公式サイトのWebpack プラグインを追加するには?を参考にして導入する。webpackをimportすること、既出のESlintで
_
を許可すること、の2点を忘れずに。nuxt.config.jsconst webpack = require('webpack') export default { ... build: { plugins: [ new webpack.ProvidePlugin({ '_': 'lodash' }) ], } }vue-scrollto
ここから、pluginsの中にファイルを作って、nuxt.config.js内のpluginに登録していく。まずは、vue用プラグインで感覚を覚える。ちなみにvue-scrolltoはnuxtに正式に対応しているので、nuxt.config.jsにモジュールとしてオプションを記載することもできる。素晴らしい。
が、今回は、plaugins経由で読み込んで見る。
ファイル作成
touch src/plugins/vue-scrollto.jsvue-scrollto.jsに必要なこと記載する。
plugins/vue-scrollto.jsimport Vue from 'vue' import VueScrollTo from 'vue-scrollto' Vue.use(VueScrollTo, { // ↓以下オプション Doc: https://vue-scrollto.netlify.com/ duration: 700, easing: [0, 0, 0.1, 1], offset: -150 })nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue-scrollto', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div nuxt-link(to="" v-scroll-to="{ el: '#test' }") click! .test#test スクロールした! </template> <style lang="scss" scoped> .test { margin-top: 200vh; } </style>OK。
vue-mq
ブレークポイントを設定してくれるナウいやつ。今回は、Bulmaで変更したブレークポイントと合わせて修正をしてみる。だけじゃつまらないので、mixinを追加して使い勝手を良くしてみる。
ファイル作成
touch src/plugins/vue-mq.jsvue-mq.jsに必要なこと記載する。
plugins/vue-mq.jsimport Vue from 'vue' import VueMq from 'vue-mq' Vue.use(VueMq, { breakpoints: { mobile: 666, tablet: 969, desktop: Infinity, // fullhd: Infinity, }, defaultBreakpoint: 'mobile' }) Vue.mixin({ data() { return { mobile: false, // until 666px tabletOnly: false, // from 667px and until 969px tablet: false, // from 667px desktop: false, // from 970px } }, watch: { $mq() { this.mobile = this.$mq === 'mobile' this.tabletOnly = this.$mq === 'tablet' this.tablet = this.$mq === 'tablet' || this.$mq === 'desktop' this.desktop = this.$mq === 'desktop' } }, mounted() { this.mobile = this.$mq === 'mobile' this.tabletOnly = this.$mq === 'tablet' this.tablet = this.$mq === 'tablet' || this.$mq === 'desktop' this.desktop = this.$mq === 'desktop' } })nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue-mq', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div div {{ $mq }}サイズだよ div(v-if="mobile") mobileサイズだよ! div(v-else) 他のサイズだよ </template>OK。
vee-validate
buefyに公式で対応している上に、日本語対応も簡単なのでおすすめのフロントバリデート。自作する余力があるならバリデートの制度を高めることに時間を使ったほうがいいと思う。
ファイル作成
touch src/plugins/vee-validate.jsvee-validate.jsに必要なこと記載する。合わせて日本語対応をする。
plugins/vee-validate.jsimport Vue from 'vue' import VeeValidate from 'vee-validate' import VeeValidateJa from 'vee-validate/dist/locale/ja' // 「VeeValidate」標準の日本語ローカル情報を使用しています // vee-validateの日本語 Vue.use(VeeValidate, { locale: 'ja', dictionary: { ja: VeeValidateJa }, // events: '' })nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vee-validate', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div.content form table.table tbody tr th Email td b-field( :type="{'is-danger': errors.has('email')}" :message="errors.first('email')" ) b-input(v-model="form.email.value" placeholder="例) @kurilon.co.jp" name="email" v-validate="'required|email'" :data-vv-as="'Email'") button.button(@click.prevent="validateBeforeSubmit()") 送信! </template> <script> export default { data() { return { form: { email: { name: 'Email', value: null, }, } } }, methods: { validateBeforeSubmit() { this.$validator.validateAll().then((result) => { if (result) { alert('OKだよ!') return } this.$toast.open({ message: '赤字で記載された項目を確認してください。', type: 'is-danger', position: 'is-top' }) }) }, } } </script>それっぽいものが悩まず作れる…OK。
vue2-perfect-scrollbar
scrollをコントロールしたいときに必ずお世話になるperfect-scrollbarのvueプラグイン。自作しようとすると実機テストが必須になるところ、だいたいカバーしてくれるナウいやつ。
ファイル作成
touch src/plugins/vue2-perfect-scrollbar.jsvue2-perfect-scrollbar.jsに必要なこと記載する。今回はCSSも追加する。
plugins/vue2-perfect-scrollbar.jsimport Vue from 'vue' import PerfectScrollbar from 'vue2-perfect-scrollbar' import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css' Vue.use(PerfectScrollbar)nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue2-perfect-scrollbar', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div .test-area perfect-scrollbar div.test つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)つれづれなるまゝに、日暮らし(100文字) </template> <style lang="scss" scoped> .test-area { width: 300px; } .test { width: 600px; background-color: $secondary; } </style>OK。
vue-rellax
vue-rellaxは、簡易的なパララックスを表現してくれるrellax.jsのvueプラグイン。工夫をすればサイト全体の雰囲気をコントロールすることも可能なので、デザイナーやクライアントとのやり取りをスムーズにするためにも、はじめから導入しておく。
ファイル作成
touch src/plugins/vue-rellax.jsvue-rellax.jsに必要なこと記載する。
plugins/vue-rellax.jsimport Vue from 'vue' import VueRellax from 'vue-rellax' Vue.use(VueRellax)nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/vue-rellax', ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div .test .test-text div(v-rellax) ぱらららっくすぅ </template> <style lang="scss" scoped> .test { height: 200vh; margin-top: 50vh; } .test-text { width: 100%; background-color: $light; } </style>OK。
aos
aosはスクロールアクション系プラグイン。見出しなどのワンポイント導入にはもってこいなので、初めから入れておく。
ファイル作成
touch src/plugins/aos.jsaos.jsに必要なこと記載する。ESLintに引っかかるため、eslintの設定を一部無視する記述を追加しておく。また、ページ推移した際に一度AOSを解除する記述をMixinで追加しておくと幸せになれる。
plugins/aos.jsimport Vue from 'vue' import AOS from 'aos' import 'aos/dist/aos.css' // ↓eslintの設定を一部無視 /* eslint new-cap: 0 */ export default ({ app }) => { app.AOS = new AOS.init({ // disable: 'phone' duration: 800 }) } Vue.mixin({ watch: { '$route.path': function () { setTimeout(() => { AOS.refresh() }, 500) } } })nuxt.config.jsのplugins内に登録…だけど、aosはSSRに対応していないためオプションを追加する。
nuxt.config.jsmodule.exports = { plugins: [ { src: '~/plugins/aos', ssr: false }, ... ], }動作テスト
src/pages/index.vue<template lang="pug"> div h1(data-aos="zoom-in") 見出し1 .test h2(data-aos="zoom-in") 見出し2 </template> <style lang="scss" scoped> .test { padding-top: 120vh; padding-bottom: 50vh; } </style>OK。
csvtojson
最後は、csvtojsonをベースとした関数をどこでも使えるようにしてみる。プラグインを自作できるとやっとエンジニアっぽい仕事ができてる感があって楽しい。
ファイル作成
touch src/plugins/csvtojson.jsinjectを使って、
$csvToJson
でどこでも関数を使えるようにする。plugins/csvtojson.jsimport csvtojson from 'csvtojson' export default ({ app }, inject) => { inject('csvToJson', (csv) => { // ↓以下処理はお好みで const nl2br = (str) => { return str.replace(/\n/g, '<br>') } return new Promise((resolve) => { csvtojson() .fromString(csv) .then((data) => { _.each(data, (row, index) => { _.each(row, (v, key) => { data[index][key] = nl2br(v) }) }) resolve(data) }) }) }) }nuxt.config.jsのplugins内に登録
nuxt.config.jsmodule.exports = { plugins: [ '~/plugins/csvtojson', ... ], }動作テスト1(asyncData内で描画前に処理)
src/pages/index.vue<template lang="pug"> div pre {{ json }} </template> <script> export default { async asyncData({ app }) { const csv = await app.$axios.$get('test.csv') const json = await app.$csvToJson(csv) return { json: json } }, } </script>動作テスト2(mounted内で描画後に処理)
src/pages/index.vue<template lang="pug"> div pre {{ json }} </template> <script> export default { data() { return { json: null } }, async mounted() { const csv = await this.$axios.$get('test.csv') const json = await this.$csvToJson(csv) this.json = json }, } </script>axiosと同じように書ける。こういうの気持ちいい。OK。
moment
あれ?momentは??
何かとエラーの起きやすい日付関連をよしなにしてくれるmoment。頻出は多くないので、pages内で利用する方法を記載しておく。
src/pages/index.vue<template lang="pug"> div p {{ day }} </template> <script> import moment from 'moment' export default { data() { return { day: null } }, mounted() { const date = new Date() const day = moment(date).format('YYYY.MM.DD') this.day = day } } </script>このように、コンポーネントに個別で呼び出しても使える。そいうことも覚えておくとよい。
これで、やっと基本的な設定完了。「その2/3」では、Workbox、Manifest、Meta、Icon、Ogp(OneSignalはサーバーが関わるの本稿ではやらない)などのSEOやPWAに係る部分を記載予定。「その3/3」になって、やっとコンテンツ部分に入ってコンポーネントとか。
さて、時間が取れるだろうか…。