7.1 组件化概述

什么是组件化

组件化是现代前端开发的核心思想,它将复杂的用户界面拆分成独立、可复用的组件。在Sciter中,组件化开发能够帮助我们:

组件化的优势

  • 可复用性:一次编写,多处使用
  • 可维护性:独立的组件便于维护和调试
  • 可测试性:组件可以独立进行单元测试
  • 团队协作:不同开发者可以并行开发不同组件
  • 代码组织:清晰的代码结构和职责分离

Sciter组件特性

  • HTML模板:使用HTML定义组件结构
  • CSS样式:独立的样式作用域
  • TIScript逻辑:组件的行为和交互逻辑
  • 属性传递:父子组件间的数据传递
  • 事件通信:组件间的事件通信机制
  • 生命周期:组件的创建、更新、销毁过程

组件的基本结构

// 基础组件类
class Component {
    function this(element, props = {}) {
        this.element = element;
        this.props = props;
        this.state = {};
        this.children = [];
        this.eventListeners = [];
        
        this.init();
    }
    
    // 初始化组件
    function init() {
        this.beforeMount();
        this.render();
        this.mounted();
        this.bindEvents();
    }
    
    // 生命周期钩子
    function beforeMount() {
        // 组件挂载前
    }
    
    function mounted() {
        // 组件挂载后
    }
    
    function beforeUpdate() {
        // 组件更新前
    }
    
    function updated() {
        // 组件更新后
    }
    
    function beforeDestroy() {
        // 组件销毁前
    }
    
    function destroyed() {
        // 组件销毁后
    }
    
    // 渲染方法
    function render() {
        // 子类实现
    }
    
    // 绑定事件
    function bindEvents() {
        // 子类实现
    }
    
    // 更新组件
    function update(newProps = {}) {
        this.beforeUpdate();
        
        // 更新属性
        Object.assign(this.props, newProps);
        
        // 重新渲染
        this.render();
        
        this.updated();
    }
    
    // 设置状态
    function setState(newState) {
        Object.assign(this.state, newState);
        this.update();
    }
    
    // 销毁组件
    function destroy() {
        this.beforeDestroy();
        
        // 移除事件监听器
        for (var listener of this.eventListeners) {
            listener.element.off(listener.event, listener.handler);
        }
        
        // 销毁子组件
        for (var child of this.children) {
            if (child.destroy) {
                child.destroy();
            }
        }
        
        // 移除DOM元素
        if (this.element && this.element.parentNode) {
            this.element.parentNode.removeChild(this.element);
        }
        
        this.destroyed();
    }
    
    // 添加事件监听器
    function addEventListener(element, event, handler) {
        element.on(event, handler);
        this.eventListeners.push({ element, event, handler });
    }
    
    // 查找子元素
    function $(selector) {
        return this.element.$(selector);
    }
    
    function $$(selector) {
        return this.element.$$(selector);
    }
    
    // 触发自定义事件
    function emit(eventName, data) {
        var event = new Event(eventName, { bubbles: true, cancelable: true });
        event.detail = data;
        this.element.dispatchEvent(event);
    }
}

7.2 基础组件开发

按钮组件

// 按钮组件
class Button extends Component {
    function this(element, props = {}) {
        // 默认属性
        var defaultProps = {
            type: 'default', // default, primary, success, warning, danger
            size: 'medium',  // small, medium, large
            disabled: false,
            loading: false,
            icon: null,
            text: '按钮'
        };
        
        super(element, Object.assign(defaultProps, props));
    }
    
    function render() {
        var { type, size, disabled, loading, icon, text } = this.props;
        
        // 设置CSS类
        var classes = ['btn', `btn-${type}`, `btn-${size}`];
        
        if (disabled) classes.push('btn-disabled');
        if (loading) classes.push('btn-loading');
        
        this.element.className = classes.join(' ');
        
        // 设置内容
        var content = '';
        
        if (loading) {
            content += '<span class="btn-spinner"></span>';
        } else if (icon) {
            content += `<i class="btn-icon ${icon}"></i>`;
        }
        
        content += `<span class="btn-text">${text}</span>`;
        
        this.element.innerHTML = content;
        
        // 设置属性
        this.element.disabled = disabled || loading;
    }
    
    function bindEvents() {
        var self = this;
        
        this.addEventListener(this.element, 'click', function(evt) {
            if (self.props.disabled || self.props.loading) {
                evt.preventDefault();
                evt.stopPropagation();
                return;
            }
            
            // 触发点击事件
            self.emit('click', {
                button: self,
                originalEvent: evt
            });
        });
        
        // 鼠标悬停效果
        this.addEventListener(this.element, 'mouseenter', function() {
            if (!self.props.disabled && !self.props.loading) {
                self.element.addClass('btn-hover');
            }
        });
        
        this.addEventListener(this.element, 'mouseleave', function() {
            self.element.removeClass('btn-hover');
        });
        
        // 按下效果
        this.addEventListener(this.element, 'mousedown', function() {
            if (!self.props.disabled && !self.props.loading) {
                self.element.addClass('btn-active');
            }
        });
        
        this.addEventListener(this.element, 'mouseup', function() {
            self.element.removeClass('btn-active');
        });
    }
    
    // 设置加载状态
    function setLoading(loading) {
        this.update({ loading: loading });
    }
    
    // 设置禁用状态
    function setDisabled(disabled) {
        this.update({ disabled: disabled });
    }
    
    // 设置文本
    function setText(text) {
        this.update({ text: text });
    }
}

// 使用示例
function createButton() {
    var buttonElement = Element.create('button');
    document.body.append(buttonElement);
    
    var button = new Button(buttonElement, {
        type: 'primary',
        text: '点击我',
        icon: 'icon-star'
    });
    
    // 监听点击事件
    buttonElement.on('click', function(evt) {
        stdout.println('按钮被点击了!');
        
        // 模拟异步操作
        button.setLoading(true);
        
        setTimeout(function() {
            button.setLoading(false);
            button.setText('操作完成');
        }, 2000);
    });
    
    return button;
}

输入框组件

// 输入框组件
class Input extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            type: 'text',
            placeholder: '',
            value: '',
            disabled: false,
            readonly: false,
            maxLength: null,
            pattern: null,
            required: false,
            size: 'medium',
            prefix: null,
            suffix: null,
            clearable: false,
            showPassword: false
        };
        
        super(element, Object.assign(defaultProps, props));
    }
    
    function render() {
        var { type, placeholder, value, disabled, readonly, maxLength, pattern, required, size, prefix, suffix, clearable, showPassword } = this.props;
        
        // 创建输入框容器
        var classes = ['input-wrapper', `input-${size}`];
        if (disabled) classes.push('input-disabled');
        if (readonly) classes.push('input-readonly');
        
        this.element.className = classes.join(' ');
        
        var html = '';
        
        // 前缀
        if (prefix) {
            html += `<span class="input-prefix">${prefix}</span>`;
        }
        
        // 输入框
        var inputType = (type === 'password' && this.state.showPassword) ? 'text' : type;
        var inputAttrs = [
            `type="${inputType}"`,
            `placeholder="${placeholder}"`,
            `value="${value}"`,
            'class="input-field"'
        ];
        
        if (disabled) inputAttrs.push('disabled');
        if (readonly) inputAttrs.push('readonly');
        if (required) inputAttrs.push('required');
        if (maxLength) inputAttrs.push(`maxlength="${maxLength}"`);
        if (pattern) inputAttrs.push(`pattern="${pattern}"`);
        
        html += `<input ${inputAttrs.join(' ')} />`;
        
        // 清除按钮
        if (clearable && value && !disabled && !readonly) {
            html += '<span class="input-clear" title="清除">×</span>';
        }
        
        // 密码显示切换
        if (type === 'password' && showPassword) {
            var eyeIcon = this.state.showPassword ? 'icon-eye-off' : 'icon-eye';
            html += `<span class="input-password-toggle ${eyeIcon}" title="${this.state.showPassword ? '隐藏' : '显示'}密码"></span>`;
        }
        
        // 后缀
        if (suffix) {
            html += `<span class="input-suffix">${suffix}</span>`;
        }
        
        this.element.innerHTML = html;
        
        // 缓存输入框元素
        this.inputElement = this.$('.input-field');
    }
    
    function bindEvents() {
        var self = this;
        
        if (!this.inputElement) return;
        
        // 输入事件
        this.addEventListener(this.inputElement, 'input', function(evt) {
            var value = this.value;
            self.props.value = value;
            
            self.emit('input', {
                value: value,
                input: self,
                originalEvent: evt
            });
            
            // 更新清除按钮
            self.updateClearButton();
        });
        
        // 变化事件
        this.addEventListener(this.inputElement, 'change', function(evt) {
            self.emit('change', {
                value: this.value,
                input: self,
                originalEvent: evt
            });
        });
        
        // 焦点事件
        this.addEventListener(this.inputElement, 'focus', function(evt) {
            self.element.addClass('input-focused');
            self.emit('focus', {
                input: self,
                originalEvent: evt
            });
        });
        
        this.addEventListener(this.inputElement, 'blur', function(evt) {
            self.element.removeClass('input-focused');
            self.emit('blur', {
                input: self,
                originalEvent: evt
            });
        });
        
        // 键盘事件
        this.addEventListener(this.inputElement, 'keydown', function(evt) {
            self.emit('keydown', {
                key: evt.key,
                keyCode: evt.keyCode,
                input: self,
                originalEvent: evt
            });
            
            // Enter键
            if (evt.keyCode === 13) {
                self.emit('enter', {
                    value: this.value,
                    input: self,
                    originalEvent: evt
                });
            }
        });
        
        // 清除按钮
        var clearButton = this.$('.input-clear');
        if (clearButton) {
            this.addEventListener(clearButton, 'click', function(evt) {
                evt.preventDefault();
                self.setValue('');
                self.inputElement.focus();
            });
        }
        
        // 密码显示切换
        var passwordToggle = this.$('.input-password-toggle');
        if (passwordToggle) {
            this.addEventListener(passwordToggle, 'click', function(evt) {
                evt.preventDefault();
                self.togglePasswordVisibility();
            });
        }
    }
    
    function updateClearButton() {
        if (this.props.clearable) {
            this.render(); // 重新渲染以更新清除按钮
        }
    }
    
    function togglePasswordVisibility() {
        this.setState({
            showPassword: !this.state.showPassword
        });
    }
    
    // 获取值
    function getValue() {
        return this.inputElement ? this.inputElement.value : this.props.value;
    }
    
    // 设置值
    function setValue(value) {
        this.props.value = value;
        if (this.inputElement) {
            this.inputElement.value = value;
        }
        this.updateClearButton();
        
        this.emit('change', {
            value: value,
            input: this
        });
    }
    
    // 聚焦
    function focus() {
        if (this.inputElement) {
            this.inputElement.focus();
        }
    }
    
    // 失焦
    function blur() {
        if (this.inputElement) {
            this.inputElement.blur();
        }
    }
    
    // 选择文本
    function select() {
        if (this.inputElement) {
            this.inputElement.select();
        }
    }
    
    // 验证
    function validate() {
        if (!this.inputElement) return true;
        
        var value = this.getValue();
        var { required, pattern, maxLength } = this.props;
        
        // 必填验证
        if (required && (!value || value.trim() === '')) {
            this.setError('此字段为必填项');
            return false;
        }
        
        // 长度验证
        if (maxLength && value.length > maxLength) {
            this.setError(`最多允许${maxLength}个字符`);
            return false;
        }
        
        // 模式验证
        if (pattern && value && !new RegExp(pattern).test(value)) {
            this.setError('格式不正确');
            return false;
        }
        
        this.clearError();
        return true;
    }
    
    // 设置错误
    function setError(message) {
        this.element.addClass('input-error');
        
        var errorElement = this.$('.input-error-message');
        if (!errorElement) {
            errorElement = Element.create('div', {
                class: 'input-error-message',
                text: message
            });
            this.element.append(errorElement);
        } else {
            errorElement.innerText = message;
        }
    }
    
    // 清除错误
    function clearError() {
        this.element.removeClass('input-error');
        var errorElement = this.$('.input-error-message');
        if (errorElement) {
            errorElement.remove();
        }
    }
}

// 使用示例
function createInput() {
    var inputElement = Element.create('div');
    document.body.append(inputElement);
    
    var input = new Input(inputElement, {
        type: 'text',
        placeholder: '请输入用户名',
        clearable: true,
        required: true
    });
    
    // 监听输入事件
    inputElement.on('input', function(evt) {
        stdout.println('输入值: ' + evt.detail.value);
    });
    
    // 监听回车事件
    inputElement.on('enter', function(evt) {
        stdout.println('按下回车,值: ' + evt.detail.value);
        input.validate();
    });
    
    return input;
}

模态框组件

// 模态框组件
class Modal extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            title: '',
            content: '',
            width: '500px',
            height: 'auto',
            closable: true,
            maskClosable: true,
            showFooter: true,
            okText: '确定',
            cancelText: '取消',
            visible: false,
            destroyOnClose: false,
            zIndex: 1000
        };
        
        super(element, Object.assign(defaultProps, props));
    }
    
    function render() {
        var { title, content, width, height, closable, showFooter, okText, cancelText, visible, zIndex } = this.props;
        
        // 设置模态框样式
        this.element.className = 'modal-wrapper';
        this.element.style.display = visible ? 'flex' : 'none';
        this.element.style.zIndex = zIndex;
        
        var html = `
            <div class="modal-mask"></div>
            <div class="modal-container" style="width: ${width}; height: ${height};">
                <div class="modal-header">
                    <div class="modal-title">${title}</div>
                    ${closable ? '<button class="modal-close">×</button>' : ''}
                </div>
                <div class="modal-body">
                    ${content}
                </div>
                ${showFooter ? `
                    <div class="modal-footer">
                        <button class="modal-cancel btn btn-default">${cancelText}</button>
                        <button class="modal-ok btn btn-primary">${okText}</button>
                    </div>
                ` : ''}
            </div>
        `;
        
        this.element.innerHTML = html;
        
        // 缓存元素
        this.maskElement = this.$('.modal-mask');
        this.containerElement = this.$('.modal-container');
        this.bodyElement = this.$('.modal-body');
    }
    
    function bindEvents() {
        var self = this;
        
        // 关闭按钮
        var closeButton = this.$('.modal-close');
        if (closeButton) {
            this.addEventListener(closeButton, 'click', function(evt) {
                evt.preventDefault();
                self.close();
            });
        }
        
        // 确定按钮
        var okButton = this.$('.modal-ok');
        if (okButton) {
            this.addEventListener(okButton, 'click', function(evt) {
                evt.preventDefault();
                
                var result = self.emit('ok', {
                    modal: self,
                    originalEvent: evt
                });
                
                // 如果事件没有被阻止,则关闭模态框
                if (!evt.defaultPrevented) {
                    self.close();
                }
            });
        }
        
        // 取消按钮
        var cancelButton = this.$('.modal-cancel');
        if (cancelButton) {
            this.addEventListener(cancelButton, 'click', function(evt) {
                evt.preventDefault();
                self.cancel();
            });
        }
        
        // 遮罩点击
        if (this.maskElement && this.props.maskClosable) {
            this.addEventListener(this.maskElement, 'click', function(evt) {
                if (evt.target === this) {
                    self.close();
                }
            });
        }
        
        // ESC键关闭
        this.addEventListener(document, 'keydown', function(evt) {
            if (evt.keyCode === 27 && self.props.visible && self.props.closable) {
                self.close();
            }
        });
        
        // 阻止容器点击事件冒泡
        if (this.containerElement) {
            this.addEventListener(this.containerElement, 'click', function(evt) {
                evt.stopPropagation();
            });
        }
    }
    
    // 显示模态框
    function show() {
        this.update({ visible: true });
        
        // 添加动画效果
        setTimeout(function() {
            this.element.addClass('modal-show');
        }.bind(this), 10);
        
        // 聚焦到模态框
        if (this.containerElement) {
            this.containerElement.focus();
        }
        
        this.emit('show', { modal: this });
    }
    
    // 隐藏模态框
    function hide() {
        this.element.removeClass('modal-show');
        
        // 等待动画完成后隐藏
        setTimeout(function() {
            this.update({ visible: false });
            this.emit('hide', { modal: this });
            
            // 如果设置了销毁选项,则销毁组件
            if (this.props.destroyOnClose) {
                this.destroy();
            }
        }.bind(this), 300);
    }
    
    // 关闭模态框
    function close() {
        var result = this.emit('close', { modal: this });
        
        // 如果事件没有被阻止,则隐藏模态框
        if (!result || !result.defaultPrevented) {
            this.hide();
        }
    }
    
    // 取消
    function cancel() {
        this.emit('cancel', { modal: this });
        this.close();
    }
    
    // 设置内容
    function setContent(content) {
        this.update({ content: content });
    }
    
    // 设置标题
    function setTitle(title) {
        this.update({ title: title });
    }
    
    // 获取内容元素
    function getBodyElement() {
        return this.bodyElement;
    }
}

// 模态框管理器
namespace ModalManager {
    var modals = [];
    var zIndexBase = 1000;
    
    function create(props) {
        var modalElement = Element.create('div');
        document.body.append(modalElement);
        
        var modal = new Modal(modalElement, Object.assign({
            zIndex: zIndexBase + modals.length
        }, props));
        
        modals.push(modal);
        
        // 监听销毁事件
        modalElement.on('destroyed', function() {
            var index = modals.indexOf(modal);
            if (index >= 0) {
                modals.splice(index, 1);
            }
        });
        
        return modal;
    }
    
    function alert(message, title = '提示') {
        return new Promise(function(resolve) {
            var modal = create({
                title: title,
                content: message,
                showFooter: true,
                cancelText: '',
                destroyOnClose: true
            });
            
            modal.element.on('ok', function() {
                resolve(true);
            });
            
            modal.element.on('close', function() {
                resolve(false);
            });
            
            modal.show();
        });
    }
    
    function confirm(message, title = '确认') {
        return new Promise(function(resolve) {
            var modal = create({
                title: title,
                content: message,
                showFooter: true,
                destroyOnClose: true
            });
            
            modal.element.on('ok', function() {
                resolve(true);
            });
            
            modal.element.on('cancel', function() {
                resolve(false);
            });
            
            modal.element.on('close', function() {
                resolve(false);
            });
            
            modal.show();
        });
    }
    
    function prompt(message, defaultValue = '', title = '输入') {
        return new Promise(function(resolve) {
            var inputId = 'prompt-input-' + Date.now();
            var content = `
                <div class="prompt-content">
                    <p>${message}</p>
                    <input type="text" id="${inputId}" value="${defaultValue}" class="prompt-input" />
                </div>
            `;
            
            var modal = create({
                title: title,
                content: content,
                showFooter: true,
                destroyOnClose: true
            });
            
            modal.element.on('ok', function(evt) {
                var input = modal.$('#' + inputId);
                var value = input ? input.value : '';
                resolve(value);
            });
            
            modal.element.on('cancel', function() {
                resolve(null);
            });
            
            modal.element.on('close', function() {
                resolve(null);
            });
            
            modal.show();
            
            // 聚焦到输入框
            setTimeout(function() {
                var input = modal.$('#' + inputId);
                if (input) {
                    input.focus();
                    input.select();
                }
            }, 100);
        });
    }
    
    function closeAll() {
        for (var modal of modals.slice()) {
            modal.close();
        }
    }
    
    function getTopModal() {
        return modals.length > 0 ? modals[modals.length - 1] : null;
    }
    
    // 导出函数
    self.create = create;
    self.alert = alert;
    self.confirm = confirm;
    self.prompt = prompt;
    self.closeAll = closeAll;
    self.getTopModal = getTopModal;
}

// 使用示例
function testModal() {
    // 创建自定义模态框
    var modal = ModalManager.create({
        title: '用户信息',
        content: '<p>这是一个自定义模态框</p>',
        width: '400px'
    });
    
    modal.show();
    
    // 使用内置对话框
    setTimeout(function() {
        ModalManager.alert('这是一个警告消息!', '警告')
            .then(function(result) {
                stdout.println('Alert结果: ' + result);
                
                return ModalManager.confirm('确定要删除吗?', '确认删除');
            })
            .then(function(result) {
                stdout.println('Confirm结果: ' + result);
                
                if (result) {
                    return ModalManager.prompt('请输入新名称:', '默认名称', '重命名');
                }
                return null;
            })
            .then(function(result) {
                if (result !== null) {
                    stdout.println('Prompt结果: ' + result);
                }
            });
    }, 2000);
}

7.3 组件通信

父子组件通信

// 父子组件通信示例

// 子组件:计数器
class Counter extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            value: 0,
            min: null,
            max: null,
            step: 1,
            disabled: false
        };
        
        super(element, Object.assign(defaultProps, props));
        
        this.state = {
            value: this.props.value
        };
    }
    
    function render() {
        var { min, max, step, disabled } = this.props;
        var { value } = this.state;
        
        var canDecrease = !disabled && (min === null || value > min);
        var canIncrease = !disabled && (max === null || value < max);
        
        this.element.className = 'counter' + (disabled ? ' counter-disabled' : '');
        
        this.element.innerHTML = `
            <button class="counter-decrease" ${!canDecrease ? 'disabled' : ''}>-</button>
            <span class="counter-value">${value}</span>
            <button class="counter-increase" ${!canIncrease ? 'disabled' : ''}>+</button>
        `;
    }
    
    function bindEvents() {
        var self = this;
        
        // 减少按钮
        var decreaseBtn = this.$('.counter-decrease');
        if (decreaseBtn) {
            this.addEventListener(decreaseBtn, 'click', function() {
                self.decrease();
            });
        }
        
        // 增加按钮
        var increaseBtn = this.$('.counter-increase');
        if (increaseBtn) {
            this.addEventListener(increaseBtn, 'click', function() {
                self.increase();
            });
        }
    }
    
    function decrease() {
        var { min, step } = this.props;
        var newValue = this.state.value - step;
        
        if (min !== null && newValue < min) {
            newValue = min;
        }
        
        this.setValue(newValue);
    }
    
    function increase() {
        var { max, step } = this.props;
        var newValue = this.state.value + step;
        
        if (max !== null && newValue > max) {
            newValue = max;
        }
        
        this.setValue(newValue);
    }
    
    function setValue(value) {
        var oldValue = this.state.value;
        this.setState({ value: value });
        
        // 通知父组件值变化
        this.emit('change', {
            value: value,
            oldValue: oldValue,
            counter: this
        });
    }
    
    function getValue() {
        return this.state.value;
    }
}

// 父组件:购物车项目
class CartItem extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            product: {
                id: null,
                name: '',
                price: 0,
                image: ''
            },
            quantity: 1,
            editable: true
        };
        
        super(element, Object.assign(defaultProps, props));
        
        this.state = {
            quantity: this.props.quantity
        };
    }
    
    function render() {
        var { product, editable } = this.props;
        var { quantity } = this.state;
        
        var total = (product.price * quantity).toFixed(2);
        
        this.element.className = 'cart-item';
        
        this.element.innerHTML = `
            <div class="cart-item-image">
                <img src="${product.image}" alt="${product.name}" />
            </div>
            <div class="cart-item-info">
                <h4 class="cart-item-name">${product.name}</h4>
                <p class="cart-item-price">¥${product.price}</p>
            </div>
            <div class="cart-item-quantity">
                <div class="quantity-counter"></div>
            </div>
            <div class="cart-item-total">
                ¥${total}
            </div>
            <div class="cart-item-actions">
                <button class="cart-item-remove">删除</button>
            </div>
        `;
        
        // 创建数量计数器子组件
        this.createQuantityCounter();
    }
    
    function createQuantityCounter() {
        var counterContainer = this.$('.quantity-counter');
        if (counterContainer) {
            // 销毁旧的计数器
            if (this.quantityCounter) {
                this.quantityCounter.destroy();
            }
            
            // 创建新的计数器
            this.quantityCounter = new Counter(counterContainer, {
                value: this.state.quantity,
                min: 1,
                max: 99,
                disabled: !this.props.editable
            });
            
            // 监听计数器变化
            var self = this;
            counterContainer.on('change', function(evt) {
                self.onQuantityChange(evt.detail.value);
            });
            
            // 添加到子组件列表
            this.children.push(this.quantityCounter);
        }
    }
    
    function bindEvents() {
        var self = this;
        
        // 删除按钮
        var removeBtn = this.$('.cart-item-remove');
        if (removeBtn) {
            this.addEventListener(removeBtn, 'click', function() {
                self.remove();
            });
        }
    }
    
    function onQuantityChange(newQuantity) {
        this.setState({ quantity: newQuantity });
        
        // 通知父组件
        this.emit('quantity-change', {
            productId: this.props.product.id,
            quantity: newQuantity,
            cartItem: this
        });
    }
    
    function remove() {
        this.emit('remove', {
            productId: this.props.product.id,
            cartItem: this
        });
    }
    
    function getTotal() {
        return this.props.product.price * this.state.quantity;
    }
    
    function setQuantity(quantity) {
        this.setState({ quantity: quantity });
        if (this.quantityCounter) {
            this.quantityCounter.setValue(quantity);
        }
    }
}

// 祖父组件:购物车
class ShoppingCart extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            items: [],
            editable: true
        };
        
        super(element, Object.assign(defaultProps, props));
        
        this.state = {
            items: this.props.items.slice()
        };
    }
    
    function render() {
        var { editable } = this.props;
        var { items } = this.state;
        
        var totalAmount = this.calculateTotal();
        var totalItems = items.reduce((sum, item) => sum + item.quantity, 0);
        
        this.element.className = 'shopping-cart';
        
        this.element.innerHTML = `
            <div class="cart-header">
                <h2>购物车 (${totalItems}件商品)</h2>
            </div>
            <div class="cart-items"></div>
            <div class="cart-footer">
                <div class="cart-total">
                    <span class="total-label">总计:</span>
                    <span class="total-amount">¥${totalAmount.toFixed(2)}</span>
                </div>
                <div class="cart-actions">
                    <button class="cart-clear">清空购物车</button>
                    <button class="cart-checkout">结算</button>
                </div>
            </div>
        `;
        
        // 创建购物车项目子组件
        this.createCartItems();
    }
    
    function createCartItems() {
        var itemsContainer = this.$('.cart-items');
        if (!itemsContainer) return;
        
        // 销毁旧的子组件
        for (var child of this.children) {
            child.destroy();
        }
        this.children = [];
        
        // 清空容器
        itemsContainer.innerHTML = '';
        
        // 创建新的子组件
        for (var item of this.state.items) {
            var itemElement = Element.create('div');
            itemsContainer.append(itemElement);
            
            var cartItem = new CartItem(itemElement, {
                product: item.product,
                quantity: item.quantity,
                editable: this.props.editable
            });
            
            // 监听子组件事件
            this.setupCartItemEvents(cartItem, item);
            
            this.children.push(cartItem);
        }
    }
    
    function setupCartItemEvents(cartItem, item) {
        var self = this;
        
        // 监听数量变化
        cartItem.element.on('quantity-change', function(evt) {
            self.updateItemQuantity(evt.detail.productId, evt.detail.quantity);
        });
        
        // 监听删除事件
        cartItem.element.on('remove', function(evt) {
            self.removeItem(evt.detail.productId);
        });
    }
    
    function bindEvents() {
        var self = this;
        
        // 清空购物车
        var clearBtn = this.$('.cart-clear');
        if (clearBtn) {
            this.addEventListener(clearBtn, 'click', function() {
                self.clear();
            });
        }
        
        // 结算
        var checkoutBtn = this.$('.cart-checkout');
        if (checkoutBtn) {
            this.addEventListener(checkoutBtn, 'click', function() {
                self.checkout();
            });
        }
    }
    
    function addItem(product, quantity = 1) {
        var existingItem = this.state.items.find(item => item.product.id === product.id);
        
        if (existingItem) {
            existingItem.quantity += quantity;
        } else {
            this.state.items.push({
                product: product,
                quantity: quantity
            });
        }
        
        this.update();
        
        this.emit('item-added', {
            product: product,
            quantity: quantity,
            cart: this
        });
    }
    
    function removeItem(productId) {
        var index = this.state.items.findIndex(item => item.product.id === productId);
        if (index >= 0) {
            var removedItem = this.state.items.splice(index, 1)[0];
            this.update();
            
            this.emit('item-removed', {
                product: removedItem.product,
                cart: this
            });
        }
    }
    
    function updateItemQuantity(productId, quantity) {
        var item = this.state.items.find(item => item.product.id === productId);
        if (item) {
            item.quantity = quantity;
            this.update();
            
            this.emit('item-updated', {
                product: item.product,
                quantity: quantity,
                cart: this
            });
        }
    }
    
    function clear() {
        this.setState({ items: [] });
        
        this.emit('cleared', {
            cart: this
        });
    }
    
    function checkout() {
        var total = this.calculateTotal();
        var items = this.state.items.slice();
        
        this.emit('checkout', {
            items: items,
            total: total,
            cart: this
        });
    }
    
    function calculateTotal() {
        return this.state.items.reduce((total, item) => {
            return total + (item.product.price * item.quantity);
        }, 0);
    }
    
    function getItemCount() {
        return this.state.items.reduce((count, item) => count + item.quantity, 0);
    }
    
    function isEmpty() {
        return this.state.items.length === 0;
    }
}

// 使用示例
function createShoppingCart() {
    var cartElement = Element.create('div');
    document.body.append(cartElement);
    
    var cart = new ShoppingCart(cartElement, {
        items: [
            {
                product: {
                    id: 1,
                    name: 'iPhone 15',
                    price: 5999,
                    image: 'iphone15.jpg'
                },
                quantity: 1
            },
            {
                product: {
                    id: 2,
                    name: 'MacBook Pro',
                    price: 12999,
                    image: 'macbook.jpg'
                },
                quantity: 2
            }
        ]
    });
    
    // 监听购物车事件
    cartElement.on('item-added', function(evt) {
        stdout.println('添加商品: ' + evt.detail.product.name);
    });
    
    cartElement.on('item-removed', function(evt) {
        stdout.println('删除商品: ' + evt.detail.product.name);
    });
    
    cartElement.on('checkout', function(evt) {
        stdout.println('结算金额: ¥' + evt.detail.total.toFixed(2));
    });
    
    return cart;
}

兄弟组件通信

// 事件总线
class EventBus {
    function this() {
        this.events = new Map();
    }
    
    // 订阅事件
    function on(eventName, callback) {
        if (!this.events.has(eventName)) {
            this.events.set(eventName, []);
        }
        
        this.events.get(eventName).push(callback);
        
        // 返回取消订阅函数
        var self = this;
        return function off() {
            var callbacks = self.events.get(eventName);
            if (callbacks) {
                var index = callbacks.indexOf(callback);
                if (index >= 0) {
                    callbacks.splice(index, 1);
                }
            }
        };
    }
    
    // 触发事件
    function emit(eventName, data) {
        var callbacks = this.events.get(eventName);
        if (callbacks) {
            for (var callback of callbacks) {
                try {
                    callback(data);
                } catch (error) {
                    stderr.println(`事件回调错误: ${error.message}`);
                }
            }
        }
    }
    
    // 一次性订阅
    function once(eventName, callback) {
        var self = this;
        var off = this.on(eventName, function(data) {
            off();
            callback(data);
        });
        return off;
    }
    
    // 移除所有监听器
    function removeAllListeners(eventName) {
        if (eventName) {
            this.events.delete(eventName);
        } else {
            this.events.clear();
        }
    }
}

// 全局事件总线
var globalEventBus = new EventBus();

// 产品列表组件
class ProductList extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            products: []
        };
        
        super(element, Object.assign(defaultProps, props));
        
        this.state = {
            products: this.props.products.slice()
        };
    }
    
    function render() {
        var { products } = this.state;
        
        this.element.className = 'product-list';
        
        var html = '<div class="product-list-header"><h3>商品列表</h3></div>';
        html += '<div class="product-items">';
        
        for (var product of products) {
            html += `
                <div class="product-item" data-product-id="${product.id}">
                    <img src="${product.image}" alt="${product.name}" class="product-image" />
                    <h4 class="product-name">${product.name}</h4>
                    <p class="product-price">¥${product.price}</p>
                    <button class="product-add-to-cart" data-product-id="${product.id}">加入购物车</button>
                </div>
            `;
        }
        
        html += '</div>';
        this.element.innerHTML = html;
    }
    
    function bindEvents() {
        var self = this;
        
        // 加入购物车按钮
        this.addEventListener(this.element, 'click', function(evt) {
            if (evt.target.classList.contains('product-add-to-cart')) {
                var productId = parseInt(evt.target.getAttribute('data-product-id'));
                var product = self.state.products.find(p => p.id === productId);
                
                if (product) {
                    // 通过事件总线通知购物车组件
                    globalEventBus.emit('add-to-cart', {
                        product: product,
                        quantity: 1
                    });
                    
                    // 显示添加成功提示
                    self.showAddToCartSuccess(product);
                }
            }
        });
    }
    
    function showAddToCartSuccess(product) {
        // 创建提示元素
        var toast = Element.create('div', {
            class: 'add-to-cart-toast',
            text: `${product.name} 已加入购物车`
        });
        
        document.body.append(toast);
        
        // 自动移除
        setTimeout(function() {
            toast.remove();
        }, 2000);
    }
}

// 购物车摘要组件
class CartSummary extends Component {
    function this(element, props = {}) {
        var defaultProps = {
            itemCount: 0,
            totalAmount: 0
        };
        
        super(element, Object.assign(defaultProps, props));
        
        this.state = {
            itemCount: this.props.itemCount,
            totalAmount: this.props.totalAmount
        };
        
        // 订阅事件总线
        this.setupEventListeners();
    }
    
    function setupEventListeners() {
        var self = this;
        
        // 监听添加到购物车事件
        this.unsubscribeAddToCart = globalEventBus.on('add-to-cart', function(data) {
            self.onAddToCart(data.product, data.quantity);
        });
        
        // 监听购物车更新事件
        this.unsubscribeCartUpdate = globalEventBus.on('cart-updated', function(data) {
            self.updateSummary(data.itemCount, data.totalAmount);
        });
    }
    
    function render() {
        var { itemCount, totalAmount } = this.state;
        
        this.element.className = 'cart-summary';
        
        this.element.innerHTML = `
            <div class="cart-summary-header">
                <h4>购物车</h4>
            </div>
            <div class="cart-summary-content">
                <div class="cart-item-count">
                    <span class="label">商品数量:</span>
                    <span class="value">${itemCount}</span>
                </div>
                <div class="cart-total-amount">
                    <span class="label">总金额:</span>
                    <span class="value">¥${totalAmount.toFixed(2)}</span>
                </div>
            </div>
            <div class="cart-summary-actions">
                <button class="view-cart-btn">查看购物车</button>
            </div>
        `;
    }
    
    function bindEvents() {
        var self = this;
        
        // 查看购物车按钮
        var viewCartBtn = this.$('.view-cart-btn');
        if (viewCartBtn) {
            this.addEventListener(viewCartBtn, 'click', function() {
                // 通过事件总线通知显示购物车
                globalEventBus.emit('show-cart');
            });
        }
    }
    
    function onAddToCart(product, quantity) {
        this.setState({
            itemCount: this.state.itemCount + quantity,
            totalAmount: this.state.totalAmount + (product.price * quantity)
        });
        
        // 添加动画效果
        this.element.addClass('cart-updated');
        setTimeout(function() {
            this.element.removeClass('cart-updated');
        }.bind(this), 500);
    }
    
    function updateSummary(itemCount, totalAmount) {
        this.setState({
            itemCount: itemCount,
            totalAmount: totalAmount
        });
    }
    
    function beforeDestroy() {
        // 取消事件订阅
        if (this.unsubscribeAddToCart) {
            this.unsubscribeAddToCart();
        }
        if (this.unsubscribeCartUpdate) {
            this.unsubscribeCartUpdate();
        }
    }
}

// 使用示例
function createProductShop() {
    // 创建产品列表
    var productListElement = Element.create('div');
    document.body.append(productListElement);
    
    var productList = new ProductList(productListElement, {
        products: [
            { id: 1, name: 'iPhone 15', price: 5999, image: 'iphone15.jpg' },
            { id: 2, name: 'MacBook Pro', price: 12999, image: 'macbook.jpg' },
            { id: 3, name: 'iPad Air', price: 4599, image: 'ipad.jpg' }
        ]
    });
    
    // 创建购物车摘要
    var cartSummaryElement = Element.create('div');
    document.body.append(cartSummaryElement);
    
    var cartSummary = new CartSummary(cartSummaryElement);
    
    // 监听显示购物车事件
    globalEventBus.on('show-cart', function() {
        stdout.println('显示购物车详情');
        // 这里可以显示完整的购物车组件
    });
    
    return { productList, cartSummary };
}

7.4 组件生命周期

生命周期管理

// 增强的组件基类,包含完整的生命周期管理
class AdvancedComponent extends Component {
    function this(element, props = {}) {
        this.lifecycleState = 'created';
        this.updateQueue = [];
        this.isUpdating = false;
        this.shouldUpdateFlag = true;
        
        super(element, props);
    }
    
    function init() {
        this.lifecycleState = 'beforeMount';
        this.beforeMount();
        
        this.lifecycleState = 'mounting';
        this.render();
        
        this.lifecycleState = 'mounted';
        this.mounted();
        this.bindEvents();
    }
    
    // 生命周期钩子
    function beforeMount() {
        // 组件挂载前,可以进行数据初始化
        this.onLifecycleHook('beforeMount');
    }
    
    function mounted() {
        // 组件挂载后,可以访问DOM,设置定时器等
        this.onLifecycleHook('mounted');
    }
    
    function beforeUpdate(newProps, newState) {
        // 组件更新前,可以进行性能优化判断
        this.onLifecycleHook('beforeUpdate', { newProps, newState });
        return this.shouldUpdate(newProps, newState);
    }
    
    function updated(oldProps, oldState) {
        // 组件更新后,可以进行DOM操作
        this.onLifecycleHook('updated', { oldProps, oldState });
    }
    
    function beforeDestroy() {
        // 组件销毁前,清理资源
        this.lifecycleState = 'beforeDestroy';
        this.onLifecycleHook('beforeDestroy');
        this.cleanup();
    }
    
    function destroyed() {
        // 组件销毁后
        this.lifecycleState = 'destroyed';
        this.onLifecycleHook('destroyed');
    }
    
    // 错误处理生命周期
    function errorCaptured(error, instance, info) {
        // 捕获子组件错误
        this.onLifecycleHook('errorCaptured', { error, instance, info });
        stderr.println(`组件错误: ${error.message}`);
        return false; // 阻止错误继续传播
    }
    
    // 性能优化:判断是否需要更新
    function shouldUpdate(newProps, newState) {
        // 默认实现:浅比较props和state
        if (!this.shouldUpdateFlag) return false;
        
        // 比较props
        for (var key in newProps) {
            if (newProps[key] !== this.props[key]) {
                return true;
            }
        }
        
        // 比较state
        for (var key in newState) {
            if (newState[key] !== this.state[key]) {
                return true;
            }
        }
        
        return false;
    }
    
    // 更新组件(增强版)
    function update(newProps = {}, newState = {}) {
        if (this.lifecycleState === 'destroyed') {
            stderr.println('尝试更新已销毁的组件');
            return;
        }
        
        // 如果正在更新,加入队列
        if (this.isUpdating) {
            this.updateQueue.push({ newProps, newState });
            return;
        }
        
        this.isUpdating = true;
        
        try {
            var oldProps = Object.assign({}, this.props);
            var oldState = Object.assign({}, this.state);
            
            var mergedProps = Object.assign({}, this.props, newProps);
            var mergedState = Object.assign({}, this.state, newState);
            
            // 检查是否需要更新
            if (this.beforeUpdate(mergedProps, mergedState)) {
                this.lifecycleState = 'updating';
                
                // 更新props和state
                this.props = mergedProps;
                this.state = mergedState;
                
                // 重新渲染
                this.render();
                
                this.lifecycleState = 'updated';
                this.updated(oldProps, oldState);
            }
        } catch (error) {
            this.handleError(error, 'update');
        } finally {
            this.isUpdating = false;
            
            // 处理更新队列
            this.processUpdateQueue();
        }
    }
    
    // 处理更新队列
    function processUpdateQueue() {
        if (this.updateQueue.length > 0) {
            var nextUpdate = this.updateQueue.shift();
            setTimeout(function() {
                this.update(nextUpdate.newProps, nextUpdate.newState);
            }.bind(this), 0);
        }
    }
    
    // 错误处理
    function handleError(error, context) {
        var errorInfo = {
            error: error,
            context: context,
            component: this.constructor.name,
            props: this.props,
            state: this.state
        };
        
        // 触发错误事件
        this.emit('error', errorInfo);
        
        // 如果有父组件,通知父组件
        if (this.parent && this.parent.errorCaptured) {
            this.parent.errorCaptured(error, this, context);
        }
        
        stderr.println(`组件错误 [${context}]: ${error.message}`);
    }
    
    // 生命周期钩子回调
    function onLifecycleHook(hookName, data) {
        this.emit('lifecycle:' + hookName, {
            component: this,
            hookName: hookName,
            data: data
        });
    }
    
    // 清理资源
    function cleanup() {
        // 清理定时器
        if (this.timers) {
            for (var timer of this.timers) {
                clearTimeout(timer);
            }
            this.timers = [];
        }
        
        // 清理事件监听器
        if (this.globalEventListeners) {
            for (var listener of this.globalEventListeners) {
                listener.element.off(listener.event, listener.handler);
            }
            this.globalEventListeners = [];
        }
        
        // 清理观察者
        if (this.observers) {
            for (var observer of this.observers) {
                if (observer.disconnect) {
                    observer.disconnect();
                }
            }
            this.observers = [];
        }
    }
    
    // 安全的定时器
    function setTimeout(callback, delay) {
        if (!this.timers) this.timers = [];
        
        var timer = window.setTimeout(function() {
            if (this.lifecycleState !== 'destroyed') {
                callback.call(this);
            }
        }.bind(this), delay);
        
        this.timers.push(timer);
        return timer;
    }
    
    // 安全的间隔定时器
    function setInterval(callback, interval) {
        if (!this.timers) this.timers = [];
        
        var timer = window.setInterval(function() {
            if (this.lifecycleState !== 'destroyed') {
                callback.call(this);
            } else {
                clearInterval(timer);
            }
        }.bind(this), interval);
        
        this.timers.push(timer);
        return timer;
    }
    
    // 获取生命周期状态
    function getLifecycleState() {
        return this.lifecycleState;
    }
    
    // 强制更新
    function forceUpdate() {
        this.shouldUpdateFlag = false;
        this.update();
        this.shouldUpdateFlag = true;
    }
}

// 生命周期示例组件
class LifecycleDemo extends AdvancedComponent {
    function this(element, props = {}) {
        super(element, props);
        
        this.state = {
            count: 0,
            message: 'Hello World'
        };
    }
    
    function beforeMount() {
        stdout.println('LifecycleDemo: beforeMount');
        // 可以在这里进行数据预处理
    }
    
    function mounted() {
        stdout.println('LifecycleDemo: mounted');
        
        // 设置定时器
        this.setTimeout(function() {
            this.setState({ message: 'Component mounted!' });
        }, 1000);
        
        // 设置间隔定时器
        this.setInterval(function() {
            this.setState({ count: this.state.count + 1 });
        }, 2000);
    }
    
    function beforeUpdate(newProps, newState) {
        stdout.println('LifecycleDemo: beforeUpdate');
        
        // 性能优化:如果count大于10就不再更新
        if (newState.count > 10) {
            return false;
        }
        
        return super.beforeUpdate(newProps, newState);
    }
    
    function updated(oldProps, oldState) {
        stdout.println('LifecycleDemo: updated');
        stdout.println(`Count changed from ${oldState.count} to ${this.state.count}`);
    }
    
    function beforeDestroy() {
        stdout.println('LifecycleDemo: beforeDestroy');
        super.beforeDestroy();
    }
    
    function destroyed() {
        stdout.println('LifecycleDemo: destroyed');
        super.destroyed();
    }
    
    function render() {
        var { count, message } = this.state;
        
        this.element.innerHTML = `
            <div class="lifecycle-demo">
                <h3>生命周期演示</h3>
                <p>消息: ${message}</p>
                <p>计数: ${count}</p>
                <button class="increment-btn">手动增加</button>
                <button class="destroy-btn">销毁组件</button>
            </div>
        `;
    }
    
    function bindEvents() {
        var self = this;
        
        // 手动增加按钮
        var incrementBtn = this.$('.increment-btn');
        if (incrementBtn) {
            this.addEventListener(incrementBtn, 'click', function() {
                self.setState({ count: self.state.count + 1 });
            });
        }
        
        // 销毁按钮
        var destroyBtn = this.$('.destroy-btn');
        if (destroyBtn) {
            this.addEventListener(destroyBtn, 'click', function() {
                self.destroy();
            });
        }
    }
    
    function errorCaptured(error, instance, info) {
        stdout.println(`捕获到错误: ${error.message}`);
        return true; // 阻止错误继续传播
    }
}

// 使用示例
function createLifecycleDemo() {
    var demoElement = Element.create('div');
    document.body.append(demoElement);
    
    var demo = new LifecycleDemo(demoElement);
    
    // 监听生命周期事件
    demoElement.on('lifecycle:mounted', function(evt) {
        stdout.println('外部监听到mounted事件');
    });
    
    demoElement.on('lifecycle:updated', function(evt) {
        stdout.println('外部监听到updated事件');
    });
    
    demoElement.on('error', function(evt) {
        stdout.println('外部监听到错误事件: ' + evt.detail.error.message);
    });
    
    return demo;
}

7.5 高级组件模式

高阶组件(HOC)

// 高阶组件:为组件添加加载状态
function withLoading(WrappedComponent) {
    return class LoadingWrapper extends AdvancedComponent {
        function this(element, props = {}) {
            super(element, props);
            
            this.state = {
                loading: props.loading || false,
                error: null
            };
        }
        
        function render() {
            var { loading, error } = this.state;
            
            if (error) {
                this.element.innerHTML = `
                    <div class="error-wrapper">
                        <p class="error-message">加载失败: ${error}</p>
                        <button class="retry-btn">重试</button>
                    </div>
                `;
                return;
            }
            
            if (loading) {
                this.element.innerHTML = `
                    <div class="loading-wrapper">
                        <div class="loading-spinner"></div>
                        <p class="loading-text">加载中...</p>
                    </div>
                `;
                return;
            }
            
            // 渲染包装的组件
            this.renderWrappedComponent();
        }
        
        function renderWrappedComponent() {
            if (!this.wrappedComponent) {
                this.wrappedComponent = new WrappedComponent(this.element, this.props);
                this.children.push(this.wrappedComponent);
            } else {
                this.wrappedComponent.update(this.props);
            }
        }
        
        function bindEvents() {
            var self = this;
            
            // 重试按钮
            var retryBtn = this.$('.retry-btn');
            if (retryBtn) {
                this.addEventListener(retryBtn, 'click', function() {
                    self.retry();
                });
            }
        }
        
        function setLoading(loading) {
            this.setState({ loading: loading, error: null });
        }
        
        function setError(error) {
            this.setState({ loading: false, error: error });
        }
        
        function retry() {
            this.setState({ loading: true, error: null });
            this.emit('retry');
        }
        
        function getWrappedComponent() {
            return this.wrappedComponent;
        }
    };
}

// 高阶组件:为组件添加权限控制
function withPermission(WrappedComponent, requiredPermissions = []) {
    return class PermissionWrapper extends AdvancedComponent {
        function this(element, props = {}) {
            super(element, props);
            
            this.state = {
                hasPermission: this.checkPermissions()
            };
        }
        
        function checkPermissions() {
            // 模拟权限检查
            var userPermissions = this.props.userPermissions || [];
            
            return requiredPermissions.every(permission => 
                userPermissions.includes(permission)
            );
        }
        
        function render() {
            if (!this.state.hasPermission) {
                this.element.innerHTML = `
                    <div class="permission-denied">
                        <p>您没有权限访问此内容</p>
                        <p>需要权限: ${requiredPermissions.join(', ')}</p>
                    </div>
                `;
                return;
            }
            
            // 渲染包装的组件
            if (!this.wrappedComponent) {
                this.wrappedComponent = new WrappedComponent(this.element, this.props);
                this.children.push(this.wrappedComponent);
            } else {
                this.wrappedComponent.update(this.props);
            }
        }
        
        function updatePermissions(newPermissions) {
            this.props.userPermissions = newPermissions;
            this.setState({ hasPermission: this.checkPermissions() });
        }
    };
}

// 使用高阶组件
class UserProfile extends AdvancedComponent {
    function render() {
        this.element.innerHTML = `
            <div class="user-profile">
                <h3>用户资料</h3>
                <p>姓名: ${this.props.user.name}</p>
                <p>邮箱: ${this.props.user.email}</p>
            </div>
        `;
    }
}

// 创建增强的组件
var LoadingUserProfile = withLoading(UserProfile);
var ProtectedUserProfile = withPermission(LoadingUserProfile, ['user:read']);

function createEnhancedUserProfile() {
    var element = Element.create('div');
    document.body.append(element);
    
    var profile = new ProtectedUserProfile(element, {
        user: { name: 'John Doe', email: 'john@example.com' },
        userPermissions: ['user:read'],
        loading: true
    });
    
    // 模拟异步加载
    setTimeout(function() {
        profile.setLoading(false);
    }, 2000);
    
    return profile;
}

渲染属性模式(Render Props)

// 渲染属性组件:数据获取器
class DataFetcher extends AdvancedComponent {
    function this(element, props = {}) {
        super(element, props);
        
        this.state = {
            data: null,
            loading: false,
            error: null
        };
    }
    
    function mounted() {
        if (this.props.url) {
            this.fetchData();
        }
    }
    
    function fetchData() {
        this.setState({ loading: true, error: null });
        
        // 模拟异步数据获取
        setTimeout(function() {
            try {
                // 模拟API调用
                var mockData = {
                    users: [
                        { id: 1, name: 'Alice', email: 'alice@example.com' },
                        { id: 2, name: 'Bob', email: 'bob@example.com' }
                    ]
                };
                
                this.setState({
                    data: mockData,
                    loading: false
                });
            } catch (error) {
                this.setState({
                    error: error.message,
                    loading: false
                });
            }
        }.bind(this), 1000);
    }
    
    function render() {
        var { data, loading, error } = this.state;
        var { render } = this.props;
        
        if (typeof render === 'function') {
            // 调用渲染函数,传递状态和方法
            var renderResult = render({
                data: data,
                loading: loading,
                error: error,
                refetch: this.fetchData.bind(this)
            });
            
            this.element.innerHTML = renderResult;
        } else {
            this.element.innerHTML = '<div>No render function provided</div>';
        }
    }
    
    function refetch() {
        this.fetchData();
    }
}

// 使用渲染属性
function createDataFetcherExample() {
    var element = Element.create('div');
    document.body.append(element);
    
    var fetcher = new DataFetcher(element, {
        url: '/api/users',
        render: function(props) {
            var { data, loading, error, refetch } = props;
            
            if (loading) {
                return '<div class="loading">加载中...</div>';
            }
            
            if (error) {
                return `
                    <div class="error">
                        <p>错误: ${error}</p>
                        <button onclick="${refetch}">重试</button>
                    </div>
                `;
            }
            
            if (data && data.users) {
                var userList = data.users.map(user => 
                    `<li>${user.name} - ${user.email}</li>`
                ).join('');
                
                return `
                    <div class="user-list">
                        <h3>用户列表</h3>
                        <ul>${userList}</ul>
                        <button onclick="${refetch}">刷新</button>
                    </div>
                `;
            }
            
            return '<div>暂无数据</div>';
        }
    });
    
    return fetcher;
}

// 鼠标位置跟踪器(另一个渲染属性示例)
class MouseTracker extends AdvancedComponent {
    function this(element, props = {}) {
        super(element, props);
        
        this.state = {
            x: 0,
            y: 0
        };
    }
    
    function mounted() {
        this.addEventListener(document, 'mousemove', this.handleMouseMove.bind(this));
    }
    
    function handleMouseMove(event) {
        this.setState({
            x: event.clientX,
            y: event.clientY
        });
    }
    
    function render() {
        var { x, y } = this.state;
        var { render } = this.props;
        
        if (typeof render === 'function') {
            var renderResult = render({ x, y });
            this.element.innerHTML = renderResult;
        } else {
            this.element.innerHTML = `<div>鼠标位置: (${x}, ${y})</div>`;
        }
    }
}

function createMouseTrackerExample() {
    var element = Element.create('div');
    document.body.append(element);
    
    var tracker = new MouseTracker(element, {
        render: function(mouse) {
            return `
                <div class="mouse-tracker">
                    <h3>鼠标跟踪器</h3>
                    <p>当前位置: (${mouse.x}, ${mouse.y})</p>
                    <div class="crosshair" style="left: ${mouse.x}px; top: ${mouse.y}px;"></div>
                </div>
            `;
        }
    });
    
    return tracker;
}

组件组合模式

// 复合组件:卡片组件
class Card extends AdvancedComponent {
    function render() {
        this.element.className = 'card';
        
        // 保留子元素内容
        // 卡片组件作为容器,不修改内部内容
    }
    
    static Header = class CardHeader extends AdvancedComponent {
        function render() {
            this.element.className = 'card-header';
            
            if (this.props.title) {
                this.element.innerHTML = `<h3 class="card-title">${this.props.title}</h3>`;
            }
        }
    };
    
    static Body = class CardBody extends AdvancedComponent {
        function render() {
            this.element.className = 'card-body';
            
            if (this.props.content) {
                this.element.innerHTML = this.props.content;
            }
        }
    };
    
    static Footer = class CardFooter extends AdvancedComponent {
        function render() {
            this.element.className = 'card-footer';
            
            if (this.props.actions) {
                var actionsHtml = this.props.actions.map(action => 
                    `<button class="card-action" data-action="${action.name}">${action.label}</button>`
                ).join('');
                
                this.element.innerHTML = actionsHtml;
            }
        }
        
        function bindEvents() {
            var self = this;
            
            this.addEventListener(this.element, 'click', function(evt) {
                if (evt.target.classList.contains('card-action')) {
                    var actionName = evt.target.getAttribute('data-action');
                    var action = self.props.actions.find(a => a.name === actionName);
                    
                    if (action && action.handler) {
                        action.handler(evt);
                    }
                    
                    self.emit('action', {
                        action: actionName,
                        originalEvent: evt
                    });
                }
            });
        }
    };
}

// 使用复合组件
function createCardExample() {
    // 创建卡片容器
    var cardElement = Element.create('div');
    document.body.append(cardElement);
    
    var card = new Card(cardElement);
    
    // 创建卡片头部
    var headerElement = Element.create('div');
    cardElement.append(headerElement);
    
    var header = new Card.Header(headerElement, {
        title: '用户信息'
    });
    
    // 创建卡片主体
    var bodyElement = Element.create('div');
    cardElement.append(bodyElement);
    
    var body = new Card.Body(bodyElement, {
        content: `
            <p><strong>姓名:</strong> 张三</p>
            <p><strong>邮箱:</strong> zhangsan@example.com</p>
            <p><strong>部门:</strong> 技术部</p>
        `
    });
    
    // 创建卡片底部
    var footerElement = Element.create('div');
    cardElement.append(footerElement);
    
    var footer = new Card.Footer(footerElement, {
        actions: [
            {
                name: 'edit',
                label: '编辑',
                handler: function() {
                    stdout.println('编辑用户');
                }
            },
            {
                name: 'delete',
                label: '删除',
                handler: function() {
                    stdout.println('删除用户');
                }
            }
        ]
    });
    
    // 监听底部动作
    footerElement.on('action', function(evt) {
        stdout.println('卡片动作: ' + evt.detail.action);
    });
    
    return { card, header, body, footer };
}

// 表单复合组件
class Form extends AdvancedComponent {
    function this(element, props = {}) {
        super(element, props);
        
        this.fields = new Map();
        this.validators = [];
    }
    
    function render() {
        this.element.className = 'form';
        
        if (this.props.title) {
            var titleElement = Element.create('h3', {
                class: 'form-title',
                text: this.props.title
            });
            this.element.prepend(titleElement);
        }
    }
    
    function addField(name, component) {
        this.fields.set(name, component);
    }
    
    function getFieldValue(name) {
        var field = this.fields.get(name);
        return field && field.getValue ? field.getValue() : null;
    }
    
    function setFieldValue(name, value) {
        var field = this.fields.get(name);
        if (field && field.setValue) {
            field.setValue(value);
        }
    }
    
    function getFormData() {
        var data = {};
        
        for (var [name, field] of this.fields) {
            data[name] = this.getFieldValue(name);
        }
        
        return data;
    }
    
    function validate() {
        var isValid = true;
        var errors = {};
        
        // 验证各个字段
        for (var [name, field] of this.fields) {
            if (field.validate && !field.validate()) {
                isValid = false;
                errors[name] = '字段验证失败';
            }
        }
        
        // 执行表单级验证器
        for (var validator of this.validators) {
            var result = validator(this.getFormData());
            if (!result.valid) {
                isValid = false;
                Object.assign(errors, result.errors);
            }
        }
        
        return { valid: isValid, errors: errors };
    }
    
    function addValidator(validator) {
        this.validators.push(validator);
    }
    
    function submit() {
        var validation = this.validate();
        
        if (validation.valid) {
            var formData = this.getFormData();
            
            this.emit('submit', {
                data: formData,
                form: this
            });
        } else {
            this.emit('validation-error', {
                errors: validation.errors,
                form: this
            });
        }
        
        return validation.valid;
    }
    
    static Field = class FormField extends AdvancedComponent {
        function this(element, props = {}) {
            super(element, props);
            
            // 注册到父表单
            if (this.props.form && this.props.name) {
                this.props.form.addField(this.props.name, this);
            }
        }
        
        function render() {
            var { label, name, required, type = 'text' } = this.props;
            
            this.element.className = 'form-field';
            
            var html = '';
            
            if (label) {
                html += `<label class="field-label" for="${name}">${label}${required ? ' *' : ''}</label>`;
            }
            
            html += `<input type="${type}" id="${name}" name="${name}" class="field-input" ${required ? 'required' : ''} />`;
            html += '<div class="field-error"></div>';
            
            this.element.innerHTML = html;
            
            this.inputElement = this.$('.field-input');
        }
        
        function getValue() {
            return this.inputElement ? this.inputElement.value : '';
        }
        
        function setValue(value) {
            if (this.inputElement) {
                this.inputElement.value = value;
            }
        }
        
        function validate() {
            var value = this.getValue();
            var { required, pattern } = this.props;
            
            // 必填验证
            if (required && (!value || value.trim() === '')) {
                this.setError('此字段为必填项');
                return false;
            }
            
            // 模式验证
            if (pattern && value && !new RegExp(pattern).test(value)) {
                this.setError('格式不正确');
                return false;
            }
            
            this.clearError();
            return true;
        }
        
        function setError(message) {
            this.element.addClass('field-error-state');
            var errorElement = this.$('.field-error');
            if (errorElement) {
                errorElement.innerText = message;
            }
        }
        
        function clearError() {
            this.element.removeClass('field-error-state');
            var errorElement = this.$('.field-error');
            if (errorElement) {
                errorElement.innerText = '';
            }
        }
    };
    
    static SubmitButton = class FormSubmitButton extends AdvancedComponent {
        function render() {
            var { text = '提交', form } = this.props;
            
            this.element.innerHTML = `<button type="submit" class="form-submit">${text}</button>`;
        }
        
        function bindEvents() {
            var self = this;
            
            this.addEventListener(this.element, 'click', function(evt) {
                evt.preventDefault();
                
                if (self.props.form) {
                    self.props.form.submit();
                }
            });
        }
    };
}

// 使用表单复合组件
function createFormExample() {
    var formElement = Element.create('form');
    document.body.append(formElement);
    
    var form = new Form(formElement, {
        title: '用户注册'
    });
    
    // 添加字段
    var nameFieldElement = Element.create('div');
    formElement.append(nameFieldElement);
    
    var nameField = new Form.Field(nameFieldElement, {
        name: 'name',
        label: '姓名',
        required: true,
        form: form
    });
    
    var emailFieldElement = Element.create('div');
    formElement.append(emailFieldElement);
    
    var emailField = new Form.Field(emailFieldElement, {
        name: 'email',
        label: '邮箱',
        type: 'email',
        required: true,
        pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
        form: form
    });
    
    var passwordFieldElement = Element.create('div');
    formElement.append(passwordFieldElement);
    
    var passwordField = new Form.Field(passwordFieldElement, {
        name: 'password',
        label: '密码',
        type: 'password',
        required: true,
        form: form
    });
    
    // 添加提交按钮
    var submitButtonElement = Element.create('div');
    formElement.append(submitButtonElement);
    
    var submitButton = new Form.SubmitButton(submitButtonElement, {
        text: '注册',
        form: form
    });
    
    // 添加表单级验证器
    form.addValidator(function(data) {
        var errors = {};
        
        if (data.password && data.password.length < 6) {
            errors.password = '密码长度至少6位';
        }
        
        return {
            valid: Object.keys(errors).length === 0,
            errors: errors
        };
    });
    
    // 监听表单事件
    formElement.on('submit', function(evt) {
        stdout.println('表单提交成功:');
        stdout.println(JSON.stringify(evt.detail.data, null, 2));
    });
    
    formElement.on('validation-error', function(evt) {
        stdout.println('表单验证失败:');
        stdout.println(JSON.stringify(evt.detail.errors, null, 2));
    });
    
    return { form, nameField, emailField, passwordField, submitButton };
}

7.6 本章总结

核心要点

  1. 组件化基础

    • 组件的基本结构和生命周期
    • 属性传递和状态管理
    • 事件绑定和清理
  2. 基础组件开发

    • 按钮、输入框、模态框等常用组件
    • 组件的可复用性和可配置性
    • 组件的样式和交互设计
  3. 组件通信

    • 父子组件通信(属性传递、事件触发)
    • 兄弟组件通信(事件总线)
    • 跨层级组件通信(全局状态管理)
  4. 生命周期管理

    • 完整的生命周期钩子
    • 资源清理和内存管理
    • 错误处理和异常捕获
  5. 高级组件模式

    • 高阶组件(HOC)模式
    • 渲染属性(Render Props)模式
    • 组件组合(Compound Components)模式

最佳实践

  1. 组件设计原则

    • 单一职责:每个组件只负责一个功能
    • 可复用性:设计通用的、可配置的组件
    • 可组合性:组件应该易于组合和扩展
    • 可测试性:组件应该易于单元测试
  2. 性能优化

    • 实现shouldUpdate方法避免不必要的重渲染
    • 合理使用组件缓存和懒加载
    • 及时清理事件监听器和定时器
    • 避免在render方法中进行复杂计算
  3. 代码组织

    • 按功能模块组织组件文件
    • 使用命名空间避免全局污染
    • 建立组件库和设计系统
    • 编写清晰的组件文档
  4. 错误处理

    • 实现错误边界组件
    • 提供友好的错误提示
    • 记录和上报组件错误
    • 实现错误恢复机制

练习题

基础练习

  1. 创建一个评分组件,支持星级评分和数字评分两种模式
  2. 实现一个分页组件,支持页码跳转和每页条数设置
  3. 开发一个标签页组件,支持动态添加和删除标签页

进阶练习

  1. 创建一个表格组件,支持排序、筛选、分页功能
  2. 实现一个树形组件,支持节点的展开、折叠、选择
  3. 开发一个图片上传组件,支持拖拽上传和预览功能

高级练习

  1. 设计一个可视化图表组件库,支持柱状图、折线图、饼图
  2. 实现一个富文本编辑器组件,支持格式化和插件扩展
  3. 创建一个完整的表单构建器,支持拖拽设计和动态验证

下一章预告

在下一章中,我们将学习Sciter的原生API集成,包括: - 文件系统操作 - 网络请求处理 - 系统通知和托盘 - 硬件设备访问 - 第三方库集成

通过原生API的学习,您将能够开发功能更加强大的桌面应用程序。