【涅槃】Vue3学习笔记(一)

86 阅读7分钟

认识CompositionAPI

  • Vue Composition APIVue 3 引入的一种新的 API,用于组织和复用组件逻辑。它通过setup()函数作为入口,结合响应式数据、计算属性、侦听器和生命周期钩子,使代码更加模块化和可维护。

核心概念:

  • setup()函数:作为 Composition API 的入口,接收propscontext,并返回的对象中的属性可以直接在模板中使用。
  • 响应式数据:通过refreactive创建响应式状态。 计算属性:通过computed定义计算属性。
  • 侦听器:通过watchwatchEffect监听状态变化。
  • 生命周期钩子:以on开头(如onMountedonUnmounted),直接在setup()中使用。

应用场景:

  • Vue Composition API 主要用于Vue.js开发中,帮助开发者更好地组织组件逻辑,提高代码复用性和可维护性。

认识setup

setup的基本概念

  • setup是一个函数,它在组件的初始化阶段被调用,用于定义组件的响应式状态、计算属性、方法和生命周期钩子。它的作用类似于Vue 2中的datamethods和生命周期钩子的集合。

setup的参数

setup函数接收两个参数:

  • props:组件的属性,是响应式的。
  • context:一个对象,包含以下属性:
    • attrs:非响应式的组件属性($attrs)。
    • slots:组件的插槽内容。
    • emit:用于触发事件的方法。

setup的返回值

  • setup函数的返回值是一个对象,对象中的属性和方法可以直接在模板中使用。这意味着你可以通过setup返回的对象,将数据、方法和计算属性暴露给模板。

setup的使用示例

示例1:基本用法

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

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

export default {
  setup() {
    // 定义响应式数据
    const count = ref(0);

    // 定义方法
    function increment() {
      count.value++; // 直接操作响应式数据
    }

    // 返回对象,暴露给模板
    return {
      count,
      increment
    };
  }
};
</script>

示例 2:使用生命周期钩子

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

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

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

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

    // 使用生命周期钩子
    onMounted(() => {
      console.log('Component is mounted!');
    });

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

setup的优势

  • 模块化:setup允许开发者将逻辑分解为可复用的函数,而不是将所有逻辑集中在一个组件对象中。
  • 可维护性:通过将逻辑组织为函数,代码更清晰,易于维护和扩展。
  • 灵活性:可以更灵活地组织和复用逻辑,支持逻辑的组合和重用。

setup的限制

  • 不能直接访问this:在setup中,this是未定义的。所有逻辑必须通过setup的参数或返回值来实现。
  • 不能直接访问组件实例:setup中无法直接访问组件实例的生命周期钩子(如mountedupdated等),必须通过 Composition API 提供的生命周期函数(如onMountedonUpdated)来使用。
  • 必须返回对象:setup的返回值必须是一个对象,否则模板中无法访问到定义的逻辑。 setup 的高级用法 使用computed:定义计算属性。
import { computed } from 'vue';
const fullName = computed(() => `${firstName.value} ${lastName.value}`);

使用watchwatchEffect:监听响应式数据的变化。

import { watch, watchEffect } from 'vue';
watch(count, (newValue, oldValue) => {
  console.log(`Count changed from ${oldValue} to ${newValue}`);
});
watchEffect(() => {
  console.log(`Count is now ${count.value}`);
});

组合逻辑:将逻辑分解为可复用的函数,然后在setup中组合。

import { useCounter } from './composables/useCounter';
export default {
  setup() {
    const { count, increment } = useCounter();
    return {
      count,
      increment
    };
  }
};

认识ref和reactive

  • Vue 3中,refreactiveComposition API提供的两种主要方式,用于创建响应式数据。它们是 Vue 3响应式系统的核心,允许开发者将普通数据转换为响应式状态,从而实现数据的自动更新和视图的动态渲染。虽然它们都可以创建响应式数据,但在使用场景和内部实现上有一些区别。

ref的理解:

  • ref是一个函数,用于将一个值(无论是基本数据类型还是复杂对象)包装成一个响应式引用(Ref)。ref返回的对象包含一个value属性,通过访问或修改value属性来操作原始数据。

特点:

  • 包装值:ref可以包装任何值(如数字、字符串、对象等),使其成为响应式数据。
  • 访问和修改:通过.value属性访问或修改包装的值。
  • 自动解包:在模板中使用时,Vue会自动解包ref的值,因此可以直接使用变量名,而无需显式访问  .value

使用场景ref通常用于以下场景:

  • 包装基本数据类型(如数字、字符串、布尔值)。
  • 包装单个对象或数组,但更推荐使用reactive

示例

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

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

export default {
  setup() {
    const count = ref(0); // 创建一个响应式引用

    function increment() {
      count.value++; // 通过 .value 访问和修改
    }

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

reactive的理解

  • reactive是一个函数,用于将一个对象(包括数组和普通对象)转换为响应式对象。与ref不同,reactive直接返回一个响应式对象,而不是一个包装对象。

特点

  • 对象响应式:reactive只能用于对象(包括数组)。它会递归地将对象的每个属性转换为响应式属性。
  • 直接操作:不需要通过.value属性,直接操作返回的对象即可。
  • 性能优化:由于reactive是基于 Proxy 实现的,它在处理复杂对象时比ref更高效。

使用场景

  • 包装复杂对象或数组。
  • 需要频繁操作对象的多个属性。

示例

<template>
  <div>
    <p>{{ state.count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

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

export default {
  setup() {
    const state = reactive({
      count: 0
    });

    function increment() {
      state.count++; // 直接操作对象
    }

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

ref和reactive的区别

特点refreactive
适用数据类型基本数据类型或对象仅对象(包括数组)
 访问方式通过 .value 访问直接访问对象属性
 模板中使用 自动解包,无需 .value直接使用对象属性
内部实现基于 Object.defineProperty基于 Proxy
性能对于基本数据类型更轻量对于复杂对象更高效

ref和reactive的内部实现

ref的实现:

  • ref是基于Object.defineProperty实现的,它会将值包装成一个对象,并通过value属性暴露原始值。
  • 当访问或修改.value时,Vue 的响应式系统会自动触发依赖更新。

reactive的实现:

  • reactive是基于 ES6Proxy实现的,它会递归地将对象的每个属性转换为响应式属性。
  • Proxy提供了更强大的拦截能力,可以处理对象的增删属性、数组的索引操作等复杂场景。

toRef和toRefs的认识与理解

  • Vue 3Composition API 中,toReftoRefs是两个用于处理响应式对象的工具函数,它们可以帮助开发者更好地管理响应式数据,尤其是在解构或传递响应式对象时。

toRef的理解

作用
toRefs是一个工具函数,用于将一个响应式对象的所有属性转换为一个普通对象,其中每个属性都是一个 ref对象。这在解构响应式对象时非常有用,因为直接解构会丢失响应式。

使用场景

  • 当你需要解构响应式对象,但又希望保持每个属性的响应式时。
  • 在组合式函数中返回响应式对象的属性时。

特点

  • 响应式连接:toRef创建的ref对象与原始属性保持同步。
  • 非响应式属性的处理:即使原始属性不存在,toRef仍然会返回一个ref对象,其值为undefined。如果后续原始对象添加了该属性,ref对象会自动更新。
  • 与ref的区别:ref创建一个独立的响应式引用,而toRef创建一个与原始属性绑定的响应式引用。

示例代码

import { reactive, toRef } from 'vue';

const state = reactive({
  foo: 1,
  bar: 2
});

const fooRef = toRef(state, 'foo'); // 将 state.foo 转换为 ref
console.log(fooRef.value); // 输出 1

fooRef.value = 10; // 修改 ref 的值
console.log(state.foo); // 输出 10,原始属性也被更新

state.foo = 20; // 修改原始属性
console.log(fooRef.value); // 输出 20,ref 的值也被更新

toRefs的理解

作用

  • toRefs是一个工具函数,用于将一个响应式对象的所有属性转换为一个普通对象,其中每个属性都是一个ref对象。这在解构响应式对象时非常有用,因为直接解构会丢失响应式。

使用场景

  • 当你需要解构响应式对象,但又希望保持每个属性的响应式时。
  • 在组合式函数中返回响应式对象的属性时。

特点

  • 保持响应式:toRefs将响应式对象的每个属性转换为ref,从而保持响应式。
  • 解构安全:使用toRefs解构后,属性仍然是响应式的,不会丢失响应性。

示例代码

import { reactive, toRefs } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue'
});

const { count, name } = toRefs(state); // 解构并保持响应式
console.log(count.value); // 输出 0
console.log(name.value); // 输出 'Vue'

state.count = 10; // 修改原始对象的属性
console.log(count.value); // 输出 10,响应式连接保持

name.value = 'Vue 3'; // 修改 ref 的值
console.log(state.name); // 输出 'Vue 3',原始属性也被更新

toRef   和   toRefs   的区别

特性toReftoRefs 
 作用对象  单个属性  整个对象 
 返回值单个 ref 对象包含所有属性的 ref 对象的普通对象 
 使用场景提取单个属性并保持响应式连接解构整个对象并保持响应式连接

注意事项

.value访问:

  • toReftoRefs返回的都是ref对象,因此需要通过.value访问其值。

原始属性的存在性:

  • 如果原始属性不存在,toRef仍然会返回一个ref对象,但其值为undefined

性能优化:

  • 使用toReftoRefs可以减少不必要的响应式转换,从而提高性能。