Vue3使用Composition API实现响应式


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

1. 介绍

Composition API是Vue.js 3中新增的一组API,用于在组件中组合逻辑和功能。它可以让你更好地组织和重用代码,使组件更易于理解和维护。在使用Composition API时,你可以使用<script setup>语法或setup()函数,两种方式都可以使用Composition API中的响应式API、生命周期钩子、模板引用和自定义渲染函数等特性。

2. 基本响应式

在Vue.js 3中,Composition API提供了几种创建响应式数据的方法,包括refreactivereadonlyshallowReactiveshallowReadonly

2.1 ref

ref函数用于创建一个响应式的ref对象,其值可以通过.value 属性获取或设置。当ref对象的值发生变化时,Vue.js会自动更新视图。AD:首页 | 一个覆盖广泛主题工具的高效在线平台

vue 复制代码
<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会自动更新视图。

vue 复制代码
<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函数用于创建一个只读的响应式对象,其所有属性都是只读的。当试图修改只读对象的属性时,会抛出一个错误。

vue 复制代码
<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:专业搜索引擎

vue 复制代码
<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函数用于创建一个浅只读的响应式对象,其所有属性都是只读的,但其子对象的属性不是只读的。

vue 复制代码
<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函数用于创建一个响应式的副作用函数,当响应式数据发生变化时,副作用函数会自动重新执行。

vue 复制代码
<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函数用于创建一个响应式的监听器,当响应式数据发生变化时,监听器会自动执行。

vue 复制代码
<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:漫画首页

vue 复制代码
<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函数用于在子组件中注入数据。

vue 复制代码
<template>
  <div>
    <ChildComponent />
  </div>
</template>

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

provide('message', 'Hello, world!');
</script>
vue 复制代码
<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的入口点,用于在组件创建之前执行一些初始化操作。

vue 复制代码
<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()函数在组件挂载之前执行。

vue 复制代码
<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()函数在组件挂载之后执行。

vue 复制代码
<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()函数在组件更新之前执行。

vue 复制代码
<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()函数在组件更新之后执行。

vue 复制代码
<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()函数在组件卸载之前执行。

vue 复制代码
<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()函数在组件卸载之后执行。

vue 复制代码
<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()函数在组件捕获到错误时执行。

vue 复制代码
<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相同。

示例代码:

javascript 复制代码
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来监听它的变化。同时,我们使用了onRenderTrackedonRenderTriggered来打印调试信息。当响应式系统开始跟踪或触发重新渲染时,我们会得到相应的提示。这些钩子可以帮助开发者更好地理解Vue组件的渲染过程和响应式系统的运作。

相关推荐
编程猪猪侠27 分钟前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞31 分钟前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路1 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到111 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
风清云淡_A1 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js
@大迁世界1 小时前
第7章 React性能优化核心
前端·javascript·react.js·性能优化·前端框架