深入 Lyt.js 组件系统:L2 渲染引擎层的核心

深入 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 提供了 provideinject 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'
])

生命周期变化

  • 首次挂载:onBeforeMountonMounted
  • 缓存后离开:onBeforeUnmount(不调用)
  • 重新激活:onActivated
  • 真正销毁:onBeforeUnmountonUnmounted

九、组件间通信

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 (应用实例)

组件系统依赖下层的响应式系统和编译器,同时被上层的应用实例和路由系统使用。

相关推荐
这是程序猿1 小时前
Spring Boot自动配置详解
java·大数据·前端
文心快码BaiduComate1 小时前
干货|Comate Harness Engineering工程实践指南
前端·后端·程序员
还有多久拿退休金1 小时前
一张栈的图,治好你面试答不出 script 阻塞的病
前端·javascript
光辉GuangHui1 小时前
Agent Skill 也需要测试:如何搭建 Skill 评估框架
前端·后端·llm
To_OC1 小时前
我终于搞懂 Claude Code 核心逻辑!90%的人都用错了模式
前端·ai编程
蓝宝石的傻话1 小时前
Headless浏览器的隐形陷阱:为什么你的AI自动化工具抓不到页面早期错误?
前端
irving同学462382 小时前
Node 后端实战:JWT 认证与生产级错误处理
前端·后端
莽夫搞战术2 小时前
【Google Stitch】AI原生画布重新定义设计,让想法变成可交互界面
前端·人工智能·ui
甲维斯2 小时前
Gemini3.5Flash前端是真的强!
前端·人工智能