Vue3使用Composition API實現響應式

Amd794發表於2024-05-29

title: Vue3使用Composition API實現響應式
date: 2024/5/29 下午8:10:24
updated: 2024/5/29 下午8:10:24
categories:

  • 前端開發

tags:

  • Vue3
  • Composition
  • Refs
  • Reactive
  • Watch
  • Lifecycle
  • Debugging

image

1. 介紹

Composition API是Vue.js 3中新增的一組API,用於在元件中組合邏輯和功能。它可以讓你更好地組織和重用程式碼,使元件更易於理解和維護。在使用Composition
API時,你可以使用<script setup>語法或setup()函式,兩種方式都可以使用Composition API中的響應式API、生命週期鉤子、模板引用和自定義渲染函式等特性。

2. 基本響應式

在Vue.js 3中,Composition API提供了幾種建立響應式資料的方法,包括refreactivereadonlyshallowReactive
shallowReadonly

2.1 ref

ref函式用於建立一個響應式的ref物件,其值可以透過.value
屬性獲取或設定。當ref物件的值發生變化時,Vue.js會自動更新檢視。AD:首頁 | 一個覆蓋廣泛主題工具的高效線上平臺

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const count = ref(0);

function increment() {
  count.value++;
}
</script>

2.2 reactive

reactive函式用於建立一個響應式的物件,其所有屬性都是響應式的。當物件的屬性發生變化時,Vue.js會自動更新檢視。

<template>
  <div>
    <p>name: {{ user.name }}</p>
    <p>age: {{ user.age }}</p>
    <button @click="incrementAge">+1</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue';

const user = reactive({
  name: 'Alice',
  age: 20
});

function incrementAge() {
  user.age++;
}
</script>

2.3 readonly

readonly函式用於建立一個只讀的響應式物件,其所有屬性都是隻讀的。當試圖修改只讀物件的屬性時,會丟擲一個錯誤。

<template>
  <div>
    <p>name: {{ user.name }}</p>
    <p>age: {{ user.age }}</p>
  </div>
</template>

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

const user = reactive({
  name: 'Alice',
  age: 20
});

const readonlyUser = readonly(user);

// 會丟擲一個錯誤
readonlyUser.age = 21;
</script>

2.4 shallowReactive

shallowReactive函式用於建立一個淺響應式的物件,其所有屬性都是響應式的,但其子物件的屬性不是響應式的。
AD:專業搜尋引擎

<template>
  <div>
    <p>name: {{ user.name }}</p>
    <p>age: {{ user.age }}</p>
    <p>address: {{ user.address }}</p>
    <button @click="incrementAge">+1</button>
    <button @click="changeAddress">改變地址</button>
  </div>
</template>

<script setup>
import { shallowReactive } from 'vue';

const user = shallowReactive({
  name: 'Alice',
  age: 20,
  address: {
    province: 'Beijing',
    city: 'Beijing'
  }
});

function incrementAge() {
  user.age++;
}

function changeAddress() {
  user.address = {
    province: 'Shanghai',
    city: 'Shanghai'
  };
}
</script>

2.5 shallowReadonly

shallowReadonly函式用於建立一個淺只讀的響應式物件,其所有屬性都是隻讀的,但其子物件的屬性不是隻讀的。

<template>
  <div>
    <p>name: {{ user.name }}</p>
    <p>age: {{ user.age }}</p>
    <p>address: {{ user.address }}</p>
    <!-- 會丟擲一個錯誤 -->
    <button @click="changeAddress">改變地址</button>
  </div>
</template>

<script setup>
import { shallowReactive, shallowReadonly } from 'vue';

const user = shallowReactive({
  name: 'Alice',
  age: 20,
  address: {
    province: 'Beijing',
    city: 'Beijing'
  }
});

const readonlyUser = shallowReadonly(user);

// 會丟擲一個錯誤
readonlyUser.age = 21;
</script>

3. 響應式API

Composition API提供了幾種響應式API,包括watchEffectwatchcomputedprovide/inject

3.1 watchEffect

watchEffect函式用於建立一個響應式的副作用函式,當響應式資料發生變化時,副作用函式會自動重新執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

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

const count = ref(0);

watchEffect(() => {
  console.log(`count is ${count.value}`);
});

function increment() {
  count.value++;
}
</script>

3.2 watch

watch函式用於建立一個響應式的監聽器,當響應式資料發生變化時,監聽器會自動執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

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

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});

function increment() {
  count.value++;
}
</script>

3.3 computed

computed函式用於建立一個響應式的計算屬性,其值是根據響應式資料計算得出的。當響應式資料發生變化時,計算屬性會自動重新計算。
AD:漫畫首頁

<template>
  <div>
    <p>count: {{ count }}</p>
    <p>doubleCount: {{ doubleCount }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

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

const count = ref(0);

const doubleCount = computed(() => {
  return count.value * 2;
});

function increment() {
  count.value++;
}
</script>

3.4 provide/inject

provideinject函式用於在元件樹中傳遞資料。provide函式用於在父元件中提供資料,inject函式用於在子元件中注入資料。

<template>
  <div>
    <ChildComponent />
  </div>
</template>

<script setup>
import { provide } from 'vue';
import ChildComponent from './ChildComponent.vue';

provide('message', 'Hello, world!');
</script>
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

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

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

4. 生命週期鉤子

Composition
API提供了幾種生命週期鉤子,包括setup()onBeforeMount()onMounted()onBeforeUpdate()onUpdated()onBeforeUnmount()onUnmounted()onErrorCaptured()onRenderTracked()
onRenderTriggered()

4.1 setup()

setup()函式是Composition API的入口點,用於在元件建立之前執行一些初始化操作。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    return {
      count,
      increment
    };
  }
};
</script>

4.2 onBeforeMount()

onBeforeMount()函式在元件掛載之前執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onBeforeMount(() => {
      console.log('before mount');
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.3 onMounted()

onMounted()函式在元件掛載之後執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onMounted(() => {
      console.log('mounted');
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.4 onBeforeUpdate()

onBeforeUpdate()函式在元件更新之前執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onBeforeUpdate(() => {
      console.log('before update');
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.5 onUpdated()

onUpdated()函式在元件更新之後執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onUpdated(() => {
      console.log('updated');
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.6 onBeforeUnmount()

onBeforeUnmount()函式在元件解除安裝之前執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onBeforeUnmount(() => {
      console.log('before unmount');
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.7 onUnmounted()

onUnmounted()函式在元件解除安裝之後執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onUnmounted(() => {
      console.log('unmounted');
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.8 onErrorCaptured()

onErrorCaptured()函式在元件捕獲到錯誤時執行。

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    onErrorCaptured((error, instance, info) => {
      console.error(error);
      return false;
    });

    return {
      count,
      increment
    };
  }
};
</script>

4.9 onRenderTrackedonRenderTriggered

onRenderTrackedonRenderTriggered是兩個生命週期鉤子,它們與Vue的響應式系統和編譯器有關。這兩個鉤子是在Vue
3.x版本中引入的,主要用於除錯目的,幫助開發者瞭解元件渲染過程中的跟蹤和觸發情況。

  1. onRenderTracked鉤子:

    • 當元件的響應式依賴項被追蹤時,即響應式系統開始跟蹤一個依賴項時,這個鉤子會被呼叫。
    • 它主要用於除錯,可以幫助開發者瞭解何時響應式系統開始關注某個依賴項。
    • onRenderTracked鉤子接收兩個引數:depcontextdep是依賴項物件,context是當前元件的上下文物件。
  2. onRenderTriggered鉤子:

    • 當元件的響應式依賴項被觸發時,即響應式系統因為某個依賴項的變化而觸發了重新渲染時,這個鉤子會被呼叫。
    • 它主要用於除錯,可以幫助開發者瞭解何時響應式系統因為某個依賴項的變化而重新渲染元件。
    • onRenderTriggered鉤子也接收兩個引數:depcontext,含義與onRenderTracked相同。

示例程式碼:

export default {
    setup() {
        // 定義一個響應式資料
        const count = ref(0);

        // 監聽 count 的變化
        watch(count, (newValue, oldValue) => {
            console.log(`count changed from ${oldValue} to ${newValue}`);
        });

        // 使用 onRenderTracked 和 onRenderTriggered 進行除錯
        onRenderTracked((dep, context) => {
            console.log(`onRenderTracked: ${dep}`);
        });

        onRenderTriggered((dep, context) => {
            console.log(`onRenderTriggered: ${dep}`);
        });

        return {
            count
        };
    }
};

在這個示例中,我們定義了一個響應式資料count,並使用了watch來監聽它的變化。同時,我們使用了onRenderTracked
onRenderTriggered來列印除錯資訊。當響應式系統開始跟蹤或觸發重新渲染時,我們會得到相應的提示。這些鉤子可以幫助開發者更好地理解Vue元件的渲染過程和響應式系統的運作。

相關文章