[TOC]
# 1. setup執行時機問題
* `setup`函數在`beforeCreate`生命周期函數之前執行一次且也只執行一次,此時組件對象還沒有創建。
* `this`此時是`undefined`,所以不能在`setup`內通過`this`來訪問`data/computed/methods/props`。
* 其實所有的composition API相關回調函數中也都不可以通過`this`來訪問。
```html
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup() {
console.log("setup方法執行了!");
//this此時是undefined
console.log(this);
return {};
},
beforeCreate() {
console.log("beforeCreate方法執行了!");
},
});
</script>
```
打印順序如下,可見`setup`是在`beforeCreate`之前執行的。
```
setup方法執行了!
undefined
beforeCreate方法執行了!
```
<br/>
# 2. setup的返回值
* `setup`函數一般都返回一個對象,為模板提供數據,也就是模板中可以直接使用此對象中的所有屬性/方法。
* `setup`函數返回對象中的屬性會與`data`函數返回對象的屬性合并成為組件對象的屬性。
* `setup`函數返回對象中的方法會與`methods`中的方法合并成為組件對象的方法。
* 如果`data`與`methods`有重名,`setup`優先。
* 注意:
* 一般不要將`data`與`setup`混合使用,`methods`與`setup`混合使用。
* `methods`中可以訪問`setup`提供的屬性和方法,但在`setup`方法中不能訪問`data`和`methods`,因為在`setup`中此時組件還沒創建好,`this`是`undefined`。
* `setup`不能是一個`async`函數,因為添加了`async`,則`setup`返回值不再是一個簡單對象,而是 Promise 對象,在模板中是看不到 Promise 對象中的屬性數據的。
```html
<template>
<div>
<!-- msg01與msg02都可以在模板中訪問到 -->
<h3>msg01: {{ msg01 }}</h3>
<h3>msg02: {{ msg02 }}</h3>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
//不能聲明為 async setup() {..}
setup() {
const msg01 = "setup中的msg01屬性";
const getMsg01 = () => {
console.log("setup的getMsg01函數");
};
return {
msg01,
getMsg01,
};
},
data() {
const msg02 = "data中的msg02屬性";
return {
msg02,
};
},
methods: {
getMsg02: () => {
console.log("methods中的getMsg02函數");
},
},
mounted() {
console.log(this);
},
});
</script>
```
`mounted`輸出內容如下,可以`this`其實也是一個代理對象。在`setup`、`data`、`methods`中聲明的屬性與函數都合并為了組件的屬性與函數。

<br/>
# 3. setup的參數
* `setup(props, context)` / `setup(props, {attrs, slots, emit})`
* props: 是一個對象,該對象包含了父組件向子組件傳遞的所有數據,并且是在子組件的`props`中接收到的所有屬性。
* context:是一個對象,包含`attrs、slots、emit`三個屬性,所以一般也寫成`setup(props, {attrs, slots, emit})`。
* attrs: 包含沒有在props配置中聲明的屬性的對象,相當于 this.$attrs
* slots: 包含所有傳入的插槽內容的對象,相當于 this.$slots
* emit: 用來分發自定義事件的函數,相當于 this.$emit
**1. 父組件:SetupParamsParent.vue**
```html
<template>
<h3>父組件</h3>
<h4>msg: {{ msg }}</h4>
<hr />
<!-- 在父組件中給子組件傳遞屬性和添加事件 -->
<SetupParamsChild
:message="msg"
:message2="msg"
@updateMessage="updateMessage"
></SetupParamsChild>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import SetupParamsChild from "./SetupParamsChild.vue";
export default defineComponent({
setup() {
const msg = ref("父組件傳遞的參數!");
const updateMessage = (newMsg: string) => {
msg.value += newMsg;
};
return {
msg,
updateMessage,
};
},
components: {
SetupParamsChild,
},
});
</script>
```
**2. 子組件:SetupParamsChild.vue**
```html
<template>
<h3>子組件</h3>
<!-- 3. 模板中不可以直接調用message2屬性,但是可以直接調用message屬性 -->
<h3>messsage: {{ message }}</h3>
<!-- 4. 子組件中觸發父組件的事件 -->
<button @click="updateMessage">updateMessage</button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup(props, context) {
//1. message在props中已經聲明,message2沒有
console.log(props.message);
//父組件傳遞的參數!
console.log(context.attrs.message2);
//父組件傳遞的參數!
const updateMessage = () => {
//2. 分發事件
context.emit("updateMessage", "++");
};
return {
updateMessage,
};
},
props: ["message"],
});
</script>
```
**3. 效果如下**

- nodejs
- 同時安裝多個node版本
- Vue3
- 創建Vue3項目
- 使用 vue-cli 創建
- 使用 vite 創建
- 常用的Composition API
- setup
- ref
- reactive
- 響應數據原理
- setup細節
- reactive與ref細節
- 計算屬性與監視
- 生命周期函數
- toRefs
- 其它的Composition API
- shallowReactive與shallowRef
- readonly與shallowReadonly
- toRaw與markRaw
- toRef
- customRef
- provide與inject
- 響應式數據的判斷
- 組件
- Fragment片斷
- Teleport瞬移
- Suspense
- ES6
- Promise對象
- Promise作用
- 狀態與過程
- 基本使用
- 常用API
- async與await
- Axios