当前位置:首页 » 《资源分享》 » 正文

Vue3官网-可复用&组合式API(一)组件式API介绍(==官网API== ref、toRef、toRefs)、Setup(es6解构、props、context、attrs slots emit)_ChrisP3616的博客

26 人参与  2022年01月08日 13:25  分类 : 《资源分享》  评论

点击全文阅读


Vue3官网-组合式API(一)组件式API介绍(官网API ref、toRef、toRefs)、Setup(es6解构、props、context、attrs slots emit)

文章目录

  • Vue3官网-组合式API(一)组件式API介绍(==官网API== ref、toRef、toRefs)、Setup(es6解构、props、context、attrs slots emit)
    • 1. 组合式API介绍
      • 什么是组合式 API?
      • 组合式 API 基础
        • `setup` 组件选项
        • 带 `ref` 的响应式变量
        • 在 `setup` 内注册生命周期钩子
        • `watch` 响应式更改
        • 独立的 `computed` 属性
    • 2. Setup
      • 参数
        • Props
        • Context
      • 访问组件的 property
      • 结合模板使用
      • 使用渲染函数
      • 使用 `this`

总结:

  • 补充

    • Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

    • truthy(真值):在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除 false0""nullundefinedNaN 以外皆为真值)。括号内都是假值falsy。

    • .prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

    • ==Event.preventDefault方法取消浏览器对当前事件的默认行为。==比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。

    • vue:用组件(app.component)构建一个模板(template),并反复使用模板

    • 父组件、子组件

      • const app = Vue.createApp({
          components: {
            'component-a': ComponentA,
            'component-b': ComponentB
          }
        })
        
      • 上面代码中app为父组件,ComponentA和ComponentB为子组件

      • context.emit

        • 父组件通过 :data="data" 传递数据
        • 父组件通过 @fun="fun" 传递方法
        • 子组件通过 props 接收父组件传递的值
        • 子组件通过 $emit 调用父组件的方法并传递数据
    • Vue中美元$符号的意思

      • 除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。
      • vue中$refs的作用?
        • 当我们需要获取一个dom元素进行操作的时候 可以利用原生js的getElementByXxx 而在vue中可以设置refs来获取这个dom元素
    • 第三方网站

      • greensock API(GSAP):是一套用于所有主流浏览器中制作高性能html5动画的工具。他们有一个很棒的 ease visualizer,帮助你建立精心制作的画架。
        • GSAP的全名是==GreenSock Animation Platform,这个名字是有些怪异(官网还在各种安利你加入它的Club),但它的确是一个从flash时代一直发展到今天的专业动画库。==参照CSDN:https://blog.csdn.net/weixin_39939012/article/details/80833073
      • animate.css :集成第三方 CSS 动画库
      • CSS Triggers :来查看哪些属性会在动画时触发重绘。这个网站是用来告诉开发者不同内核浏览器对css属性修改的重绘/回流情况,开发者知道了这些细节可以提高页面性能。
    • JavaScript钩子

      • 添加 :css="false",也会让 Vue 会跳过 CSS 的检测,除了性能略高之外,这可以避免过渡过程中 CSS 规则的影响。
    • 解构(Destructuring)

      • ES6允许按照一定模式从数组和对象中提取值,然后对变量进行复制,这被称为解构(Destructuring)
    • API参考

      • https://v3.cn.vuejs.org/api/refs-api.html#ref
  • 组件式API介绍

    • setup组件选项

      • setup 选项是一个接收 propscontext 的函数

        // 在我们的组件内
        setup (props) {
          let repositories = []
          const getUserRepositories = async () => {
            repositories = await fetchUserRepositories(props.user)
          }
        
      • 此外,我们将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。

    • ref 的响应式变量

      • 通过一个新的 ref 函数使任何响应式变量在任何地方起作用

      • ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值

        import { ref } from 'vue'
        
        const counter = ref(0)
        
        console.log(counter) // { value: 0 }
        console.log(counter.value) // 0
        
        counter.value++
        console.log(counter.value) // 1
        
      • 换句话说,ref 为我们的值创建了一个==响应式引用==(而不是值传递,正因为是引用才可以做到响应式?)。在整个组合式 API 中会经常使用引用的概念。

      • 将第一个逻辑关注点中(原始代码中数字1的部分)的几个部分移到了 setup 方法中

          setup (props) {
            const repositories = ref([])
            const getUserRepositories = async () => {
              repositories.value = await fetchUserRepositories(props.user)
            }
        
            return {
              repositories,
              getUserRepositories
            }
        
    • setup 内注册生命周期钩子

      • 生命周期包括不同阶段
        • 初始化 (create)— 组件挂载(mount)-----组件更新 (update)— 销毁(destroy)
      • 组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 看起来会像 onMounted
    • watch 响应式更改

      • 包含三个参数
        • 一个想要侦听的响应式引用或 getter 函数
        • 一个回调
        • 可选的配置选项
    • toRefs

      • 在我们的 setup 的顶部使用了 toRefs。这是为了确保我们的侦听器能够根据 user prop 的变化做出反应。

      • // 在我们组件中
        setup (props) {
          // 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
          const { user } = toRefs(props)
        
          const repositories = ref([])
          const getUserRepositories = async () => {
            // 更新 `prop.user` 到 `user.value` 访问引用值
            repositories.value = await fetchUserRepositories(user.value)
          }
           onMounted(getUserRepositories)
        
          // 在 user prop 的响应式引用上设置一个侦听器
          watch(user, getUserRepositories)
        
    • 独立的computed属性

      • 将两个独立的代码块提取到组合式函数useUserRepositories中

      • 现在我们有了两个单独的功能模块,接下来就可以开始在组件中使用它们了(这就是组合式API的妙用——简洁、可复用):

        Setup// src/components/UserRepositories.vue
        import useUserRepositories from '@/composables/useUserRepositories'
        import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
        import { toRefs } from 'vue'
        -----------------------------------------------
        const {
              searchQuery,
              repositoriesMatchingSearchQuery
            } = useRepositoryNameSearch(repositories)
        
  • Setup

  • 参数

    • props
    • context
  • props

    • 解构 prop

      • 属性唯一无需选择时,可以在 setup 函数中使用 toRefs 函数来完成此操作:

        import { toRefs } from 'vue'
        
        setup(props) {
          const { title } = toRefs(props)
        
          console.log(title.value)
        }
        
      • 属性不唯一可以选择时,toRefs 将不会为 title 创建一个 ref 。你需要使用 toRef 替代它

        import { toRef } from 'vue'
        setup(props) {
          const title = toRef(props, 'title')
          console.log(title.value)
        }
        
  • context

    • context 是一个普通的 JavaScript 对象,它暴露组件的三个 property
      • context.attrs
        • https://blog.csdn.net/qq_43456781/article/details/120370322?spm=1001.2014.3001.5501
        • 包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件——在创建更高层次的组件时非常有用。
        • 实现跨级传输
      • context.slots
        • 插槽(Slot)是Vue提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。
        • 模板分块,不完全按照模板来
      • context.emit
        • 父组件通过 :data="data" 传递数据
        • 父组件通过 @fun="fun" 传递方法
        • 子组件通过 props 接收父组件传递的值
        • 子组件通过 $emit 调用父组件的方法并传递数据

1. 组合式API介绍

什么是组合式 API?

提示

在阅读文档之前,你应该已经熟悉了这两个 Vue 基础和创建组件。

通过创建 Vue 组件,我们可以将界面中重复的部分连同其功能一起提取为可重用的代码段。仅此一项就可以使我们的应用在可维护性和灵活性方面走得相当远。然而,我们的经验已经证明,光靠这一点可能并不够,尤其是当你的应用变得非常大的时候——想想几百个组件。处理这样的大型应用时,共享和重用代码变得尤为重要。

假设我们的应用中有一个显示某个用户的仓库列表的视图。此外,我们还希望有搜索和筛选功能。实现此视图组件的代码可能如下所示:

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { 
      type: String,
      required: true //当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
    }
  },
  data () {
    return { 
      repositories: [], // 1 从假定的外部 API 获取该用户的仓库,并在用户有任何更改时进行刷新
      filters: { ... }, // 3
      searchQuery: '' // 2
    }
  },
  computed: {
    filteredRepositories () { ... }, // 3
    repositoriesMatchingSearchQuery () { ... }, // 2
  },
  watch: {
    user: 'getUserRepositories' // 1 从假定的外部 API 获取该用户的仓库,并在用户有任何更改时进行刷新
  },
  methods: {
    getUserRepositories () {
      // 使用 `this.user` 获取用户仓库
    }, // 1
    updateFilters () { ... }, // 3
  },
  mounted () {
    this.getUserRepositories() // 1 从假定的外部 API 获取该用户的仓库,并在用户有任何更改时进行刷新
  }
}

该组件有以下几个职责:

  1. 从假定的外部 API 获取该用户的仓库,并在用户有任何更改时进行刷新
  2. 使用 searchQuery 字符串搜索仓库
  3. 使用 filters 对象筛选仓库

使用 (datacomputedmethodswatch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。

Vue 选项式 API: 按选项类型分组的代码

这是一个大型组件的示例,其中逻辑关注点按颜色进行分组。

这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。

组合式 API 基础

既然我们知道了为什么,我们就可以知道怎么做。为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup

setup 组件选项

观看 Vue Mastery 上的免费 setup 视频。

新的 setup 选项在组件创建之前执行,一旦 props 被解析,就将作为组合式 API 的入口。

WARNING

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

setup 选项是一个接收 propscontext 的函数,我们将在之后进行讨论。此外,我们将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。

让我们把 setup 添加到组件中:

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(props) {
    console.log(props) // { user: '' }

    return {} // 这里返回的任何内容都可以用于组件的其余部分
  }
  // 组件的“其余部分”
}

现在让我们从提取第一个逻辑关注点开始 (在原始代码段中标记为“1”)。

  1. 从假定的外部 API 获取该用户的仓库,并在用户有任何更改时进行刷新

我们将从最明显的部分开始:

  • 仓库列表
  • 更新仓库列表的函数
  • 返回列表和函数,以便其他组件选项可以对它们进行访问
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'

// 在我们的组件内
setup (props) {
  let repositories = []
  const getUserRepositories = async () => {
    repositories = await fetchUserRepositories(props.user)
  }

  return {
    repositories,
    getUserRepositories // 返回的函数与方法的行为相同
  }
}

这是我们的出发点,但它还无法生效,因为 repositories 变量是非响应式的。这意味着从用户的角度来看,仓库列表将始终为空。让我们来解决这个问题!

ref 的响应式变量

在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:

import { ref } from 'vue'

const counter = ref(0)

ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值:

import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1

将值封装在一个对象中,看似没有必要,但为了保持 JavaScript 中不同数据类型的行为统一,这是必须的。这是因为在 JavaScript 中,NumberString 等基本类型是通过值而非引用传递的

按引用传递与按值传递

在任何值周围都有一个封装对象,这样我们就可以在整个应用中安全地传递它,而不必担心在某个地方失去它的响应性。

提示

换句话说,ref 为我们的值创建了一个响应式引用。在整个组合式 API 中会经常使用引用的概念。

回到我们的例子,让我们创建一个响应式的 repositories 变量:

// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'

// 在我们的组件中
setup (props) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(props.user)
  }

  return {
    repositories,
    getUserRepositories
  }
}

完成!现在,每当我们调用 getUserRepositories 时,repositories 都将发生变化,视图也会更新以反映变化。我们的组件现在应该如下所示:

// src/components/UserRepositories.vue
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup (props) {
    const repositories = ref([])
    const getUserRepositories = async () => {
      repositories.value = await fetchUserRepositories(props.user)
    }

    return {
      repositories,
      getUserRepositories
    }
  },
  data () {
    return {
      filters: { ... }, // 3
      searchQuery: '' // 2
    }
  },
  computed: {
    filteredRepositories () { ... }, // 3
    repositoriesMatchingSearchQuery () { ... }, // 2
  },
  watch: {
    user: 'getUserRepositories' // 1
  },
  methods: {
    updateFilters () { ... }, // 3
  },
  mounted () {
    this.getUserRepositories() // 1
  }
}

我们已经将第一个逻辑关注点(原始代码中数字为1的)中的几个部分移到了 setup 方法中,它们彼此非常接近。剩下的就是在 mounted 钩子中调用 getUserRepositories,并设置一个监听器,以便在 user prop 发生变化时执行此操作。

我们将从生命周期钩子开始。

setup 内注册生命周期钩子

为了使组合式 API 的功能和选项式 API 一样完整,我们还需要一种在 setup 中注册生命周期钩子的方法。这要归功于 Vue 导出的几个新函数。组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 看起来会像 onMounted

这些函数接受一个回调,当钩子被组件调用时,该回调将被执行。

让我们将其添加到 setup 函数中:

// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'

// 在我们的组件中
setup (props) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(props.user)
  }

  onMounted(getUserRepositories) // 在 `mounted` 时调用 `getUserRepositories`

  return {
    repositories,
    getUserRepositories
  }
}

现在我们需要对 user prop 的变化做出反应。为此,我们将使用独立的 watch 函数。

watch 响应式更改

就像我们在组件中使用 watch 选项并在 user property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch 函数执行相同的操作。它接受 3 个参数:

  • 一个想要侦听的响应式引用或 getter 函数
  • 一个回调
  • 可选的配置选项

下面让我们快速了解一下它是如何工作的

import { ref, watch } from 'vue'

const counter = ref(0)
watch(counter, (newValue, oldValue) => {
  console.log('The new counter value is: ' + counter.value)
})

每当 counter 被修改时,例如 counter.value=5,侦听将触发并执行回调 (第二个参数),在本例中,它将把 'The new counter value is:5' 记录到控制台中。

以下是等效的选项式 API:

export default {
  data() {
    return {
      counter: 0
    }
  },
  watch: {
    counter(newValue, oldValue) {
      console.log('The new counter value is: ' + this.counter)
    }
  }
}

有关 watch 的详细信息,请参阅我们的深入指南。

现在我们将其应用到我们的示例中:

// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'

// 在我们组件中
setup (props) {
  // 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
  const { user } = toRefs(props)

  const repositories = ref([])
  const getUserRepositories = async () => {
    // 更新 `prop.user` 到 `user.value` 访问引用值
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)

  // 在 user prop 的响应式引用上设置一个侦听器
  watch(user, getUserRepositories)

  return {
    repositories,
    getUserRepositories
  }
}

你可能已经注意到在我们的 setup 的顶部使用了 toRefs。这是为了确保我们的侦听器能够根据 user prop 的变化做出反应。

有了这些变化,我们就把第一个逻辑关注点移到了一个地方。我们现在可以对第二个关注点(原始代码中数字2代表的东西)执行相同的操作——基于 searchQuery 进行过滤,这次是使用计算属性。

独立的 computed 属性

refwatch 类似,也可以使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性。让我们回到 counter 的例子:

import { ref, computed } from 'vue'

const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)

counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2

这里我们给 computed 函数传递了第一个参数,它是一个类似 getter 的回调函数,输出的是一个只读响应式引用。为了访问新创建的计算变量的 value,我们需要像 ref 一样使用 .value property。

让我们将搜索功能移到 setup 中:

// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'

// 在我们的组件中
setup (props) {
  // 使用 `toRefs` 创建对 props 中的 `user` property 的响应式引用
  const { user } = toRefs(props)

  const repositories = ref([])
  const getUserRepositories = async () => {
    // 更新 `props.user ` 到 `user.value` 访问引用值
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)

  // 在 user prop 的响应式引用上设置一个侦听器
  watch(user, getUserRepositories)

  const searchQuery = ref('')
  const repositoriesMatchingSearchQuery = computed(() => {
    return repositories.value.filter(
      repository => repository.name.includes(searchQuery.value)
    )
  })

  return {
    repositories,
    getUserRepositories,
    searchQuery,
    repositoriesMatchingSearchQuery
  }
}

对于其他的逻辑关注点我们也可以这样做,但是你可能已经在问这个问题了——这不就是把代码移到 setup 选项并使它变得非常大吗?嗯,确实是这样的。这就是为什么我们要在继续其他任务之前,我们首先要将上述代码提取到一个独立的组合式函数中。让我们从创建 useUserRepositories 函数开始:

// src/composables/useUserRepositories.js

import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'

export default function useUserRepositories(user) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)
  watch(user, getUserRepositories)

  return {
    repositories,
    getUserRepositories
  }
}

然后是搜索功能:

// src/composables/useRepositoryNameSearch.js

import { ref, computed } from 'vue'

export default function useRepositoryNameSearch(repositories) {
  const searchQuery = ref('')
  const repositoriesMatchingSearchQuery = computed(() => {
    return repositories.value.filter(repository => {
      return repository.name.includes(searchQuery.value)
    })
  })

  return {
    searchQuery,
    repositoriesMatchingSearchQuery
  }
}

现在我们有了两个单独的功能模块,接下来就可以开始在组件中使用它们了。以下是如何做到这一点:

// src/components/UserRepositories.vue
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import { toRefs } from 'vue'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup (props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)

    return {
      // 因为我们并不关心未经过滤的仓库
      // 我们可以在 `repositories` 名称下暴露过滤后的结果
      repositories: repositoriesMatchingSearchQuery,
      getUserRepositories,
      searchQuery,
    }
  },
  data () {
    return {
      filters: { ... }, // 3
    }
  },
  computed: {
    filteredRepositories () { ... }, // 3
  },
  methods: {
    updateFilters () { ... }, // 3
  }
}

此时,你可能已经知道了其中的奥妙,所以让我们跳到最后,迁移剩余的过滤功能。我们不需要深入了解实现细节,因为这并不是本指南的重点。

// src/components/UserRepositories.vue
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)

    const {
      filters,
      updateFilters,
      filteredRepositories
    } = useRepositoryFilters(repositoriesMatchingSearchQuery)

    return {
      // 因为我们并不关心未经过滤的仓库
      // 我们可以在 `repositories` 名称下暴露过滤后的结果
      repositories: filteredRepositories,
      getUserRepositories,
      searchQuery,
      filters,
      updateFilters
    }
  }
}

我们完成了!

请记住,我们只触及了组合式 API 的表面以及它允许我们做什么。要了解更多信息,请参阅深入指南。

2. Setup

本节使用单文件组件代码示例的语法

本指南假定你已经阅读了组合式 API 简介和响应性原理。如果你不熟悉组合式 API,请先阅读这两篇文章。

参数

使用 setup 函数时,它将接收两个参数:

  1. props
  2. context

让我们更深入地研究如何使用每个参数。

Props

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

// MyBook.vue

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

WARNING

但是,因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。

如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作:

// MyBook.vue

import { toRefs } from 'vue'

setup(props) {
  const { title } = toRefs(props)

  console.log(title.value)
}

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

// MyBook.vue
import { toRef } from 'vue'
setup(props) {
  const title = toRef(props, 'title')
  console.log(title.value)
}

Context

传递给 setup 函数的第二个参数是 contextcontext 是一个普通的 JavaScript 对象,它暴露组件的三个 property:

// MyBook.vue

export default {
  setup(props, context) {
    // Attribute (非响应式对象)
    console.log(context.attrs)

    // 插槽 (非响应式对象)
    console.log(context.slots)

    // 触发事件 (方法)
    console.log(context.emit)
  }
}

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

// MyBook.vue
export default {
  setup(props, { attrs, slots, emit }) {
    ...
  }
}

attrsslots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.xslots.x 的方式引用 property。请注意,与 props 不同,attrsslots响应式的。如果你打算根据 attrsslots 更改应用副作用,那么应该在 onUpdated 生命周期钩子中执行此操作。

访问组件的 property

执行 setup 时,组件实例尚未被创建。因此,你只能访问以下 property:

  • props
  • attrs
  • slots
  • emit

换句话说,你将无法访问以下组件选项:

  • data
  • computed
  • methods

结合模板使用

如果 setup 返回一个对象,那么该对象的 property 以及传递给 setupprops 参数中的 property 就都可以在模板中访问到:

<!-- MyBook.vue -->
<template>
  <div>{{ collectionName }}: {{ readersNumber }} {{ book.title }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'

  export default {
    props: {
      collectionName: String
    },
    setup(props) {
      const readersNumber = ref(0)
      const book = reactive({ title: 'Vue 3 Guide' })

      // 暴露给 template
      return {
        readersNumber,
        book
      }
    }
  }
</script>

注意,从 setup 返回的 refs 在模板中访问时是被自动浅解包的,因此不应在模板中使用 .value

使用渲染函数

setup 还可以返回一个渲染函数,该函数可以直接使用在同一作用域中声明的响应式状态:

// MyBook.vue

import { h, ref, reactive } from 'vue'

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })
    // 请注意这里我们需要显式调用 ref 的 value
    return () => h('div', [readersNumber.value, book.title])
  }
}

使用 this

setup() 内部,this 不是该活跃实例的引用,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这使得 setup() 在和其它选项式 API 一起使用时可能会导致混淆。


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/32915.html

组件  响应  函数  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1