setup()函数是组合式 API 的入口函数,所有的组合式 API 都需要放到setup()函数内部执行。

  1. export default{
  2. setup(props, context){
  3. // do...
  4. }
  5. }

setup()函数是在beforCreate()created()之前执行的,所以setup()内部不能调用beforCreate()created()这两个生命周期函数,而是使用setup()来代替。

Vue3 没有完全去除选项 API,而是做了向下兼容,新增了组合式 API,开发者完全可以自由的选择任意一种或者混合性的编写开发项目。

  1. export default{
  2. setup(props, { attrs, slots, emit, expose }) {
  3. console.log(props);
  4. return {
  5. msg: "Hello setup!"
  6. }
  7. }
  8. }

setup()函数的第一个参数是props,其实就是对选项 API 中props的引用。
setup()函数的第二个参数是context,表示上下文的意思,该对象内部有attrsslotsemitexpose这 4 个对象。

**setup()**函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取**setup()**暴露的属性。
setup()函数内部,是无法像选项 API 那样获取this对象的,当访问this的时候会得到undefinde,你可以在选项式 API 中访问setup()暴露的值,但反过来则不行。 :::info 为什么无法访问this对象呢?
1、因为setup()函数是在组件创建之前执行的,执行的时候还没有组件实例,所以也就没有this,所以 Vue3 会把一些对象封装作为setup()函数的参数。
2、使用组合式 API 编程的时候,一些函数都是通过 ESModule 导入的而不是挂载到实例上的,所以也就没有this的必要了。 :::

props

  1. <template>
  2. <VSetup
  3. title="This is title."
  4. author="Xiechen"
  5. :content="content" />
  6. </template>
  7. <script>
  8. import VSetup from "./components/VSetup.vue";
  9. export default {
  10. components:{
  11. VSetup
  12. },
  13. data() {
  14. return {
  15. content: "This is content."
  16. };
  17. }
  18. }
  19. </script>
  1. <template>
  2. <div class="">
  3. <h1>{{ title }}</h1>
  4. <p>{{ author }}</p>
  5. <p>{{ content }}</p>
  6. </div>
  7. </template>
  8. <script>
  9. export default{
  10. pprops: {
  11. title: String,
  12. author: String,
  13. content: String
  14. },
  15. setup(props, context){
  16. console.log(props);
  17. }
  18. }
  19. </script>

image.png
你可以完全的把选项 API 和组合 API 混合使用。

props是一个响应式的对象,你不能把该对象的属性进行解构使用,这会丢失数据的响应式。

  1. <script>
  2. export default{
  3. mounted() {
  4. setTimeout(() => {
  5. this.content = "This is my content.";
  6. }, 1000);
  7. }
  8. }
  9. </script>
  1. <template>
  2. <div class="">
  3. <h1>{{ title }}</h1>
  4. <p>{{ author }}</p>
  5. <p>{{ content }}</p>
  6. <p>{{ myContent }}</p>
  7. </div>
  8. </template>
  9. <script>
  10. // 把 computed 导入进来
  11. import { computed } from "vue"
  12. export default{
  13. pprops: {
  14. title: String,
  15. author: String,
  16. content: String
  17. },
  18. setup(props, context){
  19. const { title, author, content } = props;
  20. const myContent = computed(() => "Content:" + content);
  21. // setup() 中的数据必须 return 出去,模版才能进行使用
  22. return {
  23. myContent
  24. }
  25. }
  26. }
  27. </script>

屏幕录制2023-06-02 15.32.39.gif
可以看到,模版中的content发生了变化,而解构后的myContent却没有变。

如果你非要进行解构props对象中的属性,可以使用toRefs方法进行解构:

  1. <script>
  2. import { computed, toRefs } from "vue"
  3. export default{
  4. pprops: {
  5. title: String,
  6. author: String,
  7. content: String
  8. },
  9. setup(props, context){
  10. const { title, author, content } = toRefs(props);
  11. console.log(title, author, content);
  12. // 因为 content 被包装为 ref 对象,所以需要使用 .value 来获取值
  13. const myContent = computed(() => "Content:" + content.value);
  14. return {
  15. myContent
  16. };
  17. }
  18. }
  19. </script>

image.png
这样得到的属性就全部都是响应式的了。

同样的,如果你只想把一个属性进行解构出来可以使用toRef()方法来这么操作:

  1. <script>
  2. import { computed, toRefs, toRef } from "vue"
  3. export default{
  4. pprops: {
  5. title: String,
  6. author: String,
  7. content: String
  8. },
  9. setup(props, context){
  10. const content = toRef(props, "content");
  11. const myContent = computed(() => "Content:" + content.value);
  12. return {
  13. myContent
  14. };
  15. }
  16. }
  17. </script>

context

context对象里面包含了attrsemitslotsexpose这 4 个对象。
其中,attrs对应着选项 API 中的this.$attrsslots对应着选项 API 中的this.$slotsemit对应着选项 API 中的this.$emit。而expose是 Vue3 新增的一个方法,这个本篇的后面再说。

  1. <script>
  2. export default{
  3. pprops: {
  4. title: String,
  5. author: String,
  6. content: String
  7. },
  8. setup(props, { attrs, slots, emit, expose }) {
  9. console.log(attrs);
  10. console.log(slots);
  11. console.log(emit);
  12. console.log(expose);
  13. }
  14. }
  15. </script>

image.png
其中attrsslots是对象,emitexpose是方法。
attrsslots都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过attrs.xslots.x的形式使用其中的属性。
此外还需注意,和props不同,attrsslots的属性都不是响应式的。如果你想要基于attrsslots的改变来执行副作用,那么你应该在onBeforeUpdate生命周期钩子中编写相关逻辑。

expose

expose()是干什么的?
expose()函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问expose函数暴露出的内容:

  1. export default {
  2. setup(props, { expose }) {
  3. // 让组件实例处于 “关闭状态”
  4. // 即不向父组件暴露任何东西
  5. expose()
  6. const publicCount = ref(0)
  7. const privateCount = ref(0)
  8. // 有选择地暴露局部状态
  9. expose({ count: publicCount })
  10. }
  11. }

setup()函数也可以返回一个渲染函数,此时在渲染函数中可以直接使用当前作用域下的响应式状态,但是当setup()函数返回一个渲染函数将会阻止我们返回其他东西。这对于组件内部来说,这样没有问题,但如果我们想通过模板引用将这个组件的方法暴露给父组件,那就有问题了。

  1. <!-- 父组件通过插槽的形式调用 -->
  2. <template>
  3. <VSetup ref="VSetupRef">
  4. <template #default>This is title.</template>
  5. <template #author>Xiechen</template>
  6. <template #content>{{ content }}</template>
  7. </VSetup>
  8. </template>
  9. <script>
  10. import { ref, onMounted } from "vue";
  11. import VSetup from "./components/VSetup.vue";
  12. export default {
  13. components: {
  14. VSetup
  15. },
  16. data() {
  17. return {
  18. content: "This is content."
  19. };
  20. },
  21. setup() {
  22. const VSetupRef = ref(null);
  23. onMounted(() => {
  24. console.log(VSetupRef.value);
  25. });
  26. return {
  27. VSetupRef
  28. };
  29. }
  30. };
  31. </script>

image.png

  1. <script>
  2. import { h } from "vue";
  3. export default {
  4. setup(props, context) {
  5. const testStr = "This is test str."
  6. return () =>
  7. h("div", null, [
  8. h("h1", null, context.slots.default()),
  9. h("p", null, context.slots.author()),
  10. h("p", null, context.slots.content()),
  11. h("p", null, testStr)
  12. ]);
  13. }
  14. }
  15. </script>

image.png

就目前的结构来说,父组件是拿不到子组件中的testStr属性的。
我们可以通过调用expose()解决这个问题:

  1. <script>
  2. import { h } from "vue";
  3. export default {
  4. setup(props, context) {
  5. const testStr = "This is test str."
  6. context.expose({
  7. testStr
  8. })
  9. return () =>
  10. h("div", null, [
  11. h("h1", null, context.slots.default()),
  12. h("p", null, context.slots.author()),
  13. h("p", null, context.slots.content()),
  14. h("p", null, testStr)
  15. ]);
  16. }
  17. }
  18. </script>

然后父组件就可以拿到这个属性了:
image.png

getCurrentInstance()

setup()函数中,我们可以通过导入getCurrentInstance()方法来获取当前组件的实例对象:

  1. <script>
  2. import { getCurrentInstance } from "vue";
  3. export default {
  4. setup(props, context) {
  5. console.log(getCurrentInstance());
  6. }
  7. }
  8. </script>

image.png :::warning ⚠️ 注意
但是 Vue3 不建议在应用代码中进行使用,也不建议使用getCurrentInstance()来代替this对象!!!
另外,getCurrentInstance()函数只能在setup()内部执行。 :::