Xi-Yuer

Xi-Yuer

github

ビュウ

Vue3 の一般的な API#

reactive# Vue ガイド#

データ属性 Data#

コンポーネントの data オプションは関数です。Vue は新しいコンポーネントインスタンスを作成する過程でこの関数を呼び出します。この関数はオブジェクトを返す必要があり、その後 Vue は応答性システムを介してそれをラップし、コンポーネントインスタンス内で $data の形式で保存します。便宜上、そのオブジェクトの任意のトップレベルプロパティもコンポーネントインスタンスを通じて直接公開されます:

  1. 本質的に、コンポーネントの data オプションは単なる関数です
  2. この関数は Vue がコンポーネントインスタンスを作成する際に呼び出されます
  3. この関数はオブジェクトを返します
  4. 返されたこのオブジェクトは Vue によって応答性データとしてラップされます
  5. ラップされた応答性データは、コンポーネントインスタンス内で $data の形式で保存されます
  6. 便宜上、data が返すオブジェクトの任意のトップレベルプロパティは、コンポーネントインスタンスを通じて直接取得できます。ただし、それらがすべて data 関数が返すオブジェクト内にあることを確認する必要があります
  7. コンポーネントインスタンスに新しいプロパティを直接追加すると、成功することはできますが、応答性オブジェクト $data に含まれていないため、そのプロパティは応答性を持ちません
  8. data が返すオブジェクトのプロパティでは、$ および _ で始まる名前の使用を避けるべきです

原理分析#

なぜ data 属性はオブジェクトではなく関数なのか?

  • 結論data は関数としてもオブジェクトとしても定義できますが、ルートコンポーネントのインスタンス化時にはオブジェクトまたは関数であり、コンポーネントのインスタンス化は関数のみです
  • 原理:ルートインスタンスはシングルトンであり、データ汚染を引き起こしません;コンポーネントインスタンスはシングルトンではなく、複数のコンポーネントインスタンス間で同じ data を共有することによるデータ汚染を防ぐために、関数形式を採用し、data を初期化する際にはそれをファクトリ関数として扱い、常に新しい data オブジェクトを返します

計算プロパティ Computed#

アプリケーションのシナリオ:

  1. テンプレート内で長い式はテンプレートを複雑にし、メンテナンスが難しくなります。この問題を解決するために computed が生まれました
  2. キャッシュ効果があり、パフォーマンスを向上させます。依存データが変化しない場合、キャッシュされたデータが呼び出されます

応答性データを含む 複雑なロジック には、計算プロパティ を使用するべきです

計算プロパティと関数メソッド#

計算プロパティは、それらの反応依存関係に基づいてキャッシュされます。計算プロパティは、関連する応答性依存関係が変更されたときにのみ再評価されます。

対照的に、再レンダリングがトリガーされるたびに、メソッドを呼び出すと関数が常に再実行されます。

なぜキャッシュが必要なのか?

性能コストが高い計算プロパティ list があり、それは巨大な配列を遍歴し、大量の計算を行う必要があると仮定します。その後、他の計算プロパティが list に依存している可能性があります。キャッシュがなければ、listgetter を何度も実行することになります!キャッシュを望まない場合は、method を代わりに使用してください。

リスナー Watch#

Options API による Watch の実装#

通常の watch#

export default {
    props:['dataList'],
    data(){
        return {
            sourceData:[],
            total:0
        }
    },
    // propsのdataListが変化したとき、dataの値も再計算されて代入されます
    watch:{
        dataList(newVal,oldVal){
            this.total = newVal.length
            this.sourceData = newVal
        }
    }
}

オブジェクトの watch#

export default {
    data(){
        return {
            firstName:'Li',
            lastName:'Huan'
        }
    },
    // propsのdataListが変化したとき、dataの値も再計算されて代入されます
    watch:{
        dataList:{
           // getter
            get:function(){
                return this.firstName + this.lastName
            },
            set:function(val){
                // this.dataList = 'Wang Feng'
                const [ firstName,lastName ] = val.split(' ')
                this.firstName = firstName
                this.lastName = lastName
            },
            {
            // オブジェクトと配列は参照型であり、参照型変数はアドレスを保持します。アドレスが変わらないため
            // watchはトリガーされません。この場合、深い監視を行う必要があり、属性 deep: true を追加する必要があります
            deep:true,
            // watchには特徴があり、値が最初にバインドされたときにはリスナ関数は実行されず、
            // 値が変化したときにのみ実行されます。最初にバインドされた値のときにも関数を実行したい場合は、immediate属性が必要です
            immediate:true
        }
        }
    }
}

Reactive API による watch の実装#

watch 関数は特定のデータソースを監視し、コールバック関数内で副作用を実行します。デフォルトでは 遅延 であり、つまり監視しているソースデータが変更されたときにのみコールバックが実行されます。

watch(source, callback, [options]);

パラメータの説明:

  • source:監視する応答性変数を指定するために、String、Object、Function、および Array をサポートします
  • callback:実行されるコールバック関数
  • optionsdeepimmediate、および flush オプションをサポートします

reactive を監視する#

import { defineComponent,reactive,watch } from 'vue'
export default defineComponent({
    setup(){
        const state = reactive({
            firstName:'Li',
            lastName:'Wang'
        })
        
        // firstNameを変更するとwatchのコールバック関数が自動的にトリガーされます
        watch(
            ()=>state.firstName,
            (newVal,oldVal)=>{
            	console.log(`新しい値:${newVal}`,`古い値:${oldVal}`)
        	},
        	{deep:true})
    }
})

複数のデータを監視する#

const stop = watch([() => state.firstName, () => state.lastName], ([curAge, newVal], [preAge, oldVal]) => {
  console.log('新しい値:', curAge, '古い値:', preAge);
  console.log('新しい値:', newVal, '古い値:', oldVal);
});

unmounted(()=>{
    // 監視を停止
    stop()
})

watchEffect#

watchEffectwatch のように依存関係を事前に渡す必要がなく、watchEffect は自動的に依存関係を収集します。コールバック関数を指定するだけです。コンポーネントの初期化時に、依存関係を収集するために一度実行され、その後収集された依存関係のデータが変化すると、再度コールバック関数が実行されます。

watchEffect(()=>{
    // 自動的に依存関係を収集し、初期化時に一度実行
    console.log(firstName);
    console.log(lastName)
})

スタイル設定#

オブジェクト構文#

<template>
    <div class='static' :class="{active:isActive,danger:hasError}"></div>
</template>
<script>
	export default {
        data(){
            return {
                isActive:true,
                hasError:true
            }
        }
    }
</script>
<!-- レンダリング後 -->
<div class="static active danger"></div>

オブジェクトを直接渡す#

<template>
    <div class='static' :class="classObj"></div>
</template>
<script>
	export default {
        data(){
            return {
               classObj:{
                   active:true,
                   hasError:true
               }
            }
        }
    }
</script>

computed を渡す#

<template>
	<div class="static" :class="clsObject"></div>
</template>
<script>
	export default {
        data(){
            return {
                isActive:true,
                hasError:true
            }
        },
        computed:{
            classObj(){
                return {
                    active:this.isActive,
                    hasError:this.hasError
                }
            }
        }
    }
</script>

条件レンダリング#

<template>
	<div v-if='show'></div>
	 <div v-else></div>
	<div v-show='show'></div>
</template>

<script>
	export default {
        data(){
            return {
                show:true
            }
        }
    }
</script>

v-if vs v-show:

  • v-show は単に要素の css プロパティを変更するだけで、要素の displaynone に設定します
  • v-if は要素をコンポーネントツリーから削除します。要素がコンポーネントである場合、show の値を変更すると、コンポーネント要素のライフサイクル関数がトリガーされます

内蔵コンポーネント#

component#

動的コンポーネント

// 動的コンポーネント
<template>
	<!-- currentTabが変わるとコンポーネントも変わる -->
	<component :is="tabs[currentTab]"></component>
</template>

上記の例では、:is に渡される値は以下のいずれかです:

  • 登録されたコンポーネント名
  • インポートされたコンポーネントオブジェクト

transition#

アニメーションコンポーネント

<!-- テンプレート -->
<template>
	<Transition name="slide-fade">

	</Transition>
</template>
/*
  入場と退出のアニメーションは異なる
  継続時間と速度曲線を使用できます。
*/
.slide-fade-enter-active {
  transition: all 0.3s ease-out;
}

.slide-fade-leave-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}

.slide-fade-enter-from,
.slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}

image-20230726165423050

  • トランジションフック関数
<template>
	<Transition
      @before-enter="onBeforeEnter"
      @enter="onEnter"
      @after-enter="onAfterEnter"
      @enter-cancelled="onEnterCancelled"
      @before-leave="onBeforeLeave"
      @leave="onLeave"
      @after-leave="onAfterLeave"
      @leave-cancelled="onLeaveCancelled"
  >
  </Transition>
</template>
// 要素がDOMに挿入される前に呼び出されます
// これを使用して要素の "enter-from" 状態を設定します
function onBeforeEnter(el) {}

// 要素がDOMに挿入された後の次のフレームで呼び出されます
// これを使用して入場アニメーションを開始します
function onEnter(el, done) {
  // コールバック関数doneを呼び出すことでトランジションが終了したことを示します
  // CSSと組み合わせて使用する場合、このコールバックはオプションの引数です
  done()
}

// 入場トランジションが完了したときに呼び出されます。
function onAfterEnter(el) {}
function onEnterCancelled(el) {}

// leaveフックの前に呼び出されます
// 大多数の場合、leaveフックだけを使用します
function onBeforeLeave(el) {}

// 退出トランジションが開始すると呼び出されます
// これを使用して退出アニメーションを開始します
function onLeave(el, done) {
  // コールバック関数doneを呼び出すことでトランジションが終了したことを示します
  // CSSと組み合わせて使用する場合、このコールバックはオプションの引数です
  done()
}

// 退出トランジションが完了し、
// 要素がDOMから削除されたときに呼び出されます
function onAfterLeave(el) {}

// v-showトランジションでのみ使用可能
function onLeaveCancelled(el) {}

keep-alive#

コンポーネントの状態をキャッシュし、コンポーネントの再レンダリングを避けるために使用されます

keep-alive には以下の 3 つの属性があります

  • include:文字列または正規表現で、名前が一致するコンポーネントのみがキャッシュされます。コンポーネントの name;
  • exclude:文字列または正規表現で、名前が一致するコンポーネントはキャッシュされません;
  • max:数字で、最大でいくつのコンポーネントインスタンスをキャッシュできるかを示します。

slot#

デフォルトスロット

<template>
	<!-- 親コンポーネント -->
    <Child>
      <div>デフォルトスロット</div>
    </Child>

    <!-- 子コンポーネント -->
    <template>
      <slot>
        <p>スロットのバックアップコンテンツ</p>
      </slot>
    </template>
</template>

名前付きスロット#

<!-- コンポーネント内部 -->
<div class="container">
  <header>
    <!-- ヘッダーをここに置きたい -->
  </header>
  <main>
    <!-- メインコンテンツをここに置きたい -->
  </main>
  <footer>
    <!-- フッターをここに置きたい -->
  </footer>
</div>
<!-- コンポーネント内部 -->
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

スコープ付きスロット#

親コンポーネントが子コンポーネント内のスロットコンテンツをカスタマイズする際に、子コンポーネント内のデータを取得する必要があります

<template>
	<!-- Child -->
    <ul>
      <li v-for="(item, index) in items">
        <slot :item="item"></slot>
      </li>
    </ul>

    <!-- Parent -->
    <todo-list>
      <template #default="{slotProps}">
        <span class="green">{{ slotProps.item }}</span>
      </template>
    </todo-list>
</template>

Teleport#

teleport は Vue3.x で新たに導入された機能で、HTML が DOM のどの親ノードの下にレンダリングされるかを制御するクリーンな方法を提供します

<template>
	<Teleport to="body">
      <div> bodyにテレポート </div>
	</Teleport>
</template>
import { reactive } from 'vue';
export default {
  // `setup` はコンポジションAPI専用の特別なフックです
  setup() {
    const state = reactive({ count: 0 }); // stateをテンプレートに公開

    return { state };
  },
};

ref#

import { ref } from 'vue';

const count = ref(0);

console.log(count); // { value: 0 }
console.log(count.value); // 0

count.value++;
console.log(count.value); // 1

computed#

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 計算プロパティ ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'はい' : 'いいえ'
})
</script>

<template>
  <p>出版された本がありますか:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

watch#

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

const question = ref('')
const answer = ref('質問には通常、疑問符が含まれます。;-)')

// refを直接監視できます
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.indexOf('?') > -1) {
    answer.value = '考え中...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'エラー!APIに到達できませんでした。 ' + error
    }
  }
})
</script>

<template>
  <p>
    はい/いいえの質問をしてください:
    <input v-model="question" />
  </p>
  <p>{{ answer }}</p>
</template>

watchEffect#

watchEffect () はコールバック関数を即座に一度実行します。この時、関数が副作用を生じた場合、
Vue は自動的に副作用の依存関係を追跡し、応答ソースを自動的に分析します。

watchEffect(async () => {
  const response = await fetch(url.value);
  data.value = await response.json();
});

watch vs. watchEffect#

watchwatchEffect はどちらも応答性のある副作用のあるコールバックを実行できます。主な違いは、応答性の依存関係を追跡する方法です:watch は明示的に監視されているソースのみを追跡します。コールバック内でアクセスされるものは追跡しません。また、応答ソースが実際に変更された場合にのみコールバックがトリガーされます。watch は副作用が発生したときに依存関係を追跡しないため、コールバック関数のトリガータイミングをより正確に制御できます。
一方、watchEffect は副作用が発生している間に依存関係を追跡します。同期実行中に、アクセス可能なすべての応答性プロパティを自動的に追跡します。これは便利で、コードがしばしばより簡潔になりますが、その応答性の依存関係はそれほど明確ではありません。

状態管理 pinia#

store#

// stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 };
  },
  // これは次のように定義することもできます
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++;
    },
  },
  getters(){
      finishedTodos(store){
          return store.count++
      }
  }
});

action#

import { useCounterStore } from '@/stores/counter';

export default {
  setup() {
    const counter = useCounterStore();

    counter.count++;
    // 自動補完付き ✨
    counter.$patch({ count: counter.count + 1 });
    // またはアクションを使用する代わりに
    counter.increment();
  },
};

vue-router ルーティング#

router.js

const Home = { template: '<div>ホーム</div>' };
const About = { template: '<div>アバウト</div>' };

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
];
const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes,
});

const app = Vue.createApp({});

app.use(router);

app.mount('#app');

レンダリング

<template>
  <router-view />
</template>

setup 構文#

defineProps defineEmits#

Ts 構文サポート

const props = defineProps<{
  foo: string
  bar?: number
}>()

const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

defineExpose#

setup コンポーネントはデフォルトでオフになっています

<script setup>
  import {ref} from 'vue' const a = 1 const b = ref(2) defineExpose({(a, b)})
</script>
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。