• vue3


    原文地址 v3.cn.vuejs.org

    # 什么是组合式 API?

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

    # 组合式 API 基础

    # setup 组件选项

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

    WARNING

    由于在执行 setup 时,组件实例尚未被创建,因此在 setup 选项中没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态计算属性方法

    setup 选项应该是一个接受 propscontext 的函数,我们将在稍后讨论。

    export default {
      components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
      props: {
        user: {
          type: String,
          required: true
        }
      },
      setup(props) {
        console.log(props) 
    
        return {}  // 这里返回的任何内容都可以用于组件的其余部分
      }
     
    }
    

    #ref 的响应式变量

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

    import { ref } from 'vue'
    
    const counter = ref(0)
    
    
    

    1
    2
    3

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

    import { ref } from 'vue'
    
    const counter = ref(0)
    
    console.log(counter) 
    console.log(counter.value) 
    
    counter.value++
    console.log(counter.value) 
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9

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

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

    提示

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

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

    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
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

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

    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: { ... }, 
          searchQuery: '' 
        }
      },
      computed: {
        filteredRepositories () { ... }, 
        repositoriesMatchingSearchQuery () { ... }, 
      },
      watch: {
        user: 'getUserRepositories' 
      },
      methods: {
        updateFilters () { ... }, 
      },
      mounted () {
        this.getUserRepositories() 
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43

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

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

    # 生命周期钩子注册内部 setup

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

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

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

    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) 
    
      return {
        repositories,
        getUserRepositories
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    现在我们需要对 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)
    })
    
    
    

    1
    2
    3
    4
    5
    6

    每当 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)
        }
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

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

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

    import { fetchUserRepositories } from '@/api/repositories'
    import { ref, onMounted, watch, toRefs } from 'vue'
    
    
    setup (props) {
      
      const { user } = toRefs(props)
    
      const repositories = ref([])
      const getUserRepositories = async () => {
        
        repositories.value = await fetchUserRepositories(user.value)
      }
    
      onMounted(getUserRepositories)
    
      
      watch(user, getUserRepositories)
    
      return {
        repositories,
        getUserRepositories
      }
    }
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

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

    注释:可能是因为 props.user 是字符串,不是引用类型所以结构后就不再响应。如果被结构的目标是引用类型应该会保持响应?

    有了这些变化,我们就把第一个逻辑关注点移到了一个地方。我们现在可以对第二个关注点执行相同的操作——基于 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) 
    console.log(twiceTheCounter.value) 
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8

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

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

    import { fetchUserRepositories } from '@/api/repositories'
    import { ref, onMounted, watch, toRefs, computed } from 'vue'
    
    
    setup (props) {
      
      const { user } = toRefs(props)
    
      const repositories = ref([])
      const getUserRepositories = async () => {
        
        repositories.value = await fetchUserRepositories(user.value)
      }
    
      onMounted(getUserRepositories)
    
      
      watch(user, getUserRepositories)
    
      const searchQuery = ref('')
      const repositoriesMatchingSearchQuery = computed(() => {
        return repositories.value.filter(
          repository => repository.name.includes(searchQuery.value)
        )
      })
    
      return {
        repositories,
        getUserRepositories,
        searchQuery,
        repositoriesMatchingSearchQuery
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

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

    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
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    然后是搜索功能:

    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
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

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

    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: repositoriesMatchingSearchQuery,
          getUserRepositories,
          searchQuery,
        }
      },
      data () {
        return {
          filters: { ... }, 
        }
      },
      computed: {
        filteredRepositories () { ... }, 
      },
      methods: {
        updateFilters () { ... }, 
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43

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

    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: filteredRepositories,
          getUserRepositories,
          searchQuery,
          filters,
          updateFilters
        }
      }
    }
    
    
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

    我们完成了!

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

  • 相关阅读:
    配置Nginx和Apache允许指定域名CORS跨域访问
    当遇到npm ERR! Unexpected end of JSON input while parsing near……时的解决办法
    基于thinkphp开发的项目部署到由宝塔面板创建的LNMP服务器上解决路径出错问题
    在ThinkPHP框架(5.0.24)下引入Ueditor并实现向七牛云对象存储上传图片同时将图片信息保存到MySQL数据库,同时实现lazyload懒加载
    为Sublime Text 3设置优雅的字体
    windows7x64系统中配置mysql5.7.17为本地开发环境(win2008类似)
    SQL Server实现数据的递归查询
    在.net程序中使用System.Net.Mail来发送邮件
    在 Ubuntu 13.10 中搭建Java开发环境
    Windows 系统下载安装 ZooKeeper
  • 原文地址:https://www.cnblogs.com/qq3279338858/p/14500642.html
Copyright © 2020-2023  润新知