ref
接收一个参数值并返回一个响应式且可以改变的ref对象.ref对象拥有一个指向内布置的单一属性.value
const count = ref(0)console.log(count.value) // 0count.value++console.log(count.value) //
如果传入ref的是一个对象,将调用reactive方法进行深层响应转换
- 模板中访问
当ref作为渲染上下文的属性返回(即在setup()返回的对象中)并在模板中使用时,他会自动 解套,无需再模板内额外书写.value:
<template><div>{{ count }}</div></template><script>export default {setup() {return {count: ref(0),}},}</script>
- 作为响应式对象的属性访问
当ref作为reactive对象的property被访问或修改是,也将自动解套value值,其行为类似普通属性: ```javascript const count = ref(0) const state = reactive({ count, })
console.log(state.count) // 0
state.count = 1 console.log(count.value) // 1
- 注意如果讲一个新的ref分配给现有的ref,将替换旧的ref:```javascriptconst otherCount = ref(2)state.count = otherCountconsole.log(state.count) // 2console.log(count.value) // 1
- 注意当嵌套在reactive
object中时,ref才会解套.从Array或者Map等原生集合类中访问ref时,不会自动解套: ```javascript const arr = reactive([ref(0)]) // 这里需要 .value console.log(arr[0].value)
const map = reactive(new Map([[‘foo’, ref(0)]])) // 这里需要 .value console.log(map.get(‘foo’).value)
<a name="7476383b"></a>#### `computed`传入一个getter函数,返回一个默认不可手动修改的ref对象.```javascriptconst count = ref(1)const plusOne = computed(() => count.value + 1)console.log(plusOne.value) // 2plusOne.value++ // 错误!
或者传入一个拥有get和set函数的对象,创建一个可手动修改的计算状态.
const count = ref(1)const plusOne = computed({get: () => count.value + 1,set: (val) => {count.value = val - 1},})plusOne.value = 1console.log(count.value) // 0
Reactive
接收一个普通对象然后返回该普通对象的响应式代理.等同于2.x的Vue.observable()
const obj=reative({cout:0})
响应式转换是”深层的”:会影响对象内部所有嵌套的属性.基于ES2.15的Proxy实现,返回的代理对象不等于原始对象.
Computed
传入一个getter函数,返回一个默认不可手动修改的ref对象.
const count = ref(1)const plusOne = computed(() => count.value + 1)console.log(plusOne.value) // 2plusOne.value++ // 错误!
或者传入一个拥有get和set函数的对象,创建一个可手动修改的计算状态.
const count = ref(1)const plusOne = computed({get: () => count.value + 1,set: (val) => {count.value = val - 1},})plusOne.value = 1console.log(count.value) // 0
Readonly
传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理.一个只读代理是”深层的”,对象内部任何嵌套的属性也都是制度的.
const original = reactive({ count: 0 })const copy = readonly(original)watchEffect(() => {// 依赖追踪console.log(copy.count)})// original 上的修改会触发 copy 上的侦听original.count++// 无法修改 copy 并会被警告copy.count++ // warning!
Watcheffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更是重新运行该函数.
const count = ref(0)watchEffect(() => console.log(count.value))// -> 打印出 0setTimeout(() => {count.value++// -> 打印出 1}, 100)
停止侦听
当watchEffect在组件的setup()函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止.
const stop = watchEffect(() => {/* ... */})// 之后stop()
清除副作用
有时副作用函数会执行一些一步的副作用,这些响应需要在其失效时清除(即完成之前状态已改变了).可以侦听副作用传入的函数可以接受一个onInvalidate函数作入参,用来注册清理失效时的回调.当一下情况发生时,这个失效回调会被触发:
- 副作用即将重新执行时
- 侦听器被停止(如果在
setup()或生命周期钩子函数中使用了watchEffect,则在卸载组件时)
watchEffect((onInvalidate) => {const token = performAsyncOperation(id.value)onInvalidate(() => {//每一次被触发的时候都会先执行onInvalidate内部逻辑,然后执行onInvalidate外部的逻辑// id 改变时 或 停止侦听时// 取消之前的异步操作token.cancel()//这是一个形参函数 这是你的逻辑叫做cancle只是为了很好地去比喻})})//示例<template><div><input type="text"v-model="keyword"></div></template><script>import { ref, watchEffect } from '@vue/composition-api'export default {setup() {const keyword = ref('')const asyncPrint = val => {return setTimeout(() => {console.log('user input: ', val)}, 1000)}watchEffect(onInvalidate => {const timer = asyncPrint(keyword.value)onInvalidate(() => clearTimeout(timer))console.log('keyword change: ', keyword.value)},{flush: 'post' // 默认'post',同步'sync','pre'组件更新之前})return {keyword}}}// 实现对用户输入“防抖”效果</script>
我们之所以是通过传入一个函数去注册失效回调,而不是从回调返回他(如ReactuseEffect中的方式),是因为返回值对于异步错误处理很重要
在执行数据请求时,副作用函数往往是一个异步函数:
const data = ref(null)watchEffect(async () => {data.value = await fetchData(props.id)})
我们知道异步函数都会隐式地返回一个Promise,但是清理函数必须要在Promise被resolve之前被注册.另外,Vue依赖这个返回的Promise来自动处理Promise链上的潜在错误.
Setup
setup函数是一个新的组件选项.作为在组件内使用CompositionAPi的入口点.
- 调用时机
创建组件实例,然后初始化props,紧接着就调用setup函数.从生命周期钩子的视角来看,他会在beforeCreate钩子之前被调用
- 模板中使用
如果setup返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文:
<template><div>{{ count }} {{ object.foo }}</div></template><script>import { ref, reactive } from 'vue'export default {setup() {const count = ref(0)const object = reactive({ foo: 'bar' })// 暴露给模板return {count,object,}},}</script>
注意setup返回的ref在模板中会自动解开,不需要写.value.
- 渲染函数/jsx中使用
setup也可以返回一个函数,函数中也能使用当前setup函数作用域中的响应式数据:
import { h, ref, reactive } from 'vue'export default {setup() {const count = ref(0)const object = reactive({ foo: 'bar' })return () => h('div', [count.value, object.foo])},}
- 参数
export default {props: {name: String,},setup(props) {console.log(props.name)},}
注意props对象是响应式的,watchEffect或者watch会观察
和响应props的更新
export default {props: {name: String,},setup(props) {watchEffect(() => {console.log(`name is: ` + props.name)})},}
然而不要结构props对象,那样会使其失去响应性:
export default {props: {name: String,},setup({ name }) {watchEffect(() => {console.log(`name is: ` + name) // Will not be reactive!})},}
在开发过程中,props对象对用户空间代码是不可变的(用户代码尝试修改props时会触发警告)
第二个参数提供了一个上下文对象,从原来2.x中this选择性地暴露了一些property
const MyComponent = {setup(props, context) {context.attrscontext.slotscontext.emit},}
attrs和slots都是内部组件实例上对应项的代理,可以确保在更新后仍然是最新值.都可以结构没无需担心后面访问到过期的值:
这里的attrs 相当于2.x的$attrs;相关资料说明 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件——在创建高级别的组件时非常有用。
const MyComponent = {setup(props, { attrs }) {// 一个可能之后回调用的签名function onClick() {console.log(attrs.foo) // 一定是最新的引用,没有丢失响应性}},}
处于一些原因将props作为第一个参数,而不是包含在上下文中:
组件使用
props的场景更多,有时候甚至只使用props- 将
props独立出来作为第一个参数,可以让TypeScript 对props单独做类型推导,不会和上下文中的其他属性相混淆.这也使得setup.render和其他适用了TSX的函数式组件的签名保持一致.
- 将
this的用法this在setup()中不可用.由于setup()在解析2.x选项前被调用,setup()中的this将与2.x选项中的this完全不同.同时在setup()和2.x选项中使用this时将造成混乱.在setup()中避免这种情况的另一个原因是:这对初学者来学,混淆这两种情况的this是非常常见的错误:setup() {function onClick() {this // 这里 `this` 与你期望的不一样!}}
打印的this结果

这里是vue3的this 提示: 为了获得传递为
setup()参数的类型推断,需要使用defineComponent(自我理解:这是使用typescript的操作)
