20201125のvue.jsに関する記事は13件です。

個人開発でサービスをリリースまで持っていくポイント

LGTMoon

私は個人で LGTMoon というサービスをリリース、運用しています。

LGTMoonのスクリーンショット

LGTMoon は、 LGTM 画像を簡単に作成できるサービスです。

個人開発でサービスをリリースするとき

私は仕事をしながら個人開発をしているので、個人開発に割ける時間は多くありません。一番重要なのは、 少ない時間で開発を終えて、リリースまで持っていく ことです。

それに加えて「お金(初期費用)がかからない」とより良いです。

開発時に注意した点

機能は最小限にして最速で実装を終わらせる

LGTMoon にはログイン機能がありません。データベースにも画像テーブル1つしかありませんし、アプリケーションとしてめちゃくちゃシンプルです。LGTMoonサイト内の画像検索機能すらありません。

個人開発では時間が限られているので、機能はめちゃくちゃシンプルにする必要があります。機能をめちゃくちゃシンプルにすると、サービスの使い方がわかりやすくなる というメリットもあります。

LGTMoon については、とにかくリリースする、そして、使い方がシンプルを目標にしていたので、最小の構成でリリースしました。機能追加は後でいくらでもできます。

技術習得とサービス開発を一緒にする際は注意

「新しい言語を触ってみたい」等の技術習得と、「こんなサービスをリリースしたい」という目標を混ぜると危険です。

技術習得に時間がかかってしまった結果、サービスリリースのモチベーションが失われてしまいます。

技術習得に重きを置くのか、サービスのリリースに重きを置くのかは、十分考えましょう。絶対リリースしたいなら、リリースが最終目標ですし、技術習得が目標なら、最悪リリースまで行かなくても良いと思います。

Heroku は個人開発にオススメ

これがこの記事で一番言いたいことかもしれません。

image.png

LGTMoon は Heroku にデプロイされています。Heroku は、AWS と同じ、クラウドアプリケーションプラットフォーム(アプリケーションサーバー)です。

Heroku が個人開発にオススメな理由が2つあります。

  • 一瞬でセットアップできる
  • 料金が無料

一瞬でセットアップできる

Heroku は、インスタンス立ち上げて、GitHubと連携したら完了。GitHub から最新のコードを引っ張ってきてデプロイできるし、サービスリリースに必要な設定がすぐに終わります。

ネットワーク構成どうするとか、セキュリティーグループとか、ユーザーとかロールとか、一切考える必要はありません。サーバーにApache等をインストールする作業も不要です。

料金が無料

Heroku は、1サービスまでなら無料で運用できます

独自ドメイン + HTTPS 対応も、CloudFlare を使えば無料です(CloudFlare を使わず、 Heroku だけでやろうとすると有料です)。

ただし、サービスが大規模になると料金が一気に上がります。個人開発の範囲だと、課金しても月数千円でしょうが、万が一サービスがめちゃめちゃ大きくなった場合は、VPSや、AWS等に移行したほうが良い可能性もあります。

個人開発向けの言語と、そうではない言語

利用している技術スタックのロゴ

LGTMoon のサーバーサイド開発言語は Scala で、フレームワークは Play Framework です。フロントエンドには Vue.js を使っています。

サーバーサイドの言語 Scala は個人開発向けではない

言語に Scala を選択したのは、当時 Scala を勉強していたからでしたが、今となっては、 Go で書き直したいくらいです。

Scala は言語仕様が複雑で学習コストが高いです。言語仕様が複雑だと、簡単な実装でも時間がかかります。

さらに、Scala は利用者が少ないため、ググっても情報が出てきづらいです。仕事で Scala をやる場合は、開発チームで知見を共有できますが、個人プレーだとこの点がきついです。

一方、 Scala を採用したメリットもあります。

  • Akka Actor が優秀(画像生成のキューイング処理で使っている)
  • 非同期処理でサーバーのリソースを有効活用してくれるので、Herokuのしょぼいサーバーでもちゃんと動いている(個人の感想です)

フロントエンドの Vue.js は個人開発向け

フロントエンドには Vue.js を採用しました。これは単に当時 Vue.js が流行っていたからですが、結果的には大正解でした。

大規模なサービスだと React.js を採用することが多いですが、React は学習コストが高いです。

LGTMoon は状態管理すら不要なシンプルなアプリケーションなので、 Vuex も利用していません。最初期は、 package.json とか npm も使っていませんでした(今は使っています)。

シンプルなアプリケーションだと Vue.js で一瞬で実装できるので、おすすめです。

運用について

今までに発生した運用は2つありました

  • なぜかアプリが落ちているので、Herokuの再起動ボタンを押す
  • まずい画像を消す

稀に「アプリが落ちている」とtwitterで呼ばれることがあるので、その場合は Heroku の Restart ボタンを押すだけです。特に面倒なことはありません。

最近はありませんが、たまにエロい画像が大量にアップロードされていることがあります。これは見つけたら消しています。SQL流して、CDNのキャッシュを消すだけです。

たまに、「間違えて画像をアップロードしてしまった」とtwitterに連絡が来る場合もあるので、その時も同じ運用をします。今までで2,3回くらいありました。

画像を消す場合は SQL を実行する必要があるのですが、完全にマニュアル化されており、思考停止でコマンドをコピペしていくだけなので、ほぼノーコストでできます。

完全にマニュアル化しておくと、考えなくてもできるので、頻繁に行う運用はマニュアル化して置くべきです。個人開発の場合、使える時間は限られているのでなおさらです。

その他気づいた点

Google Analytics はとりあえず入れておいたほうがいいです。見るだけで面白いし、ユーザーが増えたりするとサービスのモチベーションになったりします。

LGTMoon を使っているユーザーは、平日が圧倒的に多いです。休日は殆ど使われません。みんな仕事のコードレビューの時とかに使ってるんですかね。面白いです。

休日はほとんど使われていないので、休日にデプロイすれば、なにか不具合があっても影響が少ないなと思っています。個人開発だとちょうど休日に開発&デプロイしますし、仕事中使われるアプリは、個人開発と相性が良いのかもしれません。

実際に LGTMoon はいくらかかっているのか

用途 金額
Heroku Postgres Hobby Basic プラン $9(約1000円)/月
Google Domain(lgtmoon.dev ドメイン) 117円/月(1400円/年)
Google Cloud(画像検索API) 約1000円/月(利用頻度によって変わる)

だいたい、月2000円程度かかっていますが、広告収入は500円〜1000円程度なので、赤字です。

Google Cloud の画像検索APIは、1日あたりのリミットをかけていて、一定金額(月3000円程度)を超えないようにしているのですが、平日はたまにリミットに引っかかっています。サービス的には問題なのですが、個人開発でお金には限界があるので、こう設定させてもらっています。

当然サービスリリース初期は全部無料でした。Google画像検索も 100リクエスト/日までは無料ですし、Heroku Postgres も無料プランがあります。容量が足りなくなったため、現在は一番安い $9/月 のプランにしています。

さいごに

この記事では技術的な詳細は書きませんでしたが、使っている技術の詳細はこちらの記事に、

また、ブログでも LGTMoon に関する記事をいくつか書いています

サービスもぜひ使ってください

記事が LGTM なら、 LGTMoon で LGTM 画像を作って、コメントしてみてください。
![LGTM](https://cdn.lgtmoon.dev/images/104695) のような GitHub Markdown をコメントに書けばOKです。LGTMボタンも押してね。

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

Vuejsのモジュールのインポートエラー

Vuejsのモジュールのエラー

エラーの内容: モジュールのインポートができなかったため、"./components/sample"
以外の方法で調べた。
エラー内容:
エラー解決の手順
1、App.vueとmain.jsモジュールインポートのコードの変更
2、App.vueのコンポーネントの追加
3、開発用にファイルをビルトする。
技術:Vuejs2,VueCli

App.vueのエラーコード

import sample from "./components/sample";

export default{
  data() {
    return{
      number: 10
    }  
  },
  components: {
    Sample
  } ,

App.vueの構成とコンポーネントの追加

1、"./"を"@/"に変更
2、コンポーネントの追加

import GoodHeader from "@/components/Sample.vue";
import About from "@/components/Sample1.vue";
import Home from "@/components/Sample2.vue";

export default {
  data() {
    return {
      number: 10

      }
  },
  components: {
    Sample,
    Sample1,
    Sample2
  }

main.jsのエラー解消前

ここのファイル変更して、App.vueと同期する

import Vue from 'vue';
import App from './App.vue';
import GoodNum from "./components/sample.vue";

エラーの解消内容

App.vueと同様に、main.jsも"@/components/"に変更した。

import Vue from 'vue';
import App from './App.vue';
import sample from "@/components/sample.vue";

モジュール変更後の処理

開発用に、ファイルの構成をビルトした

npm run build 
npm run serve //サーバーの立ち上げ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】「dotenv」を使った環境変数の設定方法

「dotenv」をインストール

プロジェクト直下でnpm install --save @nuxtjs/dotenvを実行

ターミナル
$ npm install --save @nuxtjs/dotenv

package.jsonで確認

package.json
  "dependencies": {
    "@nuxtjs/dotenv": "^1.4.1",    <-
    "core-js": "^3.6.5",
    "nuxt": "^2.14.6"
  }

プロジェクト直下に.envファイルを作成

ターミナル
$ touch .env

.envファイルに適当な値を定義します。

.env
TEST = 'テスト'

nuxt.config.jsの設定

nuxt.config.js
//...省略

  // Modules (https://go.nuxtjs.dev/config-modules)
  modules: [
    '@nuxtjs/dotenv'
  ],

//...省略

console.logで確認

きちんと設定されているか確認します。
pages/index.vueconsole.logを設置。

pages/index.vue
<script>
export default {
  created() {
    console.log(process.env.TEST)
  }
}
</script>

スクリーンショット 2020-11-25 21.00.38.png

OKです!

.gitnoreファイルの確認

最後に.gitnoreファイル内に.envの記述があるか確認します。
nuxt-create-appを使用すればデフォルトで記述されますが、
無い場合は記述しておきます。

スクリーンショット 2020-11-25 21.04.17.png

これでgitの管理対象外となります。

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

Atomic Design入門

最近Atomic Designという考え方を学んだので、コンポーネント指向と絡めつつ僕なりの理解をアウトプットしてみます。
Atomic Designもコンポーネント指向も初心者なので、誤りがあれば指摘してもらえると助かります。

Atomic Designとは

Atomic DesignとはUI設計の考え方で、「小さなUIコンポーネントを組み合わせて1つのページを作っていこう」というもの。
僕のざっくりとした理解では、「小さなパーツを組み合わせ、徐々に意味のある大きな要素を作っていき、最終的に1つのページになる」みたいな感じです。

5段階で捉えるUI構造

Atomic Designで有名なのは、UIの構造を次の5段階に落とし込むという考え方。
atomic_design.png

引用元:Atomic Design Methodology

atoms

原子。それ以上分解できない最小単位。
ラベル、ボタン、アイコンなど。
ヘッダーを例にとるなら、ここがatoms。
スクリーンショット 2020-12-14 17.33.44.png

molecules

分子。atomsを組み合わせてできたもの。
ヘッダーナビゲーションや検索フォームなど。
ヘッダーを例にとるなら、ここがmolecules。
スクリーンショット 2020-12-14 17.38.10.png

organisms

生物。atomsやmoleculesを組み合わせてできたもの。意味を持っている単位。
ヘッダーそのものがorganisms。
atomsとmoleculesが組み合わさってヘッダーになる。
image.png

templates

骨組み。例えば、ヘッダーとサイドナビゲーションは全てのページで固定化し、メインコンテンツだけが異なるページを作っていく場合などに便利。
ここに関しては後半でもう一度紹介します。
スクリーンショット 2020-12-14 17.39.51.png

pages

ページ。atoms, molecules, organisms, templatesが組み合わさって1つのページとなる。
image.png

Atomic Designを採用すると何が嬉しいか?

Atomic Design/コンポーネント指向を採用するメリットはこんな感じかなと思います。
1. 再利用性が高まる
2. 小さな単位で問題を捉えられる
3. 全体のデザインに一貫性が出る

1. 再利用性が高まる

ページ内で何度も出てくるようなUIパーツは、コンポーネントとしてまとめておけば使い回すことができます。

2. 小さな単位で問題を捉えられる

コンポーネント指向の強みは疎結合な状態を作り出せることです。(コンポーネント同士の結びつきが弱く独立性が高い状態)。そのため、問題が発生したときに(変更したい/取り除きたいなど)、単位を小さく捉えることができ解決しやすくなります。

3. 全体のデザインに一貫性が出る

コンポーネントを使い回すことで、「このボタンだけデザインがおかしい」みたいなことが防げるので、全体に一貫性が出ます。

コンポーネント指向でファイルを分割すると何が嬉しいか?

Atomic Designのメリットがわかってきたところで、具体例を交えながらコンポーネント指向のメリットも見ていきます。

例1. ここのボタンの色変えたい...

スクリーンショット 2020-12-14 17.48.55.png

コンポーネントとしてこのボタンを作っておけば、ボタンコンポーネントの色を変えるだけで全てのボタンにその変更が反映されます。もしコンポーネントに分けていなかったら、1つずつボタンを定義しているコードを探し出して、それを1つずつ直していかないといけません。このページのボタンだけならまだ楽ですが、ページの数・ファイルの数が増えてくるといちいち探して直していくのはかなり面倒になってきます。

例2. この部分差し替えたい...

スクリーンショット 2020-12-14 18.06.13.png

ページのこの部分を丸ごと別の要素に差し替えたいみたいな場合。コンポーネントとして独立させておけば、それを入れ替えるだけで目的は達成されます。しかし、コンポーネントにしていなければ、ファイルの中からこの部分を探し出さなければいけません。面倒ですね。

例3. ページに応じてコンテンツだけを変更したい...

スクリーンショット 2020-12-14 17.39.51.png

ヘッダーとサイドナビゲーションは他のページでも同様のレイアウトを保ちたいという場合。ページごとで毎回それを書いていたら無駄が多くなってしまいます。そんな時はテンプレートを作ってコンテンツだけ差し替えられる形にするのが良さそうです。Vue.jsのvue-routerなどはこの考えに基づいて実装していくイメージです。

Atomic Design/コンポーネント指向のデメリット

ここまで良いところばかりを紹介してきましたが、デメリットもあります。
1. ファイル数が激増する
2. 開発メンバー間で理解の齟齬があると辛い
3. エンジニア・デザイナー間のコミュケーションの難しさ

1. ファイル数が激増する

小さなコンポーネントもどんどん切り分けていくとファイル数が激増します。1フォルダで管理しきれないほどのファイル数になる場合は、フォルダを切り分けていくのも1つの解決策かなと思います。

2. 開発メンバー間で理解の齟齬があると辛い

チーム開発する際に、コンポーネントの単位をチームメンバーとすりあわせておかないと、逆にごちゃごちゃしてしまう可能性があります。開発を始める前、コンポーネントの単位に悩んだ時は、チームで意見のすり合わせをした方がいいかなと思います。

3. エンジニア・デザイナー間のコミュニケーションの難しさ

Atomic Designはどちらかというと開発者寄りの考え方かなと思います。Atomic Designの考え方を汲んでいるデザインはやはり開発しやすいです。Atomic Designを理解していないデザイナーさんと一緒に仕事する場合は、UI設計について少し議論する必要もあるのかなと思います。

まとめ

Atomic Designとコンポーネント指向について見てきました。
Vue.js, React, Angularなど、フロントエンドの技術はコンポーネント指向に基づく実装がデファクトスタンダードになりつつあります。なので、このようなUI設計の考え方も重要性が増してきている気がします。

UI設計の考え方は他にも色々あるので、すべての場合にAtomic Designが有効かというと、そうではないと思います。さらにAtomic Designの中でも、「どの要素をmoleculesやorganismsの範疇に含めるのか」、「ディレクトリ構成はどうしていくのか」など常につきまとう問題がいくつかあります。最後まで気持ちよく開発をしていくためにも、チームメンバーと理解をすりあわせ、適切な判断をしていきたいですね。

参考

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

Vue.jsのvue-cursol-fxを使ってみる

最近は就活中で暇なんでちょこちょこVue.jsを触っています。Railsでのバックエンドばかりだったのでフロントの知識もなく、Vue用のプラグインでとりあえずそれっぽくしたいなと思いvue-cursol-fxを使ってみました。

githubのインストレーションを参考にしています。詳しくはREADME.mdを見てください。
https://github.com/LuXDAmore/vue-cursor-fx?ref=kabanoki.net

環境

  • Windowns 10
  • Node.js v14.15.1
  • @vue/cli 4.5.9

言い訳なんですがバーチャルボックスだとnodeのサーバー立ち上げた際ホットリロードが異常に遅い(約1分)ため仕方なくWindowsに直接Nodeをインストールして最近は遊んでいます。仮想環境でホットリロードがなんで遅いのかはいまだ原因分からないです。誰か教えて...

導入

まずはvue-cliでプロジェクトを作ります。

vue create sample_app

作成時の設定はデフォルトで、Vue2にしておきます。

sample_appのディレクトリに移動して、vue-cursor-fxを入れます。

cd sample_app
npm install --save @luxdamore/vue-cursor-fx

次にApp.vueにてimportしてコンポーネントを書き加えます

App.vue
<template>
  <div id="app">
    <cursor-fx color="#39B509" color-hover="#acf98e"></cursor-fx>
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Hello Vue"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';
import { CursorFx } from '@luxdamore/vue-cursor-fx';
import '@luxdamore/vue-cursor-fx/dist/CursorFx.css';

export default {
  name: 'App',
  components: {
    HelloWorld,
    'cursor-fx': CursorFx,
  },
}
</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>

これでカーソルが表示されます。

2020-11-25_17h23_07.gif

オプションでポインタの形や大きさなど変えられます。

<cursor-fx color="#39B509" color-hover="#acf98e" shape='square'></cursor-fx>

2020-11-25_17h32_13.gif

以上です。

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

V-Calendarで日付範囲を指定する

V-Calendarとは

Vueで使えるカレンダーライブラリです。
https://vcalendar.io/

日付範囲モード

まず、日付を範囲指定したい場合は、is-rangeを利用します。

  <v-date-picker v-model="dateRange" is-range />

あとは、dateRangeに開始日と終了日を渡すだけで、日付の範囲指定が可能です。

var startDate = new Date(2020, 10, 14)
var endDate = new Date(2020, 10, 28)

new Vue({
    el: '#cal',
    data: {
        mode: 'range',
        dateRange:{ "start":startDate,"end":endDate },
    }
})

うまくいくと、こんな感じで表示されます。
image.png

日付範囲の指定方法

公式に書かれている日付の渡し方を含めて4つ紹介します。

local.jsのnormalizeDateメソッドを見れば、わかる内容です。
https://github.com/nathanreyes/v-calendar

1.Dateオブジェクトで日付を指定

これが公式に書かれている方法です。new Dateで日付を渡します。

var startDate = new Date(2020, 10, 14)
var endDate = new Date(2020, 10, 28)

2.文字列で日付を指定

YYYY-MM-DD形式で文字列を渡すことでも可能です。

var startDate = '2020-11-14'
var endDate = '2020-11-28'

文字列で渡された場合は、Date.parseメソッドで解釈されるので、
Date.parseで解釈できる文字列であれば何でも良いです。

var startDate = 'Nov 14, 2020'
var endDate = '2020-11-28T15:00:00'

3. 数値(ミリ秒)で日付を指定

数値の場合、1970年 1月 1日 00:00:00 UTC からの経過ミリ秒で渡します。

var startDate = 1605312000000
var endDate = 1606489200000

なので、getTimeやvalueOfの返り値をそのまま渡すこともできます。

var startDate = (new Date(2020, 10, 14)).getTime()
var endDate = (new Date(2020, 10, 28)).valueOf()

4.オブジェクトで日付を指定

オブジェクトに、year, month, dayを格納して渡すこともできます。

var startDate = {year: 2020, month: 11, day: 14}
var endDate = {year: 2020, month: 11, day: 28}

year, month, dayは文字列でも正しく動作します。

var startDate = {year: '2020', month: '11', day: '14'}
var endDate = {year: '2020', month: '11', day: '28'}

objectで渡された場合は、new Date()の引数として処理されます。
monthに13といった存在しない月を渡すと、溢れたぶん加算されて表示されるので注意です。

var startDate = {year: '2020', month: '13', day: '14'} //2021-01-14
var endDate = {year: '2020', month: '13', day: '28'} //2021-01-28
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CLIをインストールできてるのに、command not found: quasar とかになる場合。

sudo npm install -g @quasar/cli

でモジュールをインストールして

+ @quasar/cli@1.1.2
added 376 packages from 294 contributors in 9.001s

ッて感じでインストールできたのに、いざコマンドしてみると
「ありません」みたいになるやつ

 quasar create quasar-weather
zsh: command not found: quasar

こんなかんじでハマる。

こういうのは単純に反映がされてないだけなので、

% source ~/.bash_profile

これで反映させてから、
もう一回コマンドを実行させるとうまくいきますた

Mac desktop % source ~/.bash_profile
Mac desktop % quasar create quasar-weather

  ___                             
 / _ \ _   _  __ _ ___  __ _ _ __ 
| | | | | | |/ _` / __|/ _` | '__|
| |_| | |_| | (_| \__ \ (_| | |   
 \__\_\\__,_|\__,_|___/\__,_|_|

しょっちゅう忘れるのでメモ
あと、VSコードターミナルでも同様なのでメモ

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

JavaScriptをまともに触ったことない初心者がVueを利用したポートフォリオサイトを作成する 2

前回からの続きです。

完成したポートフォリオサイト

ここから本格的にポートフォリオサイトの作成に着手していきます。

使用したライブラリ

vue-smooth-scroll

スムーススクロールを手軽に実装できるライブラリです。どうやらsmoothscrollとsmooth-scrollの二つがあるみたいなのですが、今回はスクロールの位置を調整できるsmooth-scrollを採用しております。(smoothscrollの方でも位置調整は出来るのかもしれませんが)

$ npm install --save vue-smooth-scroll
main.js
import vueSmoothScroll from 'vue-smooth-scroll'

<snip>

Vue.use(vueSmoothScroll)

<snip>

Vue.directive('scroll', {
  inserted: function (el, binding) {
    let f = function (evt) {
      if (binding.value(evt, el)) {
        window.removeEventListener('scroll', f)
      }
    }
    window.addEventListener('scroll', f)
  }
})

new Vue({
  el: '#app',
  router,
  data: {
    visible: false
  },
  methods: {
    handleScroll () {
      this.visible = window.pageYOffset > 150
    }
  },
  components: { App },
  template: '<App/>'
})

以上のコードを記述したら、あとはリンクタグ内に埋め込むだけです。今回はヘッダーコンポーネントに埋め込んで使用しています。durationはクリックしてから指定の要素までたどり着くまでの時間、offsetはその要素からの距離を指定出来ます。

components/Header.vue
<template>
  <div id="header">
    <div class="nav-ber">
      <a href="#top" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -50 }">Top</a>
      <a href="#about" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -80 }">About</a>
      <a href="#skills" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -100 }">Skills</a>
      <a href="#works" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -140 }">Works</a>
      <a href="#contact" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -50 }">Contact</a>
    </div>
  </div>
</template>

vue-light-timeline

vue-light-timelineは年表で使えるタイムラインを簡単に実装出来るライブラリです。今回は自分の履歴をシンプルに記述する為に使用しました。

$ npm install -save vue-light-timeline
main.js
import LightTimeline from 'vue-light-timeline'

<snip>

Vue.use(LightTimeline)

使い方は<light-timeline>で囲ってdataをバインディングしてあげるだけです。

components/About.vue
 <light-timeline :items='items'></light-timeline>

<snip>

<script>
export default {
  name: 'About',
  data () {
    return {
      items: [
        {
          tag: '2017-03',
          type: 'circle',
          color: '#0da8bd',
          content: '高校を卒業。'
        },
        <snip>
      ]
    }
  }
}

vue-typer

vue-typerは設定したテキストをタイピング風に表示してくれるライブラリです。

$ npm install -save vue-typer
main.js
import VueTyperPlugin from 'vue-typer'

<snip>

Vue.use(VueTyperPlugin)

今回はTopコンポーネント(開いたら最初に目に入る部分)のテキストに使用しています。タイピングの速さは結構細かく設定出来ますので、色々試してみてください。

components/Top.vue
<template>
  <div id="top">
    <vue-typer :text="['Welcome to my Portfolio Site. \n Let me introduce myself. \n Please scroll down.']"
    class="top-text" :repeat='0' :preTypeDelay="800" :typeDelay="60" :preEraseDelay="5000" :eraseDelay="2000"></vue-typer>
  </div>
</template>

設計

コンポーネント化

今回作ったポートフォリオサイトは長いページをスムーススクロールで自由に移動できるようにしてあります。表面上は一枚のページに見えますが、Vueのcomponent要素を利用して各sectionを分割して設計しました。実際にベースとなるApp.vueはこんな感じです。

App.vue
<template>
  <div id="app">
    <Header></Header>
    <Top></Top>
    <About id="about"></About>
    <Skills id="skills"></Skills>
    <Works id="works"></Works>
    <Contact id="contact"></Contact>
    <Footer id="footer"></Footer>
  </div>
</template>

<script>

import Header from './components/Header.vue'
import Top from './components/Top.vue'
import About from './components/About.vue'
import Skills from './components/Skills.vue'
import Works from './components/Works.vue'
import Contact from './components/Contact.vue'
import Footer from './components/Footer.vue'

export default {
  name: 'App',
  components: {
    Header,
    Top,
    About,
    Skills,
    Works,
    Contact,
    Footer
  }

  <snip>
</script>

App.vue内でcomponentsを読み込み、一つの大きなページにしているのが分かるかと思います。

ディレクティブの使用

次にVue特有のディレクティブを使用した部分について紹介します。

v-forによる配列処理

components/Skills.vue
<div class="skills-container">
  <ul>
    <li v-for= "(skill, key) in skills" :key = "key">
       <span class="skill-name">
       {{ skill.name }}
       </span>
       <br>
       <span class="skill-description">
       {{ skill.description }}
       </span>
    </li>
  </ul>
</div>

Skills.vueではオブジェクトを要素にした配列をv-forディレクティブで繰り返し処理しております。ここで気になるのはkey属性です。Vueではリストを更新する時各要素にユニークなkey属性を設定することが義務付けられています。(key抜きだとエラーが発生しました)なぜkeyが必要になるのでしょうか?リストの要素を削除した時の例で考えてみます。以下のようなリストを考えてみてください。

<li>リスト1</li>
<li>リスト2</li>
<li>リスト3</li>
... #以下続く

ここでリスト2を削除したと仮定します。この時、各要素にkeyがバインディングされていないと、リスト2以下の全てのリスト要素が一つずつ前にずれて、ずれた文全ての要素を更新することになってしまいます。

<li>リスト1</li>
   リスト2削除     #リスト3の要素で更新          
<li>リスト3</li>  #リスト4の要素で更新
... #以下続く     #リスト5の要素で更新...

一方でユニークなkeyを各要素にバインディングしていた場合、次のようになります。

<li>リスト1</li>
   リスト2削除     #リスト2のDOMだけ消える          
<li>リスト3</li>  #リスト3の状態で前に押し出されるだけ
... #以下続く     

説明がうまく出来なくて申し訳ないです。
各要素にidを付与して、それをkeyに設定するのが一番わかりやすいかもしれません。

モーダルウィンドウの実装とprops

次にWorks.vueでは、さらにModal.vueをimportして作品の画像をクリックした際に詳細やリンクがモーダルウィンドウで表示されるようにしました。ここが一番苦戦した部分です。

components/Works.vue
<ul>
  <li v-for= "(work, index) in works" :key="index">
      <span class="work-name">
      {{ work.name }}
      </span>
      <br>
      <br>
     <img :src="work.picture" @click="openModal(work)">
     <br>
     <br>
     <Modal :value="workData" v-show="showContent" @close="closeModal"/>
  </li>
</ul>

リスト要素にmodalを埋め込んで、各要素に対応したモーダルウィンドウを表示できるようにしてあります。
ここでは親コンポーネントから子コンポーネントへのデータの引き渡しをしています。まずはModalタグ内で、配列処理したwork一つ一つを:valueにバインディングします。次にModal.vueを見ていきます。

components/Modal.vue
<template>
  <transition name="modal" appear>
    <div class="modal modal-overlay" @click.self="$emit('close')">
      <div class="modal-window">
        <div class="modal-container">
          <div class="modal-left">
            <h2>{{ value.name }}</h2>
            <img :src="value.picture" @click="openModal(work)" width="626px" height="300px">
          </div>
          <div class="modal-right">
            <h4 class="work-description">
              <span>作品説明</span>
            </h4>
            <p class="modal-description">{{ value.description }}</p>
            <h4 class="tech-index">
              <span>使用技術</span>
            </h4>
            <p class="modal-tech">{{ value.tech }}</p>
            <div class="links">
              <a :href="value.link" target="_blank" class="web-link">Website</a>
              <a :href="value.github" target="_blank" class="github-link">Github</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: 'Modal',
  props: ['value']
}
</script>

子コンポーネントであるModal.vueのscriptタグ内でpropsとして先ほどのvalueを定義すれば、親コンポーネントからのデータ引き渡しが出来ます。実際にtemplate内でvalueをオブジェクトとして使っているのが分かるかと思います。

終わりに

JavaScript初心者でも直感的にVueでポートフォリオサイトが作成出来ました。Vueに慣れてきたら次はNuxt.jsに挑戦してみたいと思います。

参考させていただいた記事

ポートフォリオで使おうと思ったVueTyperについて
Vue.js 動的なモーダルウインドウの作り方を解説
Vue.jsでスムーススクロールを実装する「vue-smooth-scroll」

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

Vue歴5日でポートフォリオサイトを作成する 2

前回からの続きです。

完成したポートフォリオサイト

ここから本格的にポートフォリオサイトの作成に着手していきます。

使用したライブラリ

vue-smooth-scroll

スムーススクロールを手軽に実装できるライブラリです。どうやらsmoothscrollとsmooth-scrollの二つがあるみたいなのですが、今回はスクロールの位置を調整できるsmooth-scrollを採用しております。(smoothscrollの方でも位置調整は出来るのかもしれませんが)

$ npm install --save vue-smooth-scroll
main.js
import vueSmoothScroll from 'vue-smooth-scroll'

<snip>

Vue.use(vueSmoothScroll)

<snip>

Vue.directive('scroll', {
  inserted: function (el, binding) {
    let f = function (evt) {
      if (binding.value(evt, el)) {
        window.removeEventListener('scroll', f)
      }
    }
    window.addEventListener('scroll', f)
  }
})

new Vue({
  el: '#app',
  router,
  data: {
    visible: false
  },
  methods: {
    handleScroll () {
      this.visible = window.pageYOffset > 150
    }
  },
  components: { App },
  template: '<App/>'
})

以上のコードを記述したら、あとはリンクタグ内に埋め込むだけです。今回はヘッダーコンポーネントに埋め込んで使用しています。durationはクリックしてから指定の要素までたどり着くまでの時間、offsetはその要素からの距離を指定出来ます。

components/Header.vue
<template>
  <div id="header">
    <div class="nav-ber">
      <a href="#top" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -50 }">Top</a>
      <a href="#about" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -80 }">About</a>
      <a href="#skills" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -100 }">Skills</a>
      <a href="#works" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -140 }">Works</a>
      <a href="#contact" class="nav-link" v-smooth-scroll="{ duration: 1200, offset: -50 }">Contact</a>
    </div>
  </div>
</template>

vue-light-timeline

vue-light-timelineは年表で使えるタイムラインを簡単に実装出来るライブラリです。今回は自分の履歴をシンプルに記述する為に使用しました。

$ npm install -save vue-light-timeline
main.js
import LightTimeline from 'vue-light-timeline'

<snip>

Vue.use(LightTimeline)

使い方は<light-timeline>で囲ってdataをバインディングしてあげるだけです。

components/About.vue
 <light-timeline :items='items'></light-timeline>

<snip>

<script>
export default {
  name: 'About',
  data () {
    return {
      items: [
        {
          tag: '2017-03',
          type: 'circle',
          color: '#0da8bd',
          content: '高校を卒業。'
        },
        <snip>
      ]
    }
  }
}

vue-typer

vue-typerは設定したテキストをタイピング風に表示してくれるライブラリです。

$ npm install -save vue-typer
main.js
import VueTyperPlugin from 'vue-typer'

<snip>

Vue.use(VueTyperPlugin)

今回はTopコンポーネント(開いたら最初に目に入る部分)のテキストに使用しています。タイピングの速さは結構細かく設定出来ますので、色々試してみてください。

components/Top.vue
<template>
  <div id="top">
    <vue-typer :text="['Welcome to my Portfolio Site. \n Let me introduce myself. \n Please scroll down.']"
    class="top-text" :repeat='0' :preTypeDelay="800" :typeDelay="60" :preEraseDelay="5000" :eraseDelay="2000"></vue-typer>
  </div>
</template>

設計

コンポーネント化

今回作ったポートフォリオサイトは長いページをスムーススクロールで自由に移動できるようにしてあります。表面上は一枚のページに見えますが、Vueのcomponent要素を利用して各sectionを分割して設計しました。実際にベースとなるApp.vueはこんな感じです。

App.vue
<template>
  <div id="app">
    <Header></Header>
    <Top></Top>
    <About id="about"></About>
    <Skills id="skills"></Skills>
    <Works id="works"></Works>
    <Contact id="contact"></Contact>
    <Footer id="footer"></Footer>
  </div>
</template>

<script>

import Header from './components/Header.vue'
import Top from './components/Top.vue'
import About from './components/About.vue'
import Skills from './components/Skills.vue'
import Works from './components/Works.vue'
import Contact from './components/Contact.vue'
import Footer from './components/Footer.vue'

export default {
  name: 'App',
  components: {
    Header,
    Top,
    About,
    Skills,
    Works,
    Contact,
    Footer
  }

  <snip>
</script>

App.vue内でcomponentsを読み込み、一つの大きなページにしているのが分かるかと思います。

ディレクティブの使用

次にVue特有のディレクティブを使用した部分について紹介します。

v-forによる配列処理

components/Skills.vue
<div class="skills-container">
  <ul>
    <li v-for= "(skill, key) in skills" :key = "key">
       <span class="skill-name">
       {{ skill.name }}
       </span>
       <br>
       <span class="skill-description">
       {{ skill.description }}
       </span>
    </li>
  </ul>
</div>

Skills.vueではオブジェクトを要素にした配列をv-forディレクティブで繰り返し処理しております。ここで気になるのはkey属性です。Vueではリストを更新する時各要素にユニークなkey属性を設定することが義務付けられています。(key抜きだとエラーが発生しました)なぜkeyが必要になるのでしょうか?リストの要素を削除した時の例で考えてみます。

<li>リスト1</li>
<li>リスト2</li>
<li>リスト3</li>
... #以下続く

ここでリスト2を削除したと仮定します。この時、各要素にkeyがバインディングされていないと、リスト2以下の全てのリスト要素が一つずつ前にずれて、ずれた文全ての要素を更新することになってしまいます。

<li>リスト1</li>
   リスト2削除     #リスト3の要素で更新          
<li>リスト3</li>  #リスト4の要素で更新
... #以下続く     #リスト5の要素で更新...

一方でユニークなkeyを各要素にバインディングしていた場合、次のようになります。

<li>リスト1</li>
   リスト2削除     #リスト2のDOMだけ消える          
<li>リスト3</li>  #リスト3の状態で前に押し出されるだけ
... #以下続く     

説明がうまく出来なくて申し訳ないです。
各要素にidを付与して、それをkeyに設定するのが一番わかりやすいかもしれません。

モーダルウィンドウの実装とprops

次にWorks.vueでは、さらにModal.vueをimportして作品の画像をクリックした際に詳細やリンクがモーダルウィンドウで表示されるようにしました。ここが一番苦戦した部分です。

components/Works.vue
<ul>
  <li v-for= "(work, index) in works" :key="index">
      <span class="work-name">
      {{ work.name }}
      </span>
      <br>
      <br>
     <img :src="work.picture" @click="openModal(work)">
     <br>
     <br>
     <Modal :value="workData" v-show="showContent" @close="closeModal"/>
  </li>
</ul>

リスト要素にmodalを埋め込んで、各要素に対応したモーダルウィンドウを表示できるようにしてあります。
ここでは親コンポーネントから子コンポーネントへのデータの引き渡しをしています。まずはModalタグ内で、配列処理したwork一つ一つを:valueにバインディングします。次にModal.vueを見ていきます。

components/Modal.vue
<template>
  <transition name="modal" appear>
    <div class="modal modal-overlay" @click.self="$emit('close')">
      <div class="modal-window">
        <div class="modal-container">
          <div class="modal-left">
            <h2>{{ value.name }}</h2>
            <img :src="value.picture" @click="openModal(work)" width="626px" height="300px">
          </div>
          <div class="modal-right">
            <h4 class="work-description">
              <span>作品説明</span>
            </h4>
            <p class="modal-description">{{ value.description }}</p>
            <h4 class="tech-index">
              <span>使用技術</span>
            </h4>
            <p class="modal-tech">{{ value.tech }}</p>
            <div class="links">
              <a :href="value.link" target="_blank" class="web-link">Website</a>
              <a :href="value.github" target="_blank" class="github-link">Github</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: 'Modal',
  props: ['value']
}
</script>

子コンポーネントであるModal.vueのscriptタグ内でpropsとして先ほどのvalueを定義すれば、親コンポーネントからのデータ引き渡しが出来ます。実際にtemplate内でvalueをオブジェクトとして使っているのが分かるかと思います。

終わりに

Vue初心者でも直感的にVueでポートフォリオサイトが作成出来ました。Vueに慣れてきたら次はNuxt.jsに挑戦してみたいと思います。

参考にさせていただいた記事

ポートフォリオで使おうと思ったVueTyperについて
Vue.js 動的なモーダルウインドウの作り方を解説
Vue.jsでスムーススクロールを実装する「vue-smooth-scroll」

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

【 Vue + Vuetify 】ドロワーを固定表示のままリンク遷移させる方法

はじめに

皆さんおはこんばにちは。
エンジニア歴1年目、現場未経験の弱小自称 Web エンジニアです。

今回は、Twitter PC版のように
サイドナビゲーションバーを表示させたまま画面遷移させるべく
試行錯誤した経緯を記録しようと思い、本記事の作成にいたった次第でございます。

やりたいこと

  • リンクをクリックしてページの切り替えをしても、常にドロワーが表示されるようにする

成功例.gif

先に結論から述べておくと
ルートの設定を入れ子構造にすれば解決します。

最初のコード(失敗例)

特に何の考えもなく実装させようとした最初のコードを以下に記載します。

App.vue
<template>
  <v-app>
    <v-main>
      <router-view />
    </v-main>
  </v-app>
</template>

<script>

export default {
  name: 'App',
};
</script>


Main.vue
<template>
    <div>
        <SideNav />
            <v-content>
                <v-container fluid fill-height align-start justify-center>
                    <router-view />
                </v-container>
            </v-content>
    </div>
</template>

<script>
import SideNav from '../components/SideNav.vue'

export default {
    components: {
        SideNav,
    }
}
</script>


SideNav.vue
<template>
    <v-navigation-drawer permanent absolute>
        <v-list dense nav>
            <v-list-item
                v-for="item in items"
                :key="item.title"
                :to="item.link"
            >
                <v-list-item-icon>
                    <v-icon>{{ item.icon }}</v-icon>
                </v-list-item-icon>

                <v-list-item-content>
                    <v-list-item-title>{{ item.title }}</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
        </v-list>
    </v-navigation-drawer>
</template>

<script>
export default {
    data() {
        return {
            items: [
                {title: 'ホーム', icon: 'mdi-home', link: {name: 'home'}},
                {title: '通知', icon: 'mdi-bell', link: {name: 'notification'}},
                {title: 'メール', icon: 'mdi-email', link: {name: 'email'}},
            ]
        }
    }
}
</script>


router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Main from '../views/Main.vue'
import Home from '../components/Home.vue'
import Notification from '../components/Notification.vue'
import Email from '../components/Email.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
  },
  {
    path: '/home',
    name: 'home',
    component: Home,
  },
  {
    path: '/notification',
    name: 'notification',
    component: Notification,
  },
  {
    path: '/email',
    name: 'email',
    component: Email,
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

最初の失敗2.gif
↑GIF画像の通りリンクをクリックした瞬間、ドロワーが消えてしまう!!

リンク先に遷移自体は出来ているようなのに何故……

試行錯誤の経緯

自分が思い描くドロワーを実装させる為にはどうすれば良いのだろうか。
拙者が無い頭をひねって導き出した対処法は以下の3つ。

v-navigation-drawer に何かそういうプロパティ無いの??
→ ドキュメントなどを読み漁ったかぎり、見つかりませんでした。

router-view を App.vue にも Main.vue にも書いているのが不味いの??
→ 名前付きビューってやり方が出て来たけど、何かこれも違いそう。

③ ネスト? そういや Home.vue とかって Main.vue と入れ子構造になってないかな?
→ 予感的中でした!!

ルートの設定をネストにする

以下のようにルートの設定を書き換えたら、無事に目標の状態へと到達しました。

router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Main from '../views/Main.vue'
import Home from '../components/Home.vue'
import Notification from '../components/Notification.vue'
import Email from '../components/Email.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
    children: [
      { path: 'home', name:'home', component: Home },
      { path: 'notification', name:'notification', component: Notification },
      { path: 'email', name:'email', component: Email },
    ]
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

トライアンドエラーあるのみンゴね〜。

参考記事

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

【 Vue + Vuetify 】v-navigation-drawer を固定表示のままリンク遷移させる方法

はじめに

皆さんおはこんばにちは。
エンジニア歴1年目、現場未経験の弱小自称 Web エンジニアです。

今回は、Twitter PC版のように
サイドナビゲーションバーを表示させたまま画面遷移させるべく
試行錯誤した経緯を記録しようと思い、本記事の作成にいたった次第でございます。

やりたいこと

  • リンクをクリックしてページの切り替えをしても、常に左側のドロワーが表示されるようにする

成功例.gif

先に結論から述べておくと
ルートの設定を入れ子構造にすれば無事に解決しました。

最初のコード(失敗例)

特に何の考えもなく実装しようとした最初のコードを以下に記載します。

App.vue
<template>
  <v-app>
    <v-main>
      <router-view />
    </v-main>
  </v-app>
</template>

<script>

export default {
  name: 'App',
};
</script>


Main.vue
<template>
    <div>
        <SideNav />
            <v-content>
                <v-container fluid fill-height align-start justify-center>
                    <router-view />
                </v-container>
            </v-content>
    </div>
</template>

<script>
import SideNav from '../components/SideNav.vue'

export default {
    components: {
        SideNav,
    }
}
</script>


SideNav.vue
<template>
    <v-navigation-drawer permanent absolute>
        <v-list dense nav>
            <v-list-item
                v-for="item in items"
                :key="item.title"
                :to="item.link"
            >
                <v-list-item-icon>
                    <v-icon>{{ item.icon }}</v-icon>
                </v-list-item-icon>

                <v-list-item-content>
                    <v-list-item-title>{{ item.title }}</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
        </v-list>
    </v-navigation-drawer>
</template>

<script>
export default {
    data() {
        return {
            items: [
                {title: 'ホーム', icon: 'mdi-home', link: {name: 'home'}},
                {title: '通知', icon: 'mdi-bell', link: {name: 'notification'}},
                {title: 'メール', icon: 'mdi-email', link: {name: 'email'}},
            ]
        }
    }
}
</script>


router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Main from '../views/Main.vue'
import Home from '../components/Home.vue'
import Notification from '../components/Notification.vue'
import Email from '../components/Email.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
  },
  {
    path: '/home',
    name: 'home',
    component: Home,
  },
  {
    path: '/notification',
    name: 'notification',
    component: Notification,
  },
  {
    path: '/email',
    name: 'email',
    component: Email,
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

最初の失敗2.gif
↑GIF画像の通りリンクをクリックした瞬間、ドロワーが消えてしまう!!

リンク先に遷移自体は出来ているようなのに何故……

試行錯誤の経緯

自分が思い描くドロワーを実装させる為にはどうすれば良いのだろうか。
拙者が無い頭をひねって導き出した対処法は以下の3つ。

v-navigation-drawer に何かそういうプロパティ無いの??
→ ドキュメントなどを読み漁ったかぎり、見つかりませんでした。

router-view を App.vue にも Main.vue にも書いているのが不味いの??
→ 名前付きビューってやり方が出て来たけど、何かこれも違いそう。

③ ネスト? そういや Home.vue とかって Main.vue と入れ子構造になってないかな?
→ 予感的中でした!!

ルートの設定をネストにする

「ホーム画面」「通知画面」「メール画面」
それぞれを形作る Home.vue、Notification.vue、Email.vue は
Main.vue に内包されるコンポーネントになります。

そのため、以下のようにルートの設定を入れ子構造にしてあげることで
目標とする状態へと到達することが出来ました。

router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Main from '../views/Main.vue'
import Home from '../components/Home.vue'
import Notification from '../components/Notification.vue'
import Email from '../components/Email.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
    children: [
      { path: 'home', name:'home', component: Home },
      { path: 'notification', name:'notification', component: Notification },
      { path: 'email', name:'email', component: Email },
    ]
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

トライアンドエラーあるのみンゴね〜。

参考記事

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

【Vue + Vuetify】v-navigation-drawer を固定表示のままリンク遷移させる方法

はじめに

皆さんおはこんばにちは。
エンジニア歴1年目、現場未経験の弱小自称 Web エンジニアです。

今回は、Twitter PC版のように
サイドナビゲーションバーを表示させたまま画面遷移させるべく
試行錯誤した経緯を記録しようと思い、本記事の作成にいたった次第でございます。

やりたいこと

  • リンクをクリックしてページの切り替えをしても、常に左側のドロワーが表示されるようにする

成功例.gif

先に結論から述べておくと
ルートの設定を入れ子構造にすれば無事に解決しました。

最初のコード(失敗例)

特に何の考えもなく実装しようとした最初のコードを以下に記載します。

App.vue
<template>
  <v-app>
    <v-main>
      <router-view />
    </v-main>
  </v-app>
</template>

<script>

export default {
  name: 'App',
};
</script>


Main.vue
<template>
    <div>
        <SideNav />
            <v-content>
                <v-container fluid fill-height align-start justify-center>
                    <router-view />
                </v-container>
            </v-content>
    </div>
</template>

<script>
import SideNav from '../components/SideNav.vue'

export default {
    components: {
        SideNav,
    }
}
</script>


SideNav.vue
<template>
    <v-navigation-drawer permanent absolute>
        <v-list dense nav>
            <v-list-item
                v-for="item in items"
                :key="item.title"
                :to="item.link"
            >
                <v-list-item-icon>
                    <v-icon>{{ item.icon }}</v-icon>
                </v-list-item-icon>

                <v-list-item-content>
                    <v-list-item-title>{{ item.title }}</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
        </v-list>
    </v-navigation-drawer>
</template>

<script>
export default {
    data() {
        return {
            items: [
                {title: 'ホーム', icon: 'mdi-home', link: {name: 'home'}},
                {title: '通知', icon: 'mdi-bell', link: {name: 'notification'}},
                {title: 'メール', icon: 'mdi-email', link: {name: 'email'}},
            ]
        }
    }
}
</script>


router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Main from '../views/Main.vue'
import Home from '../components/Home.vue'
import Notification from '../components/Notification.vue'
import Email from '../components/Email.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
  },
  {
    path: '/home',
    name: 'home',
    component: Home,
  },
  {
    path: '/notification',
    name: 'notification',
    component: Notification,
  },
  {
    path: '/email',
    name: 'email',
    component: Email,
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

最初の失敗2.gif
↑GIF画像の通りリンクをクリックした瞬間、ドロワーが消えてしまう!!

リンク先に遷移自体は出来ているようなのに何故……

試行錯誤の経緯

自分が思い描くドロワーを実装させる為にはどうすれば良いのだろうか。
拙者が無い頭をひねって導き出した対処法は以下の3つ。

v-navigation-drawer に何かそういうプロパティ無いの??
→ ドキュメントなどを読み漁ったかぎり、見つかりませんでした。

router-view を App.vue にも Main.vue にも書いているのが不味いの??
→ 名前付きビューってやり方が出て来たけど、何かこれも違いそう。

③ ネスト? そういや Home.vue とかって Main.vue と入れ子構造になってないかな?
→ 予感的中でした!!

ルートの設定をネストにする

「ホーム画面」「通知画面」「メール画面」
それぞれを形作る Home.vue、Notification.vue、Email.vue は
Main.vue に内包されるコンポーネントになります。

そのため、以下のようにルートの設定を入れ子構造にしてあげることで
目標とする状態へと到達することが出来ました。

router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Main from '../views/Main.vue'
import Home from '../components/Home.vue'
import Notification from '../components/Notification.vue'
import Email from '../components/Email.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
    children: [
      { path: 'home', name:'home', component: Home },
      { path: 'notification', name:'notification', component: Notification },
      { path: 'email', name:'email', component: Email },
    ]
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

トライアンドエラーあるのみンゴね〜。

参考記事

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

【Firebase】Realtime Database 書き込み/読み取り/削除

書き込み ①push : 要素ない場合 → 追加 すでに要素ある場合→その要素に新しい要素を追加 ②set : 要素ない場合 → 追加 すでに要素ある場合→上書き ③update : 要素ない場合 → error すでに要素ある場合→上書き firebase.database().ref("test").push({ name: payload.name, title: payload.title }) firebase.database().ref("test").set({ name: payload.name, title: payload.title }) firebase.database().ref("test").update({ name: payload.name, title: payload.title }) 読み取り ①1回読み取り → once() ※1回だけのみトリガーすることが可能です。 ※ その後再びトリガーされることはありません。 var commentsRef = firebase.database().ref('tests') commentsRef.once('value').then(function(snapshot) { console.log(snapshot.val()); }); 削除 ①remove() ※ 他の書き込みオペレーション(set() や update() など)の値として null を指定する方法でも削除可能。 firebase.database().ref("freetalks").child(payload.id).remove() .then(()=>{ commit("deleteTalk", payload) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む