首页IT科技vue组件实现搜索界面(vue3组合式API介绍)

vue组件实现搜索界面(vue3组合式API介绍)

时间2025-05-04 08:40:43分类IT科技浏览3515
导读:为什么要使用Composition API? 根据官方的说法,vue3.0的变化包括性能上的改进、更小的 bundle 体积、对 TypeScript 更好的支持、用于处理大规模用例的全新 API,全新的api指的就是本文主要要说的组合式api。...

为什么要使用Composition API?

根据官方的说法            ,vue3.0的变化包括性能上的改进            、更小的 bundle 体积                   、对 TypeScript 更好的支持      、用于处理大规模用例的全新 API                   ,全新的api指的就是本文主要要说的组合式api            。

在 vue3 版本之前      ,我们复用组件(或者提取和重用多个组件之间的逻辑)            ,通常有以下几种方式:

Mixin:命名空间冲突 & 渲染上下文中暴露的 property 来源不清晰                   。例如在阅读一个运用了多个 mixin 的模板时                   ,很难看出某个 property 是从哪一个 mixin 中注入的      。 Renderless Component:无渲染组件需要额外的有状态的组件实例      ,从而使得性能有所损耗 Vuex:就会变得更加复杂      ,需要去定义 Mutations 也需要去定义 Actions

上述提到的几种方式                   ,也是我们项目中正在使用的方式            。对于提取和重用多个组件之间的逻辑似乎并不简单                   。我们甚至采用了 extend 来做到最大化利用已有组件逻辑            ,因此使得代码逻辑依赖严重      ,难以阅读和理解      。

Vue3 中的 Composition API 便是解决这一问题;且完美支持类型推导                   ,不再是依靠一个简单的 this 上下文来暴露 property(比如 methods 选项下的函数的 this 是指向组件实例的            ,而不是这个 methods 对象)      。其是一组低侵入式的            、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑                   。

业务实践

组合式api的出现就能解决以上两个问题                   ,此外                   ,它也对TypeScript类型推导更加友好            。

在具体使用上,对vue单文件来说            ,模板部分和样式部分基本和以前没有区别                   ,组合式api主要影响的是逻辑部分      。下面是一个经典的vue2的计数器案例.:

vue2 实现

//Counter.vue export default { data: () => ({ count: 0 }), methods: { increment() { this.count++; } }, computed: { double () { return this.count * 2; } } }

vue3 composition api

当我们在组件间提取并复用逻辑时      ,组合式API 是十分灵活的                   。一个组合函数仅依赖它的参数和 Vue 全局导出的 API            ,而不是依赖其微妙的 this 上下文            。你可以将组件内的任何一段逻辑导出为函数以复用它。

基于响应式 提供 vue 的生命周期钩子 组件销毁时自动销毁依赖监听 可复用的逻辑 // Counter.vue import { ref, computed } from "vue"; export default { setup() { const count = ref(0); const double = computed(() => count * 2) function increment() { count.value++; } return { count, double, increment } } } 代码提取

Composition API的第一个明显优点是提取逻辑很容易                   。使用Composition提取上面Counter.vue组件代码                   。

//useCounter.js 组合函数 import { ref, computed } from "vue"; export default function () { const count = ref(0); const double = computed(() => count * 2) function increment() { count.value++; } return { count, double, increment } } 代码重用

要在组件中使用该函数                   ,我们只需将模块导入组件文件并调用它(注意导入是一个函数)。这将返回我们定义的变量      ,随后我们可以从 setup 函数中返回它们            。

// MyComponent.js import useCounter from "./useCounter.js"; export default { setup() { const { count, double, increment } = useCounter(); return { count, double, increment } } }

相比而言      ,组合式 API:

暴露给模板的 property 来源十分清晰                   ,因为它们都是被组合逻辑函数返回的值 不存在命名空间冲突            ,可以通过解构任意命名 不再需要仅为逻辑复用而创建新的组件实例

常用api介绍

setup

export default { setup(props, context) { console.log(context); // { attrs, slots, emit } //context.emit(emitFun, {emit: true}) return { privateMsg: props.msg }; } }

setup函数是组件内使用 component API 的入口                   。是在组件实例被创建时      , 初始化了 props 之后调用                   ,处于 created 前      。还有以下特点:

1.可以返回一个对象或函数            ,对象的属性会合并到模板渲染的上下文中;

2.第一个参数是响应式的props对象,注意不能解构props 对象                   ,会使其失去响应性            。

也不可直接修改 props                   ,会触发警告

3.第二个参数是一个上下文对象,暴露了 attrs            ,slots                   ,emit 对象

4.this 在 setup 函数中不可用                   。因为它不会找到组件实例      。setup 的调用发生在 data                   、computed 和 methods 被解析之前      ,所以它们无法在 setup 中被获取      。

props与上下文对象attrs的区别:

1      、props 要先声明才能取值            ,attrs 不用先声明

2      、props 声明过的属性                   ,attrs 里不会再出现

3                   、props 不包含事件      ,attrs 包含                   。vue2中的$listeners 被整合到 $attrs

reactive

<template> <div> <p>{{data.msg}}</p> <button @click="updateData">更新数据</button> </div> </template> <script> import { reactive } from "vue"; export default { name: "ReactiveObject", setup() { const data = reactive({ msg: "hello world" }); const updateData = () => { data.msg= "hello world " + new Date().getTime(); }; return { data, updateData }; }, }; </script>

reactive函数接收一个普通对象然后返回对象的响应式代理      ,同 Vue.observable            。

原理:通过proxy对数据进行封装                   ,当数据变化时            ,触发模板等内容的更新      。

ref

<template> <div> <p>{{msg}}</p> <button @click="updateMessage">更新数据</button> </div> </template> <script> import { ref } from "vue"; export default { name: "ReactiveSingleValue", setup() { const msg= ref("hello world"); const updateMessage = () => { msg.value = "hello world " + new Date().getTime(); }; return { msg, updateMessage }; }, }; </script>

ref和reactive存在一定的相似性      ,所以需要完全理解它们才能高效的在各种场景下选择不同的方式                   ,它们之间最明显的区别是ref使用的时候需要通过.value来取值            ,reactive不用                   。ref是property而reactive是proxy,reactive能够深度监听各种类型对象的变化                   ,ref是处理诸如number                   ,string之类的基本数据类型            。

它们的区别也可以这么理解,ref是使某一个数据提供响应能力            ,而reactive是为包含该数据的一整个对象提供响应能力。

在模板里使用ref和嵌套在响应式对象里时不需要通过.value                   ,会自己解开:

除了响应式ref还有一个引用DOM元素的ref      ,2.x里面是通过this.$refs.xxx来引用            ,但是在setup里面没有this                   ,所以也是通过创建一个ref来使用:

<template> <div ref="node"></div> </template> <script> import { ref, onMounted } from vue export default { setup() { const node = ref(null) onMounted(() => { console.log(node.value) // 此处就是dom元素 <div ref="node"></div> }) return { node } } } </script>

computed

传入一个 getter 函数      ,返回一个默认不可修改的 ref 对象      ,同 vue 2.x 中的计算属性 computed

const count = ref(0) const sum = computed(() => count.value + 1) console.log(sum.value) // 1 sum.value = 3 // 错误

也可传入一个 get 和 set 函数对象                   ,创建一个可修改的计算状态

const count = ref(0) const sum = computed({ get: () => count.value + 1, set: (value) => { count.value = value - 1 } }) sum.value = 55 console.log(sum, count) // 55, 54

watchEffect

import { reactive, watchEffect } from "vue"; export default { name: "WatchEffect", setup() { const data = reactive({ count: 1 }); watchEffect(() => console.log(`侦听器:${data.count}`)); setInterval(() => { data.count++; }, 1000); return { data }; }, };

watchEffect用来监听数据的变化            ,它会立即执行一次      ,之后会追踪函数里面用到的所有响应式状态                   ,当变化后会重新执行该回调函数                   。

watch

完全等效于 2.x 中 watch 选项            ,对比 watchEffect,watch 允许我们:

懒执行副作用; 更明确哪些状态的改变会触发侦听器重新运行副作用; 访问侦听状态变化前后的值                   。 // 监听一个 getter const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { console.log(count, prevCount) } ) // 直接监听一个 ref const count = ref(0) watch(count, (count, prevCount) => { console.log(count, prevCount) }, { deep: true, // 深度监听 immediate: true // 初始化执行一次 }) // 监听多个数据 watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { console.log([foo, bar], [prevFoo, prevBar]) })

toRefs

把一个响应式对象转换成普通对象                   ,该普通对象的每个 property 都是一个 ref                   ,和响应式对象 property 一一对应。可以被解构且保持响应性

<template> <div> <h1>解构响应式对象数据</h1> <p>Username: {{username}}</p> <p>Age: {{age}}</p> </div> </template> <script> import { reactive, toRefs } from "vue"; export default { name: "DestructReactiveObject", setup() { const user = reactive({ username: "haihong", age: 10000, }); return { ...toRefs(user) }; }, }; </script>

toRef

toRef 可以用来为一个 reactive 对象的属性创建一个 ref            。这个 ref 可以被传递并且能够保持响应性                   。

setup() { const user = reactive({ age: 1 }); const age = toRef(user, "age"); age.value++; console.log(user.age); // 2 user.age++; console.log(age.value); // 3 }

Provide/Inject

为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive      。

当使用响应式 provide / inject 值时            ,建议尽可能将对响应式 property 的所有修改限制在定义 provide 的组件内部            。然而                   ,有时我们需要在注入数据的组件内部更新 inject 的数据                   。在这种情况下      ,我们建议 provide 一个方法来负责改变响应式 property      。

最后            ,如果要确保通过 provide 传递的数据不会被 inject 的组件更改                   ,我们建议对提供者的 property 使用 readonly      。 <!-- src/components/MyMap.vue --> <template> <MyMarker /> </template> <script> import { provide, reactive, readonly, ref } from vue import MyMarker from ./MyMarker.vue export default { components: { MyMarker }, setup() { const location = ref(North Pole) const geolocation = reactive({ longitude: 90, latitude: 135 }) const updateLocation = () => { location.value = South Pole } provide(location, readonly(location)) provide(geolocation, readonly(geolocation)) provide(updateLocation, updateLocation) } } </script> <!-- src/components/MyMarker.vue --> <script> import { inject } from vue export default { setup() { const userLocation = inject(location, The Universe) const userGeolocation = inject(geolocation) const updateUserLocation = inject(updateLocation) return { userLocation, userGeolocation, updateUserLocation } } } </script>

生命周期函数

2.x 版本生命周期相对应的组合式 API ~~beforeCreate~~ -> 使用 setup() ~~created~~ -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted errorCaptured -> onErrorCaptured

只需要将之前的生命周期改成onXXX的形式即可      ,需要注意的是created            、beforeCreate两个钩子被删除了      ,生命周期函数只能在setup函数里使用                   。

总结

使用组合式api还是需要一点时间来适应的                   ,首先需要能区分ref和reactive            ,不要在基本类型和引用类型      、响应式和非响应式对象之间搞混      ,其次就是如何拆分好每一个use函数                   ,组合式api带来了更好的代码组织方式            ,但也更容易把代码写的更难以维护,比如setup函数巨长            。

简单总结一下升级思路                   ,data选项里的数据通过reactive进行声明                   ,通过...toRefs()返回;computed                   、mounted等选项通过对应的computed            、onMounted等函数来进行替换;methods里的函数随便在哪声明,只要在setup函数里返回即可      。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
amazon数据工具(如何设置Amazon Aurora数据库的自动备份) uniapp组件使用(uni-app–》常用组件的相关介绍)