# 1. 首先 step 里面是没有 this

setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。 — coderwhy

vue
<template>
  <div></div>
</template>
<script>
export default {
  setup() {
    console.log(this);
  }
}
</script>
<style scoped></style>

参考结果:

step

# 2.step 有两个参数 (props/context)

# props

props 是用来接受其他父组件传来的数据,而想要在 setup 里面获取到 props 的数据,则需要 setup 第一个参数 props 来获取,切记不可用 this.props.info 来获取切记不可用 this!

vue
// Home.vue
<template>
  <div></div>
</template>
<script>
export default {
  props: {
    info: {
      type: Object,
      required: true,
    },
  },
  // 这里的 props 是形参,可随意命名
  setup(props) {
    console.log(props);
  },
};
</script>
<style scoped>
</style>
// App.vue
<template>
  <div>
    <home info="{name:'Neko',age:16}"></home>
  </div>
</template>
<script>
import Home from "./Home.vue";
export default {
  components: {
    Home,
  },
};
</script>
<style scoped></style>

参考结果:

step

# context

context (也称为 SetupContext) 里面有三个常用属性 (attrs/slotsemit), 意思都是顾名思义

attrs: 所有的非 prop 的 attribute (就是子组件没有使用 props 接收但父组件传递过来的数据)
slots: 父组件传递过来的插槽 (在渲染函数返回时会有作用)
emit: 当我们组件内部需要发出事件是会用到 emit (因为我们不能访问 this, 所以不可通过 this.$emit 发出事件)

vue
// Home.vue
<template>
  <div></div>
</template>
<script>
export default {
  props: {
    info: {
      type: Object,
      required: true,
    },
  },
  // 这里的 props 是形参,可随意命名
  //setup (props, { attrs, emit, slots}) { // 也可写成对象解构
  setup(props, context) {
    // console.log(props);
    console.log(context);
  },
};
</script>
<style scoped>
</style>
// App.vue
<template>
  <div>
    <home
      :info="{ name: 'Neko', age: 16 }"
      id="homeId"
      class="homeClass"
    ></home>
  </div>
</template>
<script>
import Home from "./Home.vue";
export default {
  components: {
    Home,
  },
};
</script>
<style scoped></style>

参考结果:

step

# 3.setup 中返回数据

vue
// Home.vue  
<template>
  <div>
    <h2>{{ name }}</h2>
    <h2>{{ age }}</h2>
  </div>
</template>
<script>
export default {
  props: {
    info: {
      type: Object,
      required: true,
    },
  },
  // 这里的 props 是形参,可随意命名
  //setup (props, { attrs, emit, slots}) { // 也可写成对象解构
  setup(props, context) {
    // console.log(props);
    console.log(context);
    return {
      name: "Neko",
      age: 16,
    };
  },
};
</script>
<style scoped></style>
// App.vue
<template>
  <div>
    <home
      :info="{ name: 'Neko', age: 16 }"
      id="homeId"
      class="homeClass"
    ></home>
  </div>
</template>
<script>
import Home from "./Home.vue";
export default {
  components: {
    Home,
  },
};
</script>
<style scoped></style>

参考结果:

step

# 4.setup 定义方法

vue
// Home.vue 
<template>
  <div>
    <h2>{{ counter }}</h2>
    <button @click="increment">+1</button>
  </div>
</template>
<script>
export default {
  setup() {
    let counter = 0; // 
    const increment = () => { // 点击五次时:请参考结果
      counter++;  increment函数也是个闭包,所以会引用外面的counter
      console.log(counter);
    };
    return {
      counter,
      increment,
    };
  },
};
</script>
<style scoped>
</style>
// App.vue
<template>
  <div>
    <home></home>
  </div>
</template>
<script>
import Home from "./Home.vue";
export default {
  components: {
    Home,
  },
};
</script>
<style scoped>
</style>

参考结果:

step

当我点击 increment 执行了 5 次时:我们会发现数据在页面渲染时没有发生任何变化,但在 control 打印时,确实是已经改变了,是因为默认情况下定义的变量是一个普通的变量,他不是响应式的。这就涉及到响应式 API 了 (reaactive/ref)