前言
之前在学习 React Hooks 的进程中,看到一篇外网文章,经过 Hooks 来央求数据,并将这段逻辑笼统成一个新的 Hooks 给其他组件复用,我也在我的博客里翻译了一下:《在 React Hooks 中如何央求数据?》,感兴味可以看看。虽然是去年的文章,在阅读之后一下子就掌握了 Hooks 的运用方式,而且数据央求是在业务代码中很常用的逻辑。
Vue 3 曾经发布一段时间了,其组合 API 多少有点 React Hooks 的影子在外面,明天我也计划经过这种方式来学习下组合 API。
项目初始化
为了快速启动一个 Vue 3 项目,我们直接运用当下最抢手的工具 Vite 来初始化项目。整个进程一气呵成,行云流水。
npm init vite-app vue3-app
# 翻开生成的项目文件夹
cd vue3-app
# 安装依赖
npm install
# 启动项目
npm run dev
我们翻开 App.vue 将生成的代码先删掉。
组合 API 的入口
接上去我们将经过 Hacker News API 来获取一些抢手文章,Hacker News API前往的数据结构如下:
{
"hits": [
{
"objectID": "24518295",
"title": "Vue.js 3",
"url": "https://github.com/vuejs/vue-next/releases/tag/v3.0.0",
},
{...},
{...},
]
}
我们经过 ui > li 将旧事列表展现到界面上,旧事数据从 hits 遍历中获取。
<template>
<ul>
<li
v-for="item of hits"
:key="item.objectID"
>
<a :href="item.url">{{item.title}}</a>
</li>
</ul>
</template>
<script>
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({
hits: []
})
return state
}
}
</script>
在解说数据央求前,我看先看看 setup() 办法,组合 API 需求经过 setup() 办法来启动,setup() 前往的数据可以在模板内运用,可以复杂了解为 Vue 2 外面 data() 办法前往的数据,不同的是,前往的数据需求先经过 reactive() 办法停止包裹,将数据变成照应式。
组合 API 中央求数据
在 Vue 2 中,我们央求数据时,通常需求将发起央求的代码放到某个生命周期中(created或 mounted)。在 setup() 办法内,我们可以运用 Vue 3 提供的生命周期钩子将央求放到特定生命周期内,关于生命周期钩子办法与之前生命周期的比照如下:
生命周期
可以看到,基本上就是在之前的办法名前加上了一个 on,且并没有提供 onCreated 的钩子,由于在 setup() 内执行就相当于在 created 阶段执行。下面我们在 mounted 阶段来央求数据:
import { reactive, onMounted } from 'vue'
export default {
setup() {
const state = reactive({
hits: []
})
onMounted(async () => {
const data = await fetch(
'https://hn.algolia.com/api/v1/search?query=vue'
).then(rsp => rsp.json())
state.hits = data.hits
})
return state
}
}
最后效果如下:
Demo
监听数据变动
Hacker News 的查询接口有一个 query 参数,前面的案例中,我们将这个参数固定了,如今我们经过照应式的数据来定义这个变量。
<template>
<input type="text" v-model="query" />
<ul>
<li
v-for="item of hits"
:key="item.objectID"
>
<a :href="item.url">{{item.title}}</a>
</li>
</ul>
</template>
<script>
import { reactive, onMounted } from 'vue'
export default {
setup() {
const state = reactive({
query: 'vue',
hits: []
})
onMounted((async () => {
const data = await fetch(
`https://hn.algolia.com/api/v1/search?query=${state.query}`
).then(rsp => rsp.json())
state.hits = data.hits
})
return state
}
}
</script>
如今我们在输入框修正,就能触发 state.query 同步更新,但是并不会触发 fetch 重新调用,所以我们需求经过 watchEffect() 来监听照应数据的变化。
import { reactive, onMounted, watchEffect } from 'vue'
export default {
setup() {
const state = reactive({
query: 'vue',
hits: []
})
const fetchData = async (query) => {
const data = await fetch(
`https://hn.algolia.com/api/v1/search?query=${query}`
).then(rsp => rsp.json())
state.hits = data.hits
}
onMounted(() => {
fetchData(state.query)
watchEffect(() => {
fetchData(state.query)
})
})
return state
}
}
由于 watchEffect() 初次调用的时分,其回调就会执行一次,形成初始化时会央求两次接口,所以我们需求把 onMounted 中的 fetchData 删掉。
onMounted(() => {
- fetchData(state.query)
watchEffect(() => {
fetchData(state.query)
})
})
Demo
watchEffect() 会监听传入函数内一切的照应式数据,一旦其中的某个数据发作变化,函数就会重新执行。假设要取消监听,可以调用 watchEffect() 的前往值,它的前往值为一个函数。下面举个例子:
const stop = watchEffect(() => {
if (state.query === 'vue3') {
// 当 query 为 vue3 时,中止监听
stop()
}
fetchData(state.query)
})
当我们在输入框输入 "vue3" 后,就不会再发起央求了。
Demo
前往事情办法
如今有个成绩就是 input 内的值每次修正都会触发一次央求,我们可以添加一个按钮,点击按钮后再触发 state.query 的更新。
<template>
<input type="text" v-model="input" />
<button @click="setQuery">搜索</button>
<ul>
<li
v-for="item of hits"
:key="item.objectID"
>
<a :href="item.url">{{item.title}}</a>
</li>
</ul>
</template>
<script>
import { reactive, onMounted, watchEffect } from 'vue'
export default {
setup() {
const state = reactive({
input: 'vue',
query: 'vue',
hits: []
})
const fetchData = async (query) => {
const data = await fetch(
`https://hn.algolia.com/api/v1/search?query=${query}`
).then(rsp => rsp.json())
state.hits = data.hits
}
onMounted(() => {
watchEffect(() => {
fetchData(state.query)
})
})
const setQuery = () => {
state.query = state.input
}
return { setQuery, state }
}
}
</script>
可以留意到 button 绑定的 click 事情的办法,也是经过 setup() 办法前往的,我们可以将 setup() 办法前往值了解为 Vue2 中 data() 办法和 methods 对象的兼并。
原先的前往值 state 变成了如今前往值的一个属性,所以我们在模板层取数据的时分,需求停止一些修正,在前面加上 state.。
<template>
<input type="text" v-model="state.input" />
<button @click="setQuery">搜索</button>
<ul>
<li
v-for="item of state.hits"
:key="item.objectID"
>
<a :href="item.url">{{item.title}}</a>
</li>
</ul>
</template>
Demo
前往数据
修正作为强迫症患者,在模板层经过 state.xxx 的方式获取数据真实是舒服,那我们是不是可以经过对象解构的方式将 state 的数据前往呢?
<template>
<input type="text" v-model="input" />
<button class="search-btn" @click="setQuery">搜索</button>
<ul class="results">
<li
v-for="item of hits"
:key="item.objectID"
>
<a :href="item.url">{{item.title}}</a>
</li>
</ul>
</template>
<script>
import { reactive, onMounted, watchEffect } from 'vue'
export default {
setup(props, ctx) {
const state = reactive({
input: 'vue',
query: 'vue',
hits: []
})
// 省略部分代码...
return {
...state,
setQuery,
}
}
}
</script>
答案是『不可以』。修正代码后,可以看到页面虽然发起了央求,但是页面并没有展现数据。
state 在解构后,数据就变成了静态数据,不能再被跟踪,前往值相似于:
export default {
setup(props, ctx) {
// 省略部分代码...
return {
input: 'vue',
query: 'vue',
hits: [],
setQuery,
}
}
}
Demo
为了跟踪基础类型的数据(即非对象数据),Vue3 也提出了处置方案:ref() 。
import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
下面为 Vue 3 的官方案例,ref() 办法前往的是一个对象,无论是修正还是获取,都需求取前往对象的 value 属性。
(责任编辑:admin)