20200626のvue.jsに関する記事は12件です。

コロナ禍を乗り切る!店内の人数をリアルタイムで分析するアプリ「Atsumaritai」

Atsumaritai の目的

https://atsumaritai.web.app/#/

Screenshot_20200626-175558.png
※ 水色の棒人間が人です。

ここから下に記載していることは、アプリの「使い方」ページに書いてあることをほぼコピペしているので、若干、説明調です。ご勘弁を。

本アプリは、スマホのカメラに写っている人の数をリアルタイムで分析することが可能です。そして、分析の結果をリアルタイムで公開することも可能です。

以下、本アプリの利用想定例です。
飲食店経営者のAさんは本アプリを使用して、店内の人数をリアルタイムで分析します。そして、その結果を自社HPで公開します。
Aさんの店によく行くBさんは、来店する前に店のHPを見て、店内のリアルタイムの人数を把握します。混んでいないことを確認して来店します。

cap.PNG
※ 冒頭のアプリイメージと人数は合っていません。

コロナ禍により、密を避けた行動が重要です。
店やジムのオーナーは、本アプリによって店内の人数をリアルタイムで公開することによって、
顧客は人が少ない時間を見計らって来店することができます。

現時点(2020/06/23)では、本アプリの利用による料金の発生はありません。

必要なもの

WiFiに接続されている、あるいはSIMカードの入ったインターネットに接続可能なスマホ1台が必要です。前に使っていたが、今は使っていないスマホが手元にあればお使いください。

使い方

  1. 人数を分析したい方向にスマホを向ける
  2. 「スタート」を押して計測を開始
  3. 複数台のスマホで計測する場合は、同じアカウントでログインした状態で、「スタート」を押して計測を開始
  4. 埋め込みコード(このページ下部に記載)を自社HPに貼り付け、リアルタイムの人数を公開する
  5. 分析を終了するには「ストップ」を押す

本アプリの分析によって、顔や体などの個人が特定可能な写真の取得(サーバーへのアップロード)は行っておりません。本アプリによる分析結果である人数のみを取得しています。

また、本アプリの分析結果である人数は、あくまでも概算値としてご使用ください。
以下に当てはまる条件下では分析の精度が落ちる可能性があります。

  • 暗い場所
  • 人同士が重なっている
  • 体の一部が隠れている
  • 早い動作をしている

以下は、本アプリの制限事項です。

  • 1台のカメラで計測可能な最大人数は30人
  • 1台のカメラで計測可能な距離の目安は 20m 以内程度
  • 複数台のスマホで計測する場合は、カメラに写っている範囲が重ならないようにする

自社サイトに分析結果を掲載する方法

本アプリでの分析結果を任意のサイトに埋め込むことが可能です。
以下のコードを、本アプリでの計測結果を表示したいサイトのbody部分に埋め込んでください。

<span id="covid"></span>

以下のコードをサイトのbody直後に埋め込みます。このコードはログイン後にユーザー毎に生成されます。

<script src="https://covid-3igou5xrgq-an.a.run.app/covid.js?uid=abcdefg123456789"></script>

埋め込みコードをHPのデザインに合わせてカスタマイズ

css で埋め込むHPのデザインに合わせてカスタマイズしてください。下記は、冒頭に貼っているHPのキャプチャに埋め込んでいるコードです。

<div class="covid-div">
    <p>現在店内に<span id="covid"></span>人います。</p>
</div>
<style type="text/css">
    #covid {
        color: indigo;
    }
    .covid-div {
        padding-top: 15px;
        margin-left: auto;
        margin-right: auto;
        width: 8em;
        text-align: center;
        background-color: rgba(255, 135, 30, 0.3);
        color: black;
        border-radius: 25px 25px;
    }
</style>

問い合わせ

問い合わせは以下までお願い致します。改善要望など何でもOKです。また、実際に使ってくれる店のオーナーの方、大募集しています。もし、使ってもいいよっていう方がいらっしゃれば、試験的な要素が強いので、HPの埋め込みなども積極的にお手伝いします!!!

bouldering.qr.app@gmail.com

まとめ

ピンときている方も多いと思いますが、サービス名は「サウナイキタイ」さんからインスパイアされています。開発者たちがサウナが好きすぎる、かつ、「サウナイキタイ」さんも好きなので、リスペクトの思いを込めたのと、コロナ禍が終わってまた皆で集まれる日を願い、Atsumaritai(集まりたい)にしました。
https://sauna-ikitai.com/

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

IndexedDB + Dexie.js で CRUDの作成、Vue CLI版

概要

IndexedDB で、Dexie.js ライブラリを使用した
CRUD の作成となります。
vue-router と組み合わせて、ほぼサーバレス的な構成にできるか検討してみました。

・ブラウザ内の、LocalStorageと似ているイメージで
 データは、他のPCから利用できない為。
 オフライン機能で、何か作る場合は。良さそうでした

・開発の面では、通常DBサービス起動も不要で
 Vue-cli サービスのみで。開発できるので準備は楽でした
 

構成

Vue CLI
dexie : 3.0.1
vue: 2.6.11
vue-router

参考

https://developer.mozilla.org/ja/docs/Web/API/IndexedDB_API

https://dexie.org/

https://qiita.com/yamayamasan/items/a4297e724b86f4a00fd2

vuex 追加

npm install --save dexie

package.json

https://github.com/kuc-arc-f/vue_spa3b_1crud/blob/master/package.json


実装など

・db定義
db-name, store ,db-version
storeは、テーブルみたいな扱いのようです

var db = new Dexie("friend_database");
    db.version(1).stores({
    friends: 'name,shoeSize'
});

・書く、読み込み

db.friends.put({name: "Nicolas", shoeSize: 8}).then (function(){
    return db.friends.get('Nicolas');
}).then(function (friend) {
    alert ("Nicolas has shoe size " + friend.shoeSize);
}).catch(function(error) {
    alert ("Ooops: " + error);
});

CRUD の参考

・create
https://github.com/kuc-arc-f/vue_spa3b_1crud/blob/master/src/components/DexieTasks/new.vue

・index
https://github.com/kuc-arc-f/vue_spa3b_1crud/blob/master/src/components/DexieTasks/Index.vue

・show
https://github.com/kuc-arc-f/vue_spa3b_1crud/blob/master/src/components/DexieTasks/show.vue

・edit
https://github.com/kuc-arc-f/vue_spa3b_1crud/blob/master/src/components/DexieTasks/edit.vue


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

【Vue】学習開始4週目で覚える内容

4週目で学ぶべきこと

  • フック関数
  • カスタムディレクティブ
  • フィルター
  • ミックスイン
  • Vue Router

フック関数

  • フック関数:プログラムの中に独自の処理を割りこませるために用意されている仕組み。
関数名 詳細
bind 初めて対象のhtmlタグに紐づいた時に、呼ばれる
inserted カスタムディレクティブと紐づいた要素が親Nodeに挿入された際に呼ばれる
update コンポーネント内でデータ更新が行われたタイミングで呼ばれる
子コンポーネント更新される
componentUpdated コンポーネント内でデータ更新が行われたタイミングで呼ばれる
子コンポーネント更新された
unbind htmlタグとの紐付けが解除された時点で呼ばれる

カスタムディレクティブ

  • 引数はel, binding, vnode, oldVnode。 ※基本はel, bindingを使う
  • フック関数の中で、bind, updateは頻出のため、function関数で省略可能
  • elhtmlタグのことを指す。
main.js
//カスタムディレクティブ定義方法①
Vue.directive("sample", {
  bind(el, binding) {
     el.style.color = 'red';
  }
});

//カスタムディレクティブ定義方法②
Vue.directive("sample", function(el, binding) {
  el.style.border = 'red';
});

◆ binding.value

App.vue
<template>
  <!-- "v-sample"は、カスタムディレクティブ -->
  <p v-sample="red">Home</p>
</template>
main.js
Vue.directive("sample", function(el, binding) {
  //v-sample="red"の値を、binding.valueで受け取る
  el.style.color = binding.value;
});

◆ 引数:arg

App.vue
<template>
 <!-- "v-sample:solid"で、引数を指定する -->
  <p v-sample:solid="red">Home</p>
</template>
main.js
Vue.directive("sample", function(el, binding) {
  //"v-sample:solid"の引数を"binding.arg"を用いて取得する
  el.style.samplestyle = binding.arg;
});

フィルター

  • Vue.filterで、フィルターを作成し、パイプを使って適用する
main.js
//フィルター名:"UpperCase" input値を"大文字"に変換する
Vue.filter("UpperCase", function(value) {
  return value.toUpperCase();
});
App.vue
<template>
  <!-- "Vue.filter"で定義した"UpperCase"を挿入する -->
  <h1>{{ answer | UpperCase }}</h1>
</template>

<script>
export default {
  data() {
    return {
     //属性値:answer 初期値:"hello world!"
      answer: "hello world!"
    }
  }
};
</script>

ミックスイン

  • export constを用いてミックスインを定義する
  • ミックスインを定義したファイルをimport, exportを使って反映させる
sample.js
//ミックスイン名:sampleを定義する
export const sample = {
  data() {
    return {
     //属性値:answer 初期値:"hello world!"
      answer: "hello world!"
    }
  }
};
App.vue
<template>
  <p>{{answer}}</p>
</template>

<script>
//ミックスインを定義したファイル名をインポート
import { sample } from "@/sample";

export default {
  //ミックスインで定義した内容をエクスポートし、反映させる
  mixins: [sample]
};
</script>

Vue Router

◆ router.js

router.js
//vue-routerをインポートする
import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";

//Router(プラグイン)を適用する
Vue.use(Router);

//new Routerによって、パスルーティングを指定
export default new Router({
  routes: [
    {
      //path, componentを指定
      path: "/",
      component: Home
    }
  ]
})

◆ main.js

main.js
//vue-routerを追加する
import router from "./router"

new Vue({
  //routerを追加
  router: router,
  render: h => h(App)
}).$mount("#app");

◆ App.vue

App.vue
<template>
  <!-- "router-view"を使用することで、設定したルーティングを適用する -->
  <router-view></router-view>
</template>

同シリーズ

参考文献

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

【Vue.js 】 単一ファイルコンポーネント

はじめに

開発規模が大きければ大きいほど、ソースコードも煩雑になっていく。そのため、機能ごとにファイルを切り分け、モジュール化することで保守性や再利用性を高めていく。

モジュール・・・機能ごとにクラスや関数を1つのファイルにまとめたもの

単一ファイルコンポーネントとは

単一ファイルコンポーネントとは、Vue独自のフォーマットでコンポーネントの定義方法の1つ。アプリケーションのコンポーネント構築に特化したモジュール管理を行うことができる。
Vueのコンポーネント(モジュール)を単独のファイル(拡張子が.vueとなる)として作成する機能。この単一ファイルコンポーネントは<template> <script> <style>の3つのブロックで構成される。

component.vue
<template>
<!-- テンプレートブロック -->
</template>
<script>
// scriptブロック
</script>
<style>
// styleブロック
</style>

ビルド

単一ファイルコンポーネントはVue独自のものなので、そのままブラウザに反映させても動作しない。ブラウザが解釈できるようにファイルをビルド(変換)してあげる必要がある。
Vue CLIを利用することで、単一ファイルコンポーネントを使用するためのビルド環境を楽に構築することができる。
※Vue CLIを使用するにはNode.jsのダウンロードが必要になる。

Vue CLI 公式ドキュメント
Node.js 公式サイト

template

templateタグ内にはHTMLでテンプレートを記述していく。
ルート要素が1つになるように記述していく。

sample.vue
<template>
  <div>
    <h1>タイトル</h1>
    <p>テキストテキストテキスト</p>
  </div>
</template>

script

scriptタグ内にはjavascriptを記述していく。(設定次第でtypescriptも使用可能)
Vueのtemplateオプション以外のオプションオブジェクトをここに記述することができる。
コンポーネントを必要な場所でimport(読み込み)できるようにexportしておく。

importとexportについてはこちら

sample.vue
<script>
export default {
// オプションオブジェクトを記述
}
</script>

style

styleタグ内にはコンポーネントのCSSを記述していく。(設定次第でsassなどのスタイル言語も使用可能)
スタイルにスコープをつけて記述することもできる。
スコープ付きCSSについてはこちら

sample.vue
<style>
// ここにCSSを記述していく
</style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub Pages に Vue.js を Hosting する

GitHub Pages は静的ページや SPA を無料でサクッと公開したい時に便利ですね。
でも、dist/ ではなく docs/ に成果物を配置する必要があります。

やり方

プロジェクト直下に vue.config.js を作成します。

vue.config.js
module.exports = {
  outputDir: './docs',
}

ビルドすると docs/ 以下にビルドしてくれます。

$ yarn build

あとは master に $ git commit して $ git push すれば反映されるはずです。

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

【Nuxt.js】Nuxt文法編:v-slot(中級)

分割代入

簡単にいうと別の場所で
別の変数名をきめれて
値を代入できること
MDN分割代入

というより見た方が早いですね?
親で子の値を使えちゃいます!

❓どんな時に使うか
 まとまったdataを子で管理したい時に使います!

Text.vue
<template>
 <div class="title title-page">
   <slot v-bind:user="user">
     {{ user.lastName }}
   </slot>
 </div>
</template>

<script>
export default {
 data () {
   return {
     user: {
       firstName: 'first',
       lastName: 'last',
     },
   }
 },
}
</script>
index.vue
<template>
 <div class="page">
   <Text>
     <template #default="sample">
       {{ sample.user.firstName }}
     </template>
   </Text>
 </div>
</template>

<script>
import Text from '~/components/Text.vue'

export default {
 components: {
   Text,
 },
 data () {
   return {
   }
 },
}
</script>

動的なスロット名

まずは動的引数を理解しましょう?
https://jp.vuejs.org/v2/guide/syntax.html#動的引数

【解説/index.vue】
リンク
 これと全く同じです❗️
 リンク

Link.vue
<template>
  <a :[attributeName]="url"> リンク </a>
</template>

<script>
export default {
 data () {
   return {
     attributeName: "href",
     url: "/"
   }
 },
}
</script>
index.vue
<template>
 <div class="page">
   <Link />
 </div>
</template>

<script>
import Link from '~/components/Link.vue'

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

これをslotで…

できるのですが、
簡単な方法があります!笑

v-bind="$attrs"
これを使います!

【解説】
属性自体を親から渡したい時
v-bind="$attrs"を使います?
今回の場合はaタグの
href属性ごと渡してます。

Link.vue
<template>
  <a v-bind="$attrs">リンク</a>
</template>

<script>
export default {
}
</script>
index.vue
<template>
 <div class="page">
   <Link
     href="/"
   />
 </div>
</template>

<script>
import Link from '~/components/Link.vue'

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

まとめ

置き換えのできる3つをまとめました!

slot:テキスト
props:それ以外、クラス付与などに便利
v-bind="$attrs":属性自体

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

Vue.js+WordPressでつくったサイトがGoogleにインデックスされるまでがんばった話

GoogleはJavaScriptを実行後にインデックスしてくれるそうですね。クローラーのレンダリングエンジンはChromeの最新バージョンと大体同じで、SPA化したサイトでもちゃんと読み取ってくれるらしい。

それならば、と思い切って全面的にVue.jsを導入。WordPressでREST APIを吐き出し、フロントでAPIを叩いてVue.jsで表示、みたいなことをしてみました。IE11にも対応したのでChromeだったら最悪41ぐらいなら余裕だろ、と思ってサイトを公開しました。

ところがSearch Consoleでインデックス登録をリクエストしたところ

エラーが発生しました
問題が解消しない場合は、数時間後にもう一度お試しください

というなんのヒントも得られないエラーが発生。何回やってもダメ。公開URLのテストをやってもそのテスト自体が同じエラーで全く進みません。そして当然インデックスもされていない。

「JavaScriptで生成されたものがインデックスされるのは時間がかかる」という情報は事前に得ていたので、きっとそれ関連のエラーなんだろう、sitemap.xmlも登録済みだし時間が解決するだろう、としばらく放置したものの、1日経っても5日経っても10日経っても状況は変わりません。

というわけでお客さんから苦情が来る前に何とかしてみました。

解決までの道のり

Search Consoleの「公開URLをテスト」を押しまくってみる

今回発生しているエラーとはエラーメッセージが違うけど、こちらの記事によると、「公開URLをテストでエラーが出てもひたすらボタンをクリックし続けるとあるタイミングでインデックス登録をリクエストする画面が出て来る」ということだったので、ひたすらポチポチ。

すると何のタイミングか全くわからないんだけど、唐突にテストに成功。そしてそのままインデックス登録もリクエスト。これで解決!と思いきや...

ソフト404になってしまいインデックスされない

翌日Search Consoleを確認してみると、

送信されたURLはソフト404エラーのようです

という理由でインデックスされていませんでした。ソフト404...つまりGoogleのクローラー的に「あるって言ってるけど実質このページは無いに等しいよね〜〜〜」と認識されている。

いやいやちゃんと画像と文章がふんだんにあるサイトですよ。
貴方が理解できるというJavaScriptで吐き出してますよ...あれ?つまりJavaScriptが実行されてない?クローラー側が何を見ているのかちゃんと理解する必要がありそう。

ということで前日ポチポチしまくっていた「公開URLをテスト」を再びポチポチ。これでクローラー側で取得しているHTMLがどんなものか見ることができる。

そこでわかったのが、APIが「その他のエラー」という謎のエラーで読み込まれていないということ。おかげでサイトのコンテンツ部分が全く表示されていなかった。そりゃインデックスされないよ...。

「その他のエラー」について調べてみると、読み込みに時間がかかりすぎのために発生しているようだ。タイミングの問題だろうか?と思って何回かテストしてみても全くダメ。普通にサイトにアクセスする分にはスマホで見てもほぼ一瞬で表示されてるしこれ以上どうしたら...。

ダイナミックレンダリングをしてみる

頭を悩ませつつ色々調べているうちに行き着いたのが、ダイナミックレンダリング
「普通のユーザーが見るときはJavaScriptで表示するけど、クローラーが見るときはプリレンダリングしたコンテンツを見せるよ!」というやつ。Google側もクローラーのレンダリングが完璧ではないとわかっているが故にこういうのを勧めているみたい。

そして今回のサイトの裏側ではWordPressが動いている。これは案外楽にできるのでは?というわけでこんな感じにしてみた。bot判定の関数はこちらの記事のものを流用。

functions.php
function is_bot() {
    $bots = array(
        "googlebot",
        "bingbot",
        "Y!J", 
    );

    $ua = $_SERVER['HTTP_USER_AGENT'];
    foreach( $bots as $bot ) {
        if (stripos( $ua, $bot ) !== false) {
            return true;
        }
    }
    return false;
}
index.php
<?php if( is_bot() ) {
    get_template_part( 'bot' );
} else {
    echo '<div id="app"></div>';
} ?>

そしてis_botでtrueになったときしか読まれないbot.phpを用意し、そこにコンテンツを表示するための記載をすれば完成。

試しにSearch Consoleで公開URLをテストしてみると、無事意図したHTMLが表示された!しかも一発で!そのままインデックス登録をリクエストし、翌日には無事インデックスされているのを確認できました。

というわけでクローラーの性能が上がるまではしばらくこのままでいくことになりそうです。サイトの構造によってはJavaScriptだけでゴリゴリやっていくにはSEO的な観点でいくとまだまだ厳しいのかもしれませんね。

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

nuxt+Typescriptで「Property 'component' does not exist on type 'RouteConfig'」のエラーを直す

image.png

nuxtで普通にTypescriptのプロジェクトを作成すると,いきなりエラー出てびっくりした.

エラーメッセージはこんな感じ

・・・
・・・
Property 'component' does not exist on type 'RouteConfig'.

    12 |   chunkName?: string
    13 |   chunkNames?: Record<string, string>
  > 14 |   component?: RouteConfig['component'] | string
       |                           ^
    15 | }
    16 | 
    17 | export interface NuxtConfigurationRouter extends RouterOptions {
・・・
・・・

調べてみると,すでに解決済みっぽい

Githubのissueにこんなものがあった.

vue-router breaks nuxt/types #7502

解決方法としては,

tsconfig.jsonの中にある@nuxt/typesをちょっとだけ書き換える

変更後

・・・
    "types": [
      "@types/node",
      "@nuxt/types@0.7.9+"
    ]
・・・

これだけ.

誰かの参考になれば

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

CSSのみでアコーディオンを実現!detailsとsummaryについて

jsを駆使してアコーディオンを作るたびに思います。
「毎回めんどくさい…もっと秒でやりたい…」
そんな悩みを解決してくれる要素について記事にしました。

使用方法

See the Pen HowToUseDetails&Summary by shake (@mgmgshake) on CodePen.

とっても簡単ですよね!
jsいらずで、HTMLのみでアコーディオンが実装できます♪
また、スタイルを上書きできるので自分好みのアコーディオンにすることもできます。

対応するブラウザ

便利なdetail&summaryですが、対応するブラウザは以下のようになっております。


引用元:https://developer.mozilla.org/ja/docs/Web/HTML/Element/details

…まぁ、いつも通りですね!

chrome,Firefox,safariでは普通に動くが、癖があります。(後述)
IE, Edgeはpolyfillを入れることにより使用可能になります。

↓Edgeで見た状態

使用したpolyfill

details-element-polyfillを使用しました。

結構種類が豊富であり、4個ほどpolyfillを試したのですがまともに動作したのが2つだけでした…。
→シンプルなHTML構造ではないと使えない可能性あり?

ブラウザごとのクセ

デフォルトでついているアイコンを削除する必要があります。
そのアイコンが、ブラウザによって扱いが異なります。

ブラウザ 状態 削除方法
chorome details-markerという特殊な擬似要素 summary::-webkit-details-marker { display: none; }
safari 上と同じ 上と同じ
firefox list-itemなのでlist-style扱い list-style none あるいはlist-style-image設定
IE,Edge 擬似要素(before) ::before content ''

※IE, Edgeはpolyfillにより異なる

firefoxのみ無駄にlist-styleのため、summaryにdisplay: blockをかけるとその瞬間にアイコンが消えます。ちょっと不便。
少し面倒くさいですが、list-styleを消してしまって擬似要素で共通のアイコンを設定したが早いです。

Vue.jsとの共存

条件によってアコーディオンの初期状態を制御したいとき、
detailsにはopenという独自の属性があるので、そちらをv-bindします

<details v-bind:open="closeOption ? false : 'open' ">
    <summary>まぐろ</summary>
    ビチビチ
</details>

closeOptionがtrue→open=falseになる

html
 // 閉じられる(普通のアコーディオン)
<details>
    <summary>まぐろ</summary>
    ビチビチ
</details>

false→open=openになる

html
// 最初から開かれる
<details open="open">
    <summary>まぐろ</summary>
    ビチビチ
</details>

扱いにくい点

開いたら開きっぱなしにしたいときや、ユーザの状態によってアコーディオンそのものを無くしたいときなどがあると思います。

例えば、アンケートページでの任意項目はアコーディオンにして隠して置いて、ユーザが開いたら出しっぱなしにしたい時などです。
(あくまで例なので、details&summaryじゃなくてv-if使えば?というご意見はなしで…!)

summaryにv-if効かない問題

html
// 最初から開かれる
<details open="open">
    <summary v-if="false">まぐろ</summary>
    ビチビチ
</details>


↑openDetail=falseのとき、summaryは消えずに「詳細」という内容に置き換わるだけ

v-ifでsummaryを消すと、勝手にどこかでsummaryが生成されてしまいます
CSSをあてることもできません
→どうやらsummaryのDOMがないと無理やりどこかで付け加える仕様のようです

detailsがopenのときに、summaryをdisplay noneにする記述を書けば解決することができます。

details[open]
    summary
        display none

所感

まだまだ制約が多いので、デザインやアニメーションなどを凝りたい時は従来のアコーディオンの生成方法で良い気がしますが、
時間がなくてちゃちゃっと実装したい時にはいいかもしれません。

新しめのhtmlタグってなかなか触れる機会がなくて困ります…。

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

初めてのLaravel+Vue.js

わたしの100のリスト

Laravelや、Vueを勉強中です。
それらのアウトプットを考えていたところ、家族から「シンプルなやることリストが欲しい!」という要望をもらったので、シンプルなToDoアプリを作成しました。

その時の制作日誌として、学びと反省点をまとめます。
今後学ぶ人たちのヒントになればと思います。

公開URL
https://wishlist100.herokuapp.com

背景、目的

市場には多くのToDoアプリがあるが、多くは短期的なToDoをこなすことを目的としている。

人生では長期的な目標を成し遂げることがとても重要であり、そのような人生でやりたいことに焦点を当てたToDoアプリ「私の100のリスト」を作成した。

類似アプリとの差別化

すでに本アプリのような長期的な目標、夢をリスト化するアプリはあるが、リストを複数作れたり、いくつも追加できたりして本当に重要なものが明確になりにくい。

そこで、本アプリではリストに登録できる項目は100個までとした。

また、やりたいことと、すでにやったことを別々に表示して、やりたいことに集中できるように、またやったことは歴史として振り返られるようにした。

スペック

  • 言語

    • PHP 7.3
    • JavaScript
  • フレームワーク

    • Laravel 5.8.38
    • Vue.js 2.5.17
  • サーバ

    • heroku

開発環境

  • MacBook Air
  • Homestead
  • VSCode

機能一覧

  • ユーザー登録
  • アカウント編集
  • 退会
  • ログイン
  • ログアウト
  • やりたいこと一覧表示
  • やったこと一覧表示
  • カテゴリ別表示
  • やりたいこと作成
  • やりたいこと編集
  • やりたいこと削除
  • 達成、未達成の切り替え

機能詳細

  • ユーザー登録
    • ユーザー名、Email、パスワードによりユーザーを登録する。
  • アカウント編集
    • ユーザー設定から、ユーザー名、Email、パスワードを変更する。
  • 退会
    • ユーザー設定から、退会ボタンの押下によりアラートを出し、OKであれば退会する。
    • 退会処理後は、トップページへ遷移する。
  • ログイン
    • ログイン画面でEmailとパスワードを入力しログインボタンの押下によりログインする。
  • ログアウト
    • ログアウトボタンの押下によりログアウトする。
  • やりたいこと一覧表示
    • やりたいことのリストを表示する。
    • 各項目には、カテゴリアイコン、内容、達成ボタン、登録日時を表示する。
    • 達成ボタンを押すと一覧から除外され、やったことリストに移動する。
  • やったことリスト
    • 達成したことをリストで表示する
    • 各項目には、カテゴリアイコン、内容、達成日時を表示する。
  • やりたいこと作成
    • 入力エリアにやりたいことを入力、カテゴリ一覧からアイコンを選択、登録ボタンの押下により、一覧へ追加する。
    • やりたいことが空で登録された場合、エラーを表示する。
    • やりたいことは30文字以内で入力する。
  • やりたいこと編集
    • やりたいことを押下により、やりたいこと、カテゴリを編集できる。
    • 変更ボタンの押下により、編集を終了し一覧を更新する。
  • やりたいこと削除
    • やりたいことを押下により、削除ボタンを表示する。
    • 削除ボタンの押下により、削除確認のアラートを出す。OKであれば一覧から削除する。
  • 達成、未達成の切り替え
    • 各やりたいことの達成ボタンを押下により、やったこと一覧へ移動する。
    • やったこと一覧において、Undoボタンの押下により、やりたいこと一覧へ移動する。

DB設計

  • Usersテーブル・・・LaravelのAuthで作成されたテーブルをそのまま採用。
    • id, name, email, email_verified_at, password, remember_token
  • Wishesテーブル
    • id, user_id
  • Itemsテーブル
    • id, list_id, category_id, text, done_flg
  • Categoriesテーブル
    • id, name

Users:Wishes = 1:1
Wishes:Items = 1:多
Items:Categories = 1:1

躓いた点、学び

1. bladeテンプレートでいくかVue.jsのコンポーネントでいくか悩んで手戻り発生

リストの表示、アイテムの追加、切り替え、削除などは、ページ遷移なしでSPA風に作った。

ただ、どこからどこまでシングルページのアプリとするか、できるところだけやってみるか、当初からどうするか迷っていた。

まずはbladeテンプレートで作ってみようという考えで作って、「モーダルで操作するならページ遷移しない方がよくない?」と、途中から方針変更。

その結果いろいろ作り直し。
最終的なコードももっとスマートな構成があるかも。

結論:方針をしっかり決めておくこと

結局、方針がしっかり定まっていなかったから。

モーダル使うなら使うでしっかり決めて、ページ遷移するところ、しないところしっかり決めること。

2. 本番環境で見ることは大事

herokuにアップして公開したが、ローカルではわからない、スマホで触ってみないと気づかない不具合が結構あり。

やりたいことの追加ボタンを押すと入力用のモーダルウインドウを表示するが、いざ入力しようとするとスマホの予測変換が被って入力範囲が見れない・・・。

モーダルの実装に問題があると思うが、これは開発環境じゃ見つけられなかった。
(ちなみに、入力順序を入れ替えて対応しているだけで、まだ根本解決できてません・・・)

結論:現地・現物・現場

三現主義は何にしても重要。

制作時間

ざっくりとした制作時間。
Laravelとvueの実装部分は、この作業はフロント側だなと感じたものを自己流に分けているだけで、何かを拠り所にしているわけではありません。

当初の見積もりに比べて、下記の原因で増加。

  • UIの部分で時間がかかった
  • 上記で挙げたbladeとvueの方針の部分でいろいろ悩んだ
  • Homesteadの環境構築も初めてで時間取られた
工程 見積もり (h) 実質 (h)
機能洗い出し 1 1.5
画面設計   3 3
DB設計 0.5 0.5
モック作成 6 11.5
環境構築 0.5 2
フロント側実装 20 21
サーバー側実装 10 15.5
テスト、デプロイ 5 3
合計 46 56

まとめ

荒削りではありますが、LaravelとVue.jsを使って、一通りのCRUD処理を使ったアプリを作成することができました。

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

【Vue.js】テンプレート構文のあれこれ【基本だけ】

忘備録アウトプット。間違っていたらごめんなさい

テンプレート制御ディレクティブ

v-once

初回だけテキスタバインディングを行う時に使う。似たようなものに、単方向のv-bind,双方向のv-modelがある。

v-pre

要素と子要素のコンパイルをスキップするときに使う。

  • XSS対策
  • マスタッシュ構文をそのまま出力

などに使用される。

v-html

サーバーサイドから取得したhtmlタグを出力する時に使う。

v-cloak

チラツキ防止などに使われる。インスタンスのコンパイルが終わるまで非表示にしたい場合など。(非表示自体はcss等で制御。)

v-text

マスタッシュ構文を使わなくても、vue.js内部にあるデータ(変数)などを表示することができる。

バインディング式

マスタッシュタグ内部のテキストのこと。{{ココ}}

JavaScript式

マスタッシュ構文内には、数列,変数などの処理を含んだ単一式を入れることができる。

 フィルタ

vue.jsでは式の終わりにフィルタを追加できる。vueインスタンスのオプションとして、filtersとして定義(ローカルフィルタ)。下記のように使用できる。ちなみに、フィルタは複数使える。

  • ローカルフィルタ
  • グローバルフィルタ

の二つが存在する。

{{ 式 | フィルタ }}

の形で使用できる。

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

Uncaught TypeError: fs.existsSync is not a functionのエラー解決

状況

Windowsアップデートついでにnode.jsとかモジュールをアップデートしたらelectronでエラーが出るようになった。

Uncaught TypeError: fs.existsSync is not a function'

err.png

環境

  • windows 10 Pro Insider Preview 2004 (20150.1000)
  • node v14.4.0
  • Electron 9.0.4
  • Vue.js 2.6.11
  • Vuex 3.4.0
  • vuetify 2.2.11

原因

ipcRendererが原因のもよう。
レンダラープロセスがノードモジュールにアクセスしようとして失敗しているみたい。

解決方法

vue.config.jsファイルに下記を追記したら動いてくれた。

module.exports = {
  transpileDependencies: [
    'vuetify'
  ],
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true
    }
  }
}

これでレンダラープロセスがノードモジュールにアクセスできるようになりエラーが回避できる。
ただし脆弱性になりうるので、公開するアプリなどでは注意が必要とのこと。

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