- vue 2.0 options api
- vue 3.0 composition api
setup
setup
函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。setup
选项是一个接收props 和 context
的函数
调用时机
创建组件实例,然后初始化
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
。
setup
参数
该函数接收 props
作为其第一个参数:
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)
})
watch(() => props.name, (newValue) => {
console.log(newValue)
})
},
}
然而不要解构 props
对象,那样会使其失去响应性:
export default {
props: {
name: String,
},
setup({ name }) {
watchEffect(() => {
console.log(`name is: ` + name) // Will not be reactive!
})
},
}
export default {
components: { 'mbt':MyButton },
props: {
btnm: String
},
name: 'Menu',
setup(props, ctx) {
watch(() => props.btnm, (newValue) => {
console.log(newValue)
})
}
}
在开发过程中,props
对象对用户空间代码是不可变的(用户代码尝试修改 props
时会触发警告)。
props
context
第二个参数提供了一个上下文对象,从原来 2.x 中
this
选择性地暴露了一些 property。const MyComponent = { setup(props, context) { context.attrs context.slots context.emit }, }
attrs
和slots
都是内部组件实例上对应项的代理,可以确保在更新后仍然是最新值。所以可以解构,无需担心后面访问到过期的值:const MyComponent = { setup(props, { attrs }) { // 一个可能之后回调用的签名 function onClick() { console.log(attrs.foo) // 一定是最新的引用,没有丢失响应性 } }, }
出于一些原因将
props
作为第一个参数,而不是包含在上下文中:- 组件使用
props
的场景更多,有时候甚至只使用props
- 将
props
独立出来作为第一个参数,可以让 TypeScript 对props
单独做类型推导,不会和上下文中的其他属性相混淆。这也使得setup
、render
和其他使用了 TSX 的函数式组件的签名保持一致。
- 组件使用
this的用法
- getCurrentInstancethis 在 setup() 中不可用。由于
setup()
在解析 2.x 选项前被调用,setup()
中的this
将与 2.x 选项中的this
完全不同。同时在setup()
和 2.x 选项中使用this
时将造成混乱。在setup()
中避免这种情况的另一个原因是:这对于初学者来说,混淆这两种情况的this
是非常常见的错误:
import { getCurrentInstance } from 'vue'
setup() {
// 获取当前上下文对象
const th = getCurrentInstance()
function onClick() {
this // 这里 `this` 与你期望的不一样!
}
}
- 类型定义
defineComponent
interface Data {
[key: string]: unknown
}
interface SetupContext {
attrs: Data
slots: Slots
emit: (event: string, ...args: unknown[]) => void
}
function setup(props: Data, context: SetupContext): Data
提示
为了获得传递给 setup()
参数的类型推断,需要使用 defineComponent
。
生命周期钩子Hooks
- 可以直接导入
onXXX
一族的函数来注册生命周期钩子:
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
},
}
这些生命周期钩子注册函数只能在 setup()
期间同步使用, 因为它们依赖于内部的全局状态来定位当前组件实例(正在调用 setup()
的组件实例), 不在当前组件下调用这些函数会抛出一个错误。
组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。
与 2.x 版本生命周期相对应的组合式 API
beforeCreate
-> 使用setup()
created
-> 使用setup()
beforeMount
->onBeforeMount
mounted
->onMounted
beforeUpdate
->onBeforeUpdate
updated
->onUpdated
beforeDestroy
->onBeforeUnmount
destroyed
->onUnmounted
errorCaptured
->onErrorCaptured
新增的钩子函数
除了和 2.x 生命周期等效项之外,组合式 API 还提供了以下调试钩子函数:
onRenderTracked
onRenderTriggered
两个钩子函数都接收一个
DebuggerEvent
,与watchEffect
参数选项中的onTrack
和onTrigger
类似:export default { onRenderTriggered(e) { debugger // 检查哪个依赖性导致组件重新渲染 }, }
contextParam
setup context
emit
子组件触发父组件emits:['emit_Method']
注册方法
// parent
/*
<template>
<HelloWorld :msg='state' @plus='plusParent' />
</template>
*/
import { ref ,onMounted ,onBeforeMount} from 'vue'
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: "App",
components: {
HelloWorld
},
setup() {
let state = ref(0)
const plusParent = (num) => {
state.value += num;
}
return { plusParent }
}
}
// -------child
{{ msg }}
// <button @click='plus'> add</button>
export default {
name: 'HelloWorld',
props: {
msg: Number
},
emits: ['plus'],
setup(props, { emit }) {
const plus = () => {
// this.$emit() -- 2.0
emit('plus', 2)
}
return { plus }
}
}