tp/docs/widget-optimization-proposals.md
copilot-swe-agent[bot] fbd1e72836 Update widget slot system to boolean and support multiple slots, add optimization doc
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
2025-12-29 01:14:33 +00:00

10 KiB
Raw Blame History

组件优化提案文档Widget Optimization Proposals

本文档基于当前 tp-define-widget 的实现,参考 Vue3 组合式 API 的设计理念,提出一系列优化和扩展方案。

当前实现

现有特性

  • :props - 支持属性定义,包含默认值 (prop . default)
  • :slot - 布尔值,t 表示支持 slotnil 表示不支持
  • :render - 渲染函数 (lambda (props slot) ...)
  • 支持多个 slot 值(字符串和嵌套组件)

优化提案

1. 生命周期钩子Lifecycle Hooks

参考: Vue3 的 onMounted, onBeforeUpdate, onUpdated

功能描述:

(tp-define-widget my-widget
  :props '(value)
  :slot t
  :on-render (lambda (props slot) ...)  ; 渲染前
  :on-rendered (lambda (result) ...)    ; 渲染后
  :render (lambda (props slot) ...))

作用:

  • 在渲染前后执行特定逻辑(如日志记录、性能监控)
  • 支持渲染结果的后处理

是否必要: 低优先级

  • 目前可通过在 render 函数中处理
  • 如果组件变得复杂且需要统一的渲染管道处理,则有价值

2. 命名插槽Named Slots

参考: Vue3 的 <slot name="header">, v-slot:header

功能描述:

(tp-define-widget card
  :props '(title)
  :slots '(header content footer)  ; 定义多个命名插槽
  :render (lambda (props slots)
            (concat (plist-get slots :header)
                    "\n"
                    (plist-get slots :content)
                    "\n"
                    (plist-get slots :footer))))

;; 使用
(tp-widget-parse
 '(card :title "My Card"
        :header-slot "Header Content"
        :content-slot "Main Content"
        :footer-slot "Footer"))

作用:

  • 支持更灵活的内容分发
  • 组件可以有多个内容区域

是否必要: 中等优先级

  • 当组件需要在不同位置插入内容时非常有用
  • 类似 Vue/React 的 slot 概念

3. 作用域插槽Scoped Slots

参考: Vue3 的作用域插槽,允许父组件访问子组件数据

功能描述:

(tp-define-widget list-item
  :props '(items)
  :slot t  ; slot 可以是函数
  :render (lambda (props slot-fn)
            (mapconcat
             (lambda (item)
               ;; slot-fn 可以访问当前 item
               (funcall slot-fn item))
             (plist-get props :items)
             "\n")))

;; 使用
(tp-widget-parse
 '(list-item :items ("apple" "banana" "orange")
             (lambda (item)
               (tp-set item 'face 'bold))))

作用:

  • 父组件可以访问子组件的内部数据
  • 更灵活的渲染控制

是否必要: 低优先级

  • 增加复杂度
  • Emacs Lisp 的闭包可以部分实现此功能

4. 组件继承/组合Component Inheritance/Composition

参考: Vue3 的 mixins, extends

功能描述:

(tp-define-widget base-button
  :props '((type . "default"))
  :slot t
  :render (lambda (props slot)
            (tp-set slot 'face 'button)))

(tp-define-widget primary-button
  :extends 'base-button
  :props '((type . "primary"))  ; 覆盖默认值
  :render (lambda (props slot parent-render)
            (let ((result (funcall parent-render props slot)))
              (tp-add result 'face '(:foreground "blue")))))

作用:

  • 代码复用
  • 创建组件变体

是否必要: 中等优先级

  • 对于创建组件库非常有用
  • 避免重复代码

5. 响应式状态Reactive State

参考: Vue3 的 ref, reactive

功能描述:

(tp-define-widget counter
  :state '((count . 0))  ; 组件内部状态
  :slot t
  :render (lambda (props state slot)
            (let ((count (plist-get state :count)))
              (format "Count: %d %s" count slot))))

;; 状态更新时自动重新渲染
(tp-widget-update 'counter :count 5)

作用:

  • 组件拥有自己的内部状态
  • 与现有的响应式系统($variable)集成

是否必要: 高优先级

  • 对于交互式组件非常重要
  • 可以利用现有的 tp reactive 系统

6. 事件系统Event System

参考: Vue3 的 $emit, v-on

功能描述:

(tp-define-widget button
  :props '(label)
  :emits '(click hover)  ; 声明可触发的事件
  :slot t
  :render (lambda (props slot emit)
            (tp-add slot
                    'mouse-1 (lambda () (funcall emit :click))
                    'pointer 'hand)))

;; 使用
(tp-widget-parse
 '(button :label "Click Me"
          :on-click (lambda () (message "Clicked!"))
          "Submit"))

作用:

  • 组件间通信
  • 事件驱动的交互

是否必要: 高优先级

  • 对于交互式 UI 必要
  • 支持按钮、链接等组件的回调

7. 依赖注入Provide/Inject

参考: Vue3 的 provide, inject

功能描述:

(tp-define-widget theme-provider
  :provide '(theme)  ; 向下提供
  :props '((theme . "dark"))
  :slot t
  :render (lambda (props slot) slot))

(tp-define-widget themed-text
  :inject '(theme)  ; 从上层获取
  :slot t
  :render (lambda (props slot injected)
            (let ((theme (plist-get injected :theme)))
              (tp-set slot 'face
                      (if (equal theme "dark")
                          '(:foreground "white" :background "black")
                        '(:foreground "black" :background "white"))))))

作用:

  • 跨层级的数据传递
  • 主题、配置等全局状态的共享

是否必要: 低优先级

  • Emacs 可以使用动态绑定实现
  • 如果组件树很深,可能有价值

8. 条件渲染辅助Conditional Rendering Helpers

参考: Vue3 的 v-if, v-show, v-for

功能描述:

;; 辅助函数
(defun tp-if (condition then &optional else)
  "条件渲染"
  (if condition then (or else "")))

(defun tp-for (items template)
  "列表渲染"
  (mapconcat template items ""))

;; 使用
(tp-define-widget user-list
  :props '(users show-email)
  :render (lambda (props _slot)
            (tp-for (plist-get props :users)
                    (lambda (user)
                      (concat (plist-get user :name)
                              (tp-if (plist-get props :show-email)
                                     (format " <%s>" (plist-get user :email))))))))

作用:

  • 简化常见的渲染模式
  • 提高代码可读性

是否必要: 中等优先级

  • 作为辅助函数很有用
  • 可以独立于核心组件系统实现

9. 插槽类型验证Slot Type Validation

功能描述:

(tp-define-widget container
  :slot 'string  ; 只接受字符串
  ;; 或
  :slot '(string widget)  ; 接受字符串和组件
  ;; 或
  :slot '(widget button text)  ; 只接受特定组件
  :render ...)

作用:

  • 类型安全
  • 更好的错误提示

是否必要: 低优先级

  • 开发时有用
  • 可能影响性能

10. 异步组件Async Components

参考: Vue3 的 defineAsyncComponent

功能描述:

(tp-define-async-widget remote-content
  :props '(url)
  :loading "Loading..."
  :error "Failed to load"
  :render (lambda (props slot)
            (url-retrieve-synchronously (plist-get props :url))
            ...))

作用:

  • 支持异步数据加载
  • 加载和错误状态处理

是否必要: 最低优先级

  • Emacs 的异步模型与 Web 不同
  • 可能需要使用 url-retrieve 和回调

优先级总结

优化项 优先级 复杂度 价值
响应式状态
事件系统
命名插槽
组件继承
条件渲染辅助
生命周期钩子
作用域插槽
依赖注入
类型验证
异步组件

建议实施顺序

  1. 第一阶段: 事件系统 + 响应式状态集成

    • 这两个特性对交互式组件最重要
    • 可以利用现有的 tp reactive 系统
  2. 第二阶段: 命名插槽 + 条件渲染辅助

    • 提升组件的灵活性和开发体验
  3. 第三阶段: 组件继承 + 生命周期钩子

    • 对于构建组件库有价值
  4. 第四阶段: 其他高级特性

    • 根据实际需求决定

示例:完整的组件定义(理想状态)

(tp-define-widget button
  ;; 属性定义
  :props '(action
           (type . "default")
           (size . "medium")
           (disabled . nil))
  
  ;; 状态(响应式)
  :state '((loading . nil)
           (focused . nil))
  
  ;; 支持插槽
  :slot t
  
  ;; 可触发的事件
  :emits '(click focus blur)
  
  ;; 生命周期
  :on-render (lambda (props) 
               (unless (plist-get props :disabled)
                 (message "Button rendering...")))
  
  ;; 渲染函数
  :render (lambda (props state slot emit)
            (let* ((type (plist-get props :type))
                   (size (plist-get props :size))
                   (disabled (plist-get props :disabled))
                   (loading (plist-get state :loading))
                   (content (if loading "Loading..." slot))
                   (face (cond
                          (disabled '(:foreground "gray"))
                          ((equal type "primary") '(:foreground "white" :background "blue"))
                          ((equal type "danger") '(:foreground "white" :background "red"))
                          (t '(:foreground "black" :background "#eee")))))
              (tp-add content
                      'face face
                      'mouse-1 (unless disabled
                                 (lambda ()
                                   (funcall emit :click)
                                   (funcall (plist-get props :action))))
                      'pointer (unless disabled 'hand)))))

结论

当前的组件系统已经具备基本功能。上述优化提案可以根据实际使用场景和需求逐步实施。建议从事件系统响应式状态集成开始,因为这两个特性对于构建交互式 UI 组件最为重要。