20200318のvue.jsに関する記事は10件です。

【Vue】喘ぎ声メーカー作ってみた

image.png
スーパー喘ぎ声メーカー
用意した単語をシャッフルして喘ぎ声を生成するツールを作ってみました。
喘ぎ声に限らず、好きな単語を入れて遊べます。

何故作ろうと思ったのか

BL小説書きの友人が「この喘ぎ声の組み合わせさっきも書いたな?
となるので自動生成してくれるツールがほしい
」と言っていたので。
BLに限らずいやらしい小説を書く方への負担を少しでも減らせないかと思い、作成にいたりました。
あとVueの勉強になりそうだと思ったので。
筆者は文系のwebデザイナー(見習い)であり、プログラミングの分野は勉強中です。

使ったもの

作成の流れ

プロトタイプ①

02.png
まずHTMLとJavaScriptのみでプロトタイプを作成。
「生成!」ボタンを押すと配列をシャッフルした結果が表示されます。
決め打ちのデータしか組み合わせられませんが、大体のイメージができあがりました。

ちなみに「やっそん(おそらく「やめて、そんな」→「やっ…そん…」→「やっそん」と転じた)」は
一部界隈のスラングでBLの性行為のことを指します。永遠に使えないムダ知識をあなたに。

使用例:出会って5秒でやっそん

プロトタイプ②

image.png
Vue.jsでよくあるTodoアプリのコードを元にプロトタイプ第二弾を作成。
入力したデータを組み合わせられるようになった点はいい感じです。

Vue.js
random: function () {
  const rnd = Math.floor(Math.random() * this.words.length);
  return this.words[rnd];
}

ただこのメソッドだと同じ単語を何度も使ってしまうため、「あっあっあっ」と
ネフェルピトーに脳を弄くられるポックルみたいになってしまいます。
配列をシャッフルするコードに書き換えなければなりません。

モックアップ

image.png
Bootstrap4で見た目だけのものを作成。レスポンシブにも対応です。

完成品

完成品がこちらです!→スーパー喘ぎ声メーカー

Vue.js
var vm = new Vue({
  el: '#app',
  data: {
    newItem: '',
    array: [
      'やっ',
      'あっ',
      'んっ…',
    ],
  },
  methods: {
    addItem: function () {
      this.array.push(this.newItem);
      this.newItem = '';
    },
    deleteItem: function (index) {
      this.array.splice(index, 1);
    },
    shuffle: function (array) {
      var sArray = [];
      sArray = array.slice(0, array.length);
      var n = sArray.length, t, i;
      while (n) {
        i = Math.floor(Math.random() * n--);
        t = sArray[n];
        sArray[n] = sArray[i];
        sArray[i] = t;
      }
      return sArray.join('');
    },
    reset: function () {
      this.array = []
    }
  }
});

長くなるのでVue部分だけ掲載。(HTML含む全文はgithubで公開してます

Vue.js
shuffle: function (array) {
  var n = array.length, t, i;
  while (n) {
    i = Math.floor(Math.random() * n--);
    t = array[n];
    array[n] = array[i];
    array[i] = t;
  }
  return array.join('');
}

これは過去のコードなのですが、このメソッドだと
シャッフル実行時配列そのものもシャッフルされてしまいます。下図参照。
image.png

Vue.js
shuffle: function (array) {
  var sArray = [];
  sArray = array.slice(0, array.length);
  var n = sArray.length, t, i;
  while (n) {
    i = Math.floor(Math.random() * n--);
    t = sArray[n];
    sArray[n] = sArray[i];
    sArray[i] = t;
  }
  return sArray.join('');
}

コピーした配列にシャッフル処理をするように書き換えたら
単語のリストはシャッフルされなくなりました。
この対処法が正しいかはわかりませんが…

list.pngimage.png
また生成結果を表示するリストは、idを個別に用意するため
clipboard-target1~clipboard-target10までわざわざ一つ一つ<li>要素を作成していました。
何故idを個別に用意する必要があるのかと言うと、
clipboard.jsで特定のdivのテキストをコピーするために
個別のターゲットを指定しなければならないからです。
data-clipboard-targetの指定先がid名になっているのがお分かりいただけると思います。

でもVueならもっとスッキリ書けるんじゃないか?とよく考えたら、
v-forのループ処理でなんとかできそうだと思い至る。

HTML
<li v-bind:id="`clipboard-target${n}`" class="clipboard-target mb-2" v-for="n in 10">
  <span>{{ shuffle(array) }}</span>
  <div class="btn btn-danger btn-sm ml-3 btn-clipboard" data-toggle="tooltip"
    v-bind:data-clipboard-target="`#clipboard-target${n}`">
    コピー
  </div>
</li>

v-bindv-forでidとdata-clipboard-targetが連番になった<li>要素を10個作ることができました。
v-for="n in 10"の10の部分を変更すれば、喘ぎ声セットを10個でも100個でも作成できますね!
JavaScriptでやったら多分めちゃくちゃ面倒くさい処理ですよね…
ありがとうVue。

感想

  • 明確な目的があったためVue勉強のモチベーションが保ててよかった
  • 友達にややウケした

課題・問題点

  • 双方向バインディングすぎてEnterを押すたびに結果が再生成される
  • 最初のイメージに合わせる知識がなかった(本当は「生成」ボタン押下時に結果が表示されるようにしたかった)
  • 一度リセットしてから再度単語を追加して生成すると、ツールチップが表示されない(コピー自体は問題なくできる)
  • 本当はVueのコンポーネント機能を使いたかったが調べてもわからなかった

GitHubでコードを公開しておりますので、アドバイスいただけたら嬉しいです。
https://github.com/mitaru/superxxxmaker
コード見たら私がマジの初心者であることはお分かりになるかと思います…
プルリクお待ちしております。

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

Vue.js 現在時刻の表示 ~時計~

Vue.jsとは

ユーザーインターフェイスを構築するためのプログレッシブフレームワークです。他の一枚板(モノリシック: monolithic)なフレームワークとは異なり、Vue は少しずつ適用していけるように設計されています。中核となるライブラリは view 層だけに焦点を当てています。そのため、使い始めるのも、他のライブラリや既存のプロジェクトに統合するのも、とても簡単です。また、モダンなツールやサポートライブラリと併用することで、洗練されたシングルページアプリケーションの開発も可能です。

参考URL
https://jp.vuejs.org/v2/guide/index.html

Vue.jsを使用するメリット

  • 気軽に使える: Vue.js はjQueryと同様に、scriptタグを1行書くだけで使い始めることができます。
  • DOM操作を自動的に行ってくれる:
    HTMLドキュメント全体の要素の構成をDOM(Document Object Model)といいます。Vue.jsはHTML側の要素とJavaScript側の値やイベントとの対応付を自動で行ってくれます。これにより、jQueryよりも簡潔に分かりやすくコードを記載することができます。

  • 学習コストが低い:
    AngularやReactと比較してフレームワークの規模が小さい分、覚えることも少なくて済みます。JavaScriptやjQueryの基礎知識があれば数時間の学習で開発を開始することができるでしょう。

Vue.jsで現在時刻を取得して表示する

初めに全体像を掴んでもらうために、完成品を記述します。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <p>{{ now }}</p>
    <button v-on:click="time">現在時刻</button> <!-- v-on:event -->
  </div>
  <!-- Vue.jsをインストール -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- ここから記述していきます -->
  <script>
    let app = new Vue({
      el: "#app",
      data:{
        now: "00:00:00"
      },
      methods: {
        time: function(e){ function(e) 
          var date = new Date();

          this.now = date.getHours() + ":"
          + date.getMinutes() + ":" +
          date.getSeconds();
        }
      }
    });
  </script>
</body>
</html>

完成品

スクリーンショット 2020-03-18 22.33.27.png

ここから要素ごとに説明していきます。ちなみにVue.jsは始めたばかりになるので、説明が至らぬところがあればコメントお願いします。

  <div id="app">
    <p>{{ now }}</p>
    <button v-on:click="time">現在時刻</button> <!-- v-on:event -->
  </div>

まずはHTMLでのこちら。

{{ }}
こちらの記述がJavaScriptのメッセージに自動的に置換してくれます。
v-on
v-on ディレクティブを使うことで、DOM イベントの購読、イベント発火時の JavaScript の実行が可能になります。

参考URL
https://jp.vuejs.org/v2/guide/events.html#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E8%B3%BC%E8%AA%AD

次にコメントアウトにてscript以降の説明していきます。

let app = new Vue({
  el: "#app", //el: "ID要素"を取得します
  data:{
    //こちらのプロパティでHTMLの{{}}を置換します
  },
})

次にmethod以降の説明していきます。

let app = new Vue({
  el: "#app",
   data:{
     now: "00:00:00" //now: == {{}}
   },

   methods: {
     time: function(e){  //function(e) この引数eは、eventの「e」
       let date = new Date();  //new演算子でオブジェクトのインスタンスを生成
        //現在時刻の取得 **ここからはjavascript**
         this.now = date.getHours() + ":"
         + date.getMinutes() + ":" +
         date.getSeconds();
     }
   }
});

ボタンを押す前

スクリーンショット 2020-03-18 23.03.14.png

どうでしょうか?
クリックイベントで、現在の時間を取得できましたか?

私もまだまだ勉強中ですが、書き方自体はシンプルでとてもわかりやすいですよね。

ちなみに私はこちらをYoutubeで見て学習をしました。
是非そちらもご参考までに見てみてください。

Youtube たにぐち まことのともすたチャンネル様
https://www.youtube.com/watch?v=jdcZ3LvTs78

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

Vuexのmutations/actionsが実行された事をテストする方法を調べたメモ

Store側のテストについては言及があっても、コンポーネント側のテストについてはあまり見かけなかったので調べた内容をメモ。

結論コード

概要

  • Vuex Storeを用意するよ
  • mutationsのStubを用意するよ
  • 上2つをセットしたwrapperでcalledを見るよ

コード

ButtonStuff.vue
<template>
  <button
    type='button'
    @click='doSomething'
  >
    Do something
  </button>
</template>

<script>
export default {
  name: 'ButtonStuff',
  props: {
    // some properties 
  },
  methods: {
    doSomething () {
      // Mutation 
      this.$store.commit('doStoreMutation', {
        hoge: 'hoge',
        foo: 'foo'
      })

    }
  }
}
</script>

ButtonStuff.spec.js
import Vuex from 'vuex'
import chai, { expect, assert } from 'chai'
import sinon from 'sinon'
import sinonChai from 'sinon-chai'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Component from '@/components/Atoms/ButonStuff.vue'

chai.use(sinonChai)

const localVue = createLocalVue()
localVue.use(Vuex)

describe('ButtonStuff.vue', () => {
    describe('Events', () => {
      describe('Click', () => {
        let store

        // 実際のMutationと同じ名前にする。
        const mutations = {
          doStoreMutationA: sinon.stub(),
          doStoreMutationB: sinon.stub(),
          doStoreMutationC: sinon.stub()
        }

        beforeEach(() => {
          store = new Vuex.Store({
            state: {},
            mutations
          })
        })

        it('should be triggered the mutation', () => {
          const wrapper = shallowMount(Component, { store, localVue })
          wrapper.find('button').trigger('click')
          assert(mutations.doStoreMutationA.called)
        })
      })
    })
  })
})

※ mutationsを例に書いていますが、actionsも同じ方法でいけるはず。

ハマった所

  • テストコード側のmutationsオブジェクトに用意したmutationの名前は実際のstoreにあるmutation名と合わせるところ。あまり意識せずに doStoreMutationMock みたいにしたらキックされなくて悩んだ
  • JestとMocha&chaiでの書き方の違い、それぞれの記法でもう片方はどうするか、実際できるのかがわからず調べるのに時間がかかった。
  • assertを使ってるけど、本当はexpectでやりたいが、書き方がまだわかってない(何パターンか試したがコケる)のでこの項目に関してassertで妥協している
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part6

前回のパート

前回はログアウト機能の実装を行いました
前回で認証まわりの実装が終わったので、今パートからはTodo機能の実装に入っていきます
前回のパートはこちらVue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part5

Todoリストの機能

まず、今回のTodoリストの機能をおさらいしておきます
Todoリストの実装する機能は下記の4点になります
削除や新規作成の際などにアニメーションは付けないシンプルなものになっていますので
アニメーションが欲しい方は、一通り実装が終わった際に自身で挑戦してみてください

  • todoの一覧表示
  • todoの新規追加
  • todoの更新
  • todoの削除(完了時)

それでは、上記の4点の実装を行っていきます
まず最初に新規Todoの追加機能の実装から入ります

Laravel側の実装

まずはコントローラーの作成を行います
上記の機能で確認した通り、表示,追加,更新,削除の機能なので
Todo専用の一つのコントローラーである
リソースコントローラーを作成しそれを利用していきます

コマンドにて下記を実行しコントローラーの作成を行ってください
$ php artisan make:controller TodoController --resource
実行で作成されるのが下記のファイルになっているかと思います

TodoController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Todo;
use Log;

class TodoController extends Controller
{
    public function index()
    {
        //
    }

    public function create()
    {
        //
    }

    public function store(Request $request)
    {
        //
    }

    public function show($id)
    {
        //
    }

    public function edit($id)
    {
        //
    }

    public function update(Request $request, $id)
    {
        //
    }

    public function destroy($id)
    {
        //
    }
}

リソースコントローラーについて、わからない方はこちらを参照ください→こちら

これで、Todoに関する全ての処理を行うコントローラーの作成が終わりました
次にapi.phpに作成したリソースフルコントローラーのルートを定義します
api.phpを開いて下記を追加してください

api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/register', 'Auth\RegisterController@register')->name('register');
Route::post('/login', 'Auth\LoginController@login')->name('login');
Route::post('/logout', 'Auth\LoginController@logout')->name('logout');
Route::get('/user', function(){
    return Auth::user();
});
Route::resource('/todo', 'TodoController');    //追加

もう少しLaravel側での実装がありますが
一旦Vue側の実装に移ります

Vue側の実装

それでは次に、Vue側で既に作成したTodo.vueを開きそちらを編集していきます
Todo.vueを開いて下記を追加してください
内容については、一つづ説明していきます

Todo.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            {{ user.name }}のTodoリスト    //・・・①

            <v-text-field v-model="text" label="todo" required></v-text-field>
            <v-btn class="btn" @click="create">登録</v-btn>    //・・・②

            <template v-for="item in items">
                <v-card max-width="450" class="mx-auto" style="marign-top: 10px" :key="item.id">
                    <v-list three-line>
                        <v-list-item :key="item.id">
                            <v-list-item-content>
                                <input type="text" v-model="item.text">    //・・・③
                                <v-btn small>完了</v-btn>
                            </v-list-item-content>
                        </v-list-item>
                    </v-list>
                </v-card>
            </template>
        </div>

    </div>
</template>

<script>
    import Header from './Header';
    import axios from 'axios';

    export default {
        components: {
            Header
        },
        metaInfo: {
            title: 'Todo',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        created () {    //・・・④
            const user = this.$store.getters['auth/user'];
            if (user === null) {
                this.$router.push('/login');
            }
        },
        computed: {
            user() {    //・・・⑤
                return this.$store.getters['auth/user'];
            }
        },
        data () {
            return {
                items: [    //・・・⑥
                    { id: 1, text: 'テスト'},
                    { id: 2, text: 'テスト'}
                ],
                text: ''
            }
        },
        methods: {
            async create() {    //・・・⑦
                await axios.post('/api/todo', { user_id: this.user.id, text: this.text });
                this.$router.go({ path: this.$router.currentRoute.path, force: true });
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
    .btn {
        margin-bottom: 20px;
    }
</style>

①は認証ユーザーの情報のVuexで保管してるデータを所得して、ユーザーの名前を表示しています
②クリックイベントでtodoの登録処理のメソッドを発火させます
③今後の機能実装でtodoの内容を表示し書き換えて変更など出来る様に<input>にv-modelで現在のダミーのデータを表示しています
④ログインしているかをチェックしています。ログインしていなければ/loginにリダイレクトされる様にしています
⑤こちらは①でユーザー名を参照するためにcomputedを使ってauth.jsのgetterを利用してstateのuserの情報を所得しています
⑥現在はtodo内容の表示機能がないため、ダミーデータを配置してます
⑦実際のtodoの登録処理のメソッドになります。詳しくは下で話します

⑦のメソッドについて説明します
ここで行っている処理は、フォームに追加したいtodoの内容が入力され登録ボタンが押されると
Laravel側の先ほど定義した/todoに対してPOSTリクエストを送信します
そうすることでLaravelで作成したTodoController.phpstore()アクションが呼び出されます
store()には登録したい内容であるユーザーのIDとtodoの内容を送ります
それがここの第二引数で指定していますawait axios.post('/api/todo', { user_id: this.user.id, text: this.text });
これを受け取ったLaravel側のTodoController.phpstore()アクションがDBに登録処理を行います
その処理が完了すれば、現在のパスにリダイレクトする様にしています
それがこの部分になりますthis.$router.go({ path: this.$router.currentRoute.path, force: true });

上記で登録処理をするにあったって、Vue側で必要な実装は完了しましたので
残るは、TodoController.phpstore()アクションの実装を行えば登録ができるようになります

store()アクションの機能実装

それでは、VueからリクエストでPOSTされて送られてきたデータをLaravel側でDBに登録する処理を
store()に記述していきます
TodoController.phpを開きstore()に下記を追加してください

TodoController.php
public function store(Request $request)
{
    Todo::create($request->all());

    return response('', 200);
}

$request->all()でVue側で送ったユーザーのIDとtodoの内容が受け取れます
それをTodo::create()で新規で登録を行っています

次にTodo::create($request->all())を実行するためにTodoモデルの作成と編集を行います
コマンドにて下記を実行してください
$ php artisan make:model Todo
上記を実行することで、appディレクトリ配下にTodo.phpが作成されます
これが、todosテーブルと紐づいたTodoのモデルになります
それでは、Todo.phpに下記を追加してください

Todo.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;    //追加

class Todo extends Model
{
    use SoftDeletes;    //追加

    protected $fillable = [    //追加
        'user_id','text'
    ];
}

ここではSoftDeletes(論理削除)を利用するための記述と
$fillabelを追加しました

論理削除はIlluminate\Database\Eloquent\SoftDeletesをuseで追加しモデル内でuse SoftDeletesとすることで利用できます

protected $fillabelここでuser_idtextを指定してます
todosテーブルのuser_idとtextを一括で更新をすることを許可しています
これを行うことで、先ほど記述したstore()内の処理Todo::create($request->all())で登録が行えます

これでtodo登録に必要な処理は一通り実装できました
一度フォームに入力して登録ボタンをクリックしてみてください
その後、DBのtodosテーブルに実際に登録できているかを確認してみてください
無事データがあれば完了です

確認方法が分からない方は、このまま一旦進んでください
次回のパートで表示機能を作りますので、そこで正常に登録が行えてるのかも確認できます

終わりに

今回は、todoの登録機能を実装しました
残る機能は、表示,更新,削除です
次回、表示機能の実装を行っていきます

次回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part7

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

Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part5

前回のパート

前回は、ログイン機能の実装とTodo.vueページの作成を行いました
今回はログアウト機能を実装していこうと思います
今回のパートで会員登録、ログイン、ログアウトの機能が揃いますのでようやく、Todoリストの機能実装に移れそうです
前回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part4

Laravel側の実装

Laravel側の実装はルートの定義のみです
api.phpに下記のルートを追加してください

api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/register', 'Auth\RegisterController@register')->name('register');
Route::post('/login', 'Auth\LoginController@login')->name('login');
Route::post('/logout', 'Auth\LoginController@logout')->name('logout');    //追加
Route::get('/user', function(){
    return Auth::user();
});

Header.vueの編集

Header.vueでLogoutのクリックアクションを追加していきます
Header.vueで下記を追加してください

Header.vue
<template>
    <v-card color="grey lighten-4" flat height="200px" tile>
        <v-toolbar dense>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Todo-List</v-toolbar-title>

            <v-spacer></v-spacer>

            <router-link to="/signup">
                <v-btn icon>
                    <v-icon>mdi-account</v-icon>
                </v-btn>
            </router-link>

            <router-link to="/todo">
                <v-btn icon>
                    <v-icon>mdi-align-horizontal-left</v-icon>
                </v-btn>
            </router-link>

            <router-link to="/login">
                <v-btn icon>
                    <v-icon>mdi-login</v-icon>
                </v-btn>
            </router-link>

            <v-btn icon @click="logout">    //追加
                <v-icon>mdi-logout</v-icon>
            </v-btn>
        </v-toolbar>
    </v-card>
</template>

<script>    //追加
    export default {
        methods: {
            async logout() {
                await this.$store.dispatch('auth/logout');
                this.$router.push('/login');
            }
        }
    }
</script>

ヘッダーのLogoutのアイコンをクリックする事で、イベントを発火させ
logout()メソッドを呼び出す様に変更しました
次に、logout()で呼び出してるauth.js側の処理を追加していきます
auth.jsのアクションに下記を追加してください

auth.js
import axios from 'axios';

const state = {
    user: null
}

const getters = {
    user: state => state.user
}

const mutations = {
    setUser (state, user) {
        state.user = user;
    }
}

const actions = {
    async register({ commit }, data) {
        const response = await axios.post('/api/register', data)
        commit('setUser', response.data);
    },

    async searchUser({ commit }) {
        const response = await axios.get('/api/user');
        const user = response.data ? response.data : null;
        commit('setUser', user);
    },

    async login({ commit }, data) {
        const response = await axios.post('/api/login', data);
        commit('setUser', response.data);
    },

    async logout({ commit }) {    //追加
        await axios.post('/api/logout');
        commit('setUser', null);
    }
}

export default {
    namespaced: true,
    state,
    getters,
    mutations,
    actions
}

ここまでの実装で説明をしていきます

まず、Header.vueのログアウトアイコンがクリックされる事で
auth.jsのactionsであるlogout()が呼ばれます
logout()の中でLaravel側で定義した/logoutにルーティングされ、ログアウト処理が実行されます
ログアウト処理が完了したところで、commit('setUser', null)としてstateのUserを空にして
ログアウト状態である事にしています

ログアウト処理は以上になります
一度ログインを行い、その後ログアウトを行ってみてください
正常であれば、Login.vueのページに戻るはずです

終わりに

今回はログアウト機能を作成しました
今回で会員登録からログイン、ログアウトと認証まわりの実装が全て終わりましたので
次のパートから、Todo.vueのTodoリストの実装に入っていこうと思います

次回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part6

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

laravel環境構築 および migrateができず詰まったのメモ

環境構築の手順

1 create your own .env file / .envファイルを作る

cp .env.example .env.local

* .env.local is generally included in the .gitignore / .gitignoreファイルに.env.localが基本的に含まれている

Create your database, and put the login information in the .env.local file / データベースを作成し、ログイン情報を.env.localファイルに入れる

2 Copy your .env.local file to .env / .env.local ファイルを .env ファイルにコピーする

cp .env.local .env

3 Run composer / composer を実行する

composer update

4 Run npm install / npm install を実行する

npm install

5 Run npm / npm を実行する。 *npm updateしないとダメかも(下のハマったことに記載)

npm run dev

6 Create key / key を生成させる

php artisan key:generate

7 Run migration and seed / migrationとseed を実行する

php artisan migrate

php artisan db:seed

8 Serve on local machine / ローカルでホストする/サーバーの起動

php artisan serve

当たり前にローカルにデータベース作り忘れてたらダメ

 ハマったこと

php artisan migrateの失敗

画像のようなエラーが発生した。
以下の記事を参考に設定したが、上手くいかず。
【Laravel】初期設定でDB設定(MySQL)にハマったので抜け方解説

database.phpと.envの設定を確認するもだめだった..
スクリーンショット 2020-03-18 18.13.41.png

dbへの接続を確認することにした。もしかしたらパスワードが間違っているかもしれない。
そこで、$ mysql -u root -pで確認したら、そもそもパスワード設定してなかったわ笑

migrateした時には.envファイルdatabase.phpより優先的に読み込まれるので、.envファイルのパスワードを変更したらいけた。
しょうもないことに時間をかけてしまった

npm install の失敗

npm installを実施するとエラーが発生。
npmパッケージで脆弱性がある場合に出るらしい。そこで、
1. npm audit fix
2. npm update ←これで脆弱性のパッケージの自動修正してくれる
3. npm isntall

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

Vue.js セットアップメモ(Mac)

この記事の目的

Vue.jsの学習を始める際に実施したセットアップ手順のメモです。

開発環境、必要なもの

  • Mac OS Catalina 10.15.2 ←自分の場合(参考程度)

セットアップ

ndenvのインストール

  • 複数バージョンのNodeを切り替えて使用することになると思いますので、ndenv をインストールします。
$ brew install ndenv
  • PATHを追加します
echo 'export PATH="$HOME/.ndenv/bin:$PATH"'
echo 'eval "$(ndenv init -)"'
  • Nodeをインストールするために node-buildをインストールします。
$ git clone https://github.com/riywo/node-build.git $(ndenv root)/plugins/node-build

Node.jsのインストール

  • インストールできるNode.jsバージョンを確認
$ ndenv install -l
  • Node.jsのインストール
$ ndenv install v12.16.1  #LTS版がおすすめ
  • 使用するNode.jsの設定
$ ndenv versions # インストールしているNode.jsのバージョンを確認する
$ ndenv global v12.16.1 # デフォルトバージョンを設定するとき
$ ndenv local v12.16.1 # プロジェクトごとに変更するとき
  • Node.jsのバージョン確認
$ node --version

Vue.jsのインストール

  • Vue CLIのインストール
$ npm install -g @vue/cli
  • Vue cli-service-globalのインストール
$ npm install -g @vue/cli-service-global

動作確認

  • vueファイルを作成します。
$ cat app.vue
<template>
<div id="app">
    <h1>Hello World</h1>
</div>
</template>
  • 実行する
$ vue serve
  • ブラウザで確認する。
    • http://localhost:8080 にアクセスしてみてください。
    • 期待通りにブラウザに表示されていたら、セットアップ完了です!

終わりに

  • 最低限のセットアップになりますが、上記の手順で一通りの実行環境は整います。
  • editor用のプラグインの設定等の設定も追記予定です。

補足

  • Google Chrome用にvue-devtoolsをインストールしておくのも必須のようです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

addEventListenerにアロー関数で値を渡すとremoveEventのとき困る

mountedでイベントリスナーを動かし、destroyedで削除するパターンでうまくイベント削除が行えなかったのでメモ。

下記はVueを使っていますが、別にVueに限らず問題起こるみたい。

mounted () {
    window.addEventListener('mousedown', (e) => {
        this.hoge(e)
    })
},
destroyed () {
    window.removeEventListener('mousedown',this.hoge)
},
methods: {
    hoge (e) {
        console.log(e.clientX)
    }
}

下記のように書き換えました。

mounted () {
    window.addEventListener('mousedown', this.hoge)
},
destroyed () {
    window.removeEventListener('mousedown',this.hoge)
},
methods: {
    hoge (e) {
        console.log(e.clientX)
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt2.12.0で新しくなったfetchについて

Nuxt2.12.0でfetch methodが新しくなりました。

https://github.com/nuxt/nuxt.js/releases/tag/v2.12.0

...fetch?

っていうか、fetchって使います? 多分使わないと思います。なぜならasyncDataと機能的にほぼ同じなので。fetchにできることは全部asyncDataで出来ていました。

新しくなったというか、ついに定義された真のfetch

で、2.12からは完全に新しくなりました。将来的にはむしろasyncDataを不要にしていくと思います(後述)

APIドキュメント: https://nuxtjs.org/api/pages-fetch

要点としては

  • pageComponent限定の機能ではなくなった
  • fetch methodの引数にcontextを受け取らなくなったかわりに、thisが使えるようになった(なのでpropを用いたfetchなどが可能に)
  • thisに $fetch, $fetchState が生えた
  • this.$fetch() で再実行ができるようになった

というあたりです。

fetch処理中の表示の出し分け

  data() {
    return {
      post: {}
    }
  },
  async fetch() {
    this.post = await this.$http.$get(
      `${this.api}${this.$route.params.id}`
    )
  },

fetchでapi叩いてthis.postに入れるとします。

<template>
  <p v-if="$fetchState.pending">Loading...</p>
  <p v-else-if="$fetchState.error">Error: {{ $fetchState.error.message }}</p>
  <div v-else>
    <h1>{{post.title}}</h1>
    <div>{{post.body}}</div>
  </div>
</template>

従来のページコンポーネントでは非同期処理が完了してからページ遷移・表示が行われていましたが、今回新しくコンポーネント単位になったことによりクライアントサイドで実行される場合には処理中の表示が発生します。

そこで 「実行中/エラー/実行完了」 の3つの表示を作るわけですが、$fetchStateにはその状態が自動的に入るので、template側ではこのように表示を出し分けることができます。

Frame 2.png

イメージを図にするとこんな感じでしょうか。fetchはmount前(SSR含む)とmethodを叩いたときに呼ばれます。pageComponentではないので、propの値に応じてfetchするようなコンポーネントも作りやすいと思います。

propの値でfetchする例

image.png

image.png

image.png

https://twitter.com/Atinux/status/1106201847734456320?s=20

古いツイートなので$isFetchingとか変数名が異なってますが、propの値を使ってfetchしてSSRする例です(画像2枚目)。
fetchOnServer: false にすることで一部のコンポーネントはクライアント処理のみにするといったこともできます(画像3枚目)

仕組み

async function $fetch() {
  this.$nuxt.nbFetching++
  this.$fetchState.pending = true
  this.$fetchState.error = null
  this._hydrated = false
  let error = null
  const startTime = Date.now()

  try {
    await this.$options.fetch.call(this)
  } catch (err) {
    error = normalizeError(err)
  }

  const delayLeft = this._fetchDelay - (Date.now() - startTime)
  if (delayLeft > 0) {
    await new Promise(resolve => setTimeout(resolve, delayLeft))
  }

  this.$fetchState.error = error
  this.$fetchState.pending = false
  this.$fetchState.timestamp = Date.now()

  this.$nextTick(() => this.$nuxt.nbFetching--)
}

https://github.com/nuxt/nuxt.js/blob/1f3c3ad9f3f0da93e1fb59aa0bed4be768f2f1fe/packages/vue-app/template/mixins/fetch.client.js#L55-L79

この辺の処理は上記のコードにある通り、fetchメソッドの実行が完了するまでは $fetchState.pending がtrueになり、fetchメソッドでエラー発生した場合には $fetchState.error にエラーオブジェクトが入るという形になっています。

つまり、fetchメソッドの中では敢えてtry catchせずエラーをそのまま投げるように書くことになります。

asyncDataとの違い

これでやっとasyncDataと住み分けができてきた感じです。

現状でfetchに無いのは context です。以前のfetchには引数に渡されていましたが今回の変更で無くなりました。

redirectやresponseのstatusCodeを設定するにはcontextが必要ですが、fetchでは使えないのでしょうか。現状では使えません。そういう処理のためには引き続きasyncDataが必要です。

Nuxt 3: Introducing fetch() hook

Context
fetch hook does not receive any context as 1st argument anymore since it has access to this.

The context will be updated to be available through this.\$ctx, this.\$config and this.\$nuxt, learn more on #25

Nuxt 3: Context changes

ですがRFCにあるように将来的には this.$ctx が生えるようになるようです。
こちらを使うことでasyncDataの役割はfetchでできるようになっていくと思います。

追記: context使えます

https://twitter.com/Atinux/status/1240193656969101312

context、this.$nuxt.context で使えました。

  • redirectは this.$nuxt.context.redirect(path)
  • resは process.server がtrueのときに this.$nuxt.context.res に入るので設定できる感じです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

爆速でLambda@edgeにNuxt.jsを構築できるテンプレートを作りました

注意事項

Lambda@Edgeに無料利用枠は無いので注意
詳細な利用料金はこちら

ある日の夜

はぁ・・・やる事無くて暇だしなんかサービス作りたい・・・
とりあえずNuxt.jsでなんか作るか:thinking:
デプロイ先はどこにしようか、、、Lambda@Edgeにデプロイするの面白そうだなぁ・・・

:thinking::thinking::thinking:

いっちょやってみっか:hugging:

Serverless Nuxt

ということで作っちゃいました。
成果物はこちら→ serverless-nuxt
不具合修正、機能追加のプルリク待ってます!
あと:star:付けてくれたらめっちゃ喜びます。

誰向け?

  • サーバー費用ケチりたいけど、ちゃんとしたサービス作りたい人
  • とりあえずNuxt/Vue使ってみたい人
  • 環境構築めんどくせぇ!!誰かやってくれって人

何が良いの?

  • .envをちょろっと弄ってコマンド叩くだけで簡単デプロイ
  • Nuxtやる時に大体使うModuleやらPluginやらが導入済み
  • デプロイ時にS3に静的ファイルも自動でアップロード!!
  • 静的ファイルはCloudFrontを通して取得するから爆速!!

導入済みModule/Plugin

  • @nuxt/typescript-build
  • @nuxt/typescript-runtime
  • @nuxtjs/tailwindcss
  • @nuxtjs/dotenv
  • @nuxtjs/axios
  • @nuxtjs/pwa
  • @vue/composition-api
  • nuxt-webfontloader
  • Stylus

個人的に好きな奴全部ブチ込んでます。
Pugは嫌いなんで入れてないです、使いたい人は自分で入れてどうぞ。

ネットワーク図

architecture.png

環境構築

.env編集

AWS_ACCESS_KEY_ID=EXAMPLE_ACCES_KEY
AWS_SECRET_ACCESS_KEY=EXAMPLE_SECRET_ACCES_KEY

適切な権限が割り振られたユーザーのACCES_KEYを指定する

AWS_DOMAIN=example.com

Route53に登録しているドメインを指定する

Docker構築

$ dcoker-compose build
$ docker-compose up -d
$ docker-compose exec node ash

Dockerは使わなくても大丈夫です。
使いたくない人は飛ばしてください。

依存ライブラリのインストール

$ npm install

説明要らない気がする

開発ビルド

$ npm run dev

Nuxtのビルドを行います。
もちろん開発ビルドなのでファイル変更のWatch, 自動更新も行ってくれます。

本番ビルド

$ npm run build
$ npm run prod

本番とほぼ同じ条件での動作テストが行えます。
ファイル変更のWatch, 自動更新は行いません。

AWSへデプロイ

$ npm run build
$ npm run deploy

下記の処理を上記のコマンドで全て行います。

  • CloudFrontのDistributionの設定、Route53との紐付け
  • S3バケットの作成、静的ファイルアップロード
  • Lambdaの登録、CloudFrontへの紐付け

初回デプロイ時CloudFrontの設定に時間が掛かるので注意

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