深入 Lyt.js 组件系统:L2 渲染引擎层的核心
基于 Lyt.js v6.6.0 @lytjs/component 包源码,深入解析组件系统的设计理念、生命周期管理、Props/Emits 机制和插槽实现。
一、组件系统架构
在 Lyt.js v6.6.0 的 8 层架构中,组件系统跨越多个层级:
bash
┌─────────────────────────────────────────────────────────────┐
│ L2: 渲染引擎层 │
│ @lytjs/component - 组件系统 │
│ @lytjs/renderer - 渲染器(Vapor/VDOM 双模式) │
│ @lytjs/dom-runtime - DOM 运行时工具 │
├─────────────────────────────────────────────────────────────┤
│ L3: 核心运行时层 │
│ @lytjs/core - 应用实例创建、生命周期、插件系统 │
└─────────────────────────────────────────────────────────────┘
组件系统是连接上层业务逻辑和下层渲染引擎的桥梁,负责:
- 组件定义与注册
- 生命周期管理
- Props/Emits 传递
- 插槽系统
- 组件通信
- KeepAlive 缓存
- 异步组件
二、组件定义
2.1 defineComponent
Lyt.js 提供了与 Vue 3 完全兼容的 defineComponent API:
typescript
import { defineComponent } from '@lytjs/core'
// Options API 风格
const UserCard = defineComponent({
name: 'UserCard',
props: {
username: String,
avatar: String,
},
emits: ['click'],
state() {
return {
isFollowing: false,
}
},
computed: {
displayName() {
return this.isFollowing ? `★ ${this.username}` : this.username
}
},
methods: {
toggleFollow() {
this.isFollowing = !this.isFollowing
this.$emit('click', this.isFollowing)
}
},
template: `
<div class="user-card">
<img :src="avatar" />
<span>{{ displayName }}</span>
<button on:click="toggleFollow">
{{ isFollowing ? '取消关注' : '关注' }}
</button>
</div>
`
})
// Composition API 风格
const UserCard = defineComponent({
name: 'UserCard',
props: {
username: String,
avatar: String,
},
emits: ['click'],
setup(props, { emit }) {
const isFollowing = ref(false)
const displayName = computed(() =>
isFollowing.value ? `★ ${props.username}` : props.username
)
function toggleFollow() {
isFollowing.value = !isFollowing.value
emit('click', isFollowing.value)
}
return { isFollowing, displayName, toggleFollow }
},
template: `
<div class="user-card">
<img :src="avatar" />
<span>{{ displayName }}</span>
<button on:click="toggleFollow">
{{ isFollowing ? '取消关注' : '关注' }}
</button>
</div>
`
})
2.2 组件类型推断
defineComponent 提供完整的 TypeScript 类型推断:
typescript
// Props 类型自动推断
interface UserCardProps {
username: string
avatar?: string
type?: 'compact' | 'full'
}
// Emits 类型自动推断
interface Emits {
(e: 'click', value: boolean): void
(e: 'follow', user: string): void
}
const UserCard = defineComponent({
name: 'UserCard',
props: {
username: String,
avatar: String,
type: {
type: String,
default: 'full'
}
},
emits: ['click', 'follow'],
// ...
})
// 使用时自动获得类型提示
const card = new UserCard({
props: {
username: 'Alice',
type: 'compact'
}
})
三、生命周期钩子
Lyt.js 提供了完整的生命周期钩子,与 Vue 3 完全兼容:
3.1 挂载生命周期
| 钩子 | 说明 |
|---|---|
onBeforeCreate |
实例初始化之前调用 |
onCreated |
实例创建完成后调用 |
onBeforeMount |
挂载之前调用 |
onMounted |
挂载完成后调用 |
typescript
defineComponent({
setup() {
onBeforeCreate(() => {
// 无法访问 props、data、methods
})
onCreated(() => {
// 可以访问 props、data、computed、methods
})
onBeforeMount(() => {
// DOM 还未挂载
})
onMounted(() => {
// DOM 已挂载,可以访问 this.$el
})
}
})
3.2 更新生命周期
| 钩子 | 说明 |
|---|---|
onBeforeUpdate |
更新之前调用 |
onUpdated |
更新完成后调用 |
typescript
defineComponent({
setup() {
onBeforeUpdate(() => {
// DOM 还未更新,但响应式数据已变化
})
onUpdated(() => {
// DOM 已更新完成
})
}
})
3.3 卸载生命周期
| 钩子 | 说明 |
|---|---|
onBeforeUnmount |
卸载之前调用 |
onUnmounted |
卸载完成后调用 |
typescript
defineComponent({
setup() {
onBeforeUnmount(() => {
// 清理定时器、事件监听器等
})
onUnmounted(() => {
// 组件完全卸载
})
}
})
3.4 错误捕获钩子
| 钩子 | 说明 |
|---|---|
onErrorCaptured |
捕获子组件错误 |
onRenderTracked |
渲染时追踪依赖(开发模式) |
onRenderTriggered |
渲染时触发更新(开发模式) |
typescript
defineComponent({
setup() {
onErrorCaptured((err, instance, info) => {
console.error('组件错误:', err)
console.error('错误来源:', info)
return false // 阻止错误继续传播
})
onRenderTracked(({ key, target, type }) => {
console.log('追踪:', key, target, type)
})
onRenderTriggered(({ key, target, type }) => {
console.log('触发:', key, target, type)
})
}
})
四、Props 与 Emits
4.1 Props 定义
Lyt.js 支持多种 Props 定义方式:
typescript
// 基础类型定义
defineComponent({
props: {
title: String,
count: Number,
isActive: Boolean,
items: Array,
config: Object,
}
})
// 详细定义
defineComponent({
props: {
title: {
type: String,
required: true,
default: ''
},
count: {
type: Number,
default: 0
},
isActive: {
type: Boolean,
default: false
},
validator: (value) => ['small', 'medium', 'large'].includes(value)
}
})
// TypeScript 类型定义
defineComponent({
props: {
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
} as PropType<{
title: string
count?: number
}>
})
4.2 Emits 定义
typescript
defineComponent({
emits: ['click', 'change', 'update:modelValue'],
// 或详细定义
emits: {
click: (value: boolean) => typeof value === 'boolean',
change: (value: string) => typeof value === 'string',
'update:modelValue': (value: any) => true,
},
setup(props, { emit }) {
function handleClick() {
emit('click', true)
}
function handleChange(value: string) {
emit('change', value)
}
}
})
4.3 v-model 支持
Lyt.js 支持 model 指令实现双向绑定:
html
<!-- 父组件 -->
<ChildComponent model="value" />
<!-- 子组件内部 -->
<script>
defineComponent({
emits: ['update:modelValue'],
setup(props, { emit }) {
// 当内部值变化时
emit('update:modelValue', newValue)
}
})
</script>
五、插槽系统
5.1 默认插槽
html
<!-- 父组件 -->
<Card>
<p>这是插槽内容</p>
</Card>
<!-- Card 组件 -->
<template>
<div class="card">
<slot></slot>
</div>
</template>
5.2 具名插槽
html
<!-- 父组件 -->
<Dialog>
<template #header>
<h2>对话框标题</h2>
</template>
<template #body>
<p>对话框内容</p>
</template>
<template #footer>
<button on:click="close">关闭</button>
</template>
</Dialog>
<!-- Dialog 组件 -->
<template>
<div class="dialog">
<div class="header"><slot name="header"></slot></div>
<div class="body"><slot name="body"></slot></div>
<div class="footer"><slot name="footer"></slot></div>
</div>
</template>
5.3 作用域插槽
html
<!-- 父组件 -->
<List>
<template #item="{ item, index }">
<div>{{ index }}: {{ item.name }}</div>
</template>
</List>
<!-- List 组件 -->
<template>
<div class="list">
<slot name="item" v-for="(item, index) in items" :item="item" :index="index" />
</div>
</template>
六、依赖注入
Lyt.js 提供了 provide 和 inject API 实现跨层级组件通信:
typescript
// 祖先组件
defineComponent({
setup() {
provide('theme', 'dark')
provide('user', reactive({ name: 'Alice', age: 25 }))
// 提供可更新的值
const count = ref(0)
provide('count', count)
provide('updateCount', (n) => { count.value += n })
}
})
// 后代组件
defineComponent({
setup() {
const theme = inject('theme')
const user = inject('user')
const count = inject('count')
const updateCount = inject('updateCount')
return { theme, user, count, updateCount }
}
})
// 带默认值的 inject
const config = inject('config', { debug: false })
// 类型安全的 inject
interface ThemeProvider {
theme: Ref<string>
toggleTheme: () => void
}
const themeProvider = inject<ThemeProvider>('themeProvider')
七、异步组件
typescript
import { defineAsyncComponent } from '@lytjs/core'
// 基础异步组件
const AsyncUserList = defineAsyncComponent(() =>
import('./components/UserList.vue')
)
// 带选项的异步组件
const AsyncUserList = defineAsyncComponent({
loader: () => import('./components/UserList.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorBoundary,
delay: 200,
timeout: 3000,
})
// Suspense 配合
defineComponent({
template: `
<Suspense>
<template #default>
<AsyncUserList />
</template>
<template #fallback>
<LoadingSpinner />
</template>
</Suspense>
`
})
八、KeepAlive 缓存
KeepAlive 组件可以缓存动态组件,避免重复创建和销毁:
typescript
// 缓存动态组件
defineComponent({
template: `
<KeepAlive :include="cachedComponents" :max="10">
<component :is="currentView" />
</KeepAlive>
`
})
// 组件定义
const cachedComponents = markRaw([
'Home',
'About',
'Profile'
])
生命周期变化:
- 首次挂载:
onBeforeMount→onMounted - 缓存后离开:
onBeforeUnmount(不调用) - 重新激活:
onActivated - 真正销毁:
onBeforeUnmount→onUnmounted
九、组件间通信
9.1 Props/Emits(父子通信)
typescript
// 父 -> 子:通过 props
// 子 -> 父:通过 emits
9.2 事件总线(跨层级通信)
typescript
import { createEventBus } from '@lytjs/common'
// 创建事件总线
const bus = createEventBus()
// 组件 A:发布事件
bus.emit('user:login', { username: 'Alice' })
// 组件 B:订阅事件
bus.on('user:login', (data) => {
console.log('用户登录:', data.username)
})
9.3 依赖注入(祖先 -> 后代)
typescript
// 祖先提供
provide('message', 'Hello from ancestor!')
// 后代注入
const message = inject('message')
9.4 Slot Props(子 -> 父)
html
<Child v-slot="{ data }">
<p>{{ data }}</p>
</Child>
十、在 v6.6.0 中的位置
组件系统在 8 层架构中的位置:
bash
L1: 核心原语层
├── @lytjs/reactivity (响应式系统)
├── @lytjs/vdom (虚拟 DOM)
└── @lytjs/compiler (模板编译器)
↓
L2: 渲染引擎层
├── @lytjs/component (组件系统) ← 当前层
├── @lytjs/renderer (渲染器)
└── @lytjs/dom-runtime (DOM 运行时)
↓
L3: 核心运行时层
└── @lytjs/core (应用实例)
组件系统依赖下层的响应式系统和编译器,同时被上层的应用实例和路由系统使用。