自学内容网 自学内容网

vue3组合式API,看这篇就够了

创建vue3项目

组合式API

setup

setup在beforecreate前执行

左边是原始写法,右边是语法糖写法。

区别在于,左边需要定义变量或者函数,然后手动一个个返回。

语法糖写法,加上setup,就能在你定义后以对象方式自动return

需要注意的是,在setup里不用this访问,直接访问即可

reactive和ref函数

<script setup>
import {reactive} from "vue";
const state = reactive({
  count:0
})

const add = ()=>{
  state.count++;
}
</script>

<template>
  <div>
    <button @click="add">{{ state.count}}</button>
  </div>
</template>

<script setup>
import {reactive, ref} from "vue";
const count = ref(0)

const add = ()=>{
  count.value++;
  //用value是因为,count实际上是个对象
}
</script>

<template>
  <div>
    <button @click="add">{{ count}}</button>
                           //这里不是用count.value,
                           //应该直接使用 count,因为 Vue 会自动解包 ref 的值。
  </div>
</template>

选择使用

  • 使用 reactive 当你需要处理一个复杂对象或数组时。
  • 使用 ref 当你处理的是单个基本类型值,或希望明确表示某个值是响应式的。

computed计算属性函数

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

const list = ref([1, 2, 3, 4, 5, 6, 7, 8, 9]);
const filterList = computed(() => {
  return list.value.filter(i => i > 3);
});
</script>

<template>
  <div>
    {{ filterList }}
  </div>
</template>

需要注意的是,计算属性中只能用来计算,不要用来修改。这样能使代码更规范

watch函数

用法

watch 函数用于在 Vue 中观察响应式数据的变化。当被观察的数据变化时,watch 会执行一个回调函数。这在处理副作用或需要响应数据变化时特别有用。

import { watch, ref } from 'vue';

const count = ref(0);

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

参数

  1. 第一个参数:要观察的响应式数据或计算属性。
  2. 第二个参数:回调函数,接受两个参数:
    • newValue:新值
    • oldValue:旧值

特点

  • 可以观察多个响应式数据,使用数组作为第一个参数。
  • 可以选择在值变化时立即执行回调,通过第三个参数 { immediate: true }
  • 支持深度观察,使用 { deep: true } 选项。
示例

immediate是立即执行一次

watch(
  count,
  (newValue, oldValue) => {
    console.log(`Count changed from ${oldValue} to ${newValue}`);
  },
  { immediate: true } // 立即执行
);

<script setup>
import { computed, ref, watch } from "vue";
const count = ref(0)
const name = ref('abc')
const change = ()=>{
  count.value = 10
  name.value = 'qwetyu'
}
watch(
    [count,name],
    ([oldCount,newCount],[oldName,newName])=>{
      console.log([oldCount,newCount],[oldName,newName])
    },
    {
      immediate:true
    }
)

</script>

<template>
  <div>
    {{count+"  "+name}}
    <button @click="change">改变</button>
  </div>
</template>

deep

深度监听(deep watch)在 Vue 中用于观察对象内部的变化。当你希望监测一个嵌套对象的属性变化时,普通的监听可能无法捕捉到这些变化。这时就可以使用深度监听。

深度监听用法

watch 中,可以通过选项 { deep: true } 来启用深度监听。

深度监听示例
import { ref, watch } from 'vue';

const user = ref({
  name: 'Alice',
  address: {
    city: 'New York',
    zip: '10001'
  }
});

watch(user, (newValue, oldValue) => {
  console.log('User changed:', newValue);
}, { deep: true });

// 修改嵌套属性
user.value.address.city = 'Los Angeles';

深度监听关键点
  • 性能:深度监听会遍历对象的每个层级,可能会影响性能,尤其在对象很大时。
  • 适用场景:适用于需要细致跟踪嵌套状态的场合,例如表单数据、配置对象等。

深度监听能帮助你在复杂的数据结构中捕捉变化,提供更精细的控制!

监听对象注意点

如果你只想监听对象中的某一个特定属性,可以直接指定该属性,而不是对整个对象进行深度监听。这样可以提高性能并减少不必要的计算。

示例

假设你有一个对象 user,你只想监听 user.name 的变化:

import { ref, watch } from 'vue';

const user = ref({
  name: 'Alice',
  age: 30
});

// 只监听 user.name 的变化
watch(() => user.value.name, (newValue, oldValue) => {
  console.log(`Name changed from ${oldValue} to ${newValue}`);
});

// 修改 user.name
user.value.name = 'Bob'; // 控制台会输出: Name changed from Alice to Bob

关键点
  • 使用箭头函数 () => user.value.name 来明确指定要观察的属性。
  • 只在该属性变化时执行回调,从而避免对整个对象的监听。

这种方法使得监听更高效,也更容易理解!

生命周期

左边对应右边

onMounted可以执行多次

父子通信

父传子

父组件

<script setup>
import { computed, ref, watch } from "vue";
import Son from './components/Son.vue';

const count = ref(1)
const add = ()=>{
  count.value++;
}
</script>

<template>
  <div>
    <son msg="from father" :count="count"></son>
    <button @click="add">增加</button>
  </div>
</template>

子组件

<script setup>
const props = defineProps({
  msg:String,
  count:Number
})
</script>

<template>
  <div>
    父组件传来--{{msg}}和{{count}}
  </div>
</template>


<style scoped>

</style>

子传父

1. 子组件(Child.vue)

在子组件中,你可以使用 defineEmits 来定义要触发的事件,并通过 emit 方法来发送数据。

<template>
  <div>
    <button @click="sendData">Send Data to Parent</button>
  </div>
</template>

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

const emit = defineEmits();

const sendData = () => {
  const data = 'Hello from Child!';
  emit('child-to-parent', data); // 触发事件并传递数据
};
</script>

2. 父组件(Parent.vue)

在父组件中,你可以监听子组件发出的事件,并处理接收到的数据。

<template>
  <div>
    <h1>Parent Component</h1>
    <child @child-to-parent="handleChildData" />
  </div>
</template>

<script setup>
import Child from './Child.vue';

const handleChildData = (data) => {
  console.log('Received from child:', data); // 处理从子组件接收到的数据
};
</script>

3. 总结
  • 子组件使用 defineEmits 定义事件:你可以在子组件中定义事件名称(如 child-to-parent),并使用 emit 来发送数据。
  • 父组件使用 @ 监听事件:在父组件中,通过 @event-name 监听子组件的事件,并定义处理函数来处理接收到的数据。

上面可以看到有两种写法,关于defineEmits带不带参数解释如下

第一段代码

在第一个 <script setup> 中,你使用了 defineEmits(['update-list']),这是定义了一个具体的事件名 update-list,表示该组件将发出这个事件。这样做的好处是:

  • 类型安全: 如果你使用 TypeScript,定义事件名可以提供类型检查。
  • 文档化: 让其他开发者看到组件支持的事件。

const emit = defineEmits(['update-list']);

 

第二段代码

在第二段代码中,使用了 defineEmits() 而没有传入任何参数:

const emit = defineEmits();

这种用法的含义是这个组件可以发出任意事件,但没有事先定义事件名。这样做的影响包括:

  • 灵活性: 你可以在组件中发出任何事件,但失去了类型安全和明确的文档。
  • 不利于维护: 其他开发者可能不知道这个组件可以发出哪些事件。
  1. 参数定义:

    • 第一段代码明确了事件名,提供了更好的类型安全和文档化。
    • 第二段代码则没有事件名,提供了灵活性但缺乏明确性。
  2. 使用场景:

    • 如果你知道组件会发出特定事件,建议使用带参数的 defineEmits()
    • 如果组件的事件名不确定,或者会动态变化,可以使用不带参数的 defineEmits()

建议在大多数情况下使用带参数的方式,以提高代码的可读性和可维护性。

模板引用

1. 模板引用的概念

模板引用是通过在模板中使用 ref 属性来标识一个 DOM 元素或子组件。使用 ref 可以在 Vue 的 setup 函数中获取这些元素或组件的引用。

总之!是用来方便获取dom元素的!

2. 使用 ref 获取 DOM 元素

在模板中,可以给任何 DOM 元素添加 ref 属性:

<template>
  <div>
    <h1 ref="myHeading">Hello, Vue!</h1>
    <button @click="changeHeading">Change Heading</button>
  </div>
</template>

在这个示例中,<h1> 标签被标识为 myHeading

在 setup 函数中访问

setup 函数中,可以通过 ref 函数获取对该 DOM 元素的引用:

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

const myHeading = ref(null); // 初始化为 null

const changeHeading = () => {
  if (myHeading.value) {
    myHeading.value.textContent = 'Heading Changed!'; // 修改 DOM 内容
  }
};
</script>
  • myHeading.value:这是对 DOM 元素的引用,可以通过 .value 访问实际的 DOM 对象。

3. 使用 ref 获取组件实例

同样地,可以使用 ref 来引用子组件的实例:

子组件(Child.vue)
<template>
  <div>Child Component</div>
</template>

<script setup>
// 子组件逻辑
</script>

父组件(Parent.vue)
<template>
  <div>
    <Child ref="myChild" />
    <button @click="callChildMethod">Call Child Method</button>
  </div>
</template>

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

const myChild = ref(null); // 引用子组件

const callChildMethod = () => {
  if (myChild.value) {
    myChild.value.someMethod(); // 调用子组件的方法
  }
};
</script>

4. 注意事项

  • 生命周期:在组件挂载后,引用才会有效。因此在 mounted 钩子中访问这些引用是安全的。
  • 响应式:使用 ref 时,访问 DOM 元素或组件实例需要使用 .value

总结

通过 ref,开发者可以轻松地访问和操作真实的 DOM 元素或子组件实例。这为动态更新内容、调用组件方法等提供了极大的便利,使得 Vue 的开发更加灵活和强大。

ref 在 Vue 中的作用确实类似于在原生 JavaScript 中使用 idclass 属性,然后通过 querySelector 获取 DOM 元素。

相似点

  1. 获取元素:使用 ref,你可以直接在 Vue 中引用 DOM 元素或组件实例,就像用 document.querySelector 获取元素一样。

  2. 简便性ref 提供了一种更简洁的方式来访问和操作 DOM,因为你不需要查找元素,而是直接引用。

  3. 动态更新:无论是 ref 还是 querySelector,你都可以在事件处理函数中对元素进行动态更新。

不同点

  1. 响应式ref 是 Vue 的响应式系统的一部分,可以在 Vue 的生命周期内自动管理,而 querySelector 则是直接访问 DOM,不具备响应式特性。

  2. 与 Vue 生态集成:使用 ref 时,你可以更方便地访问子组件的方法和属性,这在使用原生 DOM 方法时并不容易实现。

总之,ref 使得 Vue 开发中对 DOM 的操作更加高效和集成,尤其是在处理组件时。

defineExpose

defineExpose 是 Vue 3 中一个很有用的功能,主要用于在 <script setup> 中显式地暴露组件的 API(即方法、属性等),以便外部使用。这使得子组件可以向父组件提供接口,增强了组件之间的通信和封装性

基本用法

在子组件中,你可以使用 defineExpose 来指定希望暴露给父组件的属性或方法。

子组件示例
<template>
  <div>
    <h1>Child Component</h1>
  </div>
</template>

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

const count = ref(0);

const increment = () => {
  count.value++;
};

defineExpose({
  count,
  increment,
});
</script>

在这个示例中,countincrement 被暴露给外部。

父组件使用示例
<template>
  <div>
    <Child ref="childRef" />
    <button @click="incrementChildCount">Increment Child Count</button>
    <p>Child Count: {{ childCount }}</p>
  </div>
</template>

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

const childRef = ref(null);
const childCount = ref(0);

const incrementChildCount = () => {
  if (childRef.value) {
    childRef.value.increment(); // 调用子组件的方法
    childCount.value = childRef.value.count; // 获取子组件的 count 值
  }
};
</script>

关键点

  1. 暴露的属性和方法defineExpose 允许你选择性地暴露组件内部的状态和方法,增强了封装性。

  2. 在父组件中访问:通过 ref 引用子组件,可以直接访问暴露的属性和方法。

  3. 与响应式结合:暴露的属性仍然是响应式的,可以在父组件中直接使用。

使用场景

  • 组件间通信:当子组件需要向父组件提供某些功能时,defineExpose 是一个理想的选择。
  • 封装逻辑:可以将复杂的逻辑封装在子组件中,只向外暴露必要的 API,增强组件的可重用性和维护性。

总的来说,defineExpose 是一个非常强大的工具,使得 Vue 3 组件之间的交互更加灵活和简洁。

provide和inject

provideinject 是 Vue 3 中用于组件间依赖注入的两个 API,主要用于实现跨层级的状态共享和数据传递。这种机制尤其适合在较深层级的组件树中传递数据,而不需要通过每一层的 props 逐级传递。

使用场景

  1. 跨层级通信:当你有多个层级的组件时,可以使用 provideinject 来避免通过 props 逐层传递数据。

  2. 共享状态:对于一些全局状态,比如主题、用户信息等,可以在根组件中使用 provide 进行提供,而在任何子组件中使用 inject 进行访问。

  3. 插件和库:许多 Vue 插件和库使用 provideinject 来在组件间传递配置或状态。

注意事项

  • 响应式:当你通过 provide 提供的是响应式数据(如 refreactive),子组件在使用 inject 时会保持响应性。

  • 依赖关系inject 只会注入其父组件提供的值,如果没有找到对应的 provide,会返回 undefined

  • 多重提供:如果一个组件在多层级中被提供了同样的键,子组件会优先使用最近的提供者。

跨层传递普通数据

父组件

<template>
  <Child />
</template>

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

const sharedData = ref('Hello from Parent');

provide('myData', sharedData);
</script>

子组件

<template>
  <div>{{ injectedData }}</div>
</template>

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

const injectedData = inject('myData');

if (!injectedData) {
  console.warn('myData is not provided');
}
</script>

跨层传递响应式数据

父组件

<script setup>
import {computed, provide, ref, watch} from "vue";
import Son from './components/Son.vue';

const count = ref(1000)
provide('count',count)
const add = ()=>{
  count.value++;
}

</script>

<template>
  <div>
    <Son></Son>
    <button @click="add">增加</button>
  </div>
</template>

子组件

<script setup>
import {inject, ref} from "vue";

const res = inject('count')

</script>

<template>
  <div>
    {{res}}
  </div>
</template>


<style scoped>

</style>

跨层传递方法

父组件

<script setup>
import {computed, provide, ref, watch} from "vue";
import Son from './components/Son.vue';

const count = ref(1000)

const add = ()=>{
  count.value++;
}

provide('count',count)
provide('add',add)
</script>

<template>
  <div>
    <Son></Son>
  </div>
</template>

子组件 

<script setup>
import {inject, ref} from "vue";

const res = inject('count')
const add = inject('add')

</script>

<template>
  <div>
    {{res}}
    <button @click="add">增加</button>
  </div>
</template>


<style scoped>

</style>

这个总结很重要。

案例


原文地址:https://blog.csdn.net/clmm_/article/details/142436633

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!