20210301のvue.jsに関する記事は5件です。

VueCLI + javascript環境で、WebWorkerを使用する

web workerでの記事は、webpack環境(nuxt.js環境)や、typescriptでの記事が多く存在し、
所々詰まっていたので記事にします。
Web Worker の使用(MDN)
Vue.js で WebWorker を使う
laravel-mix + Vue.js (ES) + WebWorker (TS)
これらの記事を参考にしました。

今回作成したgitリポジトリ

導入

vue ui 等でvueを立ち上げたものとして、
プロジェクト直下で
npm install worker-loader
とし、worker-loaderを導入します。

そして、プロジェクト直下のvue.config.js(無ければ作成し)に、
下記の内容を記述します

vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('worker-loader')
      .test(/\.worker\.js$/)
      .use({
        loader: 'worker-loader',
        options: {
          inline: true
        }
      })
      .loader('worker-loader')
      .end()
  }
  //...
}

実行準備

次に、srcフォルダ直下に
workersというフォルダと、worker1というjsファイルを作成します。
(※名称はなんでも良いと思います。また、フォルダは存在していなくても良いと思います。)

src
 |-assets
 |-components
 |- ...
  |-workers
     |-worker1.js
 |-App.vue
 |-main.js

このような構成になるかと思います。
次に、worker1.jsの中身を

worker1.js
addEventListener('message', e => {
    const { data } = e
    if (data && typeof data == 'number') {
        return postMessage(data*data)
    } else {
        return postMessage(10)
    }
})

export default {}

としてみます。
workerに投げられた変数が、int型の場合、二乗を返し、
それ以外の場合10を返す関数です。

次に、実際にvueファイルでworkerを呼び出します。
試しに、vuecliで生成されるHome.vueに書いていきます。

Home.vue
<script>
import Worker1 from 'worker-loader!@/workers/worker1'
export default {
  name: 'Home',
  components: {
  },
  data () {
    return {
      reload: 0
    }
  },
  methods: {
    workerTest: function () {
      const worker = new Worker1()
      worker.onmeessage = e => {
        const { data } = e
        this.reload = data
        worker.terminate()
      }
      worker.postMessage(20)
    }
  }
}
</script>

このようにします。
import文に、worker-loader!をpath名の前に挿入することに気をつけ、また、
worker.onmessageで、workerの処理が終わった場合(つまりpostMessageが返った場合)の処理を記述します。
今回の場合は、処理が終わり次第、this.reloadに代入し他で参照できるようにします。
また処理を終えたあとにworkerが存在する意味はないので、terminateで削除します。
(※importがlintに怒られるかもしれないので、その場合はruleに記述しましょう...)

実行

Home.vue
<template>
  <div class="home">
    <button @click="workerTest()">worker!!</button>
    {{ this.reload }}
  </div>
</template>

実際に、このようにして試してみると...

スクリーンショット 2021-03-01 18.51.46.png

スクリーンショット 2021-03-01 18.51.23.png

実際に、workerを通して、値が変更されたのがわかります。
また、

Home.vue
workerTest: function () {
      const worker = new Worker1()
      worker.onmessage = e => {
        const { data } = e
        this.reload = data
      }
      worker.postMessage(20)
    }

とし、
開発者ツールのSourcesをみると
スクリーンショット 2021-03-01 18.53.44.png
実際にworkerが立っているのがわかります。

other

ループの中で実行

試しに

Home.vue
workerTestLoop: function () {
      for (let i = 0; i < 10; i++) {
        const worker = new Worker1()
        worker.onmessage = e => {
          const { data } = e
          this.reload += data
          worker.terminate()
        }
        worker.postMessage(20)
      }
    }

とし、先ほど同様に実行してみると、
実際に、先ほどの数字が4000(400 * 10)となっているのがわかります。
スクリーンショット 2021-03-01 18.59.49.png

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

Vue3 の nextTick は何か

DOM の更新は非同期に行われる

Vue は 3.x, 2.x に関わらず、非同期に DOM 更新を実行しています。
DOM 更新が非同期であることは、以下の例で確認することができます。

<!DOCTYPE html>
<html lang="en">
<head>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="app"></div>
  <script>
    const { createApp, nextTick } = Vue

    const App = {
      template: `
          <div ref="sample">
            {{ msg }}
          </div>
      `,
      data() {
        return {
          msg: 'initial value'
        }
      },
      mounted () {
        this.msg = 'mounted value'
        console.log('mounted', this.$refs.sample.innerText)
        // mounted initial value
      }
    }
    createApp(App).mount('#app')
  </script>  
</body>
</html>

this.msg = 'mounted value' によって DOM 更新が必要なデータ更新をしていますが、直後に実行される console.log では、DOM 更新前のデータが出力されています。DOM 更新が同期的なら console.log では、mounted value が出力されるはずです。
DOM 更新後のデータを確認するためには、DOM 更新をする非同期関数の実行後である必要があります。

nextTick は、DOM 更新をする非同期関数を全て実行した後に、第1引数として渡された関数を実行します。そのため、nextTick に渡した関数で DOM を確認すると、DOM が更新されていることが確認できます。

<!DOCTYPE html>
<html lang="en">
<head>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="app"></div>
  <script>
    const { createApp, nextTick } = Vue

    const App = {
      template: `
          <div ref="sample">
            {{ msg }}
          </div>
      `,
      data() {
        return {
          msg: 'initial value'
        }
      },
      mounted () {
        this.msg = 'mounted value'
        nextTick(() => {
          console.log('nextTick', this.$refs.sample.innerText)
          // nextTick mounted value
        })
      }
    }
    createApp(App).mount('#app')
  </script>
</body>
</html>

console.log では、DOM 更新後のデータが出力されています。

nextTick を実装してみる

稀なケースを除き、基本的には DOM 更新をする非同期関数を全て実行した後に、DOM を確認すれば、DOM 更新後のデータを確認することができます。 これはとても簡単で、DOM 更新をする非同期関数より後に実行される非同期関数を実行し、その実行後に DOM を確認するだけです。

<!DOCTYPE html>
<html lang="en">
<head>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="app"></div>
  <script>
    const { createApp, nextTick } = Vue

    const App = {
      template: `
          <div ref="sample">
            {{ msg }}
          </div>
      `,
      data() {
        return {
          msg: 'initial value'
        }
      },
      mounted () {
        const self = this

        this.msg = 'mounted value'
        Promise.resolve().then(() => {
          console.log('promise', self.$refs.sample.innerText)
          // promise mounted value
        })
      },
    }
    createApp(App).mount('#app')
  </script>
</body>
</html>

DOM 更新をする非同期関数より後に実行される非同期関数は、Promise で実装します。とても簡単ですが、DOM 更新後のデータが console.log で出力されていることが確認できます。

まとめ

nextTick について荒くまとめました。
もっと nextTick を理解するには、イベントループ、マクロタスク、マイクロタスクについて理解を深めることが大切です。これらについて知りたい場合、以下の URL は良い助けになると思います。

https://javascript.info/event-loop#macrotasks-and-microtasks
https://www.youtube.com/watch?v=8aGhZQkoFbQ
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

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

vue routerについての話 2021/2/1

今日やること

vue.jsでurlにあるcode以下(クエリパラメーター)の文字をブラウザ上で表示する。

クエリパラメーターとは

まざまな情報をWebサーバーに伝えるためにURLに付け加える情報

今回の場合だと
localhost:8080/?code=AQUAKEOAJ....

色のついている部分がクエリパラメーターである。 ここを表示したいわけである。

まずVue.js Devtoolsを使うにあたりを導入したい。

Vue.js Devtools

Vue.js Devtoolsとは

Vue.js Devtoolsは、Vue.jsの開発をサポートする Chromeブラウザの拡張機能 です。これを導入するとconsoleを開かなくてもdataの中身などを確認することができるようになります。

Vue.js Devtools/Chrome ウェブストア

vue route

導入

マニュアルセットアップ
①yarnでインストールする

yarn add @vue-router

パラメータを取得してみる

パラメータの取得は、次のコードで可能です。
:js
this.$route.query.{キー}

{キー}の部分は、今回の例でいうと、code になります。

FBButton
<template>
    <div>
        <button @click="open">Login with Facebook</button>
        {{ info }}
    </div>
</template>

<script>
export default {
    data(){
        return {
        info:null
    }
}, mounted (){
    let code = this.$route.query.code
    if(code){
        this.info = code
        }
    }
}

DjangoApiプロジェクトを作る

今回はDjangoApiプロジェクトを公式ドキュメントにしたがって進めていく。

djagoをインストールする

pip install djangorestframework
pip install django-filter  # Filtering support

settingINSTALLED_APPSに'rest_framework'を追加する

settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',
]

次にurls.pyファイルを変更する。

urls.py
urlpatterns = [
    ...
    path('api-auth/', include('rest_framework.urls'))
]

次にsettings.pyの一番下にREST_FRAMEWORKの設定情報を追加します。ここでは標準のパーミッションの設定を追加しています。

setting.py
REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

次に
serializers.py
viewsets.py
ファイルを追加する。

serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username', 'email', 'is_staff']
viewsets.py
from django.contrib.auth.models import User
from rest_framework import viewsets
from core.serializers import User Serializer


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

以下のコードを追加する

urls.py
from rest_framework import routers
from core.viewsets import UserViewSet
from django.urls import path, include


# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    path('', include(router.urls))
]

auth_userを作る

写真

おそらくpythonを実行すると上のようなエラーが出てくると思う。

これはテーブルがないことによるエラーなのでmigrateすることで解決できる。

python manage.py makemigrations
python manage.py migrate

superuserを作る

 
Django の管理画面を利用するためには基本的にsuperuserを作ってアカウント登録する必要があります。

python manage.py createsuperuser

するとこんなコマンドが出てくるので情報を打ち込んでいきましょう。

$ python manage.py createsuperuser
Username (leave blank to use 'user1'): admin
Email address: admin@keicode.com
Password: # パスワードを入力
Password (again): 
This password is too common. # パスワードが単純過ぎる場合はエラー発生
Password: 
Password (again): 
Superuser created successfully.

新しいモデルの作り方

『model.py』の中身に使用するモデルクラスの定義していく。

モデルクラスはDjangoフレームワークが提供する『Django.db.models.Model』を親クラスとして継承して作成します。

モデルクラス作成の基本的なコードは以下のとおりです。

models.py
#モデルクラスの作成
from django.db import models

class モデル名(models.Model):
    フィールド名 = フィールドクラス(・・・,フィールドオプションの指定,・・・)

ここでは、『Account』というモデルクラスを定義して、その中のフィールドオブジェクトとして[fb_token][ig_token]を作成しています。そして、userモデルに紐付けます。

models.py
from django.db import models
from django.contrib.auth.models import User


class Account(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    fb_token = models.CharField(max_length=1000)
    ig_token = models.CharField(max_length=1000)

    def __str__(self):
        return str(self.id)

OneToOneField/ 1対1(1つのユーザーは1つのアカウントしかもてない)
on_delete=models.CASCADE/ 外部キーで参照してるオブジェクトが削除されたとき、その削除に合わせて行われる処理
models.CharField(max_length=1000)/ 文字列のフィールを作る

モデルをdjangoapiのadminに反映させる

admin.pyにアカウントモデルを登録することによりAPI上でアカウントモデルを確認することができる。

admin.py
admin.site.register(Account)

Api rootに作ったモデル情報を反映させる

先程作ったserializers.pyviewsets.pyにそれぞれのモデルに対応するコードを書きApi Rootでアカウントの情報を確認できるようにする。

serializerの機能について学びたい人はこちらを確認してください↓↓

[Django REST Framework] Serializer の 使い方 をまとめてみた

ここではAccountSerializerを作っていく

serializers.py
from rest_framework import serializers
from core.models import Account


class AccountSerializers(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'user', fb_token', 'ig_token']   

次にAccountViewSetクラスを作る。

viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer

AccountViewSetクラスト作れたらそれをurls.pyで使う

urls.py
from django.contrib import admin
from rest_framework import routers
+ from core.viewsets import UserViewSet, AccountViewSet
from django.urls import path, include



router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
+ router.register(r'accounts', AccountViewSet)

すると、こうなる

スクリーンショット 2021-03-01 9.34.39.png

 最後にviewsets.pypermissionクラスを設定しよう。

viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
+    # permission_classes = [permissions.IsAuthenticated]
+    permissions_classes = [AllowAny]

[AllowAny]では誰でも編集することができるが、[permissions.IsAuthenticated]では権限の持った人しかmodelの値を編集することができない.

今日の課題

`axiosを使ってフロントエンドから取得したクエリパラメーターをdjangoのapi(fb_token, ig_tokenフィールド)に入れる。

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

Vue.jsでページネーションを実装

Rails + Vue.jsを使用しています

Vue.js初心者なので分かりにくい説明だと思いますがよろしくお願いします:smiley:

前提条件

APIを使用してJSONでデータを返している前提で書いています
分からない方は以下などを参考にすれば分かると思います
既存のRailsアプリにJSONを返すAPIを実装してみる
Ruby on Rails, Vue.js で始めるモダン WEB アプリケーション入門

実装

vuejs-paginateを使って実装します
準備としてmain.jsに以下を記述

main.js

import Paginate from 'vuejs-paginate'
Vue.component('paginate', Paginate)

まずは全体のコード

完成コード(TweetsPage.vue)

<template>
  <div class="tweets">
    <div v-for="e in getLists" :key="e.id">
      {{e.id}}
      {{e.name}}
    </div>
    <paginate
      :v-model="currentPage" 
      :page-count="getPageCount"
      :click-handler="clickCallback"
      :page-range="3"
      :margin-pages="2"
      :prev-text="'<'"
      :next-text="'>'"
      :container-class="'pagination'"
      :page-link-class="'page-link'">
    </paginate>
  </div>
</template>
<script>
import axios from 'axios'
export default {
  data() {
    return {
      tweets: [],
      currentPage: 1,
      parPage: 10,
    }
  },
  mounted() {
    this.fetchTweets() //fetchTweetsを呼ぶ
  },
  methods: {
    fetchTweets() {
      axios
        .get(`/api/v1/tweets/tweets.json`)
        .then(response =>{
          this.tweets = response.data;
        })
    },
    clickCallback: function (pageNum) {
       this.currentPage = Number(pageNum);
    },
  },
  computed: {
     getLists: function() {
       let current = this.currentPage * this.parPage;
       let start = current - this.parPage;
       return this.tweets.slice(start, current);
     },
     getPageCount: function() {
       return Math.ceil(this.tweets.length / this.parPage);
     }
   }
}
</script>

順番に簡単に解説していきます

まずはdetaの準備

import axios from 'axios'
export default {
  data() {
    return {
      tweets: [], //axiosで取ってきたデータを格納するため
      currentPage: 1, //現在いるページ
      parPage: 10, //1ページで表示するデータ数 今回は1ページにつき10個のデータを表示
    }
  }
  mounted() {
    this.fetchTweets()
  },

axiosというのを使ってJSONで返ってきているデータを取得してtweetsに格納

  methods: {
    fetchTweets() {
      axios
        .get(`/api/v1/tweets/index.json`) //json形式のデータが確認できるURLを記述
        .then(response =>{
          this.tweets = response.data; //dataで定義したtweetsに取ってきたデータを格納
        })
    },

出力したいデータを出力
そしてページネーション実装

<template>
 <div v-for="e in getLists" :key="e.id"> //Railsでいう each doのようなものでしょうか 繰り返しでgetList(この後出てくる)の返り値であるデータを全て表示
   {{e.id}}
   {{e.name}}
 </div>

 <paginate
   :v-model="currentPage" // 現在のページ
   :page-count="getPageCount" //総ページ数
   :click-handler="clickCallback" //クリック(ページ移動)する度にclickCallbackを呼び出す
   :page-range="3" //3ページ以降は[...]で省力する
   :margin-pages="2" //ラスト2ページから省力しない
   :prev-text="'<'" //戻るボタンは[<]
   :next-text="'>'" //進むボタンは[>]
   :container-class="'pagination'"
   :page-link-class="'page-link'">
 </paginate>
</template>

ページネーションの処理

現在のページを決めたり、今いるページに合ったデータだけを取得したり、全部で何ページ必要か計算したり

  //現在のページを決める処理
    clickCallback: function (pageNum) { 
       this.currentPage = Number(pageNum); //ページネーションで選択したページを現在のページにする 2ページを選択したらcurrentPageに2が代入される 3ページなら3
    },
  },
  computed: {
  //必要なデータだけを取得する処理
     getLists: function() { 
       let current = this.currentPage * this.parPage; //現在のページ×1ページに表示する数
       let start = current - this.parPage;
       return this.tweets.slice(start, current); //そのページに合ったデータだけを表示 1ページ目ならtweetsの0〜9番目を、2ページ目なら10〜19番目のデータを表示
     },
  //総ページ数を決める処理
     getPageCount: function() {
       return Math.ceil(this.tweet.length / this.parPage); //全部で何ページ必要か
     }
   }

後はapplication.html.erbなどにBootstrapのスタイルを適用するCDNを呼び出せば見た目が整います

<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.css">

このようになるのではないでしょうか?
スクリーンショット 2021-03-01 0.40.00.png

おかしな箇所があればコメントください!
最後までご高覧いただきましてありがとうございました!:laughing:

参考記事

「vuejs-paginate」を使ってページネーションを実装する

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

express+vueで動画収録アプリを作ってみた

はじめに

PC1台で動画収録と簡易編集を行えるアプリケーションを作成した話。
https://github.com/tatsuya-nagashima/vue-electron-video-recorder.git

主な機能

  • 動画の収録
    • 外部入力の動画を収録しつつ、その様子をプレイヤーで表示する
  • 動画のファイル切り出し
    • 指定した範囲を切り出してファイル保存することができる
  • 動画のチャプター登録
    • 指定した範囲をチャプターとして登録しておき、後から見直すことができる
  • DVR再生
    • 収録しながら過去の一部分のみを再生することができる
  • その他
    • プレイヤーの再生/停止、シーク, IN点/OUT点の指定をショートカットキーで行える
    • スクラビング(プレイヤーの映像をシークに追従させる)が行える
      など

デモ

demo.gif

構成

サーバサイド: express + sqlite + ffmpeg
フロント: vue + vuetify + hls.js

UI操作からのリクエストを受け付けるアプリケーションサーバとしてexpressを利用している。
動画情報の管理はsqliteで行なっており、動画の収録や切り出しにはffmpegを用いている。

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