Files
vtj/renderer.md
“chenhuachun” d2e212aa68 docs: ✏️ docs
2025-06-28 16:50:04 +08:00

5.2 KiB
Raw Permalink Blame History

createRenderer 函数详解

createRenderer 是 VTJ 渲染系统的核心函数,用于根据 DSL (Domain Specific Language) 创建 Vue 组件渲染器。

作用

  1. 组件渲染器创建:基于 DSL 配置生成可复用的 Vue 组件
  2. 上下文管理:创建并维护组件运行时上下文(Context)
  3. 响应式系统集成:处理状态(state)、计算属性(computed)和方法(methods)
  4. 生命周期管理:集成 DSL 定义的生命周期钩子
  5. 跨框架支持:通过抽象层支持不同版本的 Vue

工作原理

输入参数 (CreateRendererOptions)

interface CreateRendererOptions {
  Vue?: any; // Vue 实例 (默认为全局 Vue)
  mode?: ContextMode; // 上下文模式 (默认 Runtime)
  dsl?: BlockSchema; // 块定义 DSL
  components?: Record<string, any>; // 可用组件
  libs?: Record<string, any>; // 第三方库
  apis?: Record<string, any>; // API 集合
  loader?: BlockLoader; // 块加载器
  window?: Window; // 目标窗口对象
}

核心工作流程

  1. 初始化上下文

    const context = new Context({
      mode,
      dsl: dsl.value,
      attrs: { $components, $libs, $apis }
    });
    
  2. 创建 Vue 组件

    • 使用 Vue.defineComponent 定义组件
    • 处理 props、setup、render 等核心选项
  3. 响应式系统集成

    context.state = createState(Vue, dsl.value.state, context);
    const computed = createComputed(Vue, dsl.value.computed, context);
    const methods = createMethods(dsl.value.methods, context);
    
  4. 依赖注入处理

    const injects = createInject(Vue, dsl.value.inject, context);
    
  5. 数据源初始化

    const dataSources = createDataSources(dsl.value.dataSources, context);
    
  6. 生命周期管理

    ...createLifeCycles(dsl.value.lifeCycles, context)
    
  7. 渲染逻辑

    render() {
      const nodes: NodeSchema[] = dsl.value.nodes || [];
      // 单节点渲染
      if (nodes.length === 1) return nodeRender(nodes[0], context, Vue, loader, nodes);
      // 多节点渲染
      return nodes.map(child => nodeRender(child, context, Vue, loader, nodes));
    }
    

关键辅助函数

  1. createProps() - 处理组件 props

    • 支持字符串和对象形式定义
    • 自动解析 JS 表达式默认值
    const propsDef = createProps(dsl.value.props, context);
    
  2. createState() - 创建响应式状态

    • 使用 Vue.reactive 创建响应式对象
    • 自动解析 JS 表达式和函数
  3. createComputed() - 创建计算属性

    • 使用 Vue.computed 包装计算逻辑
  4. createDataSources() - 初始化数据源

    • 支持 mock 和 API 两种类型
    • 自动处理数据转换
  5. setWatches() - 设置侦听器

    Vue.watch(
      context.__parseExpression(n.source),
      context.__parseFunction(n.handler),
      { deep, immediate }
    );
    

使用方法

基本使用

import { createRenderer } from '@vtj/renderer';

const { renderer, context } = createRenderer({
  Vue, // 可选 Vue 实例
  dsl: blockSchema, // 块定义 DSL
  components: { Button, Input }, // 注册组件
  apis: { fetchData } // 注册 API
});

// 在 Vue 应用中作为组件使用
app.component('BlockRenderer', renderer);

DSL 配置示例

{
  "id": "my-block",
  "name": "MyBlock",
  "props": [{ "name": "title", "type": "string", "required": true }],
  "state": {
    "count": 0,
    "message": "Hello VTJ"
  },
  "methods": {
    "increment": {
      "type": "JSFunction",
      "value": "function() { this.count++ }"
    }
  },
  "nodes": [
    {
      "type": "element",
      "tag": "div",
      "children": [
        { "type": "text", "value": "{{ title }}" },
        {
          "type": "element",
          "tag": "button",
          "events": { "click": "increment" }
        }
      ]
    }
  ]
}

高级功能

  1. 依赖注入

    "inject": [
      { "name": "theme", "from": "THEME_PROVIDER", "default": "light" }
    ]
    
  2. 数据源配置

    "dataSources": {
      "userData": {
        "type": "api",
        "ref": "getUserInfo",
        "transform": {
          "type": "JSFunction",
          "value": "function(data) { return data.user }"
        }
      }
    }
    
  3. 生命周期钩子

    "lifeCycles": {
      "mounted": {
        "type": "JSFunction",
        "value": "function() { console.log('组件挂载') }"
      }
    }
    

最佳实践

  1. 组件拆分:复杂 UI 拆分为多个 block
  2. 状态管理:优先使用块级 state复杂场景考虑外部状态管理
  3. 性能优化
    • 使用 Vue.markRaw 避免不必要的响应式转换
    • 合理使用计算属性缓存复杂运算
  4. 样式隔离:利用 adoptedStyleSheets 实现 CSS 隔离
  5. 错误处理:在数据源中添加错误处理逻辑

注意事项

  1. 确保 DSL 中的 JS 表达式/函数可安全执行
  2. 跨窗口渲染时提供正确的 window 对象
  3. 生产环境移除调试用的 mock 数据源
  4. 避免在 render 函数中进行复杂计算