1081 字
5 分钟
Vue重学计划(六)

上一节聊完了SCSS和BEM架构,这一节来说说Vue中的组件部分,组件内容比较多,可能会用好几期博客来慢慢复习了。

这一期简单讲讲组件基础知识吧。

生命周期#

前端工程尽管是面向对象的,但是由于视觉上的呈现,需要经过一个渲染的加载过程,许多前端的框架和引擎都会天然提供生命周期的钩子,方便在不同的加载阶段执行不同的操作

例如,在Unity开发中,我们的代码是这样的:

using System;
using UnityEngine;

public void GameBoot : Monobehaviour
{
 // 组件初始化
 void Awake(){}

 // 组件初始化完成
 void Start(){}

 // 组件启用
 void OnEnable(){}

 // 组件禁用
 void OnDisable(){}

 // 组件每一帧更新
 void Update(){}

 // 组件销毁
 void OnDestroy(){}
}

类似地,Vue框架也提供了生命周期钩子:

<template>
    <h1>
        This is a component demo
    </h1>
    <div ref="div">{{ str }}</div>
    <button @click="change">修改</button>
</template>

<script setup lang="ts">
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onRenderTracked, onRenderTriggered, onErrorCaptured, onServerPrefetch } from 'vue'
// beforeCreate,created 在setup语法糖模式中没有这两个生命周期
console.log('setup')
const str = ref('hello')
const div = ref<HTMLDivElement>()

const change = () => {
    str.value = 'world'
}

//创建
onBeforeMount(() => {
    console.log('beforeMount', div.value)
})

onMounted(() => {
    console.log('mounted', div.value)
})
//更新
// 为什么这里面加了?.innerText前后,控制台输出的值不一样???
onBeforeUpdate(() => {
    console.log('beforeUpdate', div.value?.innerText)
})
onUpdated(() => {
    console.log('updated', div.value?.innerText)
})
//销毁
onBeforeUnmount(() => {
    console.log('beforeUnmount')
})

onUnmounted(() => {
    console.log('unmounted')
})
//调试
onRenderTracked((e) => {
    console.log('renderTracked')
    console.log(e)
})
onRenderTriggered((e) => {
    console.log('renderTriggered')
    console.log(e)
})
</script>

<style lang="css" scoped></style>

值得一提的是:

在我们使用Vue3 组合式API 是没有 beforeCreate 和 created这两个生命周期的

组件传参#

组件传参大概有两个方向

父组件向子组件传参#

子组件通过defineProperties来定义自己的属性参数,父组件通过v-bind指令来给每个属性传入一个参数,例如:

子组件.ts

<template>
    <div class="menu">
        菜单区域 {{ title }}
        <div>{{ data }}</div>
    </div>
</template>
 
<script setup lang="ts">
defineProps<{
    title:string,
    data:number[]
}>()
</script>

父组件.ts

<template>
    <div class="layout">
        <Menu v-bind:data="data"  title="我是标题"></Menu>
        <div class="layout-right">
            <Header></Header>
            <Content></Content>
        </div>
    </div>
</template>
 
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
 
const data = reactive<number[]>([1, 2, 3])
</script>

传递字符串类型,不需要v-bind,传递非字符串类型需要加v-bind,当然也可以用简写形式.

如果想设置默认值,可以使用withDefault函数

type Props = {
    title?: string,
    data?: number[]
}
withDefaults(defineProps<Props>(), {
    title: "张三",
    data: () => [1, 2, 3]
})

子组件向父组件传参#

这种传参有两个形式,一种是父组件通过方法来从子组件获取参数,另一种是子组件主动向父组件暴露参数,这两种方法各有各的应用场景,咱们分别来看看例子:

  1. 子组件通过emit暴露事件
<template>
    <div class="menu">
        <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'])
 
//如果用了ts可以这样两种方式
// const emit = defineEmits<{
//     (e: "on-click", name: string): void
// }>()
const clickTap = () => {
    emit('on-click', list)
}
 
</script>

我们在子组件绑定了一个click 事件 然后通过defineEmits 注册了一个自定义事件

点击click 触发 emit 去调用我们注册的事件 然后传递参数,父组件接受子组件的事件。

<template>
    <div class="layout">
        <Menu @on-click="getList"></Menu>
        <div class="layout-right">
            <Header></Header>
            <Content></Content>
        </div>
    </div>
</template>
 
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
 
const data = reactive<number[]>([1, 2, 3])
 
const getList = (list: number[]) => {
    console.log(list,'父组件接受子组件');
}
</script>

我们从Menu 组件接受子组件派发的事件on-click 后面是我们自己定义的函数名称getList,会把参数返回过来

  1. 子组件暴露给父组件内部属性

子组件暴露属性通过defineExpose

我们从父组件获取子组件实例通过ref:

 <Menu ref="refMenu"></Menu>
//这样获取是有代码提示的
<script setup lang="ts">
import MenuCom from '../xxxxxxx.vue'
//注意这儿的typeof里面放的是组件名字(MenuCom)不是ref的名字 ref的名字对应开头的变量名(refMenu)
const refMenu = ref<InstanceType<typeof MenuCom>>()
</script>

这时候父组件想要读到子组件的属性可以通过 defineExpose暴露,子组件中这样写:

const list = reactive<number[]>([4, 5, 6])
 
defineExpose({
    list
})
Vue重学计划(六)
https://monsterstation.netlify.app/posts/old/vue重学计划六/
作者
Furry Monster
发布于
2024-10-16
许可协议
CC BY-NC-SA 4.0