vue2和vue3的一个区别:vue3可以吧vue2的Options API转换成Composition API
Options API即下面所示
Options API的弊端
在Vue2中,我们编写组件的方式是Options API:
Options API的一大特点就是在对应的属性中编写对应的功能模块;
比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命周期钩子;
但是这种代码有一个很大的弊端:
当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中;
当我们组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散;
尤其对于那些一开始没有编写这些组件的人来说,这个组件的代码是难以阅读和理解的(阅读组件的其他人);
一个非常大的组件,其中的逻辑功能按照颜色进行了划分:
种碎片化的代码使用理解和维护这个复杂的组件变得异常困难,并且隐藏了潜在的逻辑问题;
并且当我们处理单个逻辑关注点时,需要不断的跳到相应的代码块中;
如果我们能将同一个逻辑关注 点相关的代码收集在一起会更好。
这就是Composition API想要做的事情,以及可以帮助我们完成的事情。
也有人把Vue Composition API简称为VCA。
那么既然知道Composition API想要帮助我们做什么事情,接下来看一下到底是怎么做呢?
为了开始使用Composition API,我们需要有一个可以实际使用它(编写代码)的地方;
在Vue组件中,这个位置就是 setup 函数;
setup其实就是组件的另外一个选项:
只不过这个选项强大到我们可以用它来替代之前所编写的大部分其他选项;
比如methods、computed、watch、data、生命周期等等;
接下来我们一起学习这个函数的使用:
函数的参数
函数的返回值
我们先来研究一个setup函数的参数,它主要有两个参数:
第一个参数:props
第二个参数:context
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发出事件);
Home Page
{{message}}
{{title}}
当前计数: {{counter}}
setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?
setup的返回值可以在模板template中被使用;
也就是说我们可以通过setup的返回值来替代data选项;
甚至是我们可以返回一个执行函数来代替在methods中定义的方法:
Home Page
{{message}}
{{title}}
当前计数: {{counter}}
但是,如果我们将 counter 在 increment 或者 decrement进行操作时,是否可以实现界面的响应式呢?
答案是不可以;
这是因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作;
官方关于this有这样一段描述(这段描述是我给官方提交了PR之后的一段描述):
表达的含义是this并没有指向当前组件实例;
并且在setup被调用之前,data、computed、methods等都没有被解析;
所以无法在setup中获取this;
如果想为在setup中定义的数据提供响应式的特性,那么我们可以使用reactive的函数:
那么这是什么原因呢?为什么就可以变成响应式的呢?
这是因为当我们使用reactive函数处理我们的数据之后,数据再次被使用时就会进行依赖收集;
当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);
事实上,我们编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的;
Home Page
{{message}}
当前计数: {{state.counter}}
reactive API对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型:
如果我们传入一个基本数据类型(String、Number、Boolean)会报一个警告;
这个时候Vue3给我们提供了另外一个API:ref API
ref 会返回一个可变的响应式对象,该对象作为一个 响应式的引用 维护着它内部的值,这就是ref名称的来源;
它内部的值是在ref的 value 属性中被维护的;
这里有两个注意事项:
在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过 ref.value 的方式来使用;
但是在 setup 函数内部,它依然是一个 ref引用, 所以对其进行操作时,我们依然需要使用 ref.value的方式;
Home Page
{{message}}
当前计数: {{counter}}
模板中的解包是浅层的解包,如果我们的代码是下面的方式:
如果我们将ref放到一个reactive的属性当中,那么在模板中使用时,它会自动解包:
Home Page
{{message}}
当前计数: {{counter}}
当前计数: {{info.counter.value}}
当前计数: {{reactiveInfo.counter}}
在我们传递给其他组件数据时,往往希望其他组件使用我们传递的内容,但是不允许它们修改时,就可以使用readonly了;