vue3(四)(生命周期,父子组件传参,动态组件)
组件的生命周期
简单来说就是一个组件从创建 到 销毁的 过程 成为生命周期

在我们使用Vue3 组合式Api是没有 beforeCreate 和 created 这两个生命周期的,用setup函数代替,但setup又是在beforeCreate和created之前执行。
<template>
<div>
<input type="text" v-model="message">
</div>
</template>
<script setup lang="ts">
import { ref ,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
let message = ref<string>('hello')
console.log('setup')
onBeforeMount(()=>{
console.log('创建之前') //在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。
})
onMounted(()=>{
console.log('创建完成') //在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
})
onBeforeUpdate(()=>{
console.log('更新之前') //数据更新时调用,发生在虚拟 DOM 打补丁之前
})
onUpdated(()=>{
console.log('更新完成') //DOM更新后,updated的方法即会调用。
})
onBeforeUnmount(()=>{
console.log('销毁之前') //在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
})
onUnmounted(()=>{
console.log('销毁完成') //卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载
})
</script>
<style>
</style>
setup函数在script标签上写是语法糖,还可以写成函数形式,但是要return出来
<template>
<div class="about">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
import {reactive} from 'vue'
export default {
setup() {
let msg=reactive({name:'hello'})
return {msg}
},
}
</script>
父子组件传参
方案一
vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的
普通的组件就是一个普通的对象,既然是一个普通的对象,那自然就不会获得自动的提示,加上 defineComponent() 之后,就完全不一样了,可以获得自动提示,vue2、vue3的自动提示都有
父组件
<template>
<div class="about">
<h1>{{ msg }}</h1>
<HelloWorld title="我是标题"> </HelloWorld>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld.vue'
import {reactive,defineComponent} from 'vue'
export default defineComponent({
name:"AboutView",
components:{
HelloWorld
},
setup() {
let msg=reactive({name:'hello'})
return {msg}
},
})
</script>
子组件
<template>
<div class="hello">
<h1>{{ title }}</h1>
<h3>这是HelloWorld组件</h3>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'HelloWorld',
props: {
title: String,
},
setup(props){
console.log(props.title)
}
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
h3 {
margin: 40px 0 0;
}
</style>
方案二
子组件通过defineProps接受传过来的值
如果defineProps报错,找到eslint.js文件,在env处添加代码 'vue/setup-compiler-macros': true,重启即可解决,如图

父组件
<template>
<div>
<input type="text" v-model="message">
<HelloWorld title='我是标题' :data="data"> </HelloWorld>
</div>
</template>
<script setup lang="ts">
import HelloWorld from '../components/HelloWorld.vue'
import { ref ,reactive} from 'vue'
let message = ref<string>('hello')
const data = reactive<number[]>([1, 2, 3])
</script>
<style>
</style>
子组件
<template>
<div class="HelloWorld">
子组件
<h1>{{ title }}</h1>
<div>{{ data }}</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
title:string,
data:number[]
}>()
</script>
如果你使用的不是TS
defineProps({
title:{
default:"",
type:String
},
data:Array
})
TS 特有的默认值方式
withDefaults是个函数也是无须引入开箱即用接受一个props函数第二个参数是一个对象设置默认值
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(), {
title: "张三",
data: () => [1, 2, 3]
})
子组件给父组件传参
是通过defineEmits派发一个事件
子组件
<template>
<div class="HelloWorld">
<button @click="clickTap">派发给父组件</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([4, 5, 6])
const emit = defineEmits(['on-click'])
const clickTap = () => {
emit('on-click', list)
}
</script>
父组件
<template>
<div>
<HelloWorld @on-click="getList"> </HelloWorld>
</div>
</template>
<script setup lang="ts">
import HelloWorld from '../components/HelloWorld.vue'
import { reactive } from 'vue';
const data = reactive<number[]>([1, 2, 3])
const getList = (list: number[]) => {
console.log(list,'父组件接受子组件');
}
</script>
<style>
</style>
子组件暴露给父组件内部属性
通过defineExpose
我们从父组件获取子组件实例通过ref
子组件
<template>
<div class="HelloWorld">
<button @click="clickTap">派发给父组件</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([4, 5, 6])
const emit = defineEmits(['on-click'])
const clickTap = () => {
emit('on-click', list)
}
//子组件暴露给父组件内部属性
defineExpose({
list
})
</script>
父组件
<template>
<div>
<HelloWorld ref="helloRef" @on-click="getList"> </HelloWorld>
</div>
</template>
<script setup lang="ts">
import HelloWorld from '../components/HelloWorld.vue'
import { reactive,ref } from 'vue';
const helloRef = ref(null)
const data = reactive<number[]>([1, 2, 3])
const getList = (list: number[]) => {
console.log(list,'父组件接受子组件');
console.log(helloRef.value)
}
</script>
<style>
</style>
动态组件
什么是动态组件 就是:让多个组件使用同一个挂载点,并动态切换,这就是动态组件。
在挂载点使用component标签,然后使用v-bind:is=”组件”
使用场景
tab切换 居多
注意事项
1.在Vue2 的时候is 是通过组件名称切换的 在Vue3 setup 是通过组件实例切换的
2.如果你把组件实例放到Reactive Vue会给你一个警告runtime-core.esm-bundler.js:38 [Vue warn]: Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with `markRaw` or using `shallowRef` instead of `ref`.
Component that was made reactive:
这是因为reactive 会进行proxy 代理 而我们组件代理之后毫无用处 节省性能开销 推荐我们使用shallowRef 或者 markRaw 跳过proxy 代理
修改如下
<template>
<div class="about">
<button v-for="item in tab" :key="item.name" @click="onchange(item)">
{{ item.name }}
</button>
<component :is="current.comName"></component>
</div>
</template>
<script setup lang="ts">
import { reactive, markRaw } from "vue";
import ComA from "../components/ComA.vue";
import ComB from "../components/ComB.vue";
type Tabs = {
name: string;
comName: any;
};
const tab = reactive<Tabs[]>([
{
name: "A组件",
comName: markRaw(ComA),
},
{
name: "B组件",
comName: markRaw(ComB),
},
]);
let current = reactive({
comName: tab[0].comName,
});
const onchange = (val) => {
current.comName = val.comName;
};
</script>


浙公网安备 33010602011771号