Props

setup 函数中的第一个参数是 props。正如在一个标准组件中所期望的那样,setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

  1. // MyBook.vue
  2. export default {
  3. props: {
  4. title: String
  5. },
  6. setup(props) {
  7. console.log(props.title)
  8. }
  9. }

但是,因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。
如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作:

toRefs

  1. // MyBook.vue
  2. import { toRefs } from 'vue'
  3. setup(props) {
  4. const { title } = toRefs(props)
  5. console.log(title.value)
  6. }

如果 title 是可选的 prop,则传入的 props 中可能没有 title 。在这种情况下,toRefs 将不会为 title 创建一个 ref 。你需要使用 toRef 替代它:

toRef

  1. // MyBook.vue
  2. import { toRef } from 'vue'
  3. setup(props) {
  4. const title = toRef(props, 'title')
  5. console.log(title.value)
  6. }

Context

传递给 setup 函数的第二个参数是 context。context 是一个普通 JavaScript 对象,暴露了其它可能在 setup 中有用的值:

  1. // MyBook.vue
  2. export default {
  3. setup(props, context) {
  4. // Attribute (非响应式对象,等同于 $attrs)
  5. console.log(context.attrs)
  6. // 插槽 (非响应式对象,等同于 $slots)
  7. console.log(context.slots)
  8. // 触发事件 (方法,等同于 $emit)
  9. console.log(context.emit)
  10. // 暴露公共 property (函数)
  11. console.log(context.expose)
  12. }
  13. }


context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构。

  1. // MyBook.vue
  2. export default {
  3. setup(props, { attrs, slots, emit, expose }) {
  4. ...
  5. }
  6. }

attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 的 property 是非响应式的。如果你打算根据 attrs 或 slots 的更改应用副作用,那么应该在 onBeforeUpdate 生命周期钩子中执行此操作。
我们将在稍后解释 expose 所扮演的角色。

ref & reactive

  1. <!-- MyBook.vue -->
  2. <template>
  3. <div>{{ collectionName }}: {{ readersNumber }} {{ book.title }}</div>
  4. </template>
  5. <script>
  6. import { ref, reactive } from 'vue'
  7. export default {
  8. props: {
  9. collectionName: String
  10. },
  11. setup(props) {
  12. const readersNumber = ref(0)
  13. const book = reactive({ title: 'Vue 3 Guide' })
  14. // 暴露给 template
  15. return {
  16. readersNumber,
  17. book
  18. }
  19. }
  20. }
  21. </script>

模块化封装

image.png

watch&watchEffect

image.png

watchEffect

与 watch 有什么不同?

  • 第一点我们可以从示例代码中看到 watchEffect 不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而 watch 只能监听指定的属性而做出变更(v3开始可以同时指定多个)。
  • 第二点就是 watch 可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的。
  • 第三点是 watchEffect 如果存在的话,在组件初始化的时候就会执行一次用以收集依赖(与computed同理),而后收集到的依赖发生变化,这个回调才会再次执行,而 watch 不需要,因为他一开始就指定了依赖。

从他们的不同点可以看出,他们的优缺点。并且可以在业务需求面前做出正确的选择。

生命周期

image.png

Provide&Inject

遵循单向数据流,父组件提供修改方法
修改响应式 property
当使用响应式 provide / inject 值时,建议尽可能将对响应式 property 的所有修改限制在定义 provide 的组件内部。
image.png
image.png
image.png

模板引用

在使用组合式 API 时,响应式引用模板引用的概念是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回:

  1. <template>
  2. <div ref="root">This is a root element</div>
  3. </template>
  4. <script>
  5. import { ref, onMounted } from 'vue'
  6. export default {
  7. setup() {
  8. const root = ref(null)
  9. onMounted(() => {
  10. // DOM 元素将在初始渲染后分配给 ref
  11. console.log(root.value) // <div>This is a root element</div>
  12. })
  13. return {
  14. root
  15. }
  16. }
  17. }
  18. </script>