实现响应式框架的关键点

30 阅读3分钟

 

1. 响应式系统 (Reactivity)

这是最核心的部分,需要实现数据变化的自动检测和更新。

// 简单的响应式实现

class Reactive {
    constructor() {
        this.deps = new Map(); // 依赖收集
        this.currentEffect = null;
    }
    
    // 数据劫持
    observe(obj) {
        const self = this;
        return new Proxy(obj, {
            get(target, key) {
                // 依赖收集
                self.track(target, key);
                return target[key];
            },
            set(target, key, value) {
                target[key] = value;
                // 触发更新
                self.trigger(target, key);
                return true;
            }
        });
    }
    
    // 收集依赖
    track(target, key) {
        if (this.currentEffect) {
            if (!this.deps.has(target)) {
                this.deps.set(target, new Map());
            }
            if (!this.deps.get(target).has(key)) {
                this.deps.get(target).set(key, new Set());
            }
            this.deps.get(target).get(key).add(this.currentEffect);
        }
    }
    
    // 触发更新
    trigger(target, key) {
        const targetDeps = this.deps.get(target);
        if (targetDeps && targetDeps.has(key)) {
            targetDeps.get(key).forEach(effect => effect());
        }
    }
    
    // 副作用函数
    effect(fn) {
        this.currentEffect = fn;
        fn(); // 立即执行一次,收集依赖
        this.currentEffect = null;
    }
}

2. 虚拟 DOM (Virtual DOM)

// 虚拟节点
class VNode {
    constructor(tag, props = {}, children = []) {
        this.tag = tag;
        this.props = props;
        this.children = children;
        this.el = null; // 对应的真实 DOM
    }
}


// 创建虚拟节点
function h(tag, props, ...children) {
    return new VNode(tag, props, children.flat());
}


// 渲染虚拟 DOM 到真实 DOM
function render(vnode, container) {
    if (typeof vnode.tag === 'string') {
        // 创建元素
        const el = document.createElement(vnode.tag);
        vnode.el = el;
        
        // 设置属性
        for (const key in vnode.props) {
            if (key.startsWith('on')) {
                // 事件处理
                const event = key.slice(2).toLowerCase();
                el.addEventListener(event, vnode.props[key]);
            } else {
                el.setAttribute(key, vnode.props[key]);
            }
        }
        
        // 处理子节点
        vnode.children.forEach(child => {
            if (typeof child === 'string') {
                el.appendChild(document.createTextNode(child));
            } else {
                render(child, el);
            }
        });
        
        container.appendChild(el);
    }
}

3. DOM Diff 算法

function diff(oldVNode, newVNode) {
    if (!oldVNode) {
        return { type: 'CREATE', newVNode };
    }
    
    if (!newVNode) {
        return { type: 'REMOVE', oldVNode };
    }
    
    if (typeof oldVNode !== typeof newVNode || oldVNode.tag !== newVNode.tag) {
        return { type: 'REPLACE', oldVNode, newVNode };
    }
    
    if (typeof oldVNode === 'string') {
        if (oldVNode !== newVNode) {
            return { type: 'TEXT', newText: newVNode };
        }
        return null;
    }
    
    const patches = [];
    
    // 比较属性
    const propsDiff = diffProps(oldVNode.props, newVNode.props);
    if (propsDiff) {
        patches.push({ type: 'PROPS', props: propsDiff });
    }
    
    // 比较子节点
    const childrenDiff = diffChildren(oldVNode.children, newVNode.children);
    if (childrenDiff.length > 0) {
        patches.push({ type: 'CHILDREN', children: childrenDiff });
    }
    
    return patches.length > 0 ? patches : null;
}


function diffProps(oldProps, newProps) {
    const patches = {};
    
    // 检查新属性和修改的属性
    for (const key in newProps) {
        if (oldProps[key] !== newProps[key]) {
            patches[key] = newProps[key];
        }
    }
    
    // 检查删除的属性
    for (const key in oldProps) {
        if (!(key in newProps)) {
            patches[key] = null;
        }
    }
    
    return Object.keys(patches).length > 0 ? patches : null;
}

4. 模板编译系统

class TemplateCompiler {
    compile(template) {
        // 解析模板为 AST
        const ast = this.parse(template);
        // 生成渲染函数
        return this.generate(ast);
    }
    
    parse(template) {
        // 简化的模板解析器
        const tagReg = /<(\w+)([^>]*)>(.*?)</\1>/gs;
        const textReg = /{{(.*?)}}/g;
        
        return {
            type: 'element',
            tag: 'div',
            children: this.parseChildren(template)
        };
    }
    
    parseChildren(content) {
        const children = [];
        const tagReg = /<(\w+)([^>]*)>(.*?)</\1>/g;
        
        let lastIndex = 0;
        let match;
        
        while ((match = tagReg.exec(content)) !== null) {
            // 处理文本节点
            if (match.index > lastIndex) {
                const text = content.slice(lastIndex, match.index).trim();
                if (text) {
                    children.push({
                        type: 'text',
                        content: text
                    });
                }
            }
            
            // 处理元素节点
            children.push({
                type: 'element',
                tag: match[1],
                props: this.parseProps(match[2]),
                children: this.parseChildren(match[3])
            });
            
            lastIndex = match.index + match[0].length;
        }
        
        return children;
    }
    
    generate(ast) {
        return `
            function render(data) {
                return ${this.generateNode(ast)};
            }
        `;
    }
    
    generateNode(node) {
        if (node.type === 'element') {
            const children = node.children.map(child => this.generateNode(child)).join(',');
            return `h('${node.tag}', ${JSON.stringify(node.props)}, [${children}])`;
        } else if (node.type === 'text') {
            // 处理插值表达式
            const content = node.content.replace(/{{(.*?)}}/g, '${data.$1}');
            return ``${content}``;
        }
    }
}

5. 组件系统

class Component {
    constructor(options = {}) {
        this.data = this.reactive.observe(options.data || {});
        this.template = options.template;
        this.methods = options.methods || {};
        this.mounted = options.mounted;
        
        this.reactive = new Reactive();
        this.compiler = new TemplateCompiler();
        
        this.render = this.compileTemplate();
        this.oldVNode = null;
    }
    
    compileTemplate() {
        const renderCode = this.compiler.compile(this.template);
        return new Function('h', 'data', renderCode + '; return render(data);');
    }
    
    mount(container) {
        this.reactive.effect(() => {
            const newVNode = this.render(h, this.data);
            
            if (!this.oldVNode) {
                render(newVNode, container);
            } else {
                const patches = diff(this.oldVNode, newVNode);
                if (patches) {
                    patch(this.oldVNode.el, patches);
                }
            }
            
            this.oldVNode = newVNode;
        });
        
        if (this.mounted) {
            this.mounted.call(this);
        }
    }
}


// 使用示例
const app = new Component({
    template: `
        <div>
            <h1>{{title}}</h1>
            <p>{{message}}</p>
            <button onclick="increment">Count: {{count}}</button>
        </div>
    `,
    {
        title: 'My Vue Clone',
        message: 'Hello World',
        count: 0
    },
    methods: {
        increment() {
            this.count++;
        }
    },
    mounted() {
        console.log('Component mounted');
    }
});

 

app.mount(``document``.getElementById(``'app'``));

6. 生命周期管理

class Lifecycle {
    constructor() {
        this.hooks = {
            beforeCreate: [],
            created: [],
            beforeMount: [],
            mounted: [],
            beforeUpdate: [],
            updated: [],
            beforeDestroy: [],
            destroyed: []
        };
    }
    
    callHook(name, ...args) {
        if (this.hooks[name]) {
            this.hooks[name].forEach(hook => hook.call(this, ...args));
        }
    }
    
    addHook(name, fn) {
        if (this.hooks[name]) {
            this.hooks[name].push(fn);
        }
    }
}

7. 指令系统

class Directive {
    static directives = new Map();
    
    static register(name, definition) {
        this.directives.set(name, definition);
    }
    
    static get(name) {
        return this.directives.get(name);
    }
}


// 内置指令
Directive.register('if', {
    bind(el, binding) {
        this.placeholder = document.createComment('v-if');
        el.parentNode.insertBefore(this.placeholder, el);
        this.el = el;
    },
    
    update(value) {
        if (value) {
            this.placeholder.parentNode.replaceChild(this.el, this.placeholder);
        } else {
            this.el.parentNode.replaceChild(this.placeholder, this.el);
        }
    }
});


Directive.register('show', {
    update(el, value) {
        el.style.display = value ? '' : 'none';
    }
});

8. 核心架构整合

class MiniVue {
    constructor(options = {}) {
        this.options = options;
        this.components = new Map();
        this.directives = new Map();
        
        // 初始化全局 API
        this.initGlobalAPI();
    }
    
    initGlobalAPI() {
        this.component = (name, definition) => {
            this.components.set(name, definition);
        };
        
        this.directive = (name, definition) => {
            this.directives.set(name, definition);
        };
    }
    
    createApp(rootComponent) {
        return {
            mount: (container) => {
                const instance = new Component(rootComponent);
                instance.mount(typeof container === 'string' 
                    ? document.querySelector(container) 
                    : container);
                return instance;
            }
        };
    }
}


// 使用
const Vue = new MiniVue();


const app = Vue.createApp({
    template: '<div>{{message}}</div>',
    { message: 'Hello Mini Vue!' }
});


app.mount('#app');

关键技术要点总结

  1. 响应式系统: Proxy/Object.defineProperty + 依赖收集
  2. 虚拟DOM: 抽象DOM表示 + 高效diff算法
  3. 模板编译: 模板语法 → 渲染函数
  4. 组件系统: 可复用的UI单元
  5. 生命周期: 组件状态管理
  6. 指令系统: DOM操作抽象
  7. 事件系统: 统一的事件处理
  8. 插件系统: 功能扩展机制

实现顺序建议:响应式 → 虚拟DOM → 模板编译 → 组件系统 → 其他特性。