Composition API 怎么编写

下面 先从setup Fuction 开始编写Compositon API.

 

1⃣️ setup

 

新的 setup 组件选项 在 创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点。

我们从 setup 返回的所有内容 都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。

 

⚠️ setup执行时间:The setup executes before any of the following options are evaluated:( Components、 Props 、Data、Methods、Computed Properties、Lifecycle methods)

⚠️ 由于在执行 setup 时,尚未创建组件实例,因此在 setup 选项中没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。与其他Component选项不同,setup该方法无权访问“ this”。为了获得对属性的访问权限(之前用this获取的),setup具有两个可选参数。

  

a、参数

第一个是props,例如:

// src/components/UserRepositories.vue

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

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

 ⚠️    setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

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

        如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成此操作。

// MyBook.vue

import { toRefs } from 'vue'

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

    console.log(title.value)
}

 

 

第二个参数是context,它可以访问一堆有用的数据:

// MyBook.vue

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

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

    // 触发事件 (方法)
    console.log(context.emit)
    context.parent;
    context.root;
  } 
}
context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着可以安全地对 context 使用 ES6 解构。

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

 

b、访问组件的property

 

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

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

 

c、结合模板使用

如果 setup 返回一个对象,则可以在组件的模板中  像传递给 setup 的 props property 一样  访问该对象的 property:

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

<template>
  <div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'
  export default {
    setup() {
      const readersNumber = ref(0)
      const book = reactive({ title: 'Vue 3 Guide' })

      // expose to template
      return {
        readersNumber,
        book
      }
    }
  }
</script>

 

d、使用渲染函数

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

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

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })
    // Please note that we need to explicitly expose ref value here
    return () => h('div', [readersNumber.value, book.title])
  }
}

 

e、使用this

在 setup() 内部,this 不会是该活跃实例的引用

因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。

这在和其它选项式 API 一起使用 setup() 时可能会导致混淆。

 

2⃣️、ref函数  & 模版引用


a、ref函数
<template>
  <div>Capacity: {{ capacity }}</div>
</template>
<script>
import { ref } from "vue";
export default {
  setup() {
    const capacity = ref(3); // 基本上,它将原始整数(3)包装在一个对象中,这将使我们能够跟踪更改。以前data()选项 已经将我们的原始(容量)包装在一个对象内。
    return { capacity }; //最后一步,我们需要显式返回一个对象,该对象的属性需要模板正确呈现。这个返回的对象是我们如何在中公开需要访问哪些数据的方法renderContext。像这样明确表示有点冗长,但这也是故意的。它有助于长期维护,因为我们可以控制暴露给模板的内容,并跟踪定义模板属性的位置。 
  } 
}; 

</script>

 

b、模版引用

 

作为模板使用的 ref 的行为与任何其他 ref 一样:它们是 响应式的,可以传递到 (或从中返回) 复合函数中。

 

为了获得 对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回。

<template> 
  <div ref="root">This is a root element</div>
</template>

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

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // DOM元素将在初始渲染后分配给ref
        console.log(root.value) // <div>这是根元素</div>
      })

      return {
        root
      }
    }
  }
</script>

 

 

  JSX 中的用法 、v-for 中的用法

  具体地址:https://vue3js.cn/docs/zh/guide/composition-api-template-refs.html

 

3⃣️、provide/inject

通常父传递给子组件,可用 props。 但是当有深层结构的传递,props就满足不了要求,可以让父组件provide, 子(孙子)组件inject.

可以看成“ long range props”。

位置:两者都只能在当前活动实例的 setup() 期间调用。

 

vue2中👇

app.component('todo-list', {
  data() {
    return {
      todos: ['Feed a cat', 'Buy tickets']
    }
  },
  provide() {

//情况1
return { todoLength: this.todos.length //⚠️ 要访问 组件实例 property,我们需要将 provide 转换为 返回对象的函数。 }

//情况2
    return {
      todoLength: Vue.computed(() => this.todos.length) //如果我们想 对祖先组件中的更改 做出反应。
    }
  },
  template: `
    ...
  `
})

vue3中👇

<!-- src/components/MyMap.vue -->
<template>
  <MyMarker />
</template>

<script>
import { provide, reactive, readonly, ref } from 'vue'
import MyMarker from './MyMarker.vue

export default {
  components: {
    MyMarker
  },
  setup() {
    const location = ref('North Pole') //为了使 依赖值和注入值 产生 响应性,在提供值时要使用 reactive,ref 函数.
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })

    const updateLocation = () => {
      location.value = 'South Pole' //有时我们需要在 注入数据的组件内部更新注入的数据。在这种情况下,我们建议 提供一个方法来负责改变响应式 property
    }

    provide('location', readonly(location)) // provide俩参数,第一个是name(必填),第二个是默认值(可选)。
    provide('geolocation', readonly(geolocation)) // 确保通过provide 传递的数据不会被 注入的组件更改,
    provide('updateLocation', updateLocation)
  }
}
</script>

 

//使用注入
<script>
import { inject } from 'vue'
export default {
  setup() {
     const userLocation = inject('location', 'The Universe') //显式导入,inject函数俩参数:要注入的property的名称(必填);一个默认的值(可选)
     const userGeolocation = inject('geolocation') 
const updateUserLocation = inject('updateLocation')
     return {
userLocation, userGeolocation,
updateUserLocation
      } 
}
}
</script>


文档地址:https://vue3js.cn/docs/zh/guide/component-provide-inject.html

 

3⃣️、setup中的钩子

组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 看起来像 onMounted

 

4⃣️、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,watch 将触发并执行回调 (第二个参数),在本例中,它将把 'The new counter value is:5' 记录到我们的控制台中

 

5⃣️、toRefs

 

 toRefs 是 为了确保 侦听器能够对 user prop 所做的更改 做出反应。

 

 

  toRefs 是 为了确保 侦听器能够对 user prop 所做的更改 做出反应。

 

6⃣️、独立的 computed 属性

与 ref 和 watch 类似,也可以使用 从 Vue 导入的 computed 函数,在 Vue 组件外部创建计算属性。

computed 函数返回一个作为 computed 的第一个参数传递的 getter 类回调的输出的一个只读的响应式引用。

为了访问新创建的计算变量的 value,我们需要像使用 ref 一样使用 .value property。

 

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

 

7⃣️、组合式函数

(现在只有两个功能,获取用户数据➕搜索功能。对于其他的逻辑关注点我们也可以这样做,setup随着功能的增多,会越来越大👇)

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

// in our component
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) // 在用户 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 } }

所以要先将一个功能逻辑的代码提取到一个独立的组合式函数(单独的文件),然后再调用👇。

// 例 :🌰 创建一个独立的组合式函数->  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/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 } }, 

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

 

总结: 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章