系列文章目录
内容 | 链接 |
---|---|
从Vue2到Vue3【零】 | Vue3简介 |
从Vue2到Vue3【一】 | Composition API(第一章) |
从Vue2到Vue3【二】 | Composition API(第二章) |
从Vue2到Vue3【三】 | Composition API(第三章) |
从Vue2到Vue3【四】 | Composition API(第四章) |
文章目录
- 系列文章目录
- 前言
- 一、customRef
- 二、provide 与 inject
- 三、响应式数据的判断
- 四、Composition API 与 Options API
- 总结
前言
随着Vue 3的发布,我们迎来了一套强大且令人兴奋的组合式API,这为开发者带来了更多灵活性和可维护性。Vue 3的组合式API不仅改变了我们编写Vue组件的方式,还引入了一些新的组件和一些小的但实用的改变。在这篇文章中,我们将深入探讨Vue 3组合式API中的customRef、provide和inject以及响应式数据的判断,,让我们一起开始这个令人兴奋的学习之旅吧!
一、customRef
-
customRef:
- 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
- customRef适用于需要自定义读取和写入逻辑的场景。
- 提供了更多灵活性,可以根据具体需求定制读取和写入的行为。
- 可以与响应式系统无缝集成,保持一致的Vue开发体验。
ref其实和customRef很相似,都可以创建响应式数据,下面说说他们的异同点
- 相同点:
- 响应式: 无论是customRef还是ref都能够创建响应式的数据。
- 访问值: 无论是customRef还是ref都能够通过.value属性来访问内部的值。(操作值时都需要加.value)
- 区别:
-
创建方式: customRef需要传入一个函数,并在函数中定义自定义的读取和写入逻辑;而ref只需要传入初始值即可。
-
读取和写入逻辑: customRef允许你定义自己的读取和写入逻辑,可以实现更灵活的响应式行为;而ref会直接访问内部的值。
-
依赖追踪: customRef允许你在读取和写入逻辑中手动追踪依赖,通过track和trigger函数来实现;而ref会自动追踪读取和写入的依赖。
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {customRef} from 'vue'
export default {
name:'Demo',
setup(){
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track() //告诉Vue这个value值是需要被“追踪”的
return value
},
set(newValue){
clearTimeout(timer) // 当再次输入时清空上次的定时器 重新开启一个新的定时器
timer = setTimeout(()=>{
value = newValue // 直到在delay这段时间不再输入时 才会更新模板上的值
trigger() 通知Vue去重新解析模板
},delay)
}
}
})
}
let keyword = myRef('hello',500) //使用程序员自定义的ref
return {
keyword
}
}
}
</script>
这个官方给出的案例:当用户在input输入时,下方的h3标签延迟更新( 本质上就是一个防抖
)
防抖指的是在一定时间内如果再次触发该事件,那么将重新计时,直到该时间内没有再次触发,才会执行事件;而节流则是在一定时间内只能触发一次事件。在实现方面可以使用定时器或时间戳等技术来实现。其中,防抖适用于像搜索框这样需要输入完整关键字才会产生最终结果的场景;而节流则更适合于需要频繁触发但每次执行事件耗时较久的场景。
myref是自定义的一个函数,所以需要有返回值,返回的值是利用customRef自定义的响应式数据
customRef内部有getter和setter,当解析模版时需要读取值(getter),当在input输入时需要重新设置值(setter)
初始读取值时
修改值时
总结来说,customRef和ref都是Vue 3提供的用于创建响应式数据的函数,它们的主要区别在于创建方式、读取和写入逻辑、依赖追踪。你可以根据自己的需要选择使用哪种函数来创建响应式数据。
customRef真实场景使用要结合组件库的处理是否支持,不能很好的应用到组件库里,因为组件库里都有自己的数据劫持
(响应式实现原理数据劫持这块,在讲完vue3的基本使用后,会出一篇文章详细讲解,欢迎订阅本系列,长期更新!
)
二、provide 与 inject
- provide 与 inject
-
provide和inject是一对用于实现父组件向子孙组件传递数据的特殊组合。它们可以帮助我们在组件树中跨层级地共享数据,而无需通过逐层传递props的方式。
-
provide 函数接收两个参数:provide( name,value ),
- name:定义 提供给子孙们数据的 name 。
- value :提供数据的值。
-
inject(name,default)
-
name:接收 provide 提供的属性名。
-
default:设置默认值,是可选参数。
(当父亲没有provide值时,子组件注入使用的就是默认值,默认值也没有的话就是undefined
)
-
-
父组件用一个 provide
选项来提供数据,后代组件用一个 inject
来接收并使用这些数据
父组件代码
<template>
<div class="app">
<h3>我是App组件(祖),{{ name }}--{{ price }}</h3>
<Child />
</div>
</template>
<script>
import { reactive, toRefs, provide } from "vue";
import Child from "./components/Child.vue";
export default {
name: "App",
components: { Child },
setup() {
let car = reactive({ name: "奔驰", price: "40W" });
provide("car", car); //给自己的后代组件传递数据
setTimeout(() => {
// 过一秒钟修改父亲的car 看下子组件数据是否改变 若改变 则说明传递给子孙组件的数据也是响应式的
car.name = '宝马'
}, 1000);
return { ...toRefs(car) };
},
};
</script>
<style>
.app {
background-color: gray;
padding: 10px;
}
</style>
子组件代码
<template>
<div class="child">
<h3>我是Child组件(子),{{ name }}--{{ price }}</h3>
<Son/>
</div>
</template>
<script>
import {inject, toRefs} from 'vue'
import Son from './Son.vue'
export default {
name:'chIld',
components:{Son},
setup(){
let car = inject('car')
return {
...toRefs(car)
}
}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
孙组件代码
<template>
<div class="son">
<h3>我是Son组件(孙),{{car.name}}--{{car.price}}</h3>
</div>
</template>
<script>
import {inject} from 'vue'
export default {
name:'sOn',
setup(){
let car = inject('car')
return {car}
}
}
</script>
<style>
.son{
background-color: orange;
padding: 10px;
}
</style>
效果 (父组件给子孙组件传递响应式数据,父亲修改值后,子孙组件数据也会变化)
Vue框架采用的是单向数据流
的数据管理模式 ===> 子孙组件不允许修改从父组件传递下来的值,这样会造成数据流向不清晰加粗样式
-
这种数据流的设计有以下几个优点:
- 数据的流动清晰可控:数据从父组件传递到子组件,形成了明确的数据流动路径,使得数据的变化可以追溯和控制。
- 组件的独立性:子组件只需要关心自己的数据和逻辑,不需要关心父组件的具体实现细节,提高了组件的独立性和可复用性。
- 数据的可预测性:由于数据只能从父组件流向子组件,数据的变化只能通过父组件来触发,减少了数据的复杂性和不可预测性。
所以这里虽然是利用 provide 和 inject 在父组件和子孙组件之间传递数据,依然不能在后代组件中修改数据,为了避免这个问题,父组件在传递值时,应当利用readonly来限制子孙组件修改数据
升级版父组件传值
provide("car", readonly(car)); //给自己的后代组件传递只读数据
子组件内修改值的后果 : 报错,并且值不能被修改,nice!
setTimeout(() => {
car.name = '五菱宏光'
}, 2000);
三、响应式数据的判断
- isRef: 检查一个值是否为一个
ref
对象 - isReactive: 检查一个对象是否是由
reactive
创建的响应式代理 - isReadonly: 检查一个对象是否是由
readonly
创建的只读代理 - isProxy: 检查一个对象是否是由
reactive
或者readonly
方法创建的代理
<template>
<h3>我是App组件</h3>
</template>
<script>
import {ref, reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
export default {
name:'App',
setup(){
let car = reactive({name:'奔驰',price:'40W'})
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum)) //true
console.log(isReactive(car)) //true
console.log(isReadonly(car2)) //true
console.log(isProxy(car)) //true
console.log(isProxy(sum)) //false
return {...toRefs(car)}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
四、Composition API 与 Options API
- Options API
在Options API中,组件的各种选项(data、methods、computed、watch)是通过一个个独立的配置项来进行定义和组织的。这样的设计可能会导致代码的分散,特别是在大型组件中,很难追踪和管理各个选项之间的关联。
- Composition API
在组合式API中,各种功能选项可以根据其逻辑关联性进行组合
在组合式API中,可以使用setup()函数来组织和定义组件的逻辑。setup()函数中可以使用各种功能函数(如reactive、ref、computed等)来声明响应式数据、计算属性、事件处理函数等,而不需要将它们分散在不同的选项中。这样可以使得逻辑代码更加集中和易于维护。
另外,由于组合式API将数据和逻辑进行组合,使得组件的代码更加模块化和可复用(hooks)。不同的逻辑可以通过将不同的功能函数组合在一起来构建不同的组件,提高了代码的可维护性和可读性。
总结来说,在图中可以看到,使用组合式API可以将相关逻辑组合在一起,简化了代码结构,并提供了更好的可读性和可维护性。相比于Options API,组合式API更加灵活和强大,尤其适用于复杂的组件和大型项目。
总结
本系列到这,讲解完了组合式API,接下来将为大家带来新的组件和小的改变