20201118のvue.jsに関する記事は11件です。

JavaScriptを学びなおしたくてポートフォリオサイトを作成した話 [Vue.js]

私の中で JavaScript の知識はかなり前から時間が止まったままでした。
最近、JavaScript の歴史を見ていく中で、JavaScript という言語をまた勉強し直したいという気持ちが強くなりました。そこで今回は、モダンなフロントエンド開発を学ぶという意味で Vue.js を使ってポートフォリオサイトを作成しました。
この記事ではサイトを制作する中で感じたこと・意識したことをまとめていきます。

ポートフォリオサイトを作成した経緯

私は、今まで約 3 年ほどプログラミングを勉強してきましたが、身につけたスキルを使って成果物を作るアウトプット型の学習より、ドットインストールや Progate を使った受動的なインプット学習が中心でした。そこでアウトプット型の学習を始めたいと思い、Vue.js を学習してポートフォリオサイトを作成しました。フレームワークとして Vue.js を選定した理由は様々あるのですが、大きく SPA 形式のサイトを構築してみたい以前に Vue.js を少し触ったことがあったためというのがあります。最近、Web 系の情報や、記事のトレンドを見ていると、React.jsNext.jsNuxt.js などの JavaScript フレームワーク名を目にすることが多くなりました。私が JavaScript を学習したのはかなり前になるので、この機会にモダンな JavaScript を学習したいという気持ちが強くなり、この機会にポートフォリオサイトを作ってみることにしました。

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

学習期間が約 2 週間、制作期間として約 2 週間、全体として約 1 ヶ月で開発しました。 Toggle という時間管理ツールを使って開発している時間の記録をしたところ、完成まで 40 時間ほどかかっていました。
以前 Web 技術の概要を勉強したときに HTML / CSSJavaScript を触ったことあり知識はあったのですが、かなり学習期間が空いてしまっている状態からのスタートでした。そのため学習期間を含めてかなり遅いペースでの開発にはなりましたが、Vue.js の教材や情報が充実しており、理解しながら進められたので結果的にモダンな環境での開発ができたことの達成感がすごくありました

主な使用技術と概要

技術 使用バージョン 説明
Vue.js 2.6.12 JavaScript フレームワーク
VueCLI 4.5.7 Vue 用コマンドラインインターフェース
Vuetify 2.3.13 マテリアルデザインフレームワーク
Vue Router 3.4.6 Vue 用のルーティングプラグイン
Netlify 静的サイトホスティングサービス

ポートフォリオサイトに含めたもの

ポートフォリオサイトを作るに当たって様々なエンジニアの方のポートフォリオサイトを拝見しました。作成しているページや記載している内容はそれぞれ個性があって面白いと感じました。かなり悩んだのですが、その中で以下の4つのページを作成することにしました。

  • 簡単なプロフィール
  • 扱える技術
  • 作成したもの
  • 連絡先・お問い合わせ

今までほとんど成果物を作って来なかったため、載せられるものが少なく寂しい感じになってしまいました。今後、個人制作で作ったものを載せてにぎやかにしていきたいです。扱える技術ページには今まで勉強したことのある技術をアイコンとレーティング(★)で載せました。今まで幅広くいろんな技術を勉強してきましたが、それぞれの知識が浅く、アウトプットを通して技術力を底上げしていかなければと危機感も覚えました。

開発で意識したこと

モダンな JavaScript の理解と開発環境構築

ポートフォリオサイトを作る際に、WSL2 が気になっていたこともあり、ローカルの開発環境として WSL2 の環境に Ubuntu 20.04.1 LTS をインストールし Vue.js の開発環境を構築しました。 最近のフロントエンド界隈について調べる中で Node.jsWebpack などの用語は聞いたことあったのですが、使えると何が便利なのかわからず、恥ずかしながら今まで食わず嫌いしていました。今思えばもっと早くに触れておくべきでした。そんなときに Qiita で見かけた@yukiji さんのJavaScript 学習ロードマップという記事に影響を受け、記事内で紹介されていたエンジニア Youtuber さんの動画を見漁りました。ここで Web 技術の流行り廃りが激しいこと、自分が学んできた JQuery等の技術はすでに廃れつつあるということを目の前にしモダンな技術、流行りの技術を取り入れる姿勢を大事にしていかなければならないという思いがより強くなりました。プロジェクトは Node.js をインストールして使うことのできるパッケージマネージャーnpmを使いインターネット上の記事を参考に作成しました。この時点で WebPacknode_module などを目にする機会が多くなり少しパニックになったのを覚えてます。それと同時に VueCLI が簡単な対話形式で簡単に動作するプロジェクトを生成してくれること、開発用のサーバーでのホットリロードがすでに用意されていることなど驚くことが多く、これから開発していくことに対してとてもワクワクしてたのを覚えてます。TypeScript での開発も視野に入れていたのですが、学習リソースに合わせるため今回は JavaScript での開発環境を構築しました。

Vue.js の学習

実は 1 年ほど前に Vue.js を勉強していた時期があります。そのときに学習していた書籍がこちら。

基礎から学ぶ Vue.js
mio さん(@mio3io)が執筆した 猫本 の愛称親しまれている有名な 1 冊です。

この記事を執筆した 2020 年 11 月現在、改定 2 版が出版されています。画像は改定前の表紙になります。

書籍サポートページが充実しているので、動くものを見ながら学習できたのが良かったです。

昨年、書店で購入して読み進めていたのですが、忙しかったこともあり、積読になってしまっていました。触りの部分しか読めていなかったため、中途半端な理解のまま Vue で成果物を作ることなく、令和を迎えてしまいました。
今回、勉強する際に、改めて最初から読み直し、重要なところはメモを取るなどして少しづつ理解を進めていきました。それと同時に Udemy で人気が高かった超 Vue.js 2 完全パック - もう他の教材は買わなくて OK! (Vue Router, Vuex 含む)を購入し、猫本と並行して学習していきました。かなりボリュームがありますが、1 回購入すれば何度も見れるということもあり、再生速度を変えながら通勤時間などで繰り返し見るなどして学習を進めました。どちらも半分くらいまで見終えたぐらいから構築した環境で開発をはじめました。なお Vue.js は公式ドキュメントも充実しているのでとても学習しやすいと感じました。

ポートフォリオサイトを作成し始めるタイミングでちょうどよく Vue3 がリリースされたのですが、書籍と Udemy の講座動画の内容に合わせるため、今回は Vue2.x 系を利用して制作しています。

レスポンシブデザインと UI

今はモバイルファーストと言われるくらいにモバイル環境(タブレット・スマートフォン)への対応が必要不可欠となっています。今回の制作でもレスポンシブデザインに対応しながらおしゃれなデザインにしたいという思いがありました。レスポンシブデザイン対応はかなり難しい印象が強くあり、今まで意識してできていなかったのですが、Vuetify というマテリアルデザインフレームワークを導入することで柔軟に対応できました。もともと Booststrap をよく使っており、BootstrapVue というライブラリを使おうとしていたのですが、公式サイトのデザインに惹かれて今回は Vuetify を採用しました。Vuetify はコンポーネント単位でデザインしていくため、Vue.js の知識が必要ですが、同時にコンポーネントの概念について勉強して対応しました。レスポンシブ対応するときにブレークポイントを使ってレイアウトするのですが、以前 Bootstrap を使った経験があったので、直感的に理解しやすくそこまで難しいと感じることはありませんでした。簡単な記述でマテリアルデザインのコンポーネントを使うことができるので、使っていて楽しかったです。レスポンシブデザインとして、公式ドキュメントとサンプルではドロワーメニューとして以下のデザインが採用されています。

ハンバーガーメニューライク
このデザインがマテリアルデザインの方針としては正しいみたいです。しかしハンバーガーメニューは今まで使ってきて、個人的にあまり好きではありませんでした。UX の観点で否定的な意見を目にすることがあり、モバイル向けメニュー実装をする上どのように実装するかで悩みました。

そこで見つけたのが v-bottom-navigation コンポーネントです。

components/Navigation.vue
<template>
  <div>
    <v-bottom-navigation color="white" dark grow shift app>
      <v-btn v-for="item in items" :key="item.link" :to="item.link">
        <span class="white--text">{{ item.name }}</span>
        <v-icon>{{ item.icon }}</v-icon>
      </v-btn>
    </v-bottom-navigation>
  </div>
</template>

これだけでナビゲーションバーを作ることができます。v-forディレクティブで中身を表示しています。
今回は思い切ってこのナビゲーションを採用してみました。モバイル向け Twitter や Youtube の公式アプリで使われているようなコンポーネントですね。しかし、このままだと PC 用のナビゲーションバーとモバイル向けのナビゲーションバーが混在してしまうため、App.vue ファイルの中でブレークポイントによってコンポーネントの出し分けをしています。ナビゲーションには公式プラグインの Vue Router を使用しています。ページのパスと呼び出したいコンポーネントを指定するだけでルーティングの制御を自動で行ってくれるのが便利でした。

src/App.vue
<MyNavigation class="hidden-md-and-up ma-0"></MyNavigation>

上の例だとコンポーネントのクラスにブレークポイント md 以上(タブレットやラップトップパソコン以上の画面サイズ)では bottom-navigationコンポーネントを表示するように指定しています。これだけの記述で出し分けができるのはすごいですね。

アイコンでページイメージがわかりやすくて好きです。アイコンは Fontawesome 使用

Vuetify はまだ日本語のドキュメントなどが少なく、コンポーネントの使い方について調べるのが少し大変な部分もありましたが、公式のドキュメントを読み込むことで一通り使うことができました。今の段階でも使えるコンポーネントが使い切れないくらいあるのですが、今も開発が進んでおりバージョンが上がり続けているため、今後のコンポーネント追加や機能追加にも期待しています。Bootstrap などの他のフレームワークにも言えますが、デザインフレームワークの導入によって簡単な記述でデザインに一体感が出せる、モダンなデザインにできるのはとても便利です。しかし、デザインがどうしても似通ってしまうためデザイン部分では個性が出しにくいと感じました
その部分で Web デザインと UX について考えることも多く、難しさや奥深さを痛感しました。いろんなサイトを見ていく中で、今後、見直していきたい部分でもあります。

サイトホスティング

ページを公開するために必要となる Web ページのホスティングですが、ホスティングサービスとして上記の書籍と Udemy の動画の中で紹介されていたNetlifyを選びました。GitHub のリポジトリと連携してデプロイできるとのことで、半信半疑ではあったのですが、やってみるとたった数分でデプロイできたのがとても感動的でした。無料枠が設けられており、制限はあるのですが、個人的に利用する文については十分すぎるほどなので SPA を手軽に公開したいときの手段としておすすめできます。コードを変更をした際もコミットして連携したリポジトリに対してプッシュするだけでビルドして反映してくれるので便利です

工夫したところ

ページ遷移時のアニメーション

vue の transitionを使って実装しています。watch プロパティで\$route の中身が変化したときに CSS ライブラリの Animate.css を適用させてアニメーションさせています。Animate.css は使ったことがなかったのですが、クラスを適用するだけでアニメーションさせることができて便利だと感じました。アニメーションが入ることでサイトに動きが生まれて華やかになるので、カスタマイズしていて楽しかったです。
アニメーション実装については以下の記事を参考にさせてもらいました。

全体のデザイン統一とアイコン

全体的に黒と白を貴重としたモノトーンな印象のサイトにしています。Vuetify のコンポーネントのデザインと合わせやすく、サイト全体的にまとまった印象になりました。アイコンは主に FontAwesomeDEVICON を使用しました。保有スキルのページでは技術アイコンを表示するために v-iconコンポーネントを使い DEVICON で配布されているアイコンを埋め込んでいます。アイコンには単色とカラーのものがあったのですが、単色のアイコンを使うことでサイトの雰囲気に合わせやすいのが良かったです。

作り終えてみて

ポートフォリオサイトを構築した結果として動くものができたことは大きな成功体験になりました。作成したものが Web 上で公開されていることが不思議な感覚です。ポートフォリオサイトを構築するにあたって JavaScript でモダンなフロントエンド開発をできたことが楽しく、アウトプットして技術を伸ばしていくことの大切さを実感しました。それと同時に、ドットインストールや Progate を何周も学習して満足していた過去の自分が恥ずかしいです。もっとはやく成果物作成を通して新しい技術に触れておきたかったという後悔もあります。今回の開発を通して新しい体験がたくさんありました。初めてのサイト制作であり、まだまだサイトデザインやサイトのパフォーマンスは改善できる余地があると考えています。細かいところまで見ると至らない点は多くあります。その部分をこれから個人開発をしていくなかで、少しづつ改善していきたいです。次は、今回身につけた Vue.js の知識を踏まえながらサーバーサイドレンダリングが特徴的な Nuxt.jsNext.js などのフレームワークや、Firebase を使ったアプリにも挑戦していきたいです。ここまで読んでいただきありがとうございました。

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

Vue.jsでフォームを監視してAPIからデータを取得する

Watchを理解できた具体例

実務で使われているなかでイマイチ理解できていなかった、watchについて理解する為に
Githubからリポジトリ一覧を取得する簡単なアプリケーションをつくりました

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>watch-github</title>
</head>
<body>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
 <!-- axiosをCDN リンクで取得する -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 <!-- VueをCDN リンクで取得する -->

  <div id="app">
    <select v-model="current"> <!-- v-modelでcurrentの値を監視する -->
      <option v-for="topic in topics" v-bind="topics.value">
        {{ topic.name }}
      </option>
    </select>
    <div v-for="item in list">{{ item.full_name }}</div>
  </div>

  <script>
    new Vue({
      el: '#app',
      data: {
        list: [],
        current: '',
        topics: [
          { value: 'vue', name: 'Vue.js'},
          { value: 'jQuery', name: 'jQuery'}
        ]
      },
      watch: {
        current: function(val) {
          // currentの選択肢が変わった時にwatchの処理が行われる
          axios.get('https://api.github.com/search/repositories', {
            params: { q: 'topic:' + val }
          }).then(function(response) {
            this.list = response.data.items
          }.bind(this))
          console.log(this.list);
        }
      },
    })
  </script>
</body>
</html>

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

[Vue.js]watch

watchはどんな時に使うのか

あるデータが変わった時に特定の処理をしたい時に使う 主に非同期処理の時に使用する

 watch:{
//numが変わった時にどんな処理をしたいか
   num:function(){
//非同期処理ではthisを使えないため最初にthisを他に代入しておく
     const vm = this
     setTimeout(function(){
//numを0にする
        vm.num = 0
//2秒後に
        },2000)
        }

理解が怪しいため間違ってるかも。。。また勉強して更新しよう。。。

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

[Vue.js]computed

computedはどう使うのか

//クリックされた回数
    <p>{{num}}</p>
//クリックするたび1増えるボタン
    <button @click ="countUp">+1</button>
    <p>{{lessThanThree}}</p>

//クリックした時に表示される回数。初期値は0
 data:{num: 0},
    computed:{
//クリックが3より上の時と3以下の条件指定 左true:右fauls
       lessThanThree:function(){
         return this.num > 3 ? '3より上' : '3以下'
        }
  },

書き方としてはmethodsと同じ

methodsと書き方が同じなら何故methodsに書かないのか

理由はその対象以外が書き換えられたときにも一緒に動作してしまうから

computedに記述している場合はその条件が書き換わる瞬間だけプログラムが変わるようにしてくれるらしいです。

二重中括弧に複雑な処理を入れるときは必ずmethodsではなくcomputedに入れる

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

【React/Vue】ReactReduxとVuex 概念の互換性

記事について

この記事は、駆け出しエンジニアの僕がVue→Reactの翻訳学習の際に、より理解を深めるために綴っている記事です。
Vueについてはあまり学習しておらず、Reactに重点を置いて学習をしています。

拙い部分があるかもしれませんが、ご指摘・ご意見がある際は是非お願いします。

概念

ReactReduxとVuexでの基本的な概念

ReactRedux: State / ActionCreator / Action / Reducer / Store
Vuex: State / Getters / Mutations / Actions / Store

それぞれの互換性について理解を深めて、React ⇆ Vueの翻訳に役立てる

Reduxの概念について

  1. State: データを保持する領域  
  2. ActionCreator: Actionオブジェクトを生成する関数

  3. Action(object): Actionの情報をもとにステート更新を行う。Storeの「dispatchメソッド」でStoreにActionオブジェクトを渡す
      ※thunkなどで非同期を実装する際にはobjではなく関数をStoreに渡す

  4. Reducer: Actionと現在のステート情報をもとに新しいステートを生成して、Storeに保持する

  5. Store: ステートをもつ。store周りのメソッドをもつ。

Vueの概念について

  1. State: データを保持する領域  
  2. Getters: ステートに加工したデータを返す機能(同期処理only)

  3. Mutations: ステートの変更を行う機能

  4. Actions: 内部でMutationを行いStateを更新する機能(同期処理&非同期処理)

  5. Store: 1~4を保持するオブジェクト

互換性

reduxで非同期を実装する場合は、適応したライブラリのインストールが必要

ReactRedux(x) / Vuex(y) State ActionCreator Action Reducer Store
State ほぼ同義 x x x x
Getters x x x x x
Mutations x x x 同様にstateの値を更新する x
Actions x 非同期のロジックはActionCreatorに記述される Actionは非同期の関数にもなる x x
Store x x x x stateを保持するが、異なる部分もある

まとめ

互換性について記事を書きましたが、VueとReactは似ている部分もありますが別のフレームワークなので、例としてReactにはGettersがなかったりします。
翻訳作業時に足りない機能などは、同等のものを自作などする必要があります。

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

Vue.jsを学ぶ #1

Vue.jsを学習するにあたり、言語化するため記事を作成しました。

ディレクティブ

「v」から始まる属性をディレクティブと呼ぶ。主にデータバインディングを行うために使用される。

データバインディング・・・データと描画を同期させる仕組み。データに変化があれば、自動的にDOMを更新させる。例としてE-mail入力欄に入力した時に確認として入力した文字列を表示させて確認させる仕組みなど。

filename.rb
 <div key="id"></div
② <div v-bind:key="id"></div>

①の場合は文字列、②の場合はv-bindであるためJavaScriptの変数となる。

v-bind=ディレクティブ :value=引数 .sync = "message"=値
引数や値はオプションで付き、②の場合は:key引数のオプションが付属している。

コンポーネント

機能ごとにJavaScript, HTML, CSSを1セットとして他機能とは分離させる仕組み。
つまり上記ファイルをよりスマートに扱うことができる。
例)header...HTML+CSS+JavaScript, main...HTML+CSS+JS, footer...HTML+CSS+JS
このように、ヘッダー・メイン・フッターを独立したコンポーネントとすることで、ファイル管理が容易になる。

2に続きます。

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

【Vue】this.$nextTick。前の処理完了をまってから指定した処理を実行する。

コードは正しいのに、メソッド実行後の処理の出力結果が狙ったものと違うものになってしまう事象が発生。

this.$nextThickを使うことで解消できた。

発生状況

・クリックイベントで変数を変更する操作。
・対象の変数が、親テンプレートでwatchや、getter、setterの対象となっていた。

クリックイベントで変数を変更したが、他のgetterやsetterでその変数を別の値に代入する処理が行われており、watchで変更を自動検知した時に、古いデータに戻っていた

処理の実行順序が入り組んでしまったことが問題。


対処法

処理を意図的に遅らせる$nextTickを使用する。

nextTickとは?

this.$nextTick(() => { 処理 })

callback関数を使って、DOMを更新後に指定した処理を実行する処理。

tickとは時計のチクタク音。次のカッチまで待つということ(と思われる)

グローバルに宣言する「Vue.nextTick(() => { })」も同じ処理になる。

Vue公式 nextTick

記述例

変数が宣言されていない場合に、親のタグで属性「:xxx.sync」で指定した変数xxxを変更する処理。

if(xxx != undefined){
        this.$nextTick(() => {
          this.$emit('update:xxxSync', xxx)
        })
      }

この変数xxxが他の場所で代入されたり、watchされていて、変更したはずのデータが反映されなかったが、nextTickを使ったことで、変更がきちんと反映された。


▼補足
nextTickは極力使用を避ける。各変数の依存が適切に記述され実行順序がクリアであれば、nextTickを使う必要はない。

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

vue-chartjsでグラフのサイズ動的に変えたいな

仕事でVue.jsを使う機会があり、グラフを表示させたかったのでvue-chartjsを使いました。
グラフサイズを動的に変えたかったのですが、壁にぶち当たりまくったのでメモ。

ぶち当たった壁

どうせwidthとheightをしていすればできるんやろ?はいよゆ~^^

<Chart
  :width="graphW"
  :height="graphH"/>

・・・何も変わりませんでした。

答え

vue-chartjs公式ガイドの「動的スタイルを使用したチャート」セクションにがっつり書いてありました。
:styleで指定するみたいです。

おわりに

結論から言うとちゃんと公式ドキュメント読めやって話。。。

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

成果物を作る際に行った、要件定義 DB設計など

こちらの記事にプロダクト実装前に行うべき事がまとめられておりました。
私自身も成果物作成の際に、要件定義などを行ったので、今回の記事はこちらに沿って残していこうと思います。
https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8

成果物のURL▼
http://share-noske55.com/top

要望:解決すべき課題(仮想サービスなので自分があったらいいなと思うサービスを作成)

現状の課題

今回想定したのは、コンビニの廃棄商品について
コンビニでは日々大量の廃棄物が出ている。
本来捨ててしまうその食品を通常価格より安く販売したら、コンビニ側も売上が増え、利用者側(廃棄を買う側)も安く食品を手に入れられる。
幅広く誰でも簡単に使えるように、シンプルに購入できるようなアプリを作る事をゴールとする。

要求

具体的に実装したい機能一覧

① 利用者側は、メールアドレス、パスワードでユーザー登録ができること
② コンビニ側はコンビニ名、支店名、住所、TEL、店長名、メールアドレス、パスワードでユーザー登録ができること
③ コンビニ側と利用者側のユーザ登録画面、ログイン画面はそれぞれ作成する

④ コンビニ側も利用者側も、ログイン・ログアウトできるようにする
⑤ コンビニ側も利用者側も、パスワードリマインダーでパスワード忘れの際に変更対応が可能である
⑥ コンビニ側は商品を投稿機能、商品情報編集機能、商品編集画面から削除できる機能がある
⑦ 利用者側もコンビニ側も、登録された商品を商品一覧画面で一覧表示でみれる
⑧ 利用者側は「購入済でない商品」ならば商品詳細画面内の購入ボタンを押すことができるようにする
⑨ 購入ボタンを押すと、コンビニ側・利用者側の両方に必要な情報を記載したメールを送る
⑩ 購入ボタンを押すと、商品が「購入済」状態になり、一覧画面で表示するときに「購入済」のラベルを付ける
11 「購入済」状態の商品の、商品詳細画面では、購入ボタンが押せないこと
12 自分が購入した商品の、商品詳細画面では、「購入をキャンセル」ボタンが表示されるように
13 コンビニ側のマイページからは、「ユーザ情報の編集」「商品出品」「商品情報編集」「出品した商品の一覧表示」 
  「購入された商品の一覧表示」が行える
14 ユーザ側のマイページでは、「ユーザ情報の編集」「購入済み商品一覧」「購入キャンセル」が行える
15 Twitterで商品を不特定多数の人がシェアできること(ブログにあるようなシェアボタンと同じ)

以上の項目を最低でも組み込んで制作する事を目指しました。

その他注意する事

① 食品を扱うので、食欲をそそるようなサイトカラーで制作をする
② レスポンシブ対応をする
③ CSS設計を行い、回収しやすい状態を目指す。

開発環境

LAMP環境
Laravel 5.8
PHP 7.4
CSS SASS 設計はFLOCSS
laravel.mixを使いコンパイル・バンドルを行った。
フロントは、Vue.js Javascriptを使用

画面設計・データ設計

必要な画面をまず洗い出しました。

今回の場合、ユーザーとコンビニサイドに分けて考えてあります。

ー共有画面ー

1.TOP画面
2.お問い合わせ
3.ヘッダー
4.フッター

ーコンビニサイドー

1.コンビニ側ユーザー登録
2.コンビニ側ログイン
3.コンビニ側プロフィール編集
4.コンビニ側出品した商品一覧
5.商品詳細画面
6.コンビニ側マイページ 
7.コンビニ側購入された商品一覧
8.コンビニ商品出品画面
9.商品編集画面 

ーユーザーサイドー

1.利用者側ユーザー登録
2.利用者側ログイン
3.利用者側プロフィール編集
4.利用者側商品一覧
5.利用者側マイページ
6.利用者側購入した商品一覧

洗い出した各画面をXDを使って作成

今回画面遷移の流れを書かなかったので、次回以降は画面遷移の流れも書き込んでおくとより分かりやすいと感じた。
デザインカンプ Haiki_share.png

データベース設計

ER図を作成して、事前にデータの流れを可視化する事で、開発をスムーズに進める事が出来た。
※後に、マイグレーションを使ってカラムの追加を行っている。
お気に入り機能とカートに入れる機能も後に追加できるように一応テーブルを作成した(現在未実装)
スクリーンショット 2020-11-18 14.09.48.png

データベースの具体的な中身

ユーザー情報

名前
メールアドレス

ストアー情報

店名
支店名
住所
メールアドレス
パスワード

商品情報

商品名
商品カテゴリー
値段
賞味期限
画像
商品出品店舗

商品カテゴリー

カテゴリー名

購入履歴

購入された商品ID
購入したユーザー
出品していたストアー
購入された日時

CSS設計に関して

前回アウトプットを作った際に、Sassを入れるのにてこずったこともあり、今回は最初に何をどこに書くのかを想定して望んだ。
ディレクトリ構成は下記のようになる。
CSS設計.png

命名規則については、https://qiita.com/manabuyasuda/items/dbb76ed36970bec95470
こちらの記事を参考にさせていただきました。
BootStrapのCSSの付け方を参考にして付けている。
深くても、2回ネストする程度の深さを意識して設計を行った。

上記の項目を制作前に設計し臨んだ。

成果物を作り終えて

今回成果物を作る工程に入る前に設計をした事で、作る際にはかなりスムーズに作成する事が出来た。
CSS設計に関しては、まだまだ経験不足を感じる事もあり、かなり不要なディレクトリもあったので今後も経験を積んでこの辺の感覚を掴んでいきたいと思う。

今回は自分用の成果物であった為、このくらいの設計でもよかったが、チームで共有する場合も考慮すると
もっと情報がわかりやすく整理されているものを作成する必要があるとも感じた。

今後は、こちらの参考記事にもあるような詳細な設計を書けるようにしようと思う。

https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8

今後について

laravel、PHPについてはかなり触ったと感じているので、今後はJavaScriptだけを使ったアプリの制作を行い
JavaScriptについての知識を深めると共に引き出しを増やしていこうと考えている。

こちらに関しては、大きなプロジェクトではなく部品をちょこちょこ作成していく。
理由としては、実際の業務でも機能ごとに実装する方が多いと感じた為である。

今回の記事についてはこれで終了とする。

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

Vue.jsでHello World!を表示させる

①Vue.jsのインストール

Vue.js公式サイト右上の「学ぶ」のタブの中から「ガイド」を選択
スクリーンショット_2020-11-18_7_38_59.png
・ガイドの中のインストールを選択
スクリーンショット_2020-11-18_7_39_12.png

インストール画面の中で、CDN(Content Delivery Network)の中から一番上の最新バージョンを使用します。
スクリーンショット_2020-11-18_7_41_15.png

CDNによって、HTMLファイルに下記のコードをコピー&ペーストするだけでVue.jsがインストールされます。

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>

②Vue.jsを使う準備

今回は「Hello World!」と表示するだけなのでjsfiddle.netというオンラインサービスを利用します。
ローカルに開発環境を用意しなくても、HTML・CSS・JavaScriptの簡単なコードの実行・動作確認、編集したコードの保存・共有を行うことができます。
https://jsfiddle.net/

jsfiddleのHTMLのエディタ内に先程のコードをペーストします、これでVueがインストールされました。
スクリーンショット 2020-11-18 8.43.15.png

Vueを書く前に一旦HTMLだけで「Hello World!」を書いてみましょう。

<div id="app">
  <p>Hello World!</p>
</div>

左上の▷Runを押して右下のResult画面に「Hello World!」が表示されればOKです。
スクリーンショット 2020-11-18 8.30.24.png

③Vueを使ってHello World!を表示させる

Vueインスタンスを宣言

JavaScriptのエディタ内でnew Vue()と書くことでVueインスタンスを宣言します。
その中にelプロパティを書くことで、このVueインスタンスがHTMLのどこのタグに対応しているかを指定することが出来ます。

vue.js
new Vue({
 el: '#app'
})

これでVueを使うという宣言が出来ました。
スクリーンショット 2020-11-18 8.32.35.png

Vueインスタンスにデータをもたせる

次にdataオブジェクトをつなげて書き、messageプロパティに「Hello World!」というデータをもたせます。

vue.js
new Vue({
    el: "#app",
  data: {
    message: "Hello World!"
  }
})

pタグにVueのmessageに対応したものを表示させるためには、二重中括弧{{ }}で囲むことでVueのデータとpタグを対応付けることが出来ます。
先程と同様に左上の▷Runを押して右下のResult画面に正しく「Hello World!」と表示されればOKです。

<div id="app">
  <p>{{ message }}</p>
</div>

スクリーンショット 2020-11-18 8.28.46.png

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

もう v-model に惑わされない

はじめに

Vue.js を触り始めて半年経ちましたが、未だに v-model で迷ってしまうことがあります。
何度 v-model 完全に理解した!! と思ったことでしょうか。
もう二度と v-model に惑わされないように、基本的な内容と躓きやすいポイントをまとめていきます。

※ この話を社内勉強会でしたところ、議論があり、もう一度惑わされてしまいました。追記してあります。

前提

本記事に出てくるコードは下記の構成のプロジェクトであることが前提になっています。

  • Vue 2.x
  • TypeScript
  • vue-property-decorator

v-model とは

公式ドキュメント では下記のように紹介されています。

form の input 要素 や textarea 要素、 select 要素に双方向 (two-way) データバインディングを作成するには、v-model ディレクティブを使用することができます。それは、自動的に入力要素のタイプに基づいて要素を更新するための正しい方法を選択します。ちょっと魔法のようですが、v-model はユーザーの入力イベントにおいてデータを更新するための基本的な糖衣構文 (syntax sugar) で、それに加えて、いくつかのエッジケースに対しては特別な配慮をしてくれます。

v-model は糖衣構文

v-modelv-bindv-on をまとめて書くための糖衣構文です。
最も基本的な例として、下記のような input 要素は

<input 
  :value="searchWord" 
  @input="searchWord = $event.target.value"
>

このように書き換えることができます。

<input v-model="searchWord">

双方向データバインディング?

双方向とは 親 → 子、子 → 親 というデータの流れのことを指します。
今回の例で説明するとこんなイメージです。

  • 親コンポーネント → input 要素
    image.png

  • (ユーザからの入力)→ input 要素 → アプリケーション
    image.png

v-model が使えるカスタムコンポーネント

現実的には html 要素を直接扱う機会は少ない

コンポーネント指向で開発をしていると、input 要素は直接使わず、ラップして使うことが多いと思います。
スタイルをプロジェクトに合わせて設定したり、バリデーション機能を付けたりします。
つまり上記の例のように input に v-model を設定することはそんなにありません。それよりも自分で作ったコンポーネントに対して v-model を設定したくなります。

v-model が使えるカスタムコンポーネントの作り方

v-model を使わない双方向バインディングの仕方をもう一度見てみましょう。

<input 
  :value="searchWord" 
  @input="searchWord = $event.target.value"
>

カスタムコンポーネントで v-model を使うためには、この例と同じインターフェースを持つコンポーネントを作ればOKです。
つまり、

  • value プロパティを持つ
  • input イベントを発火して入力された値を渡してくれる

という2つがそろっていればいいのです。
これらを満たすコンポーネントが以下です。

BaseInput.vue
<template>
  <div>
    <p>BaseInput</p>
    <input 
      :value="value"
      @input="$emit('input', $event.target.value)"
    >
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"

@Component
export default class BaseInput extends Vue {
  @Prop({ required: true })
  value!: string
}
</script>

使う側はこんな感じです。

SampleForm.vue
<template>
  <div>
    <p>SampleForm</p>

    <p>Name</p>
    <BaseInput v-model="name" />

    <p>Email</p>
    <BaseInput v-model="email" />

    <p>Address</p>
    <BaseInput v-model="address" />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator"
import BaseInput from "./BaseInput.vue"

@Component({
  components: {
    BaseInput,
  },
})
export default class SampleForm extends Vue {
  name = "default name"

  email = "default email"

  address = "default address"
}
</script>

input 要素と カスタムコンポーネントの違い

v-model は糖衣構文であると言いました。
では BaseInput を v-model を使わないパターンで書いてみましょう。
これが

<BaseInput v-model="name" />
<!-- 誤り -->
<BaseInput 
  :value="name" 
  @input="name = $event.target.value"
>

こうではありません。

正しくはこうなります。

<BaseInput 
  :value="name" 
  @input="name = $event"
>

BaseInput では @input="$emit('input', $event.target.value)" として、input 要素から送られてくるイベントオブジェクトの殻を剥いて value だけを取り出して emit してくれています。
すなわち、受け取る側では $event がそのまま欲しい値となっているのです。

このように、 v-model は糖衣構文といってもどの要素に適用するかで動き方が変わってきます。

input 以外の html 要素へ v-model を適用する

ここまではプレーンな input 要素を例に紹介してきましたが、他の html 要素にも有効です。
v-model はそれぞれの要素に対して次のような組み合わせで働きます。

html 要素 バインドする属性 イベント
input type="text" value input
textarea value input
input type="checkbox" checked change
input type="radio" checked change
select value change

この組み合わせに従うと、例えば checkbox は次のように書き換えられます。

<input
  type="checkbox"
  :value="checked"
  @change="cheked = $event.target.value"
>

<input
  type="checkbox"
  v-model="checked"
>

このように、html 要素ごとにも適用のされ方が異なることに注意が必要です。
公式ドキュメントに例が豊富なので参考になるとおもいます。

オブジェクトを v-model で扱う

ここまでは単体の input 要素に注目してきましたが、複数の値を v-model で扱いたいことが多々あります。
先ほど紹介した SampleForm の例を v-model が使えるコンポーネントに修正してみましょう。

最終的には

<SampleForm v-model="user" />

このようなインターフェースで使えるコンポーネントを目指します。

NG 例

まずはよくある NG 例を紹介します。

※ この NG 例に関しては本当に NG なのかどうか議論がありましたので後述します

SampleFormNG.vue
<template>
  <div>
    <p>Name</p>
    <BaseInput v-model="value.name" />

    <p>Email</p>
    <BaseInput v-model="value.email" />

    <p>Address</p>
    <BaseInput v-model="value.address" />
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"
import BaseInput from "./BaseInput.vue"

interface User {
  name: string
  email: string
  address: string
}

@Component({
  components: {
    BaseInput,
  },
})
export default class SampleFormNG extends Vue {
  @Prop({ default: {} })
  value!: User
}
</script>

このコンポーネントは User オブジェクトをプロパティとして受け取り、子のコンポーネントにそのインスタンスのプロパティ value.name value.email value.address をバインドしています。

実はこのコンポーネントは

<SampleFormNG v-model="user" />

のように呼び出すと、あたかも期待通りに動いているように見えます。

何がダメか

このコンポーネントを使う側で、v-model ではなく :value を使うとどうなるでしょうか。

<SampleFormNG :value="user" />

これも user を v-model に設定したときと同じように動きます。
すなわち、user が勝手に子コンポーネントに書き換えられてしまったということです。

これを許してしまうと、例えばユーザ情報を画面に表示だけし、編集はさせたくないというときに困ります。

何故こうなるのか

オブジェクトや配列の値は string や number などのプリミティブ型と異なり、値が参照渡しされるため、Vue が変更を検知してくれないことが原因です。

もう少し詳しく見ていきましょう。

SampleFormNG.vue
<BaseInput v-model="value.name" />

これは

SampleFormNG.vue
<BaseInput
  :value="value.name"
  @input="value.name = $event"
/>

これと等価です。value は prop ですが、@input で $event が代入されています。
prop の値の変更は Vue では推奨されていません

value.name ではなく string や number などのプリミティブ値であればブラウザのコンソールにこんな感じのエラーが出て、値は変更されません。

vue.runtime.esm.js?2b0e:619 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"

found in

---> <BaseInput> at src/components/BaseInput.vue
       <InputPage> at src/pages/InputPage.vue
         <App> at src/App.vue
           <Root>

value.name を v-model に設定したときは、Vue が value.name の変更を検知せず、スルーしてしまうため v-model が正しく動作しているように振る舞っていたのです。

どうすれば良いのか

では、オブジェクトを v-model で扱いたいときはどうすればいいでしょうか。
やり方は無数にありますが、とにかく prop に変更を加えないという点を守れば OK です。
以下に私が考えた一例を示します。

SampleForm.vue
<template>
  <div>
    <p>Name</p>
    <BaseInput
      :value="value.name"
      @input="onInput({ name: $event })"
    />

    <p>Email</p>
    <BaseInput
      :value="value.email"
      @input="onInput({ email: $event })"
    />

    <p>Address</p>
    <BaseInput
      :value="value.address"
      @input="onInput({ address: $event })"
    />
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"
import BaseInput from "./BaseInput.vue"

interface User {
  name: string
  email: string
  address: string
}

@Component({
  components: {
    BaseInput,
  },
})
export default class SampleForm extends Vue {
  @Prop({ default: {} })
  value!: User

  onInput(event: { key: string }): void {
    this.$emit("input", {
      ...this.value,
      ...event,
    })
  }
}
</script>

ポイント

それぞれの BaseInput の @input で、onInput メソッドを呼び出し、一組のキーと値を持つオブジェクトを渡しています。

    <BaseInput
      :value="value.name"
      @input="onInput({ name: $event })"
    />

onInput では渡されたオブジェクトで this.value を上書きしたものを emit しています

  onInput(event: { key: string }): void {
    this.$emit("input", {
      ...this.value,
      ...event,
    })
  }

このようにすることで prop を直接更新せず、入力時に変更された値だけを更新して emit できます。
コードの重複も少ないです。

(追記)イミュータブル VS 可読性

ここまでの内容を社内の勉強会で話したところ、NG 例のような書き方でも問題ないのではないか、という議論がありました。

実際には今回の例より複雑になることが多い

今回の例では単方向データフローの原則を守る修正を比較的少ないコード量で実現できましたが、それは扱うオブジェクトが単純だからではないか、という意見がありました。

確かにその通りです。例えば user オブジェクトの address が更に複数のプロパティを持つときは、新たに onInputAddress を定義する必要があります。

interface User {
  name: string
  email: string
  address: {
    state: string
    city: string
    street: string
  }
}

onInputAddress(event: { key: string }) {
  this.$emit("input", {
    ...this.value,
    address: {
      ...this.value.address,
      ...event,
    }
  })
}

扱うオブジェクトが複雑になるほど、このような関数を更に定義していく必要が出てしまいます。

フォームコンポーネントはフォームの編集を前提としている

NG 例の実装では、prop として渡したオブジェクトが勝手に変更されるのが良くない、と述べました。

<SampleFormNG :value="user" />

これに対し、フォームのようなコンポーネントに対して、上記のように value だけを渡すケースはほぼありえないのでは、という意見がありました。

確かにそのとおりです。表示だけを責務とする UserInfoCard のようなコンポーネントならありえますが、フォームのように編集を前提とするコンポーネントではこのような使われ方はしません。

結論

以上を踏まえて、下記の条件下では、v-model にオブジェクトを設定する使い方も有りなのではないかという結論に達しました。

  • コードの可読性が大幅に改善するとき
  • フォームのようにオブジェクトの編集を責務とするコンポーネントを書くとき

ただし、Vue.js の経験が浅い開発者がこのコードを見て、v-model にオブジェクトを設定するのが全く問題ないと判断するようなことがあってはいけません。コメントを残したり、チーム内で方針を共有するなどの工夫は必須となります。

まとめ

v-model の基本的な内容とハマりどころについて書いてみました。
ポイントは以下でした。

  • v-model はケースによって若干違う動きをするので注意する
  • 子コンポーネントは親コンポーネントの値を変えてはいけないという原則を守る
  • オブジェクトや配列の変更は検知されないので注意する
  • 可読性が大幅に改善される場合はオブジェクトを v-model でバインドすることを許可してもいいのでは

少し迷うこともある v-model ですが、コードを簡潔に書く上で欠かせません。
また、v-model が使えるコンポーネントを作る、ということはより Vue らしいコンポーネント分割の手助けになるはずです。
使える場面では積極的に使っていきましょう。

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