20200927のvue.jsに関する記事は4件です。

魚インベーダー

Vue.jsをもう少し覚えた

先月、算数インベーダを作成した。
↓これ
https://qiita.com/loxop/items/6acc5a11229c387f854a

vuexも詳しくなりました。xxインベーダーを作るのにあんまり関係ないけどw

魚インベーダー

魚の漢字ってあまり知らない。
じゃ、学習しようってことで作ってみました。

ロジック

落下してくる漢字に8個くらいの選択肢を表示して打つてな具合。

サーバ公開

DBとかないので普通にレンタルサーバーに公開できるはず。
これは、算数インベーダー作成時に習得済み。
ビルドしてパスの設定を vue.config.js に書いて配置。

vue.config.js
module.exports = {
  publicPath: '/sakana',
}

以下のリンクで試せます。
http://auto-giken-fukuoka.jp/sakana/

答えの選択肢のよみのボタンを選択して緑のエリアの下のほうをタップするとよみが発射されます。

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

typescriptベースでのVue.jsの書き方(基本)

はじめに

おはようございます。こんにちは。こんばんは。
Watatakuです。
今回はVue.jsでTypeScriptを始めよう(はじめかた)の続きなのですが前の章でTypeScriptの書き方が大体分かったのでTypeScriptベースでVueアプリを作っていきます。

この記事はの対象者はVueを勉強しているけど「TypeScriptベースでアプリを構築したい!」という方へ向けて書いております。

基本の書き方

ではまずはじめに実際にコードを用いて解説していきます。

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>{{ this.count }}</h2>
    <button @click="countUp">increment</button>
  </div>
</template>

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

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;

  private count: number = 0;

  public countUp(): void {
    this.count++;
  }
}
</script>

上記サンプルコードでは、ボタンをクリックするたびにカウンタの値が一つづつ増えていくものです。
今回はこちらのコードで基本の解説をしていきます。

まず最初に、private count: number = 0;です。
この部分は

data() {
  return {
    count: 0
  }
}

と同じになります。

propsの受け取りと書き方

msg="Welcome to Your Vue.js + TypeScript App"と言う文字列がpropsとして送られてくるとして解説します。
まずはPropsvue-property-decoratorからインポートします。

import { Prop } from 'vue-property-decorator';

受け取りかたは

@Prop() private msg!: string;

このように書くと親から送られてきたprops(Welcome to Your Vue.js + TypeScript App)を受け取ることができます。
因みに名称に「!」を付与する必要があります。

メソッドの書き方

本来なら

methods: {
    ・・・・・・・
}

のようにしてmethodsの中に各々メソッドを記述していきますがそんなことは必要なくただただVueクラス内でメソッドを書けばOKです。

その他の書き方

監視プロパティ:watch

ここからはスクロールの量を取得するサンプルコードを用いて解説していきます。

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <p>{{ this.scrollY }}</p>
  </div>
</template>

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

@Component
export default class About extends Vue {
  private scrollY: number = 0;

  public handleScroll(): void {
    this.scrollY = window.scrollY;
  }
  @Watch('scrollY')
  public scrollYChange(val: number, oldVal: number) {
    console.log(
      'スクロールの値が' + oldVal + 'から' + val + 'に変化しました。'
    );
  }

  private mounted(): void {
    window.addEventListener('scroll', this.handleScroll);
  }

  private destoryed(): void {
    window.removeEventListener('scroll', this.handleScroll);
  }
}
</script>

<style>
.about {
  height: 200vh;
}
</style>

このようにwatchを使用する時はvue-property-decoratorによる@Watchデコレータによって記述しています。
※また使用する時はwatchをimportして下さい。

import { Watch } from 'vue-property-decorator'
@Watch('監視するもの', {deep: <真理値>, immediate: <真理値>})
<メソッド名>(val: データ型, oldVal: データ型) {
  // 処理
}

ライフサイクル

ライフサイクルについてはこちらをご覧ください。

// ライフサイクルの書き方
public beforeCreate(): void {
  console.log('ライフサイクルbeforeCreate');
}

public created(): void {
  console.log('ライフサイクルcreated');
}

public beforeMount(): void {
  console.log('ライフサイクルbeforeMount');
}

public mounted(): void {
  console.log('ライフサイクルmounted');
}

public beforeUpdate(): void {
  console.log('ライフサイクルbeforeUpdate');
}

public updated(): void {
  console.log('ライフサイクルupdated');
}

public beforeDestroy(): void {
  console.log('ライフサイクルbeforeDestroy');
}

public destroy(): void {
  console.log('ライフサイクルdestoroy');
}

以上。

最後に

解説が浅いところとか、間違い等があると思いますのでその時はアドバイス等お願いします。

最後まで読んでいただきありがとうございました。
Twitterやってます。良ければチェックして見てください。:point_up::point_up:

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

TypeScriptベースでVue.jsを書く(基本編)

はじめに

おはようございます。こんにちは。こんばんは。
Watatakuです。
今回はVue.jsでTypeScriptを始めよう(はじめかた)の続きなのですが前の章でTypeScriptの書き方が大体分かったのでTypeScriptベースでVueアプリを作っていきます。

この記事はの対象者はVueを勉強しているけど「TypeScriptベースでアプリを構築したい!」という方へ向けて書いております。

基本の書き方

ではまずはじめに実際にコードを用いて解説していきます。

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>{{ this.count }}</h2>
    <button @click="countUp">increment</button>
  </div>
</template>

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

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;

  private count: number = 0;

  public countUp(): void {
    this.count++;
  }
}
</script>

上記サンプルコードでは、ボタンをクリックするたびにカウンタの値が一つづつ増えていくものです。
今回はこちらのコードで基本の解説をしていきます。

まず最初に、private count: number = 0;です。
この部分は

data() {
  return {
    count: 0
  }
}

と同じになります。

propsの受け取りと書き方

msg="Welcome to Your Vue.js + TypeScript App"と言う文字列がpropsとして送られてくるとして解説します。
まずはPropsvue-property-decoratorからインポートします。

import { Prop } from 'vue-property-decorator';

受け取りかたは

@Prop() private msg!: string;

このように書くと親から送られてきたprops(Welcome to Your Vue.js + TypeScript App)を受け取ることができます。
因みに名称に「!」を付与する必要があります。

メソッドの書き方

本来なら

methods: {
    ・・・・・・・
}

のようにしてmethodsの中に各々メソッドを記述していきますがそんなことは必要なくただただVueクラス内でメソッドを書けばOKです。

その他の書き方

算出プロパティ:computed

算出プロパティはアノテーションではなく、get構文を用いて実装します。

public get doubledCount(): number {
  return this.count * 2;
}

監視プロパティ:watch

ここからはスクロールの量を取得するサンプルコードを用いて解説していきます。

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <p>{{ this.scrollY }}</p>
  </div>
</template>

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

@Component
export default class About extends Vue {
  private scrollY: number = 0;

  public handleScroll(): void {
    this.scrollY = window.scrollY;
  }
  @Watch('scrollY')
  public scrollYChange(val: number, oldVal: number) {
    console.log(
      'スクロールの値が' + oldVal + 'から' + val + 'に変化しました。'
    );
  }

  private mounted(): void {
    window.addEventListener('scroll', this.handleScroll);
  }

  private destoryed(): void {
    window.removeEventListener('scroll', this.handleScroll);
  }
}
</script>

<style>
.about {
  height: 200vh;
}
</style>

このようにwatchを使用する時はvue-property-decoratorによる@Watchデコレータによって記述しています。
※また使用する時はwatchをimportして下さい。

import { Watch } from 'vue-property-decorator'
@Watch('監視するもの', {deep: <真理値>, immediate: <真理値>})
<メソッド名>(val: データ型, oldVal: データ型) {
  // 処理
}
  • deep : trueの場合、監視するプロパティがオブジェクトの場合ネストされた値の変更も検知します。
  • immediate : trueの場合、初期読み込み時にも呼び出します。

ライフサイクル

ライフサイクルについてはこちらをご覧ください。

// ライフサイクルの書き方
public beforeCreate(): void {
  console.log('ライフサイクルbeforeCreate');
}

public created(): void {
  console.log('ライフサイクルcreated');
}

public beforeMount(): void {
  console.log('ライフサイクルbeforeMount');
}

public mounted(): void {
  console.log('ライフサイクルmounted');
}

public beforeUpdate(): void {
  console.log('ライフサイクルbeforeUpdate');
}

public updated(): void {
  console.log('ライフサイクルupdated');
}

public beforeDestroy(): void {
  console.log('ライフサイクルbeforeDestroy');
}

public destroy(): void {
  console.log('ライフサイクルdestoroy');
}

参考:https://noumenon-th.net/programming/2019/07/02/class-style/
以上。

最後に

解説が浅いところとか、間違い等があると思いますのでその時はアドバイス等お願いします。

最後まで読んでいただきありがとうございました。
Twitterやってます。良ければチェックして見てください。:point_up::point_up:

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

Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #4 サインアップページの作成

Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #3 個別記事ページの作成

サインアップ(登録)ページの作成

サインアップページ及びそのロジックを作っていきますが、難易度が一気に上がります。
一気にすべて理解しようとせず、少しずつコードを読み解いていってください。

まずはサインアップページを作ります。

pages/sign_up.vue
<template>
  <v-app id="inspire">
    <v-main>
      <v-container
        class="fill-height"
        fluid
      >
        <v-row
          align="center"
          justify="center"
        >
          <v-col
            cols="12"
            sm="8"
            md="4"
          >
            <form @submit.prevent="signUp">
              <v-card class="elevation-12">
                <v-toolbar
                  color="primary"
                  dark
                  flat
                >
                  <v-toolbar-title>SignUp form</v-toolbar-title>
                </v-toolbar>
                <v-card-text>
                  <ul v-if="errors">
                    <li v-for="(message, i) in errors.full_messages" :key="i">
                      {{ message }}
                    </li>
                  </ul>
                  <v-text-field
                    id="name"
                    v-model="name"
                    label="name"
                    name="name"
                    prepend-icon="mdi-account"
                    type="text"
                  />

                  <v-text-field
                    id="email"
                    v-model="email"
                    label="email"
                    name="email"
                    prepend-icon="mdi-email"
                    type="email"
                  />

                  <v-text-field
                    id="password"
                    v-model="password"
                    label="Password"
                    name="password"
                    prepend-icon="mdi-lock"
                    type="password"
                  />
                </v-card-text>
                <v-card-actions>
                  <v-spacer />
                  <v-btn color="primary" type="submit">
                    登録
                  </v-btn>
                </v-card-actions>
              </v-card>
            </form>
          </v-col>
        </v-row>
      </v-container>
    </v-main>
  </v-app>
</template>

<script>
export default {
  data () {
    return {
      name: '',
      email: '',
      password: '',
      errors: []
    }
  },
  methods: {
    async signUp () {
      try {
        const data = await this.$store.dispatch('signUp', {
          name: this.name,
          email: this.email,
          password: this.password
        })
        this.$store.commit('setUser', data.data)
        this.$router.push('/')
      } catch (e) {
        this.errors = e.data.errors
      }
    }
  }
}
</script>

フォームを送信するとsignUp()メソッドが発火し、storeのsignUpアクション(この後作ります)でRails APIにPOSTし、正常終了したらsetUserでユーザーデータをstoreに保存(この後作ります)。
エラーが起きたらcatchしてエラーを出力している、という形です。

参考:Vue Material Component Framework — Vuetify.js

store側の実装

ログインに関わる処理はサイト全体で共通して使いそうなので、store/index.jsに書いていきます。

store/index.js
export const state = () => ({
  user: null,
  auth: {},
  logged_in: false
})

export const getters = {
  user (state) {
    return state.user
  },
  logged_in (state) {
    return state.logged_in
  },
  auth (state) {
    return state.auth
  }
}

export const mutations = {
  setUser (state, value) {
    state.logged_in = !!value
    state.user = value
  },
  setAuth (state, value) {
    state.auth = value
  }
}

export const actions = {
  async signUp (_c, user) {
    return await this.$axios.$post('/v1/auth', user)
  }
}

ログインしているかどうかの判定に使うlogged_inに注目。
setUser(state, value) {
state.logged_in = !!value
state.user = value
},

ユーザー情報セットの際、二重否定を使うことでuserの中身があればtrue, なければfalseが入るようにしています。

axiosの拡張

続いてaxiosのデフォルト挙動を変えます。
axiosでサーバサイドと通信する度にgetter['auth']してヘッダに付与するのは冗長かつしんどいので共通化します。
また、レスポンスが返ってくるたびにアクセストークンをstoreに保存するのも毎度書いていたら辛いので一緒に共通化します。

plugins/axios.js
export default ({ $axios, store }) => {
  $axios.onRequest((config) => {
    config.headers = store.getters.auth
  })

  $axios.onResponse((response) => {
    if (response.headers['access-token']) {
      const authHeaders = {
        'access-token': response.headers['access-token'],
        client: response.headers.client,
        expiry: response.headers.expiry,
        uid: response.headers.uid
      }
      store.commit('setAuth', authHeaders)
    }
  })

  $axios.onError((error) => {
    return Promise.reject(error.response)
  })
}

nuxt.config.js
   plugins: [
+    '~/plugins/axios'
   ],

これで、以後axiosを使う度、リクエストにはアクセストークンが自動付与されて、レスポンス後はトークンがstoreに自動保存されます。

セッション永続化の準備

今のままだと認証情報はstoreに入れているだけなので、画面遷移する分には問題ないのですが、F5押下のようにリロードするとログアウト状態になってしまいます。

これを永続化するためにはトークン情報をcookieかWebStorageに保存する必要があります。

今回は簡易的な機能のためlocalStorageを使いますが、トークンをlocalStorageに保存することはセキュリティ的には弱いため、ちゃんとしたプロダクトでは避けた方が良いかもしれません。

参考:HTML5のLocal Storageを使ってはいけない(翻訳)

  • cookie:サーバサイド・クライアントサイドどちらでも読み込み書き込みできるため、SSRでも使える。ただし値セットが面倒
  • WebStorage(LocalStorage):クライアントサイドのみのため、SSRでは使えない。そのため一瞬未ログイン画面が出てしまうデメリットがあるが、pluginを入れれば超簡単にstoreを永続化できる

今回は手軽さを取ってLocalStorageを使います。

$ yarn add vuex-persistedstate
nuxt.config.js
   plugins: [
-    '~/plugins/axios'
+    '~/plugins/axios',
+    { src: '~/plugins/localStorage.js', ssr: false }
   ],
plugins/localStorage.js
import createPersistedState from 'vuex-persistedstate'

export default ({ store }) => {
  createPersistedState({
    key: 'bbs_session',
    paths: [
      'auth'
    ]
  })(store)
}

これだけの設定で、state.authbbs_sessionというlocalStorageのkeyで保存されます。
特にlocalStorageを意識することなくstate.authに値をセットするとlocalStorageにもセットされるというすぐれものです。

とはいえこれだけでは保存しているだけで、画面リロード時に読み込む処理が入っていません。
そのロジックは共通レイアウトに入れてしまいます。

共通レイアウトの修正

ここまでで機能の大部分はできました。
ですが共通レイアウトが暗く見づらい上、不要なサイドバー等があるので作り直す勢いで修正します。

また、前述の通り画面リロード時のセッション復元も作ります。

layouts/default.vue
<template>
  <v-app>
    <v-app-bar
      fixed
      app
    >
      <n-link to="/">
        <v-toolbar-title v-text="title" />
      </n-link>
      <v-spacer />
      <span v-if="logged_in">
        {{ current_user.name }}さん
      </span>
      <v-btn
        v-if="logged_in"
        icon
      >
        <v-icon>mdi-logout</v-icon>
      </v-btn>
      <v-btn
        v-else
        icon
        to="/sign_up"
        nuxt
      >
        <v-icon>mdi-account-plus</v-icon>
      </v-btn>
    </v-app-bar>
    <v-main>
      <v-container>
        <nuxt />
      </v-container>
    </v-main>
    <v-footer
      app
    >
      <span>&copy; {{ new Date().getFullYear() }}</span>
    </v-footer>
  </v-app>
</template>

<script>
export default {
  data () {
    return {
      title: 'Sample bbs'
    }
  },
  computed: {
    current_user () {
      return this.$store.getters.user
    },
    logged_in () {
      return this.$store.getters.logged_in
    }
  },
  async beforeMount () {
    const session = JSON.parse(window.localStorage.getItem('bbs_session'))
    if (session && Object.keys(session.auth).length) {
      await this.$axios.$get('/v1/auth/validate_token')
        .then(data => this.$store.commit('setUser', data.data))
        .catch(() => {
          this.$store.commit('setUser', null)
          this.$store.commit('setAuth', {})
        })
    }
  }
}
</script>

beforeMount()はCSR(クライアントサイドレンダリング)でのみ最初に実行されます。
localStorageはサーバサイドで使えないので、クライアントサイド描画されるタイミングでセッションの値を取得し、authが保存されている場合(画面更新直前までログイン状態だった場合)にvalidate_tokenを叩きにいき、user情報とauthの更新をしてログイン状態を復帰します。
無効なauthパラメータだった場合はuserとauthをクリアします(自動的にlocalStorageもクリアになります)。

ここまで組み込めば、ログイン後に画面更新してもセッション復帰する状態になるはずです。
ページを開いた直後は未ログイン状態ですが、ちょっと待てばログインになります。
このタイムラグが気になる場合はcookieで実装が必要になります。

nuxt.config.js
   vuetify: {
     customVariables: ['~/assets/variables.scss'],
     theme: {
-      dark: true,
+      dark: false,
       themes: {
-        dark: {
+        light: {
           primary: colors.blue.darken2,
           accent: colors.grey.darken3,
           secondary: colors.amber.darken3,

そして色変え。これでだいぶスッキリするはずです。

参考

Rails devise token authとNuxt.jsを連携(Twitter認証)
Nuxt.jsのStoreによるデータ保存 [vuex-persistedstate][js-cookie]

続き

Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #5 ログイン・ログアウトの実装
連載目次へ

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