20210115のvue.jsに関する記事は9件です。

難しく考えずに、簡単にfirebaseに画像をアップロードしよう!

今回は、firebaseのStorageを利用して、firebase上に画像をアップロードしていきます。

また、その画像を使ってユーザーのプロフィール画像(photoURL)に変更を反映させます。

内容的には、そこまで難しくないのでぜひご覧ください!

流れとしては以下のようになります。

1, 画像をアップロードするinputタグを作成
2, 画像をアップロードした際に、firebaseのStorageに保管(この時、userごとにフォルダを作成していきます)し、firebaseのユーザー情報を更新
3, アップロードする前の画像を破棄
4, firebaseのルール設定
5, Web上に反映

このような流れになっております。

全部を理解しなくてもいいと思います。

しっかり流れを把握しつつ、一緒に説明を見ていきましょう!

1, 画像をアップロードするinputタグを作成

今回は、<input type="file">としてやっても良いのですが、僕の大好きなBuefyを用いて使っていきます。

Buefyの使い方が分からないという方は、こちらの記事初心者必見!サイト制作は楽してなんぼ。CSSフレームワークBuefyの紹介!!に書いてあるので、ぜひ時間がある方は目を通してください。

App.vue
               <b-upload type="file" style="margin-top: 1rem" @change.native="uploadImage">
                  <a class="button is-info">
                    <b-icon
                      pack="fas"
                      icon="upload"
                      size="medium"
                      style="margin-right: 0.5rem; margin-bottom: 0.05rem; vertical-align: middle"
                    ></b-icon>
                    プロフィール画像を変更する
                  </a>
                </b-upload>

@change.nativeで画像がアップロードされたかを検知しています。

inputの場合は<input type="file" @change="uploadImage" />でOKです。

2, firebaseのストレージに保管し、firebaseのユーザー情報を更新

methodsにてuploadImageという関数を定義し、以下のように記述します。

また、画像ファイルは、imagesフォルダにユーザーID(uid)ごとにファルダを作成し、そこに画像ファイルを格納します。

images/
 ├ uid/
 │ └ image.jpg

イメージとしてはこんな感じです。

ユーザー情報を更新するのにはupdateProfileを使用します。

ここで、注意なのですがupdateProfileはfirebase.auth().currentUserで取得したuserで行わないと更新されません。firebase.auth().onAuthStateChanged()でやらないようにお願いします。ちなみに理由は分かりません!

これらを踏まえて、以下の記述を参考に書いてください。

App.vue
<script>
  methods: {
      async uploadImage(event) {
      event.preventDefault() //要素のデフォルトのイベントを無効
      event.stopPropagation() //子要素のイベントが親要素にも伝播することを防ぐ
      const userStorageRef = firebase.app().storage().ref()

      // type="file"を指定されたinput要素のchangeイベントは「ファイルのリスト」を返す
      const file = event.target.files[0]

      // ファイルが存在しないか、ファイル形式が"image/*"ではないとき
      if (!file || !file.type.match(/image\/*/)) {
        alert('不適切なファイル形式です')
        return false
      }

      await userStorageRef
        // images/uid/に画像ファイルを保管
        .child(`images/${this.user.uid}/${file.name}`)
        .put(file)
        .then(() => {
          // 保管した画像ファイルのURLを取得
          userStorageRef
            .child(`images/${this.user.uid}/${file.name}`)
            .getDownloadURL()
            .then(async imageURL => {
              // ユーザーを取得
              // 注意: firebase.auth().onAuthStateChangedでやらないように!
              const user = firebase.auth().currentUser
              await user
                .updateProfile({
                  photoURL: imageURL
                })
                .then(async () => {
                  // vuex store state userの情報更新
                  this.$store.dispatch('changeUserPhotoURL', imageURL)
                  // これを行わいと変更内容が反映されない
                  // また、一番最後に行う
                  user.reload()
                })
            })
        })
    }
  }
</script>

3, アップロードする前の画像を破棄

次に、変更する前の画像はもう使わないので、これを消去します。

firebaseのStorageには、ファルダにあるファイルを一括で消去する機能が携わっていません。なので、listAll()という関数を用いて、ファイルの配列を取得し、1個1個消去していきます。
また、新しい画像をアップロードする前に行うということにも注意してください!
※これよりも良いやり方を知っている方は、ぜひコメント欄にて教えて頂くと幸いです。

App.vue
<script>
  methods: {
      async uploadImage(event) {
      event.preventDefault() //要素のデフォルトのイベントを無効
      event.stopPropagation() //子要素のイベントが親要素にも伝播することを防ぐ
      const userStorageRef = firebase.app().storage().ref()

      // type="file"を指定されたinput要素のchangeイベントは「ファイルのリスト」を返す
      const file = event.target.files[0]

      // ファイルが存在しないか、ファイル形式が"image/*"ではないとき
      if (!file || !file.type.match(/image\/*/)) {
        alert('不適切なファイル形式です')
        return false
      }

    // 追加
    await userStorageRef
        .child(`images/${this.user.uid}`)
        .listAll()
        .then(result => {
          for (let i = 0; result.items.length; i++) {
        userStorageRef.child(`images/${this.user.uid}/${result.items[i].name}`).delete()
          }
        })
        // ファイルがない場合はスルー(初期値はstorageに保存していないため)
        .catch(() => {})
    }

   //ここまで

    await userStorageRef
        // images/uid/に画像ファイルを保管
        .child(`images/${this.user.uid}/${file.name}`)
        .put(file)
        .then(() => {
          // 保管した画像ファイルのURLを取得
          userStorageRef
            .child(`images/${this.user.uid}/${file.name}`)
            .getDownloadURL()
            .then(async imageURL => {
              // ユーザーを取得
              // 注意: firebase.auth().onAuthStateChangedでやらないように!
              const user = firebase.auth().currentUser
              await user
                .updateProfile({
                  photoURL: imageURL
                })
                .then(async () => {
                  // vuex store state userの情報更新
                  this.$store.dispatch('changeUserPhotoURL', imageURL)
                  // これを行わいと変更内容が反映されない
                  // また、一番最後に行う
                  user.reload()
                })
            })
        })
  }
</script>

4, firebaseのルール設定

次に、Firebase Consoleの画面を開き、左側の「Storage」という項目を開いてください。

そして、「Rules」を開いてください。

今回のルール設定は、ユーザー本人かつ認証済みかつ画像ファイルであれば変更を行えるというルール設定を行います。

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /images/{uid}/{allPaths=**}  {
      allow read, write: if request.auth.uid == uid && request.auth != null && request.resource.contentType.matches('image/.*')
    }
  }
}

request.auth.uid == uid:ユーザー本人かの確認
request.auth != null:認証済みかの判断
request.resource.contentType.matches('image/.*'):画像ファイルであるかの確認

5, Web上に反映

後は、user情報を取得してWeb上に反映するだけです!

一応、書いておきますね。

App.vue
firebase.auth().onAuthStateChanged((user)) => {
  console.log(user.photoURL)
})

このようにしてユーザーの画像ファイルを更新できると思います!

最近、毎日知らないことだらけの連続で頭がパンクしそうです(笑)

でもめげずに頑張りたいと思います!

ぜひ皆さんも一緒に頑張りましょう!!

以上、「難しく考えずに、簡単にfirebaseに画像をアップロードしよう!」でした!

良かったら、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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

【Vue.js】初めてのコンポーネント。Vueインスタンスを再利用。

index.html
<div id="app">
  <hello-component></hello-component>
  <hello-component></hello-component>
  <hello-component></hello-component>
  <hello-component></hello-component>
  <hello-component></hello-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>

<script>
  Vue.component('hello-component', {
    data: function() {
     return {
      number: 13
     }
    },
    template: '<p>Hello{{ number }}</p>'
  })

  var app = new Vue({
    el: '#app',
  })
</script>

コンポーネントの中では、dataは関数である必要があるらしい。

Hello13

Hello13

Hello13

Hello13

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

【Vue 3】vue-composableのuseVModelを利用して複数の値のv-modelの煩雑さを回避する

Vue 3の機能変更の一つとして、コンポーネントに対するv-modelが複数の値に対応するになりました。
機能的には、3.xから削除された.sync修飾子と同様のものです。

親コンポーネントでの利用は、次のように直感的に理解できます。

App.vue
<template>
  <app-form v-model.title="title"
    v-model.description="description"
  />
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import AppForm from '@/components/AppForm.vue'

export default defineComponent({
  components: {
    AppForm,
  }  
})
</script>

子コンポーネント側の記述は次のようになります。

AppForm.vue
<template>
  <label>タイトル</label>
  <input :value="title"
    @input="$emit('update:title', $event.target.value)"
  />

  <label>概要</label>
  <input :value="description"
    @input="$emit('update:description', $event.target.value)"
  />
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  props: {
    title: {
      type: String,
      required: false,
      default: ''
    },
    description: {
      type: String,
      required: false,
      default: ''
    }
  }
})
</script>

何が煩雑か

子コンポーネント側から、親コンポーネントに対して値の変更を伝える必要があるのですが、propsの値は直接変更することができないので子コンポーネントでは便利なv-modelディレクディブを使うことができず、:value@inputに分けて記述する必要があります。

さらに、親に変更を通知する手段として$emit('update:title', $event.target.value)のようにupdate:{v-modelの引数で渡した名前}というイベント名をemitする必要があります。
似たような記述を何回も書く必要がありますし、update:{v-modelの引数で渡した名前}というイベント名を使う構文はなかなか覚えられません・・・
機能として提供されているわけではなく、emitのイベント名での提供なのでインテリヘンスが効かないですし、タイポにも気が付きにくです。

Composition APIでロジックを外だしできるようになったので共通関数にできないのか・・・と思ったらすでにありました。

vue-composableを使う

vue-composableは、Composition APIで使える便利な関数群をまとめらライブラリです。今回利用するuseVModel以外にも、バリデーション・フォーマット・マウスイベントなど汎用性の高い機能が利用できます。

まずはインストールしましょう。

# install with yarn
yarn add @vue/composition-api vue-composable

# install with npm
npm install @vue/composition-api vue-composable

useVModel

useVModelは第一引数にpropsオブジェクト、第二引数にpropsのキーを受け取り、リアクティブなpropsのキーのコピーを返します。

const _title = useVModel(props, 'title')

useVModel()で返された値をv-modelで利用すれば、それだけで親コンポーネントに変更を通知してくれます!

AppFrom.vue
<template>
  <label>タイトル</label>
  <input v-model="_title"/>

  <label>概要</label>
  <input v-model="_description"/>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { useVModel } from 'vue-composable'
export default defineComponent({
  props: {
    title: {
      type: String,
      required: false,
      default: ''
    },
    description: {
      type: String,
      required: false,
      default: ''
    }
  },
  setup (props) {
    const _title = useVModel(props, 'title')
    const _description = useVModel(props, 'description')

    return {
      _title,
      _description
    }
  }
})
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.js(Vue)でイベントの構造化データ マークアップの書き方と例

Nuxt.js(vue)でイベントの構造化データ マークアップの書き方と例になります。
JSON-JDやmicrodataと言った書き方がありますが、本記事では、JSON-JDの例になります。

Nuxt.js(Vue)でイベントの構造化データ マークアップの書き方

<script>
export default {
  head() {
    return {
      __dangerouslyDisableSanitizers: ['script'],
      script: [
        {
          innerHTML: `{
            "@context": "http://schema.org",
            "@type": "Event",
          }`,
          type: 'application/ld+json'
        }
      ]
    }
  }
}
</script>

headの中に記載していきます。

Nuxt.js(vue)でイベントの構造化データ マークアップの例

<script>
export default {
  data() {
    return {
      name: 'demo',
      description: 'demo',
      img: 'https://demo.jp/demo.jpg'
    }
  },
  head() {
    return {
      __dangerouslyDisableSanitizers: ['script'],
      script: [
        {
          innerHTML: `{
            "@context": "http://schema.org",
            "@type": "Event",
            "name": "${this.name}",
            "description": "${this.description}",
            "startDate": "2021-01-28T15:00+09:00",
            "endDate": "2021-01-28T18:00+09:00",
            "eventAttendanceMode":
              "https://schema.org/OfflineEventAttendanceMode",
            "eventStatus": "https://schema.org/EventScheduled"
            "location": {
              "@type": "VirtualLocation",
              "url": "https://demo.jp"
            },
            "offers": {
              "@type": "Offer",
              "price": "0",
              "priceCurrency": "yen",
              "url": "https://demo.jp",
              "availability": "http://schema.org/InStock"
            },
            "performer": {
              "@type": "PerformingGroup",
              "name": "${this.name}"
            },
            "organizer": {
              "@type": "Organization",
              "name": "demo",
              "url": "https://demo.jp"
            },
            "image": "${this.image}"
          }`,
          type: 'application/ld+json'
        }
      ]
    }
  }
}
</script>

上記がNuxt.js(Vue)イベントの構造化データ マークアップの例になります。
構造化データについては、下記が参考になります。
https://developers.google.com/search/docs/data-types/event?hl=ja

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

Jestで同一ファイル内で異なる振る舞いのモックを使用したい場合(axios)

初めに

メモ書きようです。
ざっくりと書いていますのでご了承ください。

同一ファイル内で異なる振る舞いのモックを使用したい場合

例えば以下のようなAPIがあったとします。

test.ts
async hoge(): Promise<Hoge> {
    const res = await $axios.$get('/hoge');
}

async fuga(): Promise<Fuga> {
    const res = await $axios.$get('/fuga');
}

異なる振る舞いのモックを作成する場合は以下のように書けます。

test.spec.ts
import { $axios } from '@/store/utils/api.ts';

jest.mock('@/store/utils/api.ts', () => ({
  $axios: {
    $get: jest.fn(),
  }
}));

describe('Test', () => {
  test('hoge', async () => {
    ($axios.$get as jest.Mock).mockImplementation(() =>
      Promise.resolve({
        ~
    }));
  });
  await hoge()
});

describe('Test', () => {
  test('fuga', async () => {
    ($axios.$get as jest.Mock).mockImplementation(() =>
      Promise.resolve({
        ~
    }));
  });
  await fuga()
});


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

pageとcomponentのやりとり

$emit

componentからpageへ値を渡す

pages/index.vue
<template>
  <div>
    <div>
      {{ count }}
    </div>
    <child @eventName="parentMethod" />
  </div>
</template>

<script>
import Child from '@/components/child'

export default {
  components: {
    Child
  },
  data: () => ({
    count: 0
  }),
  methods: {
    parentMethod(count) {
      this.count = count
    }
  }
}
</script>
components/child.vue
<template>
  <div>
    <button @click="sendParent">送信</button>
  </div>
</template>

<script>
export default {
  data: () => ({
    clickCount: 0
  }),
  methods: {
    sendParent() {
      this.$emit('eventName', ++this.clickCount)
    }
  }
}
</script>

$refs

pageでcomponentの関数を呼ぶ

pages/index.vue
<template>
  <div>
    <div>
      <button @click="doChildMethod">実行</button>
    </div>
    <child ref="child" />
  </div>
</template>

<script>
import Child from '@/components/child'

export default {
  components: {
    Child
  },
  methods: {
    doChildMethod() {
      this.$refs.child.countUp()
    }
  }
}
</script>
components/child.vue
<template>
  <div>
    {{ clickCount }}
  </div>
</template>

<script>
export default {
  data: () => ({
    clickCount: 0
  }),
  methods: {
    countUp() {
      this.clickCount++
    }
  }
}
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜだ!?actionsの引数で、どうしてもundefiendになってしまった時の話

皆さんこんにちは!

今回は、エラー備忘録として書いていきます。

同じエラーに苦しんでいる方の参考になればなと思います。

まず、初めに以下の二つのコードを見ていただきたいと思います。

sotre/index.js
changeUserPhotoURL({ commit }, {updatePhotoURL}) {
      commit('changeUserPhotoURL', updatePhotoURL)
    },
sotre/index.js
changeUserPhotoURL({ commit }, updatePhotoURL) {
      commit('changeUserPhotoURL', updatePhotoURL)
    },

実行結果は

{updatePhotoURL}undefiend
updatePhotoURLにはしっかりと値は受け取れてます。

なぜ、このような結果になるのかは分かりません。。。

分かる方がいたら、ぜひコメント欄にて教えて頂くと幸いです。

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

webpack Vue.js TypeScript を使った環境構築まで (WSL)

概要

サービスを作るにあたって、Vue.js+TypeScriptでさくっと作れないか模索する準備として、環境構築したので
まとめてみました。調べながらやっており、手順としては冗長。

やりたいこと

JavaScriptとWebサーバーだけで良い感じに開発する アプリサーバーは無し

前提環境

  • Ubuntu 18.04.2 LTS
  • node v12.13.1
  • yarn 1.19.2
  • Apache/2.4.29 (Ubuntu)

リポジトリ

ディレクトリ構成

この記事時点での最終は下記になる。
Apacheのルートディレクトリを public 配下としておく nginxでも何でもいいが

.
├── .babelrc
├── .browserslistrc
├── .gitignore
├── package.json
├── public
│   ├── index.html
│   └── js
│       └── app.js
├── src
│   ├── Hello.vue
│   └── app.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock

HTML作成

こんな感じで作成し、ブラウザで表示させる jsはまだない

public/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TEST</title>
    <script src="js/app.js"></script>
  </head>
  <body>
    <div id="app">test app</div>
  </body>
</html>

yarn初期化

yarn init -y

package.jsonが自動で作成される。

package.json
{
  "name": "test5",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}

webpack準備

yarnによるwebpackのインストール

yarn add --dev webpack webpack-cli
package.json
{
  "name": "test4",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "^5.13.0",
    "webpack-cli": "^4.3.1"
  }
}

node_modules というディレクトリが作られ、webpack利用に必要なパッケージがインストールされる。
これらの詳細の情報は、同時に作成されたyarn.lockに記録される。

テスト用のjsの準備

src/app.js
let test = ()=> {alert("webpack test");};
test();

webpackの設定ファイル準備

webpack.config.js
const path = require('path');

const env = process.env.NODE_ENV || 'development';

module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, 'public/js'),
  },
  mode: env,
}

webpack実行

npxとは、パッケージ管理しているnodeモジュールを簡単に実行できる仕組み
node_modules/.bin 以下の実行ファイルを実行できる。

npx webpack --mode=development
asset app.js 802 bytes [emitted] (name: main)
./src/app.js 49 bytes [built] [code generated]
webpack 5.14.0 compiled successfully in 137 ms
src/js/app.js
/*
 * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
/******/ (() => { // webpackBootstrap
/*!********************!*\
  !*** ./src/app.js ***!
  \********************/
eval("let test = ()=> {alert(\"webpack test\");};\ntest();\n\n//# sourceURL=webpack://test5/./src/app.js?");
/******/ })()
;
【参考】webpackのmodeオプション

下記のようにproductionモードで実行すると、出力結果が異なる 不要な記述が省略されていることが分かる

npx webpack --mode=production # もしくはmodeを付けない場合はproductionで実行される
dist/app.js
console.log("webpack test");

https://webpack.js.org/configuration/mode/

babelの導入

yarn add --dev @babel/core @babel/preset-env babel-loader core-js

webpackの設定に、jsファイルはbabelを通すように設定

webpack.config.js
  module: {
    rules: [
        {
          test: /\.js$/,
          loader: 'babel-loader',
        }
      ]
  },

babelの設定用にファイルを2つ追加する

.babelrc
{
    "presets": [[ "@babel/env", { "modules": false } ]],
    "env": {
       "test": {
            "presets": [[ "@babel/env", { "targets": { "node": "current" } } ]]
        }
    }
}
.browserslistrc
> 1%

再び、webpackを実行すると、ES5に対応した書き方に変換されていることがわかる。

public/js/app.js
/*
 * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
/******/ (function() { // webpackBootstrap
/*!********************!*\
  !*** ./src/app.js ***!
  \********************/
eval("var test = function test() {\n  alert(\"webpack test\");\n};\n\ntest();\n\n//# sourceURL=webpack://test5/./src/app.js?");
/******/ })()
;

IEでエラーが出ないことを確認

jsをVueのものに書き換えていく

src/app.js
import Vue from 'vue';
import Hello from './Hello.vue';

document.addEventListener('DOMContentLoaded', () => {
  new Vue(Hello).$mount('#app');
});

vueの単一コンポーネントファイルを追加

src/Hello.vue
<template>
  <div>
    <p>{{ greeting }} World!</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      greeting: ''
    };
  },
  created() {
    this.greeting = 'hello';
  }
}
</script>

webpack 実行

npx webpack --mode="development"
asset app.js 239 KiB [emitted] (name: main)
runtime modules 916 bytes 4 modules
cacheable modules 223 KiB
  modules by path ./src/ 2.13 KiB
    ./src/app.js 152 bytes [built] [code generated]
    ./src/Hello.vue 1.04 KiB [built] [code generated]
    ./src/Hello.vue?vue&type=template&id=184cbba9& 197 bytes [built] [code generated]
    ./src/Hello.vue?vue&type=script&lang=js& 336 bytes [built] [code generated]
    ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/Hello.vue?vue&type=template&id=184cbba9& 267 bytes [built] [code generated]
    ./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/Hello.vue?vue&type=script&lang=js& 172 bytes [built] [code generated]
  modules by path ./node_modules/ 221 KiB
    ./node_modules/vue/dist/vue.runtime.esm.js 218 KiB [built] [code generated]
    ./node_modules/vue-loader/lib/runtime/componentNormalizer.js 2.71 KiB [built] [code generated]
webpack 5.14.0 compiled successfully in 2064 ms

ブラウザで問題なく表示されていることを確認

単一コンポ―ネント CSS対応

yarn add --dev style-loader css-loader

webpackにcss用の設定を追加

webpack.config.js
 module: {
    rules: 
    [
        {
          test: /\.vue$/,
          loader: 'vue-loader'
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
        },
        {
          test: /\.css$/,
          loader: 'vue-style-loader',
          loader: 'css-loader',
        }
      ]
  },
npx webpack --mode="development"
asset app.js 257 KiB [compared for emit] (name: main)
runtime modules 1.19 KiB 5 modules
cacheable modules 232 KiB
  modules by path ./src/ 3.39 KiB 9 modules
  modules by path ./node_modules/ 229 KiB
    ./node_modules/vue/dist/vue.runtime.esm.js 218 KiB [built] [code generated]
    ./node_modules/vue-loader/lib/runtime/componentNormalizer.js 2.71 KiB [built] [code generated]
    ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
    ./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
webpack 5.14.0 compiled successfully in 2071 ms

CSSが app.js に埋め込まれ、適用されるようになった。

Typescript対応

yarn add --dev typescript ts-loader

app.js → app.tsへ

内容はとくにいじらず

Hello.vueをts対応

Hello.vue
<script lang="ts">

webpack設定

webpack.config.js
--
entry: './src/app.ts',
--

        {
          test: /\.tsx?$/,
          exclude: /node_modules/,
          use: [
              {
              loader: 'ts-loader',
              options: {
                  appendTsSuffixTo: [/\.vue$/]
              }
              }
          ]
        }
--
[参考]

"moduleResolution": "node",
"allowSyntheticDefaultImports": true
の2行が無いと、import Vue from 'vue' のようなインポートができなかった。Typescript初心者なのであとで調べる。

TypeScript設定ファイルの準備

tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "module": "es2015",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "sourceMap": true,
        "baseUrl": "./",
        "moduleResolution": "node",
        "allowSyntheticDefaultImports": true
    },
    "include": [
        "./src/*.vue",
        "./src/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}

試しに、型をつけてみる

Hello.vue
-略-
<script lang="ts">
export default {
  data() {
    return {
      greeting: ''
    };
  },
  created() {
    this.greeting = 'hello';
  },
  computed:{
      computed_greeting():string{
          return this.greeting;
      }
  }
}
</script>
-略-

これでwebpackが通り、TypeScriptが扱えている

npx webpack --mode="development"
asset app.js 257 KiB [compared for emit] (name: main)
runtime modules 1.19 KiB 5 modules
cacheable modules 233 KiB
  modules by path ./src/ 3.57 KiB 9 modules
  modules by path ./node_modules/ 229 KiB
    ./node_modules/vue/dist/vue.runtime.esm.js 218 KiB [built] [code generated]
    ./node_modules/vue-loader/lib/runtime/componentNormalizer.js 2.71 KiB [built] [code generated]
    ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
    ./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
webpack 5.14.0 compiled successfully in 4685 ms

ためしに型に合っていない値を返すと、こける

src/Hello.vue
-略-
  computed:{
      computed_greeting():string{
          return 1;
      }
  }
-略-
npx webpack --mode="development"
asset app.js 257 KiB [emitted] (name: main)
runtime modules 1.19 KiB 5 modules
cacheable modules 232 KiB
  modules by path ./src/ 3.56 KiB 9 modules
  modules by path ./node_modules/ 229 KiB
    ./node_modules/vue/dist/vue.runtime.esm.js 218 KiB [built] [code generated]
    ./node_modules/vue-loader/lib/runtime/componentNormalizer.js 2.71 KiB [built] [code generated]
    ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
    ./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]

ERROR in /home/obana/js/vue/test5/src/Hello.vue.ts
[tsl] ERROR in /home/obana/js/vue/test5/src/Hello.vue.ts(19,11)
      TS2322: Type 'number' is not assignable to type 'string'.

.gitignore

git管理にあたり、作成 とりあえずnodeモジュール除外すれば良いか。

node_modules/

ここまでで、一応開発できるようになったものの、
まだまだ準備したいところなので、続きは別でまとめていく。

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

Vuexでオブジェクトのプロパティの値の変更を検知したいときの対処法!!

今日は、Vuexについての記事を書いていこうかなと思います。

Vuexでオブジェクトのプロパティの値を変更しても、値の変更は検知されません。(これで3,4時間くらい時間食われた)

これは、Vue本来の仕様にになっておりどうしようもすることはできません。

では、どうやってプロパティの変更を検知してくれるのでしょうか?

結論から言うと、オブジェクト自体を書き換えるという動作をします。

めんどくさいですよね。

でも大丈夫!

同じようなことで頭を抱える人に、とっておき解決策をご用意しております。

ぜひ皆さんのお力になれればなと思います!!

それでは、順を追って説明していきます。

オブジェクトのコピーを作成

今回は、userというオブジェクトを例に挙げて説明していきます。

store/index.js
state: {
    user: {}, // user情報
  },

changeUserPhotoURL(state, payload) {
      let user = Vue.util.extend({}, state.user)
    },

Vue.util.extendでコピーを作成し、第一引数には肩を定義します。第二引数には、コピーする対象を選択します。

プロパティの値を変更し、元のオブジェクトに代入

今回は、ユーザーのプロフィール画像(photoURL)を変更します。

store/index.js
state: {
    user: [], // user情報
  },

changeUserPhotoURL(state, payload) {
      let user = Vue.util.extend({}, state.user)
      user.photoURL = payload
      state.user = user
    },

state.user = userこの部分は、元のオブジェクト(state.user)にコピー、編集したオブジェクト(user)を代入しています。

ページの読み込み

this.$router.go()

最後に、routerなどを使ってページのリロードを行いましょう!

このようにして、オブジェクトのプロパティの値の変更を検知してくれます。

いかがだったでしょうか?

これで時間を潰すなんてもったいないですよね??

なので、この記事を参考にしてぜひ学習を進めていってください!

以上、「Vuexでオブジェクトのプロパティを変更したいときの対処法!!」でした!

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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