作者:炮哥
watchEffect
代码语言:javascript复制接受一个函数,当依赖改变时,重新调用该函数。
const count = ref(0)
watchEffect(() => console.log(count.value))
setTimeout(() => {
count.value
}, 100)
当watchEffect()
在setup()或生命周期钩子中被调用时,监听就始终存在该组件的生命周期中,直到组件unmount
.
另一种卸载监听的情况是,watchEffect()返回一个stop handler
,调用该handler即可停止监听。
const stop = watchEffect(() => {
/* ... */
})
// later
stop()
当向后台获取数据时,watchEffect()接受async
回调函数。
const data = ref(null)
watchEffect(async () => {
data.value = await fetchData(props.id)
})
组件的update
函数也有watch effect
。用户定义的watchEffect会在组件update之后再去调用。
<template>
<div>{{ count }}</div>
</template>
<script>
export default {
setup() {
const count = ref(0)
watchEffect(() => {
console.log(count.value)
})
return {
count
}
}
}
</script>
上述代码,第一轮会同步打印count.value(在onmount
生命周期前); 当count发生改变时,先执行组件更新,然后再去log.
如果想将watchEffect中的回调函数第一次执行,放在onmount后,
代码语言:javascript复制onMounted(() => {
watchEffect(() => {
// access the DOM or template refs
})
})
如果想让watchEffect()调用发生在组件update
前,或re-run同步,需要传递一个带有flush属性(默认值为post)的option对象。
watchEffect(()=> {
//...
}, {
flush: 'sync' // 在更新前触发 flush: "pre"
})
此外,option对象还有ontrack和ontrigger两个函数属性,用于调试watcher的行为。
- onTrack will be called when a reactive property or ref is tracked as a dependency
- onTrigger will be called when the watcher callback is triggered by the mutation of a dependency
watchEffect(
() => {
/* side effect */
},
{
onTrigger(e) {
debugger // 进行交互式调试
}
}
)
watch
等价于vue 2.x中的this.$watch.
相比于watchEffect(), watch()可帮我们实现:
- Perform the side effect lazily;
- Be more specific about what state should trigger the watcher to re-run;
- Access both the previous and current value of the watched state.
watch()的数据源可以是一个返回值的getter
函数,或者是一个ref
对象。
// watching a getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// directly watching a ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
对于多数据源的监听,可借助数组。
代码语言:javascript复制watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
生命周期钩子
可以使用直接导入的onXXX
功能注册生命周期钩子:
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
这些生命周期钩子注册功能只能在期间同步使用setup()
(只能在setup()中调用),因为它们依赖于内部全局状态来定位当前活动实例(当前setup()正在被调用的组件实例)。在没有当前活动实例的情况下调用它们将导致错误。
2.x生命周期选项和3.0Composition API之间的映射
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
新钩子
除了2.x生命周期等效项之外,3.0Composition API还提供了以下调试挂钩:
- onRenderTracked
- onRenderTriggered
两个钩子都收到DebuggerEvent类似于onTrack
和onTrigger
观察者的选项:
export default {
onRenderTriggered(e) {
debugger
// inspect which dependency is causing the component to re-render
}
}
provide & inject
provide并inject启用类似于2.x provide/inject
选项的依赖项注入。两者都只能在setup()当前活动实例期间调用。
import { provide, inject } from 'vue'
const ThemeSymbol = Symbol()
const Ancestor = {
setup() {
provide(ThemeSymbol, 'dark')
}
}
const Descendent = {
setup() {
const theme = inject(ThemeSymbol, 'light' /* optional default value */)
return {
theme
}
}
}
inject接受可选的默认值作为第二个参数。如果未提供默认值,并且在Provide
上下文中找不到该属性,则inject返回undefined
。
使用响应式
为了保持提供的值和注入的值之间的响应式,可以使用ref:
代码语言:javascript复制// in provider
const themeRef = ref('dark')
provide(ThemeSymbol, themeRef)
// in consumer
const theme = inject(ThemeSymbol, ref('light'))
watch(() => {
console.log(`theme set to: ${theme.value}`)
})
模板引用
使用Composition API时,响应式引用和模板引用的概念是统一的。为了获得对模板中元素或组件实例的引用,我们可以像往常一样声明ref并从中返回setup():
代码语言:javascript复制<template>
<div ref="root"></div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
// the DOM element will be assigned to the ref after initial render
console.log(root.value) // <div/>
})
return {
root
}
}
}
</script>
用作模板ref的ref的行为与其他任何ref一样:它们是响应式的,可以传递到组合函数中(或从中返回)。
- Render/ JSX的用法
export default {
setup() {
const root = ref(null)
return () => h('div', {
ref: root
})
// with JSX
return () => <div ref={root}/>
}
}
- 内部用法 v-for
Composition API template refs do not have special handling when used inside v-for. Instead, use function refs (new feature in 3.0) to perform custom handling:
代码语言:javascript复制<template>
<div
v-for="(item, i) in list"
:ref="el => { divs[i] = el }">
{{ item }}
</div>
</template>
<script>
import { ref, reactive, onBeforeUpdate } from 'vue'
export default {
setup() {
const list = reactive([1, 2, 3])
const divs = ref([])
// make sure to reset the refs before each update
onBeforeUpdate(() => {
divs.value = []
})
return {
list,
divs
}
}
}
</script>
defineComponent
该功能仅用于类型推断。为了让TypeScript知道应该将对象视为组件定义,以便可以推断传递给的数据的类型,这是必需的setup()。这是无操作行为的明智选择。它需要一个组件定义并按原样返回参数。
代码语言:javascript复制import { defineComponent } from 'vue'
export default defineComponent({
props: {
foo: String
},
setup(props) {
props.foo // <- type: string
}
})