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

文章介绍了如何实现一个简单的响应式系统,通过遍历对象设置setter和getter,结合Dep类来收集依赖,Watcher类用于数据更新时的回调。当数据变化时,通过Dep通知Watcher执行异步更新。文章提供了完整的示例代码,并提到了GitHub上的前端手写题和一个Vue音乐项目的资源。
摘要由CSDN通过智能技术生成

写一个简单的响应式。
不是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上了,有兴趣的朋友可以看一下。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值