新建项目,项目主入口为App.vue(主组件),新建child.vue(子组件)。
1.1 setup 执行 时机问题
1.在主组件里引入子组件和ref:
import {ref} from 'vue'
import child from './components/child.vue'
2.template层:写一些基本提示性语句
<template><h1>父组件</h1><h3>{{ msg }}</h3><button @click="msg +='你好'">更新数据</button><hr><child :msg="msg"/>
</template>
3.注册放行子组件:
neme: 'App',//注册components: {child},
4.写setup方法:
setup() {const msg = ref("11")return {msg,}
},
5.子组件如下:使用props获取到父组件的值,查看beforeCreate和setup的执行顺序。
export default {name:'child',props:['msg'],beforeCreate(){console.log("beforeCreate执行");},setup(){console.log("setup执行了");return}
}
6.子组件template
<template><h2>子组件</h2><h3>msg:{{ msg }}</h3>
</template>
1.1.1 总结
1.setup 中一般都是返回一个对象,对象中的属性和方法都可以在 html 模版中直接使用
2.setup 是在 before Create 生命周期回调之前就执行了, 而且就执行一次
3.由此可以推断出:setup 在执行的时候, 当前的组件还没有创建出来, 也就意味着:组件实例对象 this 根本就不能用
this 是 undefined, 说明, 就不能通过 this 再去调用 data/computed/methods 中的相关内容了
1.2 setup细节
- setup执行的时机
- 在beforeCreate之前执行(一次), 此时组件对象还没有创建
- this是undefined, 不能通过this来访问data/computed/methods / props
- 其实所有的composition API相关回调函数中也都不可以
- setup的返回值
- 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与methods中的方法合并成功组件对象的方法
- 如果有重名, setup优先
- 注意:
- 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
- setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
- setup的参数
- setup(props, context) / setup(props, {attrs, slots, emit})
- props: 包含props配置声明且传入了的所有属性的对象
- attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
- slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
- emit: 用来分发自定义事件的函数, 相当于 this.$emit
<template><h2>App</h2><p>msg: {{msg}}</p><button @click="fn('--')">更新</button><child :msg="msg" msg2="cba" @fn="fn"/>
</template><script lang="ts">
import {reactive,ref,
} from 'vue'
import child from './child.vue'export default {components: {child},setup () {const msg = ref('abc')function fn (content: string) {msg.value += content}return {msg,fn}}
}
</script>
<template><div><h3>{{n}}</h3><h3>{{m}}</h3><h3>msg: {{msg}}</h3><h3>msg2: {{$attrs.msg2}}</h3><slot name="xxx"></slot><button @click="update">更新</button></div>
</template><script lang="ts">import {ref,defineComponent
} from 'vue'export default defineComponent({name: 'child',props: ['msg'],emits: ['fn'], // 可选的, 声明了更利于程序员阅读, 且可以对分发的事件数据进行校验data () {console.log('data', this)return {// n: 1}},beforeCreate () {console.log('beforeCreate', this)},methods: {// update () {// this.n++// this.m++// }},// setup (props, context) {setup (props, {attrs, emit, slots}) {console.log('setup', this)console.log(props.msg, attrs.msg2, slots, emit)const m = ref(2)const n = ref(3)function update () {// console.log('--', this)// this.n += 2 // this.m += 2m.value += 2n.value += 2// 分发自定义事件emit('fn', '++')}return {m,n,update,}},
})
</script>
这是一个关于Vue 3的组件开发和setup
函数使用的示例。我看到你提供了一个父组件 App.vue
和一个子组件 child.vue
的代码,以及关于setup
函数的一些细节和总结。
首先,让我简要概括一下你的代码:
- 在主组件
App.vue
中,你引入了子组件child.vue
和ref
函数。在模板中,你展示了一些基本的提示性语句,并包含一个更新数据的按钮和子组件child
。 - 你在
setup
函数中初始化了一个msg
的响应式引用,并返回了这个引用,使其在模板中可用。 - 子组件
child.vue
中,你使用props
获取了父组件传递的值,并在beforeCreate
和setup
生命周期钩子中输出了一些信息。在模板中,你展示了父组件传递的消息。
接下来,让我帮你继续写下去:
setup函数的更多用法
在setup
函数中,可以执行各种初始化逻辑,包括对props
的处理、引入其他模块、设置定时器等。
// 在 App.vue 的 setup 函数中
import { onMounted, onUnmounted } from 'vue';setup() {const msg = ref("1");// 添加一个计时器,每秒更新一次数据const timer = setInterval(() => {msg.value += '!';}, 1000);// 在组件挂载时启动计时器onMounted(() => {console.log('组件挂载了!');});// 在组件卸载时清除计时器onUnmounted(() => {clearInterval(timer);console.log('组件卸载了!');});return {msg,};
},
引入了onMounted
和onUnmounted
函数,它们分别在组件挂载和卸载时执行。在挂载时,启动了一个计时器,每秒更新一次数据;在卸载时清除了计时器。
这样, App.vue
组件现在会在挂载时启动一个定时器,在卸载时清除定时器。
当然,继续讨论 Vue 3 中 setup
函数的更多用法和细节。
1.3 setup
函数的参数
在之前的例子中,我们在 setup
函数中只使用了 setup()
,但实际上,setup
函数可以接受两个参数:props
和 context
(或者可以使用解构语法分别获取 attrs
、slots
和 emit
)。这两个参数提供了更多的灵活性和访问权限。
在 App.vue
中,我们可以修改 setup
函数,接受 props
和 context
参数:
setup(props, context) {const msg = ref("111");// 访问 propsconsole.log(props.msg);// 访问 context,包含 attrs、slots 和 emitconsole.log(context.attrs.msg2);console.log(context.slots);console.log(context.emit);return {msg,};
},
这样,我们就可以更灵活地处理传递给组件的属性以及与组件的上下文进行交互。
1.4 使用 Composition API
setup
函数是 Composition API 的一部分,它使得组件逻辑可以更灵活地组织和重用。在 setup
函数中,我们可以使用诸如 reactive
、ref
、watch
等 Composition API 提供的功能。
setup() {const msg = ref("11");const count = ref(0);// 使用 watch 监听数据变化watch(() => msg.value, (newVal, oldVal) => {console.log(`消息从 ${oldVal} 更新为 ${newVal}`);});// 使用 reactive 创建响应式对象const state = reactive({name: "Vue 3",version: "3.0.0",});return {msg,count,state,};
},
这个例子中,我们使用了 watch
监听 msg
的变化,并使用 reactive
创建了一个包含 name
和 version
的响应式对象 state
。
1.5 组合式函数的提取和重用
由于 setup
函数的灵活性,我们可以将一些逻辑提取为组合式函数,以便在多个组件中重用。例如,我们可以创建一个处理数据的函数:
// utilities.js
import { ref } from 'vue';export function useData(initialValue) {const data = ref(initialValue);function updateData() {data.value += '!';}return {data,updateData,};
}
然后在 setup
函数中使用:
// App.vue
import { useData } from './utilities.js';setup() {const { data: msg, updateData } = useData("111");return {msg,updateData,};
},
这样,我们可以更好地组织和重用代码。
1.6 注意事项
在使用 setup
函数时,需要注意一些事项:
- 不要混合使用
setup
和data
、methods
等选项。在setup
中可以访问props
,但在data
和methods
中无法访问setup
中的属性和方法。 setup
函数不能是异步的,因为返回值将不再是一个对象,而是一个Promise
。
1.7 插槽(Slots)和自定义事件
在 Vue 3 中,使用 setup
函数也能够更方便地处理插槽和自定义事件。在 child.vue
组件中,我们已经看到了如何使用插槽和自定义事件:
// child.vue
setup(props, { attrs, emit, slots }) {const m = ref(2);const n = ref(3);function update() {m.value += 2;n.value += 2;// 分发自定义事件emit('fn', '++');}return {m,n,update,};
},
在这个例子中,我们使用 emit
函数来分发自定义事件,父组件 App.vue
可以监听这个事件并执行相应的逻辑。
1.8 生命周期钩子
在 setup
函数中,Vue 3 提供了两个新的生命周期钩子函数:onBeforeMount
和 onBeforeUnmount
。这两个钩子分别在组件挂载前和卸载前执行:
// App.vue
setup() {const msg = ref("11");onBeforeMount(() => {console.log('组件即将挂载!');});onBeforeUnmount(() => {console.log('组件即将卸载!');});return {msg,};
},
这样,我们可以在组件生命周期的不同阶段执行特定的逻辑。
1.9 组件引入和注册
在 App.vue
中,我们使用 import
语句引入了 child.vue
组件,并使用 components
选项注册了这个组件:
// App.vue
import child from './components/child.vue';export default {components: {child,},// ...
};
这样,我们就可以在模板中直接使用这个组件。
1.10 总结
通过这个例子,我们更深入地了解了 Vue 3 中 setup
函数的使用方式和一些注意事项。Composition API 提供了更灵活和可复用的代码组织方式,让我们能够更好地处理组件的逻辑。
在 setup
函数中,我们可以访问 props
、context
,使用 Composition API 的功能,处理插槽和自定义事件,以及执行一些生命周期钩子。这使得我们能够更好地组织和重用组件的代码。