React講座 コンポーネントとProps

React コンポーネントとprops

コンポーネントにより UI を独立した再利用できるパーツに分割し、パーツそれぞれを分離して考えることができるようになります。今回はコンポーネントという概念を理解できることを目標にしましょう。 関数コンポーネントとクラスコンポーネント コンポーネントを定義する最もシンプルな方法は JavaScript の関数を書くことです: この関数は、データの入った”props”プロパティというオブジェクトを引数として受け取り、React要素を返します。これがコンポーネントになります。 プロパティは親からもらうプレゼントをイメージしてください。そのプレゼントには何かのデータが入っていることをイメージしましょう。※propsは常に親から子です。子のコンポーネントから親コンポーネントにpropsが渡ることはありませんので覚えておきましょう。 classを使ってコンポーネントを定義することもできます。 細かいコンポーネントの構成については次回以降に説明をするので、再度コンポーネントの概念について理解を深めていきましょう。 コンポーネントのレンダー ここまでは、DOMのタグを使うReact要素のみを使いました。 しかし、要素はユーザー定義のコンポーネントを使用することもできます。 React がユーザ定義のコンポーネントを見つけた場合、JSX に書かれている属性と子要素を単一のオブジェクトとしてこのコンポーネントに渡します。このオブジェクトのことを “props” と呼びます。 例えば以下のコードではページ上に “Hello, Sara” を表示します: この例で何が起こるのかおさらいしてみましょう。 補足: コンポーネント名は常に大文字で始めてください。 React は小文字で始まるコンポーネントを DOM タグとして扱います。例えば、<div /> は HTML の div タグを表しますが、<Welcome /> はコンポーネントを表しており、スコープ内に Welcome が存在する必要があります。 コンポーネントを組み合わせる コンポーネントは自身の出力の中で他のコンポーネントを参照できます。これにより、どの詳細度のレベルにおいても、コンポーネントという単一の抽象化を利用できます。ボタン、フォーム、ダイアログ、画面:React アプリでは、これらは共通してコンポーネントとして表現されます。 例えば、Welcome を何回もレンダーする App コンポーネントを作成できます: 典型的には、新規の React アプリは階層の一番上に単一の App コンポーネントを持っています。しかし、既存のアプリに React を統合する場合は、Button のような小さなコンポーネントからボトムアップで始め、徐々にビューの階層構造の頂上に向かって進んでいってもよいでしょう。 コンポーネントの抽出 では、コンポーネントがどのように構成されるか分かったところで、どの段階でコンポーネントにするか悩みますよね。しかし、コンポーネントをより小さなコンポーネントに分割することを恐れる必要はありません。 例えば、この Comment コンポーネントについて考えましょう: これは props として author(オブジェクト)、text(文字列)、および date(日付)を受け取り、ソーシャルメディアサイトにおける … Read more

React講座 要素のレンダー

React要素の使い方

要素(エレメント)とは React アプリケーションの最小単位の構成ブロックです。 ブラウザのDOM要素と異なり、React要素は単純なオブジェクトになり、簡単に作成されます。React DOMがReactエレメントを把握してそれに従いDOMを更新する作業を担当することになります。 補足: 要素のことを、より広く知られている概念である “コンポーネント” と混同する人もいるかもしれません。コンポーネントについては次の章で説明します。要素とはコンポーネントを “構成する” ものです。次に進む前にこの章を読んでいくことをお勧めします。 要素を DOM としてレンダーする HTMLファイルの中に<div>要素があったとしましょう。 最初の記事で説明したようにindex.htmlにはrootの要素が一つだけありましたね。 ここにReactDOMがすべてのReactのアプリケーションを管理することになるのでルートのDOMノードと呼ぶことにしましょう。 Reactだけで構築されたアプリケーションはDOMノードを一つだけ持ちます。既存のアプリにReactを組み合わせて使いたい場合は独立したDOMノードを複数使用することも可能です。 React 要素をルート DOM ノードにレンダーするには、まず ReactDOM.createRoot() に DOM 要素を渡し、root.render() に React 要素を渡します。 このコードにより”Hello World”が表示されます。 レンダーされた要素の更新 react要素はイミュータブルです。※イミューン(Immune:変更できない) 一度作成された要素の子要素や、属性などを変更することはできません。 今の学習段階でUIを更新する方法は新し要素を作成してroot.render()に渡すという事になります。 以下の例で秒刻みに動く時計の例についてみてみます。 実際の例を見てください。 この例ではsetInterval() のコールバックから root.render() を毎秒呼び出しています。 補足: 上記のコードは例として記載したものであり、実際にはroot.render()を呼び出すことは1度のみ行う事が通常になります。次の章では、上記のコードをstate付きのコンポーネントへとカプセル化する方法を学びます。飛ばさないようにしましょう。 React は必要な箇所のみを更新する ReactDomは要素とその子要素を以前のものと比較しています。その比較の際に差があった場合、必要な部分のみのDOMの更新を行うことになります。 下記の例では時間の変わる要素の部分のみ更新されていることが分かりますね。 このように必要な部分だけリアクティブに要素が変わることでユーザーにインタラクティブなインターフェイスを提供することができますね。

React講座 JSXの使い方

React JSXの使い方

ではReactの仕組みを理解したところでReactのテンプレートシンタックスのJSXを理解していきましょう。 JSXはこのように書くことができます。ストリングでもないのでクオートで囲う必要もありません。 JSXを使わない方法もありますが、Reactを使う上ではJSXを是非使っていきたいです。 JSXはReactのエレメントを作成して、そこからDOMに変換されるようになります。 JSXを使う理由 Reactはイベントへのリスポンスや状態の変化を感知してユーザー側に表示するデータをリアクティブに作成することができます。 Reactの強みでもあるのがこのロジックとマークアップ(HTML)を同じファイルに記述できることです。ファイルが別々でないので一目で見ただけで何がユーザー側に表示されるか理解しやすいです。 この概念をコンポーネントと呼びます。 JSXに式を埋め込む では、次の例を見てみましょう。 nameという変数を宣言し、中括弧{}に入れることでJSX内で使用することができます。 この方法を使うことで、計算式の2+2やオブジェクト(例:user.name)を使うことができます。 JSXは長く記載する場合もあるので()で囲むようにするとよいです。 JSX自体を式として使う 先ほどはJSXに変数を入れる方法を紹介しました。 さらに、JSX自体も式としてif文やforループの文で使用することができます。 ReactがJSXを読み込む際にコンパイルされ普通のJavaSxriptに変換されるようになります。 JSXで属性を指定する 文字列リテラルを属性として指定するために引用符(クオーテーション)を使用できます。 これでHTMLの属性(attribute)にストリングの値を入れてあげることができます。 また、属性に JavaScript 式を埋め込むために中括弧を使用することもできます。これでダイナミックなデータを流し込むことができますね。 注意しておきたいこと JSXはHTMLよりもJavaScriptに近いものになります。ですのでHTMLの属性にclassを入れたい場合はJavaScriptのclassと干渉することを防ぐためにclassNameを使うことになります。 JSXで子要素を指定 タグが空の場合は、XMLのように/>でタグを閉じることができます。 もちろんJSXのタグに子要素(HTMLのタグ、エレメント)を入れることができます。 JSX はインジェクション攻撃を防ぐ JSX にユーザの入力を埋め込むことは安全です: React DOMはJSXに埋め込まれた値をレンダー前にエスケープします。 →エスケープとは、HTML上で特殊文字を期待通りに表示するために施す処理のことです。 →特殊文字に指定されている文字は、割り当てられている記号を記述することで表示できます。 →例えば、<は<であったり、©は©など、特殊文字には必ず該当する記号が割り当たっています。 このため、XSS攻撃の防止になります。 JSXはオブジェクトの表現になる ReactがJSXをコンパイルする流れを見てみましょう。 下記の二つの例は同じものになります。 ①JSXの例 ②JSXを使わない例 上記のコードをReactのcreateElementメソッドで下記の様なオブジェクトを作成することになります。 このようなオブジェクトはReact要素(エレメント)と呼びます。Reactがこれらのオブジェクトを読み取り、必要に応じてDOMを構築し常にリアクティブなデータをユーザーに’届けるようになります。 では、次回は要素のレンダーについて学んでいきましょう。

無料で使えるAPIを紹介

無料で使えるAPI

皆さんこんにちは。

今日はフロントエンドで使えそうな、ポートフォリオのプロジェクトで使えそうな無料のAPIを紹介します。

真面目なものから、ふざけたものまで色々集めたので是非、使ってみてください。

NASAの宇宙API

ユーザー登録が必要です。

日替わりの宇宙画像とかがあるそうです。

https://api.nasa.gov/index.html

天気予報API

天気情報のAPIは誰しもが使うマストなAPIですね。簡単な天気アプリとかに使えそうです。

これもユーザー登録が必要になります。

https://openweathermap.org/api

米国株式のAPI

Polygon.ioというところが提供しているAPIです。GitHubで登録してAPIキーを手に入れた後にAPIにアクセスできるようです。


ユーザー登録なしで使えるAPI

ここから、ユーザー登録が不要ですぐに使えるAPIを紹介します。

ポケモンAPI

みんなの大好きなポケモンのデータがAPIになりました。

https://pokeapi.co/

参考URL: https://pokeapi.co/api/v2/pokemon/charizard

カクテルAPI

カクテルのレシピがAPIになってます。

https://www.thecocktaildb.com/api.php

サンプルURL:https://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita

ビットコインAPI

クリプトのAPIです。CoinBaseが提供しています。

https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-users#data-endpoints

サンプルURL:https://api.coinbase.com/v2/currencies

為替API

為替のレートのAPIです。

https://www.coingecko.com/en/api

サンプルURL:https://api.coingecko.com/api/v3/exchange_rates

アメリカの統計

アメリカの人口や統計がデータになったAPIです。

https://datausa.io/about/api/

サンプルURL: https://datausa.io/api/data?drilldowns=Nation&measures=Population

犬の画像

ランダムで犬の画像が表示されます。

https://dog.ceo/dog-api/

サンプルURL:https://dog.ceo/api/breeds/image/random

アニメAPI

アメリカで有名なMyAnimeListの非公式のAPIです。

https://jikan.moe/

サンプルURL: https://api.jikan.moe/v4/anime?q=naruto&sfw

本のAPI

OpenLibraryという組織で提供している本のAPIです。

https://openlibrary.org/developers/api

サンプルURL: http://openlibrary.org/api/volumes/brief/isbn/9780525440987.json

ランダムユーザーAPI

自動でユーザーを生成してくれるAPIです。

https://randomuser.me/

サンプルURL: https://randomuser.me/api/

とりあえず、これだけあれば次のプロジェクトの参考になるのではないでしょうか?

お疲れ様でした。

VueでPiniaを使ってみよう

VueではState Management SystemのVuexが公式のプラグインとして紹介されてきました。しかし、最新のVue3ではPiniaを使うようにとVue生みの親のEvan Youさんもお勧めしています。

では、State Management(状態管理)って何でしょうか?

State Management(状態管理)

State Managementとはいわばストレージ/ストア(倉庫)のことです。アプリケーションでストアを作っておいてそこにデータを保管できるようになります。

例えばユーザーがログインしたときのトークン、APIでフェッチしたデータ、アプリケーションの状態(例:フォームが提出したとかの状態)があげられます。

PiniaのAPIの使い方

PiniaはVue2でもVue3でも使う事ができます。また、Options API(一般的に初心者向け)でもCompotion APIの書き方でもどちらでも対応しています。私の個人的な意見ではVue3でComspostion APIで書く方法が一番良いと思います。

なぜPiniaなのか

Piniaを使う事でこの状態管理システム(Store Library)を各コンポーネントやページのどこでも使う事ができます。

もちろん、同じことが export const state = reactive({}) でもできますよね。

しかし、このやり方だとセキュリティに脆弱性があり、何を管理しているのか見られてしまう可能性があります。

この他にもデベロッパー用のツールがあったり、サーバー側でもレンダーにも対応することができるなど色々メリットがあります。

Piniaをインストール

npmかyarnのコマンドでインストールしましょう。

yarn add pinia
# or with npm
npm install pinia

※NuxtJSの場合はこちらから

インストールが完了したらmain.jsにPiniaを追加します。

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

Piniaのファイルを作成しよう

ではPiniaが追加できたらJSファイルでストアしたいデータを保管できる場所を作っていきましょう。

慣習的にsrcディレクトリの直下にstoreというフォルダを作成してそこにJavaScriptファイルで下記のコードを作るのが一般的になります。

import { defineStore } from 'pinia'

export const useCounterStore = defineStore({
  id: 'counter',
  state: () => ({
    counter: 0
  }),
  getters: {
    doubleCount: (state) => state.counter * 2
  },
  actions: {
    increment() {
      this.counter++
    }
  }
})
  • stateは保管するデータの初期値をリターンするファンクションです。(dataと同じ概念)
  • getters は stateを使ってデータをモディファイ(変更)したいときに使うファンクションです。(copmputedと同じ概念)
  • actionsはasyncにできるファンクションのことです。(methodと同じ概念)

<script setup>的なPiniaの使い方

他のサイトでは上記のやり方でPiniaを使っていますが、今年からVueで使えるようになった<script setup>を使ったやり方に合わせてPiniaも書きたい人は下のやり方をお勧めします。

import { defineStore } from 'pinia'
import { ref } from 'vue';
import EventService from "@/plugins/EventService";

export const useMemberStore = defineStore('member', ()=> {

  const data = ref(null)

  const getData = () => {
    EventService.getMember()
      .then((response) => {
        data.value = response.data;
      })
      .catch((error) => {
        console.log("data:" + error);
      });
  }

  return {
    data,
    getData
  }
})

上のコードを見ても分かるようにgetterとかactionsとかの概念はなく普通のJavaScriptのコードで<script setup>と同じようにVanilla JavaScriptに近い状態で書くこともできます。

個人的にはこちらの方が書きやすいと思ったので是非試してみてください。

ではこれをコンポーネントから読み込めるようにしましょう。

<script setup>

import { useOfficeStore } from "@/stores/members/office";

const officePinia = useOfficeStore();

const officeData = officePinia.data
</script>

このようにどのコンポーネントからでもStoreにアクセスでき、グローバルにデータを管理することで後から見やすくなりますね。

Viteで@/componentsのショートカットが使えない問題

ViteでVueのアプリを作成してコンポーネントをインポートしようとすると何か気づいたことはありませんか?

そうです。Vue CLIで作った際に使えていた@/~のショートカットが使えないです!

なので、毎回コンポーネントをインポートする際にこのように、かなり面倒になってきます。

import Component from '../../../../components/Component.vue'

//本来ならこうしたい。。。
import Component from '@/components/Component.vue'

Viteで@を使えるようにする方法

これはWebPackでついてきたショートカットを再現することで同じように@が使えるようになります。

では、Viteのコンフィグファイルを作成して、コードを書いていきます。

Viteのコンフィグファイルの書き方

Viteのコンフィグはvite.config.jsの名称でプロジェクトのルート(アプリの一番上の階層)にファイルを作成することでできます。

これで、Viteが読み込む際にこのコンフィグファイルも自動で読み込んでくれます。

import { fileURLToPath, URL } from "url";

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
});

あとはいつも通りにコンポーネント側で@を使ってコンポーネントをインポートできるようになります。

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + Vite" />
</template>

<script>
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

Vue UIライブラリのPrimeVueを使ってみよう

PrimeVueはVue3に対応したUIライブラリで無料で使えます。

公式サイトはこちらです。

この会社名はPrimeFacesといい11名のスタッフで構成されるトルコ(Turkey)の会社です。

会社の利益はテンプレートとライセンス費用で賄っているようです。

PrimeVueの良いところ

コンポーネントの数が半端ではない!!

なんと90+以上のcomponentがバンドルに含まれています。

また、デザインのスッキリしていて洗練されているのでそのまま使っても気持ちが良いです。

PrimeVueをインストールしよう

ではコマンドラインからPrimeVueをインストールします。

VueアプリケーションはViteで作成しました。(なのでWebPackは入ってません。)

#バージョンを指定したい場合
npm install primevue@^3.15.0 --save

#最新のものをインストールする場合
npm install primevue

#コンポーネントで使うアイコンのインストール
npm install primeicons --save

下記のようにpackage.jsonを見ると、インストールしたprimevueとprimeiconsが記載されデペンデンスィー:dependencies(依存されたモジュール)に追加されたことが分かります。

{
  "name": "crm-app",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "primeicons": "^5.0.0",
    "primevue": "^3.15.0",
    "vue": "^3.2.25",
    "vue-router": "^4.1.2"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^2.3.3",
    "vite": "^2.9.9"
  }
}

main.jsにprimeVueを追加

次にmain.jsにPrimeVueを追加してアプリがマウントする際にこのライブラリを読み込ませるようにしましょう。

import { createApp } from 'vue'
import App from './App.vue'
import PrimeVue from 'primevue/config';

const app = createApp(App);

app.use(PrimeVue);
app.mount("#app");

グローバルにコンポーネントを登録

PrimeVueのcomponentをグローバルで使用したい場合は下記のように設定します。

まずは、main.jsに使いたいコンポーネントを登録します。

例で例えるとButtonになります。それからapp.componentメソッドでテンプレートで使いたい名称とインポートするコンポーネント名を指定します。

import { createApp } from 'vue'
import App from './App.vue'
import PrimeVue from 'primevue/config';

import Button from 'primevue/button';

const app = createApp(App);

app.use(PrimeVue);
app.component('Button', Button );
app.mount("#app");

この後に例えばApp.vueで下記のように<Button/>をテンプレートに記載するだけで使えるようになります。

<template>
 <Button label="Disabled" disabled="disabled" />
  <Button label="Submit" icon="pi pi-check" iconPos="right" />
</template>

コンポーネントにインポートする場合

では、グローバルではなく、それぞれのcomponentにPrimeVueのコンポーネントをインポートしたい場合は下記のようにします。

例でいうと、子コンポーネントのHwlloWorld.vueに使いたいPrimeVueのコンポーネントをインポートします。

<script setup>
import Calendar from 'primevue/calendar';
</script>

<template>
  <Calendar v-model="value" />
</template>

CSSをインポートする

ここで気が付いたかもしれませんが、スタイルが入っていませんね。

では、CSS、アイコン、それとテーマをインポートしましょう。

これをmain.jsに追加してください。

import 'primevue/resources/primevue.min.css'
import 'primeicons/primeicons.css'
import 'primevue/resources/themes/luna-blue/theme.css'

ちなみにテーマはluna-blueでインポートしてますが実際には下記のように無料で使えるテーマがあるので色々試してみてください。

primevue/resources/themes/bootstrap4-light-blue/theme.css
primevue/resources/themes/bootstrap4-light-purple/theme.css
primevue/resources/themes/bootstrap4-dark-blue/theme.css
primevue/resources/themes/bootstrap4-dark-purple/theme.css
primevue/resources/themes/md-light-indigo/theme.css
primevue/resources/themes/md-light-deeppurple/theme.css
primevue/resources/themes/md-dark-indigo/theme.css
primevue/resources/themes/md-dark-deeppurple/theme.css
primevue/resources/themes/mdc-light-indigo/theme.css
primevue/resources/themes/mdc-light-deeppurple/theme.css
primevue/resources/themes/mdc-dark-indigo/theme.css
primevue/resources/themes/mdc-dark-deeppurple/theme.css
primevue/resources/themes/tailwind-light/theme.css
primevue/resources/themes/fluent-light/theme.css
primevue/resources/themes/lara-light-indigo/theme.css
primevue/resources/themes/lara-dark-indigo/theme.css
primevue/resources/themes/lara-light-purple/theme.css
primevue/resources/themes/lara-dark-purple/theme.css
primevue/resources/themes/lara-light-blue/theme.css
primevue/resources/themes/lara-dark-blue/theme.css
primevue/resources/themes/lara-light-teal/theme.css
primevue/resources/themes/lara-dark-teal/theme.css
primevue/resources/themes/saga-blue/theme.css
primevue/resources/themes/saga-green/theme.css
primevue/resources/themes/saga-orange/theme.css
primevue/resources/themes/saga-purple/theme.css
primevue/resources/themes/vela-blue/theme.css
primevue/resources/themes/vela-green/theme.css
primevue/resources/themes/vela-orange/theme.css
primevue/resources/themes/vela-purple/theme.css
primevue/resources/themes/arya-blue/theme.css
primevue/resources/themes/arya-green/theme.css
primevue/resources/themes/arya-orange/theme.css
primevue/resources/themes/arya-purple/theme.css
primevue/resources/themes/nova/theme.css
primevue/resources/themes/nova-alt/theme.css
primevue/resources/themes/nova-accent/theme.css
primevue/resources/themes/nova-vue/theme.css
primevue/resources/themes/luna-amber/theme.css
primevue/resources/themes/luna-blue/theme.css
primevue/resources/themes/luna-green/theme.css
primevue/resources/themes/luna-pink/theme.css
primevue/resources/themes/rhea/theme.css

これで下記のようにスタイルが追加されましたね。

では、半端ない数のコンポーネントを楽しんでください。

今日使ったコードはGitHubにあるので見てください。

[Vue入門] asyncコンポーネント

基本的な使い方

大規模なアプリケーションでは、アプリを小さなチャンクに分割し、必要なときにのみサーバーからコンポーネントを読み込む必要があるかもしれません。これを実現するために、Vue には defineAsyncComponent 関数があります:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...サーバーからコンポーネントを読み込む
    resolve(/* 読み込まれたコンポーネント */)
  })
})
// ... `AsyncComp` を普通のコンポーネントと同じように使用する

このように、defineAsyncComponent は Promise を返すローダー関数を受け取ります。Promise の resolve コールバックは、コンポーネントの定義をサーバーから取得したときに呼ばれます。読み込みが失敗したことを示すために、reject(reason) を呼ぶこともできます。

ES モジュールの動的インポート も Promise を返すためにほとんどの場合には defineAsyncComponent と合わせて使用します。Vite や webpack などのバンドラーもこの構文をサポートしているため、の Vue SFC をインポートするためにも使用できます。

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

結果的に得られる AsyncComp は、実際にページ上にレンダリングされるときにローダー関数を呼ぶだけのラッパーコンポーネントです。さらに、内側のコンポーネントに任意の props を渡せるため、非同期ラッパーを使用すると、コンポーネントをシームレスに置換するとともに、遅延読み込みも実現できます。

ローディングとエラーの状態

非同期の操作は必然的にローディングとエラーの状態に関係してきます。そのため、defineAsyncComponent() ではこれらの状態のハンドリングを高度なオプションによりサポートしています。

const AsyncComp = defineAsyncComponent({
  // ローダー関数
  loader: () => import('./Foo.vue'),

  // 非同期コンポーネントの読み込み中に使用するコンポーネント
  loadingComponent: LoadingComponent,
  // ローディングコンポーネント表示前の遅延。デフォルト: 200ms。
  delay: 200,

  // 読み込みに失敗した場合に使用するコンポーネント
  errorComponent: ErrorComponent,
  // エラーコンポーネントは timeout が与えられて
  // その時間を超えた場合に表示される。デフォルト: Infinity。  
  timeout: 3000
})

ローディングコンポーネントが与えられた場合、内側のコンポーネントが読み込まれている間に表示されます。ローディングコンポーネントが表示されるまでに、デフォルトで 200ms の遅延があります。このようになっているのは、高速なネットワークではローディング状態が短く、置き換えが速すぎて、ちらつきのように見えてしまう恐れがあるためです。

エラーコンポーネントが与えられた場合、ローダー関数から返された Promise が reject されたときに表示されます。リクエストが長すぎる場合にエラーコンポーネントを表示するために、timeout を指定することもできます。

Suspense とともに使用する

非同期コンポーネントは、ビルトインコンポーネント <Suspense> とともに使用することもできます。

Vue Routerをインストールしよう

Vue RouterはVueアプリケーションで複数のURLをアプリケーションに導入してView(ページ)を作成する場合に使います。

Vue Cliからインストールする場合

Vue CLI(コマンドラインインターフェイス)を使う場合はアプリを作成するときにVue Routerをインストールするにチェックをするだけで勝手にインストールしてくれます。

npmでインストールする場合

npm install vue-router@4

yarnでインストールする場合

yarn add vue-router@4

main.jsにRouterを追加

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

const app = createApp(App);

app.use(router);
app.mount("#app");

Routerディレクトリを作成

routerフォルダーをsrcフォルダの直下に作成します。その中にindex.jsを作成してrouterのコンフィグレーションを書いていきます。ファイル名はindex.jsでなくてもOKです。

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})
export default router

viewsディレクトリを作成

では、次に同じようにsrcディレクトリ直下にviewsをいう名前でフォルダーを作成しましょう。ここにページの枠組みとなるVueコンポーネントを作成していきます。

例でいうとAbout.vueとHome.vueになります。これでラウティングの設定が完了しました。あとはメニューバーなどを設置してユーザーを誘導させるようにすればよいですね。

router-linkとrouter-viewを設置

ではApp.vueに下記のようにコードを書き、ラウティングで指定したHome
とAboutにユーザーがアクセスできるようにしましょう。

その行先をクリックした際にViewを表示させるのが<router-view/>になります。

<template>
  <div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <router-view/>
</template>

はい、上のようにメニューバーができて、クリックに応じてView(ページ)が変わりましたね。

これが基本的な使い方になります。

[Vue入門]Provide / Inject

Prop の過剰的な使用(Prop Drilling)

通常、親コンポーネントから子コンポーネントにデータを渡す必要がある場合、props を使用します。しかし、大きなコンポーネントツリーがあり、深くネスト化されたコンポーネントが遠い場合に親元から複数先のコンポーネントにデータを飛ばしたいシナリオを想定してください。

propsのやり方だと、親コンポーネントからつながるすべてのcomponentに同じ prop を渡さなければなりません:

<Footer> コンポーネントは親から受け取るの props を全く使わない場合でもデータの渡し役として、コンポーネントを記載する必要があります。それから<DeepChild> が<Footer>コンポーネントからこのデータにアクセスできるようにしてやっと<Root>コンポーネントのデータを受け取ることができるようになります。これでは、コードが煩雑になり、デバッグの作業も面倒になりますね。

このPropsを何階層も下に投げる作業を省くには provide と inject を使うことで解決できます。親コンポーネントは、そのすべての子コンポーネントに対して 依存関係を提供するプロバイダー (dependency provider) として機能することができます。子ツリー内のどのコンポーネントも、その深さに関係なく、親チェーン内の上位コンポーネントが提供する依存性を注入 (inject) することができます。

Provide

親コンポーネントから子コンポーネントにデータを提供するには provide() 関数を使います:

<script setup>
import { provide } from 'vue'

provide(/* key */ 'message', /* value */ 'hello!')
</script>

<script setup> を使わない場合、setup() 内で provide() が同期的に呼び出されていることを確認してください:

import { provide } from 'vue'

export default {
  setup() {
    provide(/* key */ 'message', /* value */ 'hello!')
  }
}

provide() 関数は 2 つの引数を受け付けます。第 1 引数はインジェクションキーと呼ばれ、文字列または Symbol となります。provideで使用するデータを投げるために使うニックネームのようなものですね。

このインジェクションキーは、子のコンポーネントが、インジェクション(注入)に必要な値を探すのに使われます。1 つのコンポーネントが異なる値を提供するために、異なるインジェクションキーで provide() を複数回呼び出すことができます。

第 2 引数は提供される値です。この値は refs のようなリアクティブな状態を含む、任意の型にすることができます:

import { ref, provide } from 'vue'

const count = ref(0)
provide('key', count)

リアクティブな値を提供することで、提供された値を使用する子孫コンポーネントが、プロバイダーコンポーネントとのリアクティブな接続を確立することができます。

アプリケーションレベルの Provide

コンポーネント内だけでなく、アプリケーションレベルでデータを提供することも可能です:

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* key */ 'message', /* value */ 'hello!')

アプリケーションレベルの Provide は、アプリケーションでレンダリングされるすべてのコンポーネントで利用可能です。これは特にプラグインを書くときに便利です。プラグインは通常、コンポーネントを使ってデータを提供することができないからです。

Inject

親コンポーネントが提供するデータを注入するには inject() 関数を使用します:

<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

提供された値が ref である場合、そのまま注入され、自動的にアンラップされることはありません。これにより、インジェクターコンポーネントはプロバイダーコンポーネントとのリアクティビティの接続を保持することができます。

繰り返しますが、もし <script setup> を使用しないのであれば、inject() は setup() の内部でのみ同期的に呼び出す必要があります:

import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

インジェクションのデフォルト値

デフォルトでは、inject は注入されるキーが親チェーンのどこかで提供されることを想定しています。キーが提供されていない場合、実行時が出ます。

インジェクトされたプロパティをオプションのプロバイダーで動作させたい場合は、props と同様にデフォルト値を宣言する必要があります:

// もし "message" にマッチするデータがなかった場合は、
// `value` は "default value" になります
const value = inject('message', 'default value')

場合によっては、関数を呼び出したり、新しいクラスをインスタンス化したりして、デフォルト値を作成する必要があるかもしれません。オプションの値が使用されないケースで不要な計算や副作用を避けるために、デフォルト値を作成するためのファクトリー関数を使用することができます:

const value = inject('key', () => new ExpensiveClass())

リアクティビティと共に利用する

リアクティブな値を provide / inject する場合、可能な限り、リアクティブな状態への変更を provider の内部で維持することが推奨されます。これは、提供されるステートとその可能な変更が同じコンポーネントに配置されることを保証し、将来のメンテナンスをより容易にするためです。

インジェクターコンポーネントからデータを更新する必要がある場合があります。そのような場合は、状態の変更を担当する関数を使うことをおすすめします:

<!-- プロバイダーコンポーネント内部 -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>
<!-- インジェクターコンポーネント内部 -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

最後に、provide を通して渡されたデータが注入されたコンポーネントによって変更されないようにしたい場合は、提供された値を readonly() でラップすることができます。

<script setup>
import { ref, provide, readonly } from 'vue'

const count = ref(0)
provide('read-only-count', readonly(count))
</script>

シンボルキーと共に利用する

今までの例では、文字列のインジェクションキーを使っていました。もしあなたが多くの依存関係を提供するプロバイダーを持つ大規模なアプリケーションで作業していたり、他の開発者が使用する予定のコンポーネントを作成している場合は、名前の重複を避けるためにシンボルインジェクションキーを使用するのがベストです。

シンボルは専用のファイルに書き出しておくことをおすすめします:

// keys.js
export const myInjectionKey = Symbol()
// プロバイダーコンポーネント内
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* 提供するデータ */
})
// インジェクターコンポーネント内
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)