Vue系列(十三)-Composition API 一
这篇文章说一下Composition API,提到这就要说一下setup这个函数了,这个函数直接把vue的难度拉高了一截,更接近原生的js了,也更加灵活了。并且可以更好的兼容TypeScript,然后第二部分补充了Mixin,和CompositionAPI没有联系
一
Composition API
1.1 Options API的弊端
了解Composition API前先了解一下Options API的弊端。在Vue2中,我们编写组件的方式是Options API,包括前面章节用的也是Options API,Options API的一大特点就是在对应的属性中编写对应的功能模块;
比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命周期钩子
但是这种代码有一个很大的弊端:
当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中;当我们组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散;
如果我们能将同一个逻辑关注点相关的代码收集在一起会更好。
这就是Composition API想要做的事情,以及可以帮助我们完成的事情。
也有人把Vue CompositionAPI简称为VCA。
1.2认识Composition API
那么既然知道Composition API想要帮助我们做什么事情,接下来看一下到底是怎么做呢?
为了开始使用Composition API,我们需要有一个可以实际使用它(编写代码)的地方;
在Vue组件中,这个位置就是setup函数;
setup其实就是组件的另外一个选项:
只不过这个选项强大到我们可以用它来替代之前所编写的大部分其他选项;
比如methods、computed、watch、data、生命周期等等
1.3 setup
接下来说一下这个函数的使用:
函数的参数
函数的返回值
-
setup函数的参数
我们先来研究一个setup函数的参数,它主要有两个参数:
第一个参数:props
第二个参数:context
<script>
export default {
props: {
message: {
type: String,
required: true
}
},
/**
* 参数一: props, 父组件传递过来属性
* 参数二: context,上下文
*/
setup(props, context) {
},
}
</script>
props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取:
对于定义props的类型,我们还是和之前的规则是一样的,在props选项中定义;并且在template中依然是可以正常去使用props中的属性,比如message;
如果我们在setup函数中想要使用props,那么不可以通过this去获取(后面我会讲到为什么);
因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可;
另外一个参数是context,我们也称之为是一个SetupContext,它里面包含三个属性:
-
attrs:所有的非prop的attribute;
-
slots:父组件传递过来的插槽(这个在以渲染函数返回时会有作用,后面会讲到);
-
emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);
//可直接对第二个参数context进行解构
setup(props, {attrs, slots, emit}) {
},
-
setup函数的返回值
setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?
setup的返回值可以在模板template中被使用;
也就是说我们可以通过setup的返回值来替代data选项;
甚至是我们可以返回一个执行函数来代替在methods中定义的方法:
var counter = 100;
// 局部函数
const increment = () => {
counter++;
console.log(counter);
}
const decrement = () => {
counter--
console.log(counter);
}
return {
increment,
decrement
}
但是,如果我们将 counter 在 increment 或者 decrement进行操作时,是否可以实现界面的响应式呢?
答案是不可以;这是因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作;
注:setup不可以使用this
1.4Reactive API
如果想为在setup中定义的数据提供响应式的特性,那么我们可以使用reactive的函数:
const state = reactive({
counter: 100
})
那么这是什么原因呢?为什么就可以变成响应式的呢?
这是因为当我们使用reactive函数处理我们的数据之后,数据再次被使用时就会进行依赖收集;
当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);
事实上,我们编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的;
1.5Ref API
reactive API对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型:
如果我们传入一个基本数据类型(String、Number、Boolean)会报一个警告;
这个时候Vue3给我们提供了另外一个API:
ref API
ref会返回一个可变的响应式对象,该对象作为一个响应式的引用维护着它内部的值,这就是ref名称的来源;
它内部的值是在ref的 value 属性中被维护的;
const msg = ref('hello vue');
这里有两个注意事项:
在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过 ref.value的方式来使用;
但是在setup 函数内部,它依然是一个ref引用, 所以对其进行操作时,我们依然需要使用 ref.value的方式;
1.6了解readonly
我们通过reactive或者ref可以获取到一个响应式的对象,但是某些情况下,我们传入给其他地方(组件)的这个响应式对象希望在另外一个地方(组件)被使用,但是不能被修改,这个时候如何防止这种情况的出现呢?
Vue3为我们提供了readonly的方法;
readonly会返回原生对象的只读代理(也就是它依然是一个Proxy,这是一个proxy的set方法被劫持,并且不能对其进行修改);
在开发中常见的readonly方法会传入三个类型的参数:
-
类型一:普通对象;
-
类型二:reactive返回的对象;
-
类型三:ref的对象;
在我们传递给其他组件数据时,希望其他组件使用我们传递的内容,但是不允许它们修改时,就可以使用readonly了
1.7readonly的使用
在readonly的使用过程中,有如下规则:
readonly返回的对象都是不允许修改的;
但是经过readonly处理的原来的对象是允许被修改的;
比如const info = readonly(obj),info对象是不允许被修改的; 当obj被修改时,readonly返回的info对象也会被修改; 但是我们不能去修改readonly返回的对象info;
其实本质上就是readonly返回的对象的setter方法被劫持了而已;
//普通对象
const info1 = {
name : 'pzh',
age : 18
}
const onlyReadInfo1 = readonly(info1)
//reactice对象
const info2 = reactive({
name : 'pzh',
age : 18
})
const onlyReadInfo2 = readonly(info2)
//ref对象
const info3 = ref('pzh')
const onlyReadInfo3 = readonly(info3)
1.8Reactive判断的API
isProxy
检查对象是否是由reactive或readonly创建的 proxy;
isReactive
检查对象是否是由reactive创建的响应式代理: 如果该代理是readonly建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
isReadonly
检查对象是否是由 readonly创建的只读代理。
toRaw
返回reactive或readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)
shallowReactive
创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。
shallowReadonly
创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。
1.9toRefs
如果我们使用ES6的解构语法,对reactive返回的对象进行解构获取值,那么之后无论是修改结构后的变量,还是修改reactive返回的state对象,数据都不再是响应式的:
const state = reactive({
name : 'pzh',
age : 18
})
那么有没有办法让我们解构出来的属性是响应式的呢?
Vue为我们提供了一个toRefs的函数,可以将reactive返回的对象中的属性都转成ref;
那么我们再次进行结构出来的name和age本身都是ref的;
//这样写的话,会返回两个ref对象,它们是响应式的
const {name , age } = toRefs(state)
这种做法相当于已经在state.name和ref.value之间建立了链接,任何一个修改都会引起另外一个变化;
1.10toRef
如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法:
const name = toRef(state,'name')
二
Mixin
Mixin为单独的内容,与Composition API没有联系
2.1认识Mixin
目前我们是使用组件化的方式在开发整个Vue的应用程序,但是组件和组件之间有时候会存在相同的代码逻辑,我们希望对相同的代码逻辑进行抽取。
在Vue2和Vue3中都支持的一种方式就是使用Mixin来完成:
Mixin提供了一种非常灵活的方式,来分发Vue组件中的可复用功能;
一个Mixin对象可以包含任何组件选项;
当组件使用Mixin对象时,所有Mixin对象的选项将被混合进入该组件本身的选项中;
2.2Mixin的基本使用:
2.3Mixin的合并规则
如果Mixin对象中的选项和组件对象中的选项发生了冲突,那么Vue会如何操作呢?
这里分成不同的情况来进行处理;
-
情况一:如果是data函数的返回值对象, 返回值对象默认情况下会 进行合并 ; 如果data返回值对象的属性 发生了冲突 ,那么会 保留组件自身的数据 ;
-
情况二:如何生命周期钩子函数, 生命周期的钩子函数会被合并到数组中,都会被调用;
-
情况三:值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。 比如都有methods选项,并且都定义了方法,那么它们都会生效; 但是如果对象的key相同,那么会取组件对象的键值对;
2.4全局混入Mixin
如果组件中的某些选项,是所有的组件都需要拥有的 ,那么这个时候我们可以使用全局的mixin:
全局的Mixin可以使用 应用app的方法mixin来完成注册;
一旦注册,那么全局混入的选项将会影响每一个组件;
const app = createApp(App);
app.mixin({
data() {
return {}
},
methods: {
},
created() {
console.log("全局的created生命周期");
}
});