Options API的弊端
在Vue2中,我们编写组件的方式是Options API:
- Options API的一大特点就是在对应的属性中编写对应的功能模块;
- 比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命
周期钩子;
但是这种代码有一个很大的弊端:
- 当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中;
- 当我们组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散;
- 尤其对于那些一开始没有编写这些组件的人来说,这个组件的代码是难以阅读和理解的(阅读组件的其他人);
下面我们来看一个非常大的组件,其中的逻辑功能按照颜色进行了划分:
- 这种碎片化的代码使用,理解和维护这个复杂的组件变得异常困难,并且隐藏了潜在的逻辑问题;
-
并且当我们处理单个逻辑关注点时,需要不断的跳到相应的代码块中;
image.png
如果我们能将同一个逻辑关注点相关的代码收集在一起会更好。
这就是Composition API想要做的事情,以及可以帮助我们完成的事情。也有人把Vue CompositionAPI简称为VCA。
认识Composition API
那么既然知道Composition API想要帮助我们做什么事情,接下来看一下到底是怎么做呢?
- 为了开始使用Composition API,我们需要有一个可以实际使用它(编写代码)的地方;
- 在Vue组件中,这个位置就是 setup 函数;
setup其实就是组件的另外一个选项:
- 只不过这个选项强大到我们可以用它来替代之前所编写的大部分其他选项;
- 比如methods、computed、watch、data、生命周期等等;
接下来我们一起学习这个函数的使用:
- 函数的参数
- 函数的返回值
setup函数的参数
我们先来研究一个setup函数的参数,它主要有两个参数:
- 第一个参数:
props
- 第二个参数:
context
props非常好理解,它其实就是子组件接收的从父组件传递过来的属性
,会被放到1props对象
中,我们在setup函数
中如果需要使用
,那么就可以直接通过props
参数获取:
- 对于定义props的类型,我们还是和之前的规则是一样的,在props选项中定义;
- 并且在template中依然是可以正常去使用props中的属性,比如message;
- 如果我们在setup函数中想要使用props,那么不可以通过 this 去获取(后面我会讲到为什么);
- 因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可;
另外一个参数是context
,我们也称之为是一个SetupContext
,它里面包含三个属性:
attrs
:为一个对象,存储着所有的从父组件传递过来的非props的attribute;
slots
:父组件传递过来的插槽(这个在以渲染函数返回时会有作用,后面会讲到);
emit
:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);
setup函数的返回值
setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?
- setup的返回值可以在
模板template
中被使用; - 也就是说我们可以通过setup的返回值来替代data选项;
甚至是我们可以返回一个执行函数
来代替在methods
中定义的方法
:
setup() {
let counter = 100
const increment = () => {
counter++
}
const decrement = () => {
counter--
}
return {
counter,
increment,
decrement
}
}
但是,如果我们将 counter 在 increment 或者 decrement进行操作时,是否可以实现界面的响应式呢?
- 答案是不可以;
- 这是因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作;
案例
App.vue
所有在template模板
中使用的变量
或方法
(除了props
和$attrs
中的属性),都得在setup函数中
返回
<template>
<div>
<!--父组件在使用子组件home时,除了给其传了内部接收的props的属性message,title外,还额外传了两个非props属性id, class-->
<home
message="hahahh"
title="标题"
id="home"
class="homeClass"
@change="change"
/>
</div>
</template>
<script>
import Home from "./Home.vue";
export default {
name: "App",
components: {
Home,
},
setup() {
const change = (value) => {
console.log("监听到了change事件", value);
};
return {
//所有在template模板中使用的变量或方法(除了props和$attrs中的属性),都得在setup函数中返回
change,
};
},
};
</script>
<style></style>
Home.vue
父组件传递给子组件的非props属性
在template
中使用,这些属性都存储在$attrs
对象中,可以通过attrs.属性名
获取
父组件传递给子组件的非props属性
在setup函数
中获取,这些属性都存储在setup函数
的第二个参数对象context
的attrs
属性中,可以通过context.attrs.属性名
获取
父组件传递给子组件的props属性
在setup函数
中获取,这些属性都存储在setup函数
的第一个参数对象props
中,可以通过props.属性名
获取
在setup函数中,子组件向父组件发射事件,可以通过context.emit
发射事件
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ message }}</p>
<!-- 下面展示父组件传递给子组件的非props属性在template中的使用,这些属性都存储在$attrs对象中 -->
<div>id: {{ $attrs.id }} - class: {{ $attrs.class }}</div>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
required: true,
},
title: {
type: String,
default: "",
},
},
emits: ["change"],
setup(props, context) {
const { attrs, slots, emit } = context;
//在setup中获取props中属性,通过setup函数的第一个参数获取
console.log("props", props, props.message, props.title);
//父组件传递给子组件的非props属性在setup中使用,这些属性都存储在context.attrs对象中
console.log("attrs", attrs, attrs.id, attrs.class);
//父组件插入子组件插槽中的内容
console.log("slots", slots);
emit("change", "今天天气不错"); //emit用来向父组件发射事件
},
};
</script>
<style lang="scss" scoped></style>
image.png
setup不可以使用this
官方关于this有这样一段描述:
- 表达的含义是this并没有指向当前组件实例;
- 并且在setup被调用之前,data、computed、methods等都没有被解析;
-
所以无法在setup中获取this;
image.png
网友评论