6.1 数据绑定概述

6.1.1 什么是数据绑定

数据绑定是现代前端框架的核心特性,它建立了数据模型与视图之间的自动同步机制。在 Sciter-JS 中,我们可以实现强大的数据绑定系统。

graph TB
    A[数据模型] <--> B[数据绑定层]
    B <--> C[视图层]
    D[用户交互] --> C
    C --> E[事件处理]
    E --> A
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8

6.1.2 数据绑定类型

// 单向数据绑定
class OneWayBinding {
    constructor(source, target, property) {
        this.source = source;
        this.target = target;
        this.property = property;
        this.setupBinding();
    }
    
    setupBinding() {
        // 监听源数据变化
        Object.defineProperty(this.source, this.property, {
            get() {
                return this._value;
            },
            set(newValue) {
                this._value = newValue;
                this.updateTarget(newValue);
            }
        });
    }
    
    updateTarget(value) {
        if (this.target.tagName === 'INPUT') {
            this.target.value = value;
        } else {
            this.target.textContent = value;
        }
    }
}

// 双向数据绑定
class TwoWayBinding extends OneWayBinding {
    setupBinding() {
        super.setupBinding();
        this.setupTargetListener();
    }
    
    setupTargetListener() {
        if (this.target.tagName === 'INPUT') {
            this.target.addEventListener('input', (event) => {
                this.source[this.property] = event.target.value;
            });
        }
    }
}

6.2 响应式数据系统

6.2.1 响应式对象实现

// 响应式系统核心
class ReactiveSystem {
    constructor() {
        this.observers = new Map();
        this.currentObserver = null;
    }
    
    // 创建响应式对象
    reactive(obj) {
        if (typeof obj !== 'object' || obj === null) {
            return obj;
        }
        
        return new Proxy(obj, {
            get: (target, key) => {
                this.track(target, key);
                const value = target[key];
                return typeof value === 'object' ? this.reactive(value) : value;
            },
            
            set: (target, key, value) => {
                const oldValue = target[key];
                target[key] = value;
                
                if (oldValue !== value) {
                    this.trigger(target, key);
                }
                
                return true;
            }
        });
    }
    
    // 依赖收集
    track(target, key) {
        if (!this.currentObserver) return;
        
        const targetMap = this.observers.get(target) || new Map();
        const deps = targetMap.get(key) || new Set();
        
        deps.add(this.currentObserver);
        targetMap.set(key, deps);
        this.observers.set(target, targetMap);
    }
    
    // 触发更新
    trigger(target, key) {
        const targetMap = this.observers.get(target);
        if (!targetMap) return;
        
        const deps = targetMap.get(key);
        if (deps) {
            deps.forEach(observer => observer());
        }
    }
    
    // 监听器
    watch(fn) {
        this.currentObserver = fn;
        fn();
        this.currentObserver = null;
    }
    
    // 计算属性
    computed(fn) {
        let value;
        let dirty = true;
        
        const computedFn = () => {
            if (dirty) {
                value = fn();
                dirty = false;
            }
            return value;
        };
        
        this.watch(() => {
            dirty = true;
        });
        
        return computedFn;
    }
}

// 全局响应式系统实例
const reactiveSystem = new ReactiveSystem();

6.2.2 响应式数据使用示例

// 创建响应式数据
const state = reactiveSystem.reactive({
    user: {
        name: 'John',
        age: 25,
        email: 'john@example.com'
    },
    todos: [
        { id: 1, text: '学习 Sciter-JS', completed: false },
        { id: 2, text: '构建应用', completed: true }
    ],
    filter: 'all'
});

// 计算属性
const filteredTodos = reactiveSystem.computed(() => {
    switch (state.filter) {
        case 'active':
            return state.todos.filter(todo => !todo.completed);
        case 'completed':
            return state.todos.filter(todo => todo.completed);
        default:
            return state.todos;
    }
});

// 监听数据变化
reactiveSystem.watch(() => {
    console.log('用户名变化:', state.user.name);
});

reactiveSystem.watch(() => {
    console.log('过滤后的 todos:', filteredTodos());
});

// 数据变化会自动触发监听器
state.user.name = 'Jane'; // 输出: 用户名变化: Jane
state.filter = 'active'; // 输出: 过滤后的 todos: [...]

6.3 数据绑定指令系统

6.3.1 指令解析器

// 指令解析器
class DirectiveParser {
    constructor(reactiveSystem) {
        this.reactiveSystem = reactiveSystem;
        this.directives = new Map();
        this.setupBuiltinDirectives();
    }
    
    // 注册指令
    register(name, handler) {
        this.directives.set(name, handler);
    }
    
    // 解析元素上的指令
    parse(element, context) {
        const attributes = Array.from(element.attributes);
        
        attributes.forEach(attr => {
            if (attr.name.startsWith('v-')) {
                const directiveName = attr.name.substring(2);
                const expression = attr.value;
                
                this.applyDirective(element, directiveName, expression, context);
            }
        });
        
        // 递归处理子元素
        Array.from(element.children).forEach(child => {
            this.parse(child, context);
        });
    }
    
    // 应用指令
    applyDirective(element, name, expression, context) {
        const directive = this.directives.get(name);
        if (directive) {
            directive(element, expression, context, this.reactiveSystem);
        }
    }
    
    // 内置指令
    setupBuiltinDirectives() {
        // v-text 指令
        this.register('text', (element, expression, context, reactive) => {
            reactive.watch(() => {
                const value = this.evaluateExpression(expression, context);
                element.textContent = value;
            });
        });
        
        // v-html 指令
        this.register('html', (element, expression, context, reactive) => {
            reactive.watch(() => {
                const value = this.evaluateExpression(expression, context);
                element.innerHTML = value;
            });
        });
        
        // v-model 指令
        this.register('model', (element, expression, context, reactive) => {
            // 双向绑定
            reactive.watch(() => {
                const value = this.evaluateExpression(expression, context);
                if (element.value !== value) {
                    element.value = value;
                }
            });
            
            element.addEventListener('input', (event) => {
                this.setProperty(expression, context, event.target.value);
            });
        });
        
        // v-show 指令
        this.register('show', (element, expression, context, reactive) => {
            reactive.watch(() => {
                const value = this.evaluateExpression(expression, context);
                element.style.display = value ? '' : 'none';
            });
        });
        
        // v-if 指令
        this.register('if', (element, expression, context, reactive) => {
            const placeholder = document.createComment(`v-if: ${expression}`);
            element.parentNode.insertBefore(placeholder, element);
            
            reactive.watch(() => {
                const value = this.evaluateExpression(expression, context);
                
                if (value) {
                    if (!element.parentNode) {
                        placeholder.parentNode.insertBefore(element, placeholder.nextSibling);
                    }
                } else {
                    if (element.parentNode) {
                        element.parentNode.removeChild(element);
                    }
                }
            });
        });
        
        // v-for 指令
        this.register('for', (element, expression, context, reactive) => {
            const [itemName, listExpression] = expression.split(' in ');
            const placeholder = document.createComment(`v-for: ${expression}`);
            element.parentNode.insertBefore(placeholder, element);
            element.parentNode.removeChild(element);
            
            let renderedElements = [];
            
            reactive.watch(() => {
                const list = this.evaluateExpression(listExpression.trim(), context);
                
                // 清除之前渲染的元素
                renderedElements.forEach(el => {
                    if (el.parentNode) {
                        el.parentNode.removeChild(el);
                    }
                });
                renderedElements = [];
                
                // 渲染新元素
                if (Array.isArray(list)) {
                    list.forEach((item, index) => {
                        const clonedElement = element.cloneNode(true);
                        const itemContext = {
                            ...context,
                            [itemName.trim()]: item,
                            $index: index
                        };
                        
                        placeholder.parentNode.insertBefore(clonedElement, placeholder.nextSibling);
                        this.parse(clonedElement, itemContext);
                        renderedElements.push(clonedElement);
                    });
                }
            });
        });
        
        // v-on 指令(事件绑定)
        this.register('on', (element, expression, context, reactive) => {
            const [eventName, handlerExpression] = expression.split(':');
            
            element.addEventListener(eventName, (event) => {
                const handler = this.evaluateExpression(handlerExpression, context);
                if (typeof handler === 'function') {
                    handler(event);
                }
            });
        });
    }
    
    // 表达式求值
    evaluateExpression(expression, context) {
        try {
            const func = new Function(...Object.keys(context), `return ${expression}`);
            return func(...Object.values(context));
        } catch (error) {
            console.error('表达式求值错误:', expression, error);
            return undefined;
        }
    }
    
    // 设置属性值
    setProperty(expression, context, value) {
        try {
            const func = new Function(...Object.keys(context), 'value', `${expression} = value`);
            func(...Object.values(context), value);
        } catch (error) {
            console.error('属性设置错误:', expression, error);
        }
    }
}

6.3.2 数据绑定组件

// 数据绑定组件基类
class DataBindingComponent extends Component {
    constructor(element, props = {}) {
        super(element, props);
        this.directiveParser = new DirectiveParser(reactiveSystem);
        this.setupDataBinding();
    }
    
    setupDataBinding() {
        // 创建响应式数据
        this.data = reactiveSystem.reactive(this.getData());
        
        // 设置计算属性
        this.setupComputed();
        
        // 设置监听器
        this.setupWatchers();
        
        // 解析模板指令
        this.parseTemplate();
    }
    
    getData() {
        return {};
    }
    
    setupComputed() {
        // 子类可以重写此方法设置计算属性
    }
    
    setupWatchers() {
        // 子类可以重写此方法设置监听器
    }
    
    parseTemplate() {
        const context = {
            ...this.data,
            ...this.methods,
            $props: this.props
        };
        
        this.directiveParser.parse(this.element, context);
    }
    
    get methods() {
        return {};
    }
}

6.4 全局状态管理

6.4.1 状态管理器设计

// 全局状态管理器
class StateManager {
    constructor() {
        this.state = reactiveSystem.reactive({});
        this.mutations = new Map();
        this.actions = new Map();
        this.getters = new Map();
        this.modules = new Map();
        this.subscribers = new Set();
    }
    
    // 注册模块
    registerModule(name, module) {
        this.modules.set(name, module);
        
        // 合并状态
        if (module.state) {
            this.state[name] = reactiveSystem.reactive(module.state);
        }
        
        // 注册 mutations
        if (module.mutations) {
            Object.entries(module.mutations).forEach(([key, mutation]) => {
                this.mutations.set(`${name}/${key}`, mutation);
            });
        }
        
        // 注册 actions
        if (module.actions) {
            Object.entries(module.actions).forEach(([key, action]) => {
                this.actions.set(`${name}/${key}`, action);
            });
        }
        
        // 注册 getters
        if (module.getters) {
            Object.entries(module.getters).forEach(([key, getter]) => {
                this.getters.set(`${name}/${key}`, getter);
            });
        }
    }
    
    // 提交 mutation
    commit(type, payload) {
        const mutation = this.mutations.get(type);
        if (mutation) {
            mutation(this.state, payload);
            this.notifySubscribers({ type: 'mutation', mutation: type, payload });
        } else {
            console.error(`未知的 mutation: ${type}`);
        }
    }
    
    // 分发 action
    async dispatch(type, payload) {
        const action = this.actions.get(type);
        if (action) {
            const context = {
                state: this.state,
                commit: this.commit.bind(this),
                dispatch: this.dispatch.bind(this),
                getters: this.getGetters()
            };
            
            const result = await action(context, payload);
            this.notifySubscribers({ type: 'action', action: type, payload });
            return result;
        } else {
            console.error(`未知的 action: ${type}`);
        }
    }
    
    // 获取 getter 值
    getGetters() {
        const getters = {};
        this.getters.forEach((getter, key) => {
            Object.defineProperty(getters, key, {
                get: () => getter(this.state, getters)
            });
        });
        return getters;
    }
    
    // 订阅状态变化
    subscribe(callback) {
        this.subscribers.add(callback);
        
        // 返回取消订阅函数
        return () => {
            this.subscribers.delete(callback);
        };
    }
    
    // 通知订阅者
    notifySubscribers(event) {
        this.subscribers.forEach(callback => {
            try {
                callback(event);
            } catch (error) {
                console.error('订阅者回调错误:', error);
            }
        });
    }
    
    // 获取状态快照
    getSnapshot() {
        return JSON.parse(JSON.stringify(this.state));
    }
    
    // 恢复状态
    restoreSnapshot(snapshot) {
        Object.assign(this.state, snapshot);
    }
}

// 全局状态管理器实例
const store = new StateManager();

6.4.2 状态模块示例

// 用户模块
const userModule = {
    state: {
        currentUser: null,
        isLoggedIn: false,
        permissions: []
    },
    
    mutations: {
        SET_USER(state, user) {
            state.user.currentUser = user;
            state.user.isLoggedIn = !!user;
        },
        
        SET_PERMISSIONS(state, permissions) {
            state.user.permissions = permissions;
        },
        
        LOGOUT(state) {
            state.user.currentUser = null;
            state.user.isLoggedIn = false;
            state.user.permissions = [];
        }
    },
    
    actions: {
        async login({ commit }, credentials) {
            try {
                const response = await fetch('/api/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(credentials)
                });
                
                const user = await response.json();
                commit('user/SET_USER', user);
                commit('user/SET_PERMISSIONS', user.permissions);
                
                return user;
            } catch (error) {
                console.error('登录失败:', error);
                throw error;
            }
        },
        
        async logout({ commit }) {
            try {
                await fetch('/api/logout', { method: 'POST' });
                commit('user/LOGOUT');
            } catch (error) {
                console.error('登出失败:', error);
            }
        }
    },
    
    getters: {
        isAdmin: (state) => {
            return state.user.permissions.includes('admin');
        },
        
        userName: (state) => {
            return state.user.currentUser?.name || '游客';
        }
    }
};

// Todo 模块
const todoModule = {
    state: {
        todos: [],
        filter: 'all'
    },
    
    mutations: {
        ADD_TODO(state, todo) {
            state.todos.todos.push({
                id: Date.now(),
                text: todo.text,
                completed: false,
                createdAt: new Date()
            });
        },
        
        TOGGLE_TODO(state, id) {
            const todo = state.todos.todos.find(t => t.id === id);
            if (todo) {
                todo.completed = !todo.completed;
            }
        },
        
        DELETE_TODO(state, id) {
            const index = state.todos.todos.findIndex(t => t.id === id);
            if (index > -1) {
                state.todos.todos.splice(index, 1);
            }
        },
        
        SET_FILTER(state, filter) {
            state.todos.filter = filter;
        }
    },
    
    actions: {
        async loadTodos({ commit }) {
            try {
                const response = await fetch('/api/todos');
                const todos = await response.json();
                
                todos.forEach(todo => {
                    commit('todos/ADD_TODO', todo);
                });
            } catch (error) {
                console.error('加载 todos 失败:', error);
            }
        },
        
        async saveTodo({ commit }, todo) {
            try {
                const response = await fetch('/api/todos', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(todo)
                });
                
                const savedTodo = await response.json();
                commit('todos/ADD_TODO', savedTodo);
                
                return savedTodo;
            } catch (error) {
                console.error('保存 todo 失败:', error);
                throw error;
            }
        }
    },
    
    getters: {
        filteredTodos: (state) => {
            switch (state.todos.filter) {
                case 'active':
                    return state.todos.todos.filter(todo => !todo.completed);
                case 'completed':
                    return state.todos.todos.filter(todo => todo.completed);
                default:
                    return state.todos.todos;
            }
        },
        
        todoStats: (state) => {
            const total = state.todos.todos.length;
            const completed = state.todos.todos.filter(todo => todo.completed).length;
            const active = total - completed;
            
            return { total, completed, active };
        }
    }
};

// 注册模块
store.registerModule('user', userModule);
store.registerModule('todos', todoModule);

6.5 状态持久化

6.5.1 本地存储插件

// 状态持久化插件
class PersistencePlugin {
    constructor(options = {}) {
        this.key = options.key || 'sciter-app-state';
        this.storage = options.storage || localStorage;
        this.paths = options.paths || []; // 需要持久化的状态路径
        this.serialize = options.serialize || JSON.stringify;
        this.deserialize = options.deserialize || JSON.parse;
    }
    
    // 安装插件
    install(store) {
        // 恢复状态
        this.restoreState(store);
        
        // 订阅状态变化
        store.subscribe((event) => {
            this.saveState(store);
        });
    }
    
    // 保存状态
    saveState(store) {
        try {
            const state = store.getSnapshot();
            const persistedState = this.filterState(state);
            const serialized = this.serialize(persistedState);
            this.storage.setItem(this.key, serialized);
        } catch (error) {
            console.error('状态保存失败:', error);
        }
    }
    
    // 恢复状态
    restoreState(store) {
        try {
            const serialized = this.storage.getItem(this.key);
            if (serialized) {
                const state = this.deserialize(serialized);
                store.restoreSnapshot(state);
            }
        } catch (error) {
            console.error('状态恢复失败:', error);
        }
    }
    
    // 过滤需要持久化的状态
    filterState(state) {
        if (this.paths.length === 0) {
            return state;
        }
        
        const filtered = {};
        this.paths.forEach(path => {
            const value = this.getNestedValue(state, path);
            if (value !== undefined) {
                this.setNestedValue(filtered, path, value);
            }
        });
        
        return filtered;
    }
    
    // 获取嵌套值
    getNestedValue(obj, path) {
        return path.split('.').reduce((current, key) => {
            return current && current[key];
        }, obj);
    }
    
    // 设置嵌套值
    setNestedValue(obj, path, value) {
        const keys = path.split('.');
        const lastKey = keys.pop();
        
        const target = keys.reduce((current, key) => {
            if (!current[key]) {
                current[key] = {};
            }
            return current[key];
        }, obj);
        
        target[lastKey] = value;
    }
}

// 使用持久化插件
const persistencePlugin = new PersistencePlugin({
    key: 'my-app-state',
    paths: ['user.currentUser', 'todos.todos'], // 只持久化用户信息和 todos
    storage: localStorage
});

persistencePlugin.install(store);

6.5.2 状态同步插件

// 状态同步插件(多标签页同步)
class StateSyncPlugin {
    constructor(options = {}) {
        this.channel = options.channel || 'state-sync';
        this.debounceTime = options.debounceTime || 100;
        this.syncTimeout = null;
    }
    
    install(store) {
        // 监听其他标签页的状态变化
        window.addEventListener('storage', (event) => {
            if (event.key === this.channel && event.newValue) {
                try {
                    const syncData = JSON.parse(event.newValue);
                    this.applySyncData(store, syncData);
                } catch (error) {
                    console.error('状态同步失败:', error);
                }
            }
        });
        
        // 监听本地状态变化
        store.subscribe((event) => {
            this.scheduleBroadcast(store, event);
        });
    }
    
    // 调度广播(防抖)
    scheduleBroadcast(store, event) {
        clearTimeout(this.syncTimeout);
        this.syncTimeout = setTimeout(() => {
            this.broadcastState(store, event);
        }, this.debounceTime);
    }
    
    // 广播状态变化
    broadcastState(store, event) {
        try {
            const syncData = {
                timestamp: Date.now(),
                event: event,
                state: store.getSnapshot()
            };
            
            localStorage.setItem(this.channel, JSON.stringify(syncData));
            
            // 立即清除,避免影响持久化
            setTimeout(() => {
                localStorage.removeItem(this.channel);
            }, 50);
        } catch (error) {
            console.error('状态广播失败:', error);
        }
    }
    
    // 应用同步数据
    applySyncData(store, syncData) {
        // 避免循环同步
        if (Date.now() - syncData.timestamp > 5000) {
            return;
        }
        
        store.restoreSnapshot(syncData.state);
    }
}

// 使用状态同步插件
const stateSyncPlugin = new StateSyncPlugin({
    channel: 'my-app-sync',
    debounceTime: 200
});

stateSyncPlugin.install(store);

6.6 实践练习

6.6.1 创建一个完整的数据绑定应用

<!-- HTML 模板 -->
<div id="app">
    <header class="app-header">
        <h1 v-text="title"></h1>
        <div class="user-info" v-if="user.isLoggedIn">
            <span v-text="'欢迎, ' + user.name"></span>
            <button v-on="click:logout">登出</button>
        </div>
        <div class="login-form" v-if="!user.isLoggedIn">
            <input v-model="loginForm.username" placeholder="用户名">
            <input v-model="loginForm.password" type="password" placeholder="密码">
            <button v-on="click:login">登录</button>
        </div>
    </header>
    
    <main class="app-main" v-show="user.isLoggedIn">
        <div class="todo-input">
            <input v-model="newTodoText" placeholder="添加新任务..." 
                   v-on="keydown:handleKeyDown">
            <button v-on="click:addTodo">添加</button>
        </div>
        
        <div class="todo-filters">
            <button v-for="filter in filters" 
                    v-on="click:setFilter"
                    v-text="filter.label"
                    :class="{ active: currentFilter === filter.value }">
            </button>
        </div>
        
        <div class="todo-list">
            <div v-for="todo in filteredTodos" class="todo-item">
                <input type="checkbox" v-model="todo.completed">
                <span v-text="todo.text" 
                      :class="{ completed: todo.completed }"></span>
                <button v-on="click:deleteTodo" :data-id="todo.id">删除</button>
            </div>
        </div>
        
        <div class="todo-stats">
            <span v-text="'总计: ' + todoStats.total"></span>
            <span v-text="'未完成: ' + todoStats.active"></span>
            <span v-text="'已完成: ' + todoStats.completed"></span>
        </div>
    </main>
</div>
// 应用组件
class TodoApp extends DataBindingComponent {
    getData() {
        return {
            title: 'Todo 应用',
            user: {
                isLoggedIn: false,
                name: '',
                id: null
            },
            loginForm: {
                username: '',
                password: ''
            },
            todos: [],
            newTodoText: '',
            currentFilter: 'all',
            filters: [
                { value: 'all', label: '全部' },
                { value: 'active', label: '未完成' },
                { value: 'completed', label: '已完成' }
            ]
        };
    }
    
    setupComputed() {
        // 过滤后的 todos
        this.filteredTodos = reactiveSystem.computed(() => {
            switch (this.data.currentFilter) {
                case 'active':
                    return this.data.todos.filter(todo => !todo.completed);
                case 'completed':
                    return this.data.todos.filter(todo => todo.completed);
                default:
                    return this.data.todos;
            }
        });
        
        // todo 统计
        this.todoStats = reactiveSystem.computed(() => {
            const total = this.data.todos.length;
            const completed = this.data.todos.filter(todo => todo.completed).length;
            const active = total - completed;
            
            return { total, completed, active };
        });
    }
    
    setupWatchers() {
        // 监听用户登录状态
        reactiveSystem.watch(() => {
            if (this.data.user.isLoggedIn) {
                this.loadTodos();
            }
        });
        
        // 监听 todos 变化,自动保存
        reactiveSystem.watch(() => {
            if (this.data.user.isLoggedIn && this.data.todos.length > 0) {
                this.saveTodos();
            }
        });
    }
    
    get methods() {
        return {
            login: this.login.bind(this),
            logout: this.logout.bind(this),
            addTodo: this.addTodo.bind(this),
            deleteTodo: this.deleteTodo.bind(this),
            setFilter: this.setFilter.bind(this),
            handleKeyDown: this.handleKeyDown.bind(this),
            filteredTodos: this.filteredTodos,
            todoStats: this.todoStats
        };
    }
    
    async login() {
        try {
            // 模拟登录 API 调用
            const response = await this.mockLogin(
                this.data.loginForm.username,
                this.data.loginForm.password
            );
            
            this.data.user = {
                isLoggedIn: true,
                name: response.name,
                id: response.id
            };
            
            // 清空登录表单
            this.data.loginForm = { username: '', password: '' };
        } catch (error) {
            alert('登录失败: ' + error.message);
        }
    }
    
    logout() {
        this.data.user = {
            isLoggedIn: false,
            name: '',
            id: null
        };
        this.data.todos = [];
    }
    
    addTodo() {
        const text = this.data.newTodoText.trim();
        if (text) {
            this.data.todos.push({
                id: Date.now(),
                text: text,
                completed: false,
                createdAt: new Date()
            });
            this.data.newTodoText = '';
        }
    }
    
    deleteTodo(event) {
        const id = parseInt(event.target.dataset.id);
        const index = this.data.todos.findIndex(todo => todo.id === id);
        if (index > -1) {
            this.data.todos.splice(index, 1);
        }
    }
    
    setFilter(event) {
        const filter = event.target.dataset.filter;
        this.data.currentFilter = filter;
    }
    
    handleKeyDown(event) {
        if (event.key === 'Enter') {
            this.addTodo();
        }
    }
    
    // 模拟 API 方法
    async mockLogin(username, password) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (username === 'admin' && password === '123456') {
                    resolve({ id: 1, name: '管理员' });
                } else {
                    reject(new Error('用户名或密码错误'));
                }
            }, 1000);
        });
    }
    
    async loadTodos() {
        // 模拟加载 todos
        setTimeout(() => {
            this.data.todos = [
                { id: 1, text: '学习 Sciter-JS', completed: false },
                { id: 2, text: '构建应用', completed: true }
            ];
        }, 500);
    }
    
    async saveTodos() {
        // 模拟保存 todos
        console.log('保存 todos:', this.data.todos);
    }
}

// 初始化应用
const appElement = document.getElementById('app');
const app = new TodoApp(appElement);

6.7 本章小结

6.7.1 核心概念

  • 响应式系统:自动追踪依赖和触发更新的数据系统
  • 数据绑定:数据与视图之间的自动同步机制
  • 状态管理:集中式的应用状态管理方案
  • 状态持久化:状态的本地存储和恢复

6.7.2 技术要点

  1. Proxy 响应式:使用 Proxy 实现数据劫持和依赖收集
  2. 指令系统:声明式的数据绑定指令
  3. 模块化状态:按功能模块组织状态管理
  4. 插件系统:可扩展的状态管理插件

6.7.3 最佳实践

  • 合理设计状态结构,避免过度嵌套
  • 使用计算属性处理派生数据
  • 通过 mutations 修改状态,保持数据流的可预测性
  • 适当使用状态持久化,提升用户体验

6.7.4 下一章预告

下一章将学习 网络请求与数据处理,包括: - HTTP 客户端封装 - 请求拦截和响应处理 - 数据缓存策略 - 错误处理和重试机制

通过本章的学习,你已经掌握了完整的数据绑定和状态管理技能,能够构建复杂的数据驱动应用。