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

AWS Amplify + Nuxt.js でユーザーログイン可能なウェブチャットアプリを作成

AWSのAmplifyは、GraphQLを設計するだけで、バックエンド側コードとかリソースとかその設定を自動生成してくれるので、フロントエンドのコードをjsとかで書くだけでウェブアプリケーションを作れてしまいます。
ドキュメントを一通り読んでみて勉強を兼ねてユーザー管理できるウェブチャットアプリを作ってみたのでメモ

前提

  • AWSのアカウントは取得済み
  • Amplify CLIはインストール+設定済み
  • Nuxt.jsをインストールする準備は完了済み

環境

  • フロント: NuxtJS + Vuetify
  • バックエンド: AWS Amplify
  • API設計: GraphQL

環境のための参考資料:
Amplify CLI インストール
Nuxt.js インストール

作るもの

かんたんなウェブチャットツールです。
ユーザー登録+ログインして、チャットにコメントを投稿できます。


練習なのでこんな感じのシンプルなものです。

ポイント

  • ユーザー登録、ログインができる。
  • ユーザー毎にコメントできる
  • コメントはリアルタイムで画面に反映
  • ログインしないとコメント投稿・閲覧ができない
  • 投稿したコメントはDBに保存されて再度ログインしたときには続きが書き込める

というあたりを目指します。

Nuxt.jsインストール

とりあえずフロント側のNuxtをインストールして基本的なページを作ります。プロジェクト名はnuxt-amplifyとしました。
オプションは下のような感じで選択しました。

% npx create-nuxt-app nuxt-amplify

create-nuxt-app v3.0.0
✨  Generating Nuxt.js project in nuxt-amplify
? Project name nuxt-amplify
? Choose programming language JavaScript
? Choose the package manager Npm
? Choose UI framework Vuetify.js
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, Content
? Choose linting tools ESLint, Prettier, Lint staged files, StyleLint
? Choose test framework Jest
? Choose rendering mode Single Page App
? Choose development tools jsconfig.json (Recommended for VS Code)

基本的なページの作成

フロント側のページで見た目を適当に整えます。
必要なのは、コメントを入力するテキストボックスと一覧表示する部分だけです。

pages/chat.vue
<template>
  <div style="max-width: 800px;">
    <v-text-field
      label="コメント"
      placeholder="ここにコメントを書きましょう"
      outlined
      class="mx-auto"
      append-icon="mdi-check-bold"
      style="max-width: 100%; box-sizing: border-box;"
    ></v-text-field>

    <v-card v-for="(item, index) in items" :key="index" tile>
      <v-list-item two-line>
        <v-list-item-content>
          <v-list-item-title>{{ item.comment }}</v-list-item-title>
          <v-list-item-subtitle>by: {{ item.owner }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-card>
  </div>
</template>
<script>
export default {
  data() {
    return {
      form: {
        comment: '',
      },
      items: [],
    }
  },
  created() {
    this.getChatList()
  },
  methods: {
    getChatList() {
      // コメント取得
      this.items = [
        {
          comment: 'ここにコメントが入ります',
          owner: 'ここに投稿者名が入ります',
        },
        {
          comment: 'ここにコメントが入ります',
          owner: 'ここに投稿者名が入ります',
        },
        {
          comment: 'ここにコメントが入ります',
          owner: 'ここに投稿者名が入ります',
        },
      ]
    },
  },
}
</script>


こんな感じの見た目に。

Amplifyのインストール

フロント側さえつくれば、あとはAmplifyをインストールして必要な設定をしていくだけで、アプリケーションとして動くようになります。
まずは、現行のプロジェクトにAmplifyをインストールしていきます。
先程作ったNuxt.jsプロジェクトディレクトリのルートで amplify init コマンドを実行します。
選択オプション等は以下のようにしました。

% amplify init

? Enter a name for the project nuxtamplify #任意のプロジェクト名
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you re building javascript

? What javascript framework are you using vue
? Source Directory Path:  . # ルートを指定
? Distribution Directory Path: dist # ビルド済ファイルの保存ディレクトリを指定
? Build Command:  npm run build # ビルドコマンドを指定(※)
? Start Command: npm run start # 起動コマンドを指定(※)

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default

最後方のコマンド関連の質問は環境などにより変わると思います。
package.jsonなどを参考に。

vue.js用のライブラリを読み込み

Vue.jsでamplifyと通信したり色々できるライブラリが準備されているので追加します。

npm install aws-amplify @aws-amplify/ui-vue

Nuxt.jsのプラグインとして設定

Nuxt.jsのプラグインとして使えるように設定します。
まずはプラグインファイルを作成します。

plugins/amplify.js
import Vue from 'vue'
import Amplify from 'aws-amplify'
import '@aws-amplify/ui-vue'
import awsExports from '../aws-exports'

Amplify.configure(awsExports)
Vue.use(Amplify)

nuxt.config.jsのプラグイン設定の配列に{ src: '~/plugins/amplify.js', ssr: false }を追加します。

nuxt.config.js
//...

// pluginsの配列に追加します。
plugins: [{ src: '~/plugins/amplify.js', ssr: false }],

//...

バックエンドAPIを作成

いよいよAWS側にバックエンドエンド側を作成してエンドポイントを生成します。
ここからはコマンドと少しのコードであっという間に本格的なウェブアプリケーションが作成されていきます。
まずは、AmplifyにAPI機能を追加
amplify add api コマンドを実行するだけです。
オプション設定は以下のように答えました。

% amplify add api

? Please select from one of the below mentioned services: GraphQL
? Provide API name: chat # 任意のAPI名
? Choose the default authorization type for the API API key
? Enter a description for the API key: sample
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description) 
? Do you want to edit the schema now? Yes

これで必要なコードがプロジェクトに追加されます。

GraphQLでバックエンドの設計

バックエンドのAPIをGraphQLで設計します。
基本ファイルが出来上がっているので
amplify/backend/api/下にあるファイルを開いて以下のように書き換えます。

amplify/backend/api/chat/schema.graphql
type Chat @model {
  id: ID!
  comment: String!,
  owner: String
}

Chat というモデル(テーブル)に、id, comment, ownerというフィールドを作成して保存したり取得できるようにします。
バックエンド設計で必要なファイルはこれだけ。
GraphQLというAPI設計用のクエリ言語なんですが、これを元にバックエンド側のDB保存やAPIへのリクエスト処理などを全部自動生成してくれます。
詳しくは
- GraphQL (基本を学ぶ)
- API(GraphQL) Directives (Amplifyで使うディレクティブを学ぶ)
あたりを参考にどうぞ。
(自分もまだ学びはじめたばかりですが、いままでウェブのバックエンドのコード書いていた者にとってすごくワクワクできる内容です)

AWS側に送信して必要なリソースを生成

上記のを書いたらあとはAWS側で必要なリソースが自動的に生成されて連携して動くようにしてくれます。
amplify push コマンドするだけ。

% amplify push

? Are you sure you want to continue? Yes

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js # エンドポイント側と通信する各種コードが生成されるフォルダを指定、とりあえずデフォルトで。
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

ちょっと時間かかりますが、待っていると(たまに質問してくるけど基本デフォルトで)、自動的にAWS側の設定をしてくれます。
以上で、APIエンドポイントの生成が完了です。
ほとんどコード書かないまま実装されていきます。

(※)
Enter the file name pattern of graphql queries...
のところ、GraphQL関連のファイルの生成場所なんですが、Nuxt.jsの場合どこに置くのがよいのかな。もっとスマートな指定がありそう。。

フロント側をAPIエンドポイントと通信できるように修正

エンドポイントが動くようになったので、フロントから通信するようにコードを修正します。

pages/chat.vue>template
<template>
・・・
// v-modelとイベントハンドラを追加
<v-text-field
        ...
        v-model="form.comment"
        @keydown="onEnter"
        @click:append="createChat"
      ></v-text-field>

...
<template>
pages/chat.vue>script
<script>
import { API } from 'aws-amplify' // Amplifyライブラリを読み込み
import { createChat } from '~/src/graphql/mutations' // GraphQL Mutation(データをエンドポイントに送信する構文?)
import { listChats } from '~/src/graphql/queries' // GraphQL Query(データを読み込む構文?)

export default {
  // ...
  methods: {
    async createChat() {
      // コメントを送信する
      const comment = this.form.comment // コメント入力値を取得
      if (!comment) return // 空のときは処理しない
      const chat = { comment } // 送信用のJSONを作成
      // 送信処理
      await API.graphql({
        query: createChat, // GraphQL Mutation
        variables: { input: chat }, // 送信データ
      })
      this.form.comment = '' // 送信後にテキストフィールドを空に。
    },
    onEnter(event) {
      // ここはおまけ。(Enterを押したときもコメントを送信したかったので記述)
      if (event.keyCode !== 13) return
      this.createChat()
    },
    async getChatList() {
      // コメント一覧を取得
      const chatList = await API.graphql({
        query: listChats, // GraphQL Query
      })
      this.items = chatList.data.listChats.items // 読み込みしたデータを一覧に表示
    },
  },
}
</script>

mutationsqueriesは、amplify pushした時に設定したディレクトリに生成されています。
それぞれの内容を見るとより理解が深まりますし、応用でいろいろできると思います。

リアルタイムに反映されるようにコードを追記

また、送信と読み込みだけでなく、送信して保存された内容がリアルタイムに画面に反映されるようにコードを追記します。
GraphQLのSubscriptionという機能(仕様?)を使います。サーバからのpush通信などを受け取ったりできます。

pages/chat.vue>script
<script>
// ...
import { onCreateChat } from '~/src/graphql/subscriptions' // GraphQL Subscription

export default {
  // ...
  created() {
    this.getChatList() 
    this.subscribe() // 追加
  },
  methods: {
    //...

    // メソッド追加
    subscribe() {
      API.graphql({ query: onCreateChat }).subscribe({ 
        next: (eventData) => {
          // コメントが送信されて追加されたとき、送信内容を一覧に追加
          const chat = eventData.value.data.onCreateChat // データを読み込み
          if (this.items.some((item) => item.comment === chat.comment)) return // すでに表示されているデータは無視
          this.items = [...this.items, chat] // 新しいデータを追加
        },
      })
    },
  },
}
</script>

以上で、APIへの対応を完了。
チャットアプリケーションとして、動くようになりました。

コメント欄にテキストを入力して、Enterかチェックマークをクリックすると一覧にデータが反映されるようになっていると思います。
(投稿者名は空になっていますが、これから実装します)
また、AWSのコンソールにログインしてみると、DynamoDBにテーブルが作られて、そこにデータが保存されているのがわかると思います。
ほとんど、フロント側のコードだけで、ここまでのウェブアプリを作ることができます。

ユーザー管理機能を追加

続いてこのアプリケーションにユーザー管理機能を追加してログインした人だけが投稿できるようにしたり、投稿者の名前を表示したりできるようにしていきます。
まずは、Amplifyにユーザー認証機能を追加します。
amplify add auth コマンドを実行するだけです。

% amplify add auth

Do you want to use the default authentication and security configuration? Default configuration
How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.

機能が追加されたら、そのままAWSにpushします。
少し質問されるかもですが、とりあえずデフォルトのままで。

% amplify push

以上でバックエンド側にユーザー認証機能が実装されました。

フロント側をユーザーログイン機能に合わせて修正

pages/chat.vue
//...

<amplify-authenticator>
      <v-text-field
        v-model="form.comment"
        label="コメント"
        placeholder="ここにコメントを書きましょう"
        outlined
        class="mx-auto"
        append-icon="mdi-check-bold"
        style="max-width: 100%; box-sizing: border-box;"
        @keydown="onEnter"
        @click:append="createChat"
      ></v-text-field>
</amplify-authenticator>

//...

これだけです。入力テキストフィールドを amplify-authenticator で囲むだけ。
これで、ログインしているときは、テキストフィールドが表示され、ログインしていないときは、ログイン用のフィールドが表示されます。

こんな感じのログイン画面を自動的に生成してくれます。
ユーザー登録や、パスワードリセットも実装されているので、非常に嬉しい。
また、いろいろカスタマイズもできます。
https://docs.amplify.aws/ui/auth/authenticator/q/framework/vue

確認

では、実際にアカウントを作成してログインしてみてください。
ログインができてアプリを使えるようになっていると思います。

APIへの認証を追加

見た目的には、ログイン機能が実装されましたが、API自体に認証が追加されたわけではないので、直接エンドポイントにアクセスするなどすればデータの読み書きができてしまいます。
そこで、APIを今回追加した認証機能と連携させてユーザーログインしている人だけがエンドポイントにアクセスできるようにAPI設計を修正します。
とはいえ、schema.graphqlに少し追加するだけです。

amplify/backend/api/chat/schema.graphql
type Chat @model @auth(rules: [{ allow: owner, operations: [create, delete, update] }]) {
  id: ID!
  comment: String!,
  owner: String
}

@auth(rules: [{ allow: owner, operations: [create, delete, update] }])
を追加しました。
ownerフィールドに保存されたユーザー名で認証(ユーザー名は自動的に保存されます)して、create,delete,updateに対して操作を許可します。
これで、Chatモデルは、データの読み込みはログインしていれば誰でも可能、書き込みは作成者でなければできない。という状態になります。
参考:
https://docs.amplify.aws/cli/graphql-transformer/directives#auth

これをAWS側にpushします。

% amplify push

エラーが出た場合

ここでエラーが出た場合(上記の通りやっているとエラーになります)は対応が必要です。
apiをauthに合わせて更新しないといけません。
API認証を、AWSのCognitoで行うように更新します。
amplify update api コマンドでAPI設定の更新を行います。

% amplify update api

? Please select from one of the below mentioned services: GraphQL
? Select from the options below Walkthrough all configurations
? Choose the default authorization type for the API Amazon Cognito User Pool #Cognito User Pool を選択してください。
Use a Cognito user pool configured as a part of this project.
? Do you want to configure advanced settings for the GraphQL API No, I am done.

これで暫く待つとAWS側の設定が更新されます。
あとは、もう一度

% amplify push

で、GraphQLの設定がpushされます。
エンドポイントへの認証設定も完了です。

また、この設定により、DBにowner名が保存されるようになりました。
コメントを投稿したときにチャットツールの投稿者名も表示されるようになっていると思います。

ログインしたときに、一覧表示される(以降リアルタイムで更新される)ように修正

エンドポイントへの認証設定をしたので、すこし不都合がでてきます。

  1. ログアウト状態で画面を開く(この状態では一覧読み出しができない)
  2. ログインする。(一覧の読み出しはできるようになったが、すでにページは生成されているのでページを更新しないと一覧が表示されない)

という状態になってしまっています。
ログインしたときに、APIから一覧を読み出す処理と、それ以降はリアルタイムで更新されていくように修正します。

pages/chat.vue
<script>
//...
import { onAuthUIStateChange } from '@aws-amplify/ui-components'

//...

  created() {
    this.getChatList()
    this.subscribe()

    // 追加
    onAuthUIStateChange((authState, authData) => { // ログインステータスが変化したとき
      if (authState === 'signedin') { // ログインした場合
        this.getChatList() // 一覧呼び出し
        this.subscribe() // GraphQL Subscription
      } else {
        this.items = [] // ログアウトしたときなどは一覧を削除
      }
    })
  }

// ...
</script>

ログイン状態を監視するようにイベントを設定しました。

これで一通り作成が完了しました。
chat.vue はこのようになりました。
また、ついでにログアウトボタンも実装しています。(amplify-sign-out

pages/chat.vue
<template>
  <div style="max-width: 800px;">
    <amplify-authenticator>
      <v-text-field
        v-model="form.comment"
        label="コメント"
        placeholder="ここにコメントを書きましょう"
        outlined
        class="mx-auto"
        append-icon="mdi-check-bold"
        style="max-width: 100%; box-sizing: border-box;"
        @keydown="onEnter"
        @click:append="createChat"
      ></v-text-field>
    </amplify-authenticator>

    <v-card v-for="(item, index) in items" :key="index" tile>
      <v-list-item two-line>
        <v-list-item-content>
          <v-list-item-title>{{ item.comment }}</v-list-item-title>
          <v-list-item-subtitle>by: {{ item.owner }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-card>

    <v-card>
      <amplify-sign-out v-if="logoutBtn"></amplify-sign-out>
    </v-card>
  </div>
</template>
<script>
import { API } from 'aws-amplify'
import { onAuthUIStateChange } from '@aws-amplify/ui-components'
import { createChat } from '~/src/graphql/mutations'
import { listChats } from '~/src/graphql/queries'
import { onCreateChat } from '~/src/graphql/subscriptions'

export default {
  data() {
    return {
      form: {
        comment: '',
      },
      items: [],
      logoutBtn: false,
    }
  },
  created() {
    this.getChatList()
    this.subscribe()

    onAuthUIStateChange((authState, authData) => {
      if (authState === 'signedin') {
        this.getChatList()
        this.subscribe()
        this.logoutBtn = true
      } else {
        this.items = []
        this.logoutBtn = false
      }
    })
  },
  methods: {
    async createChat() {
      const comment = this.form.comment
      if (!comment) return
      const chat = { comment }
      await API.graphql({
        query: createChat,
        variables: { input: chat },
      })
      this.form.comment = ''
    },
    onEnter(event) {
      if (event.keyCode !== 13) return
      // console.log('save')
      this.createChat()
    },
    async getChatList() {
      // コメント取得メソッド
      const chatList = await API.graphql({
        query: listChats,
      })
      this.items = chatList.data.listChats.items
    },
    subscribe() {
      API.graphql({ query: onCreateChat }).subscribe({
        next: (eventData) => {
          const chat = eventData.value.data.onCreateChat
          if (this.items.some((item) => item.comment === chat.comment)) return // remove duplications
          this.items = [...this.items, chat]
        },
      })
    },
  },
}
</script>

ログインしてコメントを書き込めるようになりました。

デプロイ

最後は実際にAmplifyでホスティングします。

% amplify add hosting

? Select the plugin module to execute Hosting with Amplify Console 
? Choose a type Manual deployment
% amplify publish

しばらくまつとpublish完了。
URLが表示されるので、アクセスしてみましょう。
あっというまに、ウェブアプリケーションが作成されました。

あとは、せっかくチャットツールをつくったので、シークレットウィンドウや別のブラウザを並べて一人二役などでチャットをしてみましょう。
わずかな手間でここまで実装できることへの驚きと満足感と、一人でチャットをする若干の寂しさを感じることができます。

削除

せっかく作りましたがAWSにアップしたままにするのもアレなんで削除方法も。

% amplify delete

で削除できます。かんたんですね。
また、下記のアクセス制限などをしてから、しばらく色々試してみるのも楽しいです。

補足

一応アクセス制限をかけられます。
AWSのコンソールから、AWS Amplifyにアクセスして、[アクセスコントロール]の項目からベーシック認証的なものをかけることができます。

Nuxtのエラー?

今回のNuxtでコンソールにエラーがでます。(たぶんVuetify関連)
'v-content' is deprecated, use 'v-main' instead
みたいなやつです。
本筋と関係ないですし、練習なので無視してもよいのですが、気になる場合は
layouts/default.vue の、v-contentv-mainに修正すると直ります。
https://github.com/vuetifyjs/vuetify/issues/11634

参考

今回参考にさせていただいたサイト
https://docs.amplify.aws/start/q/integration/vue
https://docs.amplify.aws/cli/graphql-transformer/directives#using-modular-imports
https://ja.nuxtjs.org/
https://vuetifyjs.com/ja/
https://qiita.com/fkymnbkz/items/fa7cd15de1039e62074e
https://github.com/aws-amplify/amplify-cli/issues/3480
https://github.com/vuetifyjs/vuetify/issues/11634
https://remoter.hatenablog.com/entry/2020/02/28/Nuxt%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%A7Amplify%E3%82%92%E8%A7%A6%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B
https://qiita.com/respectakagikun/items/0b976b12ddb34f027190

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

【vue】トランジションのあらゆるタイミングでjsを走らせる【トランジションフック】

概要

トランジションが発生したタイミングでjsを処理させたいときに便利な機能

トランジションの中でも、以下のタイミングでjsの処理をさせてみる。

  1. DOMに要素が追加された後(enter)
  2. トランジションが終わるとき (after-enter)

規定に沿ったフック名をtransition要素に与えることで、各タイミングで処理が走る。

.html
<div id="app">
  <button v-on:click="hook = !hook">hook</button>
  <transition v-on:enter="enter" v-on:after-enter="afterEnter">
    <div v-if="hook">example</div>
  </transition>
</div>
.js
new Vue({
  el: '#app',
  data: {
    hook: true
  },
  methods: {
    /*1.DOMに要素が追加された後の処理*/
    enter: function(el, done) {
      console.log('enter')
      setTimeout(done, 1000)
    },
    /*2. トランジションが終わるときの処理*/
    afterEnter: function(el) {
      console.log('after-enter')
    }
  },
})

トランジションのタイミング

トランジションが発生するタイミングとして下記のように分類できる。
- 要素が追加されるとき
- 要素が削除されるとき

さらに細かく分類したときのフック名は以下の通り

要素が追加されるとき(Enterフェーズ)

  1. DOMに要素を追加: before-enter
  2. v-enterを追加: enter
  3. トランジション終了: after-enter
  4. Enterフェーズが途中でキャンセル: enter-cancelled

要素が削除されるとき(leaveフェーズ)

  1. クラス付与前: before-leave
  2. .v-leaveが追加後: leave
  3. 要素削除後: after-leave
  4. leaveフェーズが途中でキャンセル: leave-cancelled
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cordova-ios でShareExtension を実装する際に死ぬほどハマった話

はじめに

vue.js+quasar-frameworkを使ってiosアプリを開発していた時に、
iosアプリ間でURLや画像がShareできるShare Extensionを実装しようとしたけど、情報が少なく、ハマりまくったため同じような方のためにこの記事を書きます。
(quasarFrameworkとはvue.jsのフレームワークの事で一つのコードでiOS, Android, desktopAppを作成できるという夢のような?Frameworkです。とても使いやすくてお勧めではあるのですが、cordovaに関する情報が少ないため色々ハマることもあります。参考記事 https://qiita.com/onigiri_/items/88b2164478c06c9e5a9f)

環境

•quasar-framework 1.0.0
•cordova-ios 5.1.1
.cordova-plugin-openwith-ios 2.0.0
.Xcode 11.5
.MacOS 10.15.5 Catalina

やってダメだった事

基本的にShareExtensionに関してWeb検索でヒットする情報はほぼ全てSwiftを使用したNativeアプリでの情報でした。
その情報を元に試行錯誤したものの、途中でよく分からないエラーが頻発してドツボにハマりました。
特に
The following build commands failed:
CompileSwift normal x86_64

や、
PhaseScriptExecution [CP]\ Check\ Pods\ Manifest.lock
のエラーが頻回に発生し、その度にキャッシュをクリアしたり、全てBuildをやり直したり、pod fileを消してpod installしなおしり、、、、、心が何度も折れかけました。

cordova-plugin-openwith-iosを使用する!

Pod fileはいじらない!
cordova-plugin の cordova-plugin-openwith-ios を使用する!
Project、Target、の設定でターゲットのiosをpodファイルに書かれていあるiosのVersion(10.0など)と必ず一致させる!
これらを理解した後に少しずつ光が見えてきました。

cordova-plugin-openwith-iosはCordova-iosでshareExtensionを使用できるようにしてくれている神Pluginなのですが、使い方の説明がイマイチ不親切です。。。。。
https://www.npmjs.com/package/cordova-plugin-openwith-ios

ですので使い方の手順をまとめておきます。
(AppGroupの設定の仕方などは他にたくさん記事がありますので、ここでは省きます。)

①XcodeでShareExtensionを設定していない状態で、
cordova plugin add cordova-plugin-openwith-ios --variable IOS_URL_SCHEME=cordovaopenwithdemo
をTerminalからコマンドする。(IOS_URL_SCHEMEのところはどう設定すればいいのかイマイチわかりませんでした。僕はDefaultのままで設定しました。)

*注意点として、shareExtensionと元のTargetのBundleIDは一致させておくべきです

②そうすると以下のようなファイル構造になっているはずです。
スクリーンショット 2020-06-29 14.19.23.png

こちらのShareViewController.hファイルの中に
#define SHAREEXT_GROUP_IDENTIFIER @"group.**********"
と書かれているところがあるので、そのグループ名と同じようにAppGroupを設定する

この時点でShareExtensionをBuildすると共有ボタンを押すとアプリアイコンが出てきていると思います。
そこでPostを押すと、アプリのuserDefaults(WebでいうLocalStorageのようなもの?)にURLなどの情報が保存されます。

③本体アプリの方でその情報を受け取るコードを書く!
ここが一番分からなかった、、、cordova-plugin-openwith-iosのマニュアルには一言Usageとだけ
スクリーンショット 2020-06-29 14.27.56.png
と書かれているけど、ってこれどこに書いたらいいねん!!(つい言葉が汚くなりました。すみません)

結局結論としては、
src-cordova => platfomrs/ios => platform_www => cordova.js
のファイルの中に書く!(アプリ起動時に最初に読み込まれる)

④Cordova.jsをいじると、Buildの時に~info.plist file not found !** 的なエラーが出る!
もー勘弁してくれ、、、これに関しては以下の記事を参照
https://github.com/apache/cordova-ios/issues/764
Cordovaのバグレポートでまだ直していない、次回Updateで直す?みたいな事が書かれている。
src-cordova => platforms/ios => cordova => lib => projectFile.js
の中の44-50行目あたりを
var plist_file_entry = _.find(xcBuildConfiguration, function(entry) {
return (
entry.buildSettings &&
entry.buildSettings.INFOPLIST_FILE &&
entry.buildSettings.INFOPLIST_FILE.includes("プロジェクト名")
);
});

に変更したらエラーが出なくなった。

⑤Cordova.jsをいじる
Cordova.jsの2040行目あたりに
document.addEventListener("deviceready", logger.__onDeviceReady, false);
という関数があるのだが、これはアプリが立ち上がった時に、logger.onDeviceReadyという関数が呼ばれるよ!
という意味らしい。なので、マニュアルに書かれているUsageのコードをこのlogger.
onDeviceReady関数の中に埋め込んで、、、、試行錯誤。
無事URLが取れるようになりましたとさ。

終わりに

その他にも様々な苦労がありましたが、全ては書ききれず、覚えている範囲で走り書きしました。
かれこれ1週間弱沼の中でもがき苦しんでいました。
Cordovaに関しては情報が少なく、他に苦しんでいらっしゃる方の参考になれば幸いです。

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

laravel+Vueプロダクトのデプロイサーバセットアップ

書いてあること

EC2上にlaravel+vueで開発したプロダクトの実行環境を構築する方法。
OSはAmazonLinux2です。

手順

1.EC2のセットアップ

初期ユーザやrootにパスワードかける。
「yum update -y」でパッケージアップデートする。
日本時間にして日本語に対応させる。

$ timedatectl set-timezone Asia/Tokyo
$ localectl set-locale LANG=ja_JP.UTF-8
$ localectl set-keymap jp106
$ date
Wed Apr 22 13:53:00 JST 2020

ホスト名設定

$ hostnamectl set-hostname host.example.com

ホスト名設定

$ vi /etc/sysconfig/network
NETWORKING=yes
NOZEROCONF=yes
+HOSTNAME=host.example.com

2.証明書取得

httpsで公開したいので。
rootユーザで

$ wget https://dl.eff.org/certbot-auto
$ chmod 700 certbot-auto
EC2用に書き換え
$ vi certbot-auto
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
  Bootstrap() {
    ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
  }
  BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"elif grep -i "Amazon Linux" /etc/issue > /dev/null 2>&1 || \
   grep 'cpe:.*:amazon_linux:2' /etc/os-release > /dev/null 2>&1; then
  Bootstrap() {
    ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
  }
  BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"

コマンド移動

$ sudo mv ./certbot-auto /usr/local/bin

証明書取得

$ certbot-auto certonly --standalone -d csfhost.example.com --debug
/etc/letsencrypt/live/host.example.com/ 以下に証明書ができる。

3.nginxのインストール

rootユーザで

$ amazon-linux-extras install nginx1.12 -y

nginxの起動とインスタンス起動時自動起動の設定

$ sudo systemctl start nginx
$ sudo systemctl enable nginx
$ systemctl status nginx

4.phpのインストール

rootユーザで

$ amazon-linux-extras info php7.4
$ sudo amazon-linux-extras install php7.4 -y
$ php-fpm -v
PHP 7.4.x (fpm-fcgi) (built: Aug 14 2018 16:48:43)
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
$ yum install -y php-mbstring.x86_64
$ yum install -y php-xml.x86_64
$ yum install php-gd.x86_64

5.nginxとphp-fpmの連携設定

[/etc/nginx/nginx.conf]
# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
  worker_connections 1024;
}

http {
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

  access_log  /var/log/nginx/access.log  main;

  sendfile            on;
  tcp_nopush          on;
  tcp_nodelay         on;
  keepalive_timeout   65;
  types_hash_max_size 2048;

  include             /etc/nginx/mime.types;
  default_type        application/octet-stream;

  # Load modular configuration files from the /etc/nginx/conf.d directory.
  # See http://nginx.org/en/docs/ngx_core_module.html#include
  # for more information.
  #include /etc/nginx/conf.d/*.conf;
  index   index.php index.html index.htm;

  server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  localhost;

    return 301   https://$host$request_uri;
  }

  server {
    listen       443 ssl http2 default_server;
    listen       [::]:443 ssl http2 default_server;
    server_name  localhost;

    ssl_certificate "/etc/letsencrypt/live/host.example.com/fullchain.pem";
    ssl_certificate_key "/etc/letsencrypt/live/host.example.com/privkey.pem";

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    root   /usr/share/nginx/html/xxx/public;
    index  index.php index.html index.htm;

    access_log  /var/log/nginx/xxx-access.log  main;
    error_log   /var/log/nginx/xxx-error.log  warn;

    location / {
      try_files $uri $uri/ /index.php?$query_string;
    }

    error_page 404 /404.html;
    location = /40x.html {
      root   /usr/share/nginx/html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass   unix:/run/php-fpm/xxx.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root/index.php;
        fastcgi_param  HOSTNAME host.example.com;

        fastcgi_max_temp_file_size 0;
        fastcgi_buffer_size 4K;
        fastcgi_buffers 64 4k;

        include        fastcgi_params;
    }
  }
}

/etc/php-fpm.d/www.conf

/etc/php-fpm.d/xxx.conf にリネームして以下のように編集

[xxx.conf]
-; Start a new pool named 'www'.
+; Start a new pool named 'xxx'.
; the variable $pool can we used in any directive and will be replaced by the
-; pool name ('www' here)
-[www]
+; pool name ('xxx' here)
+[xxx]

; Per pool prefix
(略)
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
; RPM: apache user chosen to provide access to the same directories as httpd
-user = apache
+user = nginx
; RPM: Keep a group allowed to write in log dir.
-group = apache
+group = nginx

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific IPv4 address on
;                            a specific port;
;   '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses
;                            (IPv6 and IPv4-mapped) on a specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
-listen = /run/php-fpm/www.sock
+listen = /run/php-fpm/xxx.sock

; Set listen(2) backlog.
; Default Value: 511
;listen.backlog = 511
(略)
; these options, value is a comma separated list of user/group names.
; When set, listen.owner and listen.group are ignored
-listen.acl_users = apache
+listen.acl_users = apache,nginx
;listen.acl_groups =

; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
(略)
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
-slowlog = /var/log/php-fpm/www-slow.log
+slowlog = /var/log/php-fpm/xxx-slow.log

; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
(略)
; Default Value: nothing is defined by default except the values in php.ini and
;                specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
-php_admin_value[error_log] = /var/log/php-fpm/www-error.log
+php_admin_value[error_log] = /var/log/php-fpm/xxx-error.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 128M
(略)

php-fpm再起動

$ sudo systemctl restart php-fpm.service                            
nginx起動                         
$ sudo systemctl restart nginx                          

プロジェクトのドキュメントルートにphpinfoのファイルを作成して表示できるか確認

$ echo '<?php phpinfo(); ?>' > /usr/share/nginx/html/xxx/public/phpinfo.php

ブラウザでhttps://host.example.com/phpinfo.phpをたたいて確認

6.DBセットアップ

望むバージョンを入れるためリポジトリファイル新規作成

$ vi /etc/yum.repos.d/MariaDB.repo

以下のように定義

[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

インストール

$ yum install MariaDB-server MariaDB-client -y

起動

$ systemctl start mariadb

バージョン確認

$ mysql -V
mysql  Ver 15.1 Distrib 10.3.22-MariaDB, for Linux (x86_64) using readline 5.1

有効化

$ systemctl enable mariadb
$ systemctl is-enabled mariadb

セキュリティ設定 rootのパスワード等を設定します。

$ mysql_secure_installation
Set root password? [Y/n] Y
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] n
Remove test database and access to it? [Y/n] n
Reload privilege tables now? [Y/n] n
 ...
Thanks for using MariaDB!

rootのパスワードを設定しておく。
リモート接続できるように。

# mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 520
Server version: 10.1.31-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO root@"%" IDENTIFIED BY '[リモート接続パスワード]' WITH GRANT OPTION;
MariaDB [(none)]> FLUSH PRIVILEGES;

セキュリティを高めるためにポートを変えます。一旦停止してから設定に追記します。

$ systemctl stop mariadb
$ vi /etc/my.cnf.d/server.cnf

/etc/my.cnf.d/server.cnfの末尾に追記

port=3406

起動

$ systemctl start mariadb

7.DB構築実行

.envの接続設定のポート番号が合っているか確認する。
マイグレーション実行

$ php artisan migrate

シーダー実行

$ php artisan db:seed

10.ファイルパーミッション変更

以下4つのフォルダのパーミッションを777にする。

/usr/share/nginx/html/csf/storage/logs
/usr/share/nginx/html/csf/storage/framework/cache
/usr/share/nginx/html/csf/storage/framework/sessions
/usr/share/nginx/html/csf/storage/framework/views
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel+vueプロダクトのローカル開発環境

書いてあること

Laravelでバックエンド、Vueでフロントエンドの構成のプロダクトをWindowsクライアントで開発する際のクライアントのローカル環境の構築方法。

手順

1.「Visual Studio Code(VS Code)」のインストール

公式サイトからダウンロードしてインストール

2.「VS Code」拡張機能のインストール

「VS Code」を起動する。
アクティビティバーの「Extensions(拡張)」アイコンをクリック。
image.png

以下の検索ボックスを選択。
image.png

以下の拡張機能名を入力し、拡張機能をインストールしていく。

拡張機能名 説明
.ejs EJSファイル(HTMLのテンプレートファイル)に対して構文をハイライトしてくれる拡張機能。
Bracket Pair Colorizer 対となるカッコ色付けして見やすくしてくれる拡張機能。
ESLint 構文チェックするために必要な拡張機能。
Japanese Language Pack for Visual Studio Code 日本語化するために必要な拡張機能。
php cs fixer PHPのコード整形するために必要な拡張機能。「PHP CS Fixer」や「PHP-CS-Fixer」など、似たような名前の拡張機能が存在するため、間違えないようにインストールしてください。
PHP Intelephense PHPのコード補完をしてくれる拡張機能。
Vetur 「.Vue」ファイルを扱うために必要な拡張機能。
GitLens Gitで管理しているファイルの変更点をショートカットキー等で開ける拡張機能。
Debugger for Chrome VSCodeでフロントサイドのデバッグするための拡張機能。

インストールを実行するには以下の「Install」ボタンを実行する。
image.png

3.「Node.js」のインストール

公式サイトからダウンロードしてインストール。

4.phpインストール

公式サイトから「php-7.3.17-Win32-VC15-x64.zip」をダウンロードし解凍して任意の場所に配置。
配置したフォルダを環境変数PATHに追加
配置したフォルダに入り”php.ini-development”を”php.ini"でコピー。
php.iniを開き以下編集

[php.ini]
-;extension_dir = "ext"
-;extension=fileinfo
-;extension=mbstring
-;extension=gd2
+extension_dir = "ext"
+extension=fileinfo
+extension=mbstring
+extension=gd2

・・・

+extension=php_openssl
+extension=php_pdo_mysql

5.「Composer(コンポーザー)」のインストール

公式サイトからダウンロードしてインストール。
Developer modeはチェック入れる
phpの場所は手順4の配置フォルダ中のphp.exeを指定
update this php.iniはチェック外してスキップ
proxyは未設定のままスキップ

6.「SourceTree」のインストール

公式サイトから「SourceTreeSetup-2.6.10.exe」をダウンロードしてインストール。
求められるアカウント認証は作るかグーグルアカウントがあればそれでできます。

7.「SourceTree」を使ってソースの取得

以下の「Clone」アイコンをクリックする。
image.png

以下のような画面が表示される。
image.png

以下のとおり設定し、「クローン」ボタンを実行する。

《設定内容》 《設定値》
元のパス/URL https://develop.fan-technology.com/gitlab/InHouseDev/xxx.git
保存先のパス {SourceTreeのワークパス}{リポジトリ名}
名前 リポジトリ名
Local Folder [ルート]
詳細オプション ※デフォルトのまま

「Git Flow」メニューを実行。
image.png

デフォルト設定のまま「OK」ボタンを実行。
image.png

「develop」のブランチが作成されたことを確認する。
image.png

8.ライブラリで利用するモジュールをインストール

「Node.js」で使用している拡張機能の物理ファイル群(「node_modules」の中身)をインストールする作業です。
「laravel」で使用している拡張機能の物理ファイル群(「vendor」の中身)をインストールする作業です。

「コマンドプロンプト」を起動します。
以下のコマンドを入力して実行(Enterキー押下)し、「xxx」のソースがあるフォルダにカレントフォルダを設定します。

cd C:\Work\SourceTree\xxx

以下のコマンドを入力して実行(Enterキー押下)します。

npm install

初回はネットから落としてくるファイルが多いので10分ほどかかる場合があります。
つづいて以下のコマンドを入力して実行(Enterキー押下)します。

composer install

初回はネットから落としてくるファイルが多いので10分ほどかかる場合があります。

9.DBセットアップ

resourceフォルダのmariadb-10.4.10-winx64.msiでデフォルト設定にて。

rootユーザのパスワードは"root"で。

※「HeidiSQL」というソフトが入ってしまうようですが、使わないので無視してください。

10.DBマイグレーション

リポジトリ名のフォルダにコマンドプロンプトで入り以下実行

php artisan migrate 

11.初期データ投入

php artisan db:seed 

※「class not found」などのエラーが出る場合は「composer dump-autoload」のコマンドを実行して再度試してください。

12.実行

コマンドプロンプトを立ち上げてxxxのフォルダに移動

php artsan serve    

コマンドプロンプトを立ち上げてxxxのフォルダに移動

npm run watch

※ビルドが終わると自動でブラウザでサイトを開いてくれます。
http://localhost:3000/

また、browserSyncというホットリロードの仕組みを取り入れていますので、ソース系のファイル変更を検知してリビルドしてブラウザリロードしてくれます。

もしリロードしてほしくない場合は以下のURLでアクセスしてください。
http://localhost:8000/

13.「Postman」のインストール

「Postman」はAPIの動作確認を行う際に有用なツール。
GUIで動くcurl。
公式サイトからダウンロードしてインストール。

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

vue.jsでzlib.jsを利用して圧縮データをPOSTしてみた

問題

aws lambdaへCSVファイルをPOSTする際にペイロードが6MBに制限されているため、クライアント側で圧縮する方法を調査してみました。
lambdaのペイロード制限に関しては下記に記載があります。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html

事前準備

zlibjsというライブラリを利用するため、下記のコマンドでインストールを行います。
npm i zlibjs --save

実装例

<template>
  <div id="app">
    <form>
      <label>ファイル:</label>
      <input type="file" @change="onChange" /><br />

      <button @click="onSubmit">送信</button>
    </form>
  </div>
</template>

<script>
import {Zlib} from 'zlibjs/bin/gzip.min'

export default {
  name: 'App',
  data() {
    return {
      file: null,
      type: {
        1: 'zip',
        2: 'gzip',
      }
    }
  },
  methods: {
    onChange(e) {
      e.preventDefault()
      this.file = e.target.files[0]
    },
    onSubmit(e) {
      e.preventDefault()
      const reader = new FileReader()
      reader.readAsArrayBuffer(this.file)
      reader.onload = async() => {
        const plain = new Uint8Array(reader.result)
        const gzip = new Zlib.Gzip(plain).compress()
        console.log(gzip)

        const params = {
          data: btoa(String.fromCharCode(...gzip))
        }
        // axiosなどでPOST
        // axios.post(URL, params)
      }
    }
  }
}
</script>

まとめ

zlibjsを利用してクライアント側で簡単にgzip圧縮することができました。
テキストファイルであれば大きくファイルサイズを落とせるため、通信にかかる時間も少なくできます。
(圧縮にかかる時間はプラスされるのでどちらがベストかは環境によって要検証)

サーバサイドが100%lambdaの構成になっている場合などで選択肢として検討するのは良いかもしれません。

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

vue/nuxt/typescriptでtweenmaxを使う

はじめに

アニメーションを描こうと思った際に使おうと思ったら色々と情報が少なく調べるのが大変だったのでメモがてら

導入

今回nuxtを使っていたのでnuxtでやりますがvueも大して変わらないと思います。
nuxt、typescriptの環境は整っている前提で進めます

"nuxt": "^2.0.0",
"@nuxt/types": "^0.7.7",
"vue-property-decorator": "^8.4.2"

まずは使いたいプロジェクトファイルで

npm install --save gsap

します。
私の時は"gsap": "^3.3.4"でした。

@types/gsapというモジュールがありますがv3になって必要なくなったそうです、むしろ衝突するからいれるなとのこと
このページに書いてありました

次にtsconfig.jsonに

{
  "compilerOptions": {
    ...
  },
  "files": [
    "node_modules/gsap/types/index.d.ts"
  ]
}

を追記します。おそらくなくても大丈夫ですが、typescriptエラーが出る場合があるようです。
このページの一番下に書いてあります。

この時点で

example.vue
<template>
   <p class="hello">おはようございます</p>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { gsap } from 'gsap';
@Component
export default class Example extends Vue {
  mounted() {
    gsap.to(".hello", { x: 100, duration: 300ms })
  }
}
</script>

みたいな感じでかけば動くと思います。
後はドキュメントに乗ってるのでそれを参考に書いてください
ドキュメントはこちら

最後に

初期描画時の処理はmountedとかで描けばできるのですが、scroll時の処理がうまくできないので色々調べて頑張ってみたいと思います、やり方知っている方おられればコメント欄とかで教えていただけると嬉しいです。

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

Nuxt.js のライフサイクルを踏まえてトラッキングタグ埋め込みを実装する

Web サイトのエンハンスやマーケティングといった目的でユーザー分析や広告表示用のトラッキングタグを埋め込むことは開発の現場でよくあると思います。
Google Tag Manager などのツールを利用すれば比較的簡単に設定できますが、ユーザー ID など Web アプリケーション固有のデータをタグと併せて設定したい場合など、アプリケーション側にタグ埋め込み用のコードを書かなければならない場面もあります。

Nuxt.js のユニバーサルモードにおけるライフサイクル処理には幾つか種類があり、実行タイミングおよび SSR/CSR の実行有無がそれぞれ異なります。

これらを踏まえ、タグやカスタム変数を設定する際にどのライフサイクル処理を選択すれば良いのか整理します。

TL;DR

  • クライアントサイドで一度だけ実行したい処理(タグ埋め込みなど)は plugins を利用する。
  • ページ遷移ごとに実行したい処理(ページ遷移時の状態を表す変数の設定など)は Vue Router を利用する。

シチュエーション

以下のようなタグやカスタム変数に関する処理をまとめた composition を適宜 import して利用するようなシチュエーションを想定します。
コードは TypeScript で記述していますが、本記事の要点に関しては JavaScript の場合と特に大きな相違はありません。

compositions/sample-tag.ts
export function useSampleTag() {
  return {
    addTag() {
      // タグの埋め込み
    },
    addCustomValue() {
      // カスタム変数の設定
    }
  }
}

タグの埋め込み

タグの埋め込みは直アクセスされたタイミングで行い、 nuxt-link での遷移時には実行したくないケースが多いと思います。
このような用途では直アクセス時のみ実行される plugins が利用できます。

plugins/sample-tag.ts
import { useSampleTag } from '~/compositions/sample-tag'

export default () => {
  const st = useSampleTag()
  st.addTag()
}

カスタム変数の設定

ページ遷移ごとにログイン状態やユーザー ID などの値を設定したい場合は Vue Router の afterEach() が利用できます。

plugins/router.ts
import { useSampleTag } from '~/compositions/sample-tag'

export default () => {
  ctx.app.router!.afterEach((to, from) => {
    if (process.client) {
      const st = useSampleTag()
      st.addCustomValue()
    }
  })
}

middleware を利用する場合

middleware を用いても同様のコードで似たような処理は実現できます。
ただしユニバーサルモードでは直アクセス時に SSR でしか処理されないため、 window オブジェクトの利用といった CSR が前提となる処理が失敗する可能性があります。
特別な理由がない限りは Vue Router を利用するのが無難だと思います。

Vuex の Store を利用する場合

Vuex の Store から getter で値を取得したい場合、 addCustomValue() の引数に Store を取るようにして内部で getter を呼び出す作りにしても良いかもしれません。

compositions/sample-tag.ts
import { Store } from 'vuex/types'
import { AuthenticationState } from '~/store/authentication'

export function useSampleTag() {
  return {
    addTag() {
      // タグの埋め込み
    },
    addCustomValue(store: Store<AuthenticationState>) {
      // カスタム変数の設定
    }
  }
}
plugins/router.ts
import { Context } from '@nuxt/types'
import { useSampleTag } from '~/compositions/sample-tag'

export default (ctx: Context) => {
  ctx.app.router!.afterEach((to, from) => {
    if (process.client) {
      const st = useSampleTag()
      st.addCustomValue(ctx.store)
    }
  })
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sortをいれるとUnexpected side effect in computed propertyってエラー

Vue.jsで
Unexpected side effect in "xxx" computed property
というエラーがでたのでメモ

computedで配列をsortして返そうとしたらエラー

computed: {
  members() {
    return this.xxx.sort((a, b) => {
      if (a < b) return -1
      if (a > b) return 1
      return 0
    })
  }
}

computed: {
  members() {
    return this.xxx.slice().sort((a, b) => {
      if (a < b) return -1
      if (a > b) return 1
      return 0
    })
  }
}

これでエラー回避できた。

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

Vueで開発するときに整形ルールがごっちゃになるのをどうにかしたい

はじめに

vscodeでvueの開発をしているときに、しばしばコード整形ルールがごっちゃになってしまう。

自分の場合、慣例的にGoogle JavaScript Styleを採用しているので更にややこしいことになってしまっている。

特に困るのが以下の問題である:

  • シングルクオートにならない
  • 配列の末尾にカンマが入らない
  • 変数の終わりのセミコロンが消える
  • <template>内のタグの属性が同じ行になってしまう

結論

結論から先にいうと、vscodeのprettierがeslintの値を読まないのが原因だった。eslintのprettier/prettierの値と同じ内容の.prettierrc.jsを作成することで解決できた。

<template>タグ内の整形ルールがおかしいのはprettierのバグのようなのでoffで無効化し、eslint-plugin-vueのルールを採用するようにする。(参考:prettier not fixing vue/max-attributes-per-line

なお、vueのバージョンが新しくなってエラー扱いになるのは嫌なのでvueのコード整形ルールはvue3-recommendedを採用。filtersmethodsと統合され非推奨になるので注意。

npm install -D eslint-plugin-vue@next eslint-config-google
.eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: [
    'google',
    'plugin:vue/vue3-recommended',
    'eslint:recommended',
    '@vue/prettier',
  ],
  parserOptions: {
    parser: 'babel-eslint',
  },
  plugins: ['vue', 'prettier'],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    quotes: ['error', 'single'],
    'prettier/prettier': [
      'error',
      {
        htmlWhitespaceSensitivity: 'ignore',
        semi: true,
        singleQuote: true,
        trailingComma: 'all',
      },
    ],
    'vue/max-attributes-per-line': 'off',
  },
};
.prettierrc.js
module.exports = {
  htmlWhitespaceSensitivity: 'ignore',
  semi: true,
  singleQuote: true,
  trailingComma: 'all',
};

おまけ

さて、vueのプロジェクトをつくったときにしばしば以下の行が追加される。

.eslintrc.js
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',

しかし、要はプロダクションモードのときに圧縮するついでにconsole.log()を削除すれば済む話なのでわざわざルールとして入れる必用はないと思う。

これらの行は削除し、vue.config.jsの圧縮時の設定をいじって前回使ったTerserPluginを使って圧縮とconsole.log()の削除を同時に行う。

npm install -D terser-webpack-plugin
vue.config.js
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  configureWebpack: {
    // ...
    optimization: {
      minimize: process.env.NODE_ENV === 'production',
      minimizer: [
        new TerserPlugin({
          terserOptions: {
            ascii_only: true,
            compress: { drop_console: true },
            mangle: true,
            ecma: 6,
            output: { comments: false, beautify: false },
          },
        }),
      ],
    },
  },
  // ...省略
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vscode使用時にVueの整形ルールがごっちゃになるのをどうにかしたい

はじめに

vscodeでvueの開発をしているときに、しばしばコード整形ルールが競合を起こしておかしな形で整形されエラーや警告だらけになってしまう。

自分の場合、慣例的にGoogle JavaScript Styleを採用しているので更にややこしいことになってしまっている。

特に困るのが以下の問題である:

  • シングルクオートにならない
  • 配列の末尾にカンマが入らない
  • 行の終わりのセミコロンが消える
  • <template>内のタグの属性が同じ行になってしまう

結論

結論から先にいうと、vscodeのprettierがeslintの値を読まないのが原因だった。eslintのprettier/prettierの値と同じ内容の.prettierrc.jsを作成することで解決できた。

<template>タグ内の整形ルールがおかしいのはprettierのバグのようなのでoffで無効化し、eslint-plugin-vueのルールを採用するようにする。(参考:prettier not fixing vue/max-attributes-per-line

なお、vueのバージョンが新しくなってエラー扱いになるのは嫌なのでvueのコード整形ルールはvue3-recommendedを採用。vue3ではfiltersmethodsと統合され非推奨になるので注意。

npm install -D eslint-plugin-vue@next eslint-config-google
.eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: [
    'google',
    'plugin:vue/vue3-recommended',
    'eslint:recommended',
    '@vue/prettier',
  ],
  parserOptions: {
    parser: 'babel-eslint',
  },
  plugins: ['vue', 'prettier'],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    quotes: ['error', 'single'],
    'prettier/prettier': [
      'error',
      {
        htmlWhitespaceSensitivity: 'ignore',
        semi: true,
        singleQuote: true,
        trailingComma: 'all',
      },
    ],
    'vue/max-attributes-per-line': 'off',
  },
};
.prettierrc.js
module.exports = {
  htmlWhitespaceSensitivity: 'ignore',
  semi: true,
  singleQuote: true,
  trailingComma: 'all',
};

Veturは推奨プラグインになっているが、コード整形にprettierを使う場合は外したほうがいいようである。

おまけ

さて、vueのプロジェクトをつくったときに整形ルールにしばしば以下の行が追加される。

.eslintrc.js
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',

しかし、要はプロダクションモードのときにconsole.log()が表示されないようにすればいいのでわざわざルールとして入れる必用はないと思う。

そこで、前回使ったTerserPluginを使って圧縮し、同時にconsole.log()の削除を行うようvue.config.jsを修正する。

npm install -D terser-webpack-plugin
vue.config.js
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  configureWebpack: {
    // ...省略
    optimization: {
      minimize: process.env.NODE_ENV === 'production',
      minimizer: [
        new TerserPlugin({
          terserOptions: {
            ascii_only: true,
            compress: { drop_console: true },
            mangle: true,
            ecma: 6,
            output: { comments: false, beautify: false },
          },
        }),
      ],
    },
  },
  // ...省略
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】テンプレートリテラル記法メモ

index.html
<div id="app"></div>
app.js
const vm = new Vue({
  el: '#app',
  template: `
    <div v-if="message">
    {{ message }}
    </div>
    <div v-else>
      メッセージがありません
    </div>
  `,
  data() {
    return {
      message: 'こんにちは'
    }
  }
})

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