前端从零开始写一个简单的响应式

写一个简单的响应式。
不是vue的源码更多的是一个理解。
首先需要遍历对象,为对象的每个属性设置settergetter

function observer(obj) {
    
    
    const vm = {
    
    };
    for (const prop in obj) {
    
    
        if (Object.hasOwnProperty.call(obj, prop)) {
    
    
            const dep = new Dep();
            Object.defineProperty(vm, prop, {
    
    
                get() {
    
    
                    dep.depend();
                    return data[prop];
                },
                set(val) {
    
    
                    data[prop] = val;
                    dep.notify();
                }
            })
        }
    }
    return vm;
}

收集依赖通过Dep实例进行收集,并且收集的是Watcher实例。
Dep

class Dep {
    
    
    deps = [];
    notify() {
    
    
        const deps = this.deps;
        this.deps = [];
        for (let i = 0; i < deps.length; i++) {
    
    
            const watcher = deps[i];
            schedule.push(watcher);
        }
        schedule.nextTick();
    }
    depend() {
    
    
        watcher && this.deps.push(watcher);
    }
}

Watcher

class Watcher {
    
    
    constructor(data, prop, cb) {
    
    
        this.data = data;
        this.prop = prop;
        this.cb = cb;
        this.value = this.get();
    }
    get() {
    
    
        watcher = this;
        let value = this.data[this.prop];
        watcher = undefined;
        return value;
    }
    update() {
    
    
        const oldValue = this.value;
        this.value = this.get();
        this.cb(this.value, oldValue);
    }
}

watcher实例的执行是异步任务,并且微任务的优先级更高。需要调度器Schedule

class Schedule {
    
    
  _task = new Set();

    push(watcher) {
    
    
        this._task.add(watcher);
    }

    _run() {
    
    
        const task = this._task;
        this._task = new Set();
        for (const watcher of task) {
    
    
            watcher.update();
        }
    }

    nextTick() {
    
    
        Promise.resolve().then(this._run.bind(this));
    }
}

完整案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <div id="first">1</div>

    <script>
        let watcher = null;

        class Schedule {
      
      
            _task = new Set();

            push(watcher) {
      
      
                this._task.add(watcher);
            }

            _run() {
      
      
                const task = this._task;
                this._task = new Set();
                for (const watcher of task) {
      
      
                    watcher.update();
                }
            }

            nextTick() {
      
      
                Promise.resolve().then(this._run.bind(this));
            }

        }
        const schedule = new Schedule();
        class Dep {
      
      
            deps = [];
            notify() {
      
      
                const deps = this.deps;
                this.deps = [];
                for (let i = 0; i < deps.length; i++) {
      
      
                    const watcher = deps[i];
                    schedule.push(watcher);
                }
                schedule.nextTick();
            }
            depend() {
      
      
                watcher && this.deps.push(watcher);
            }
        }

        class Watcher {
      
      
            constructor(data, prop, cb) {
      
      
                this.data = data;
                this.prop = prop;
                this.cb = cb;
                this.value = this.get();
            }
            get() {
      
      
                watcher = this;
                let value = this.data[this.prop];
                watcher = undefined;
                return value;
            }
            update() {
      
      
                const oldValue = this.value;
                this.value = this.get();
                this.cb(this.value, oldValue);
            }
        }




        const data = {
      
       count: 1 };

        function observer(obj) {
      
      
            const vm = {
      
      };
            for (const prop in obj) {
      
      
                if (Object.hasOwnProperty.call(obj, prop)) {
      
      
                    const dep = new Dep();
                    Object.defineProperty(vm, prop, {
      
      
                        get() {
      
      
                            dep.depend();
                            return data[prop];
                        },
                        set(val) {
      
      
                            data[prop] = val;
                            dep.notify();
                        }
                    })
                }
            }
            return vm;
        }
        const observerData = observer(data);
        const firstDOM = document.getElementById("first");
        const oWatcher = new Watcher(observerData, "count", function (val, oldVal) {
      
      
            console.log(val, oldVal);
            firstDOM.innerHTML = val;
        })
        firstDOM.onclick = function () {
      
      
            observerData.count++;
            observerData.count++;
            observerData.count++;
        }

    </script>

</body>

</html>

代码是提供一个思路,有些函数并不会进行参数兼容。 响应式的代码主要是看个意思,具体什么深度递归设置 Dep,处理循环引用这些问题不是没考虑到,因为这些代码仅仅提供给大家参考学习。 如果确实有需求的,制作一个完整的 MVVM,可以直接去看 vue 的源码。

在最近几天我把一些常见的前端手写题进行了整理,也希望能有更多的前端爱好者一起来学习和维护。

github地址 :Front-end-handwriting

我同时也将自己21年写的一个vue的音乐项目整理出来放到github上了,有兴趣的朋友可以看一下。

因为你之前努力过了,才会有今天的你。

猜你喜欢

转载自blog.csdn.net/weixin_45696837/article/details/129954080