关于双向绑定-2

"本文详细记录了尝试实现Vue.js中数据响应式的步骤,从`Observer`到`Dep`和`Watcher`类的创建,探讨了数据劫持、发布订阅模式在响应式系统中的应用。作者指出在文本节点中获取{{value}
摘要由CSDN通过智能技术生成

完善了一下compile部分,还是有问题。在此简要记录下。

<!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="app">
        <input type="text" v-model="text">
        <p>{{text}} {{test}}</p>
        <button id="btn">click</button>
    </div>

    <script>

        class Observe {
            constructor(data) {
                this.data = data;
                this.walk();
            }
            walk() {
                let keys = Object.keys(this.data);
                for (let key of keys) {
                    this.defineReactive(this.data, key, this.data[key]);
                }

            }
            defineReactive(data, key, value) {
                let dep = new Dep();
                let that = this;
                Object.defineProperty(data, key, {
                    enumerable: true,
                    configurable: true,
                    get() {
                        if (Dep.target) {
                            dep.addWatcher(Dep.target); // 在消息订阅器中注册
                        }
                        return value;
                    },
                    set(newVal) {
                        if (newVal === value) return;
                        // console.log(2, newVal);
                        value = newVal;
                        data[key] = newVal;
                        that.observer(newVal);
                        dep.notify();
                        return newVal;
                    }
                })
            }
            observer(obj) {
                if (obj && typeof obj === 'object') new Observe(obj);
            }
        }

        class Dep {
            constructor() {
                this.watchers = [];
            }
            addWatcher(watcher) {
                this.watchers.push(watcher);
            }
            notify() {
                this.watchers.forEach(v => {
                    v.update();
                })
            }
        }

        class Watcher {
            constructor(exp, cb, vm) {
                this.cb = cb;//callback
                this.vm = vm;//MyVue
                this.exp = exp;//对应的key
                this.value = this.get(); // value存储旧值
            }
            update() {
                this.run();
            }
            run() {
                let value = this.vm.data[this.exp];
                let oldVal = this.value;
                this.cb.call(this.vm, value, oldVal);
            }
            get() {
                Dep.target = this;
                let value = this.vm.data[this.exp];
                Dep.target = null;
                return value;
            }
        }

        class Compile {
            constructor(vm) {
                this.vm = vm;
                new Observe(vm.data)
            }
            run() {
                let that = this;
                let node = this.vm.$el;
                let inp = document.querySelectorAll(`#${node.id} input[v-model]`);

                [...inp].forEach(ele => {
                    let exp = ele.getAttribute('v-model');
                    ele.value = that.vm.data[exp]; // 初始化
                    new Watcher(exp, function (value, oldVal) {
                        // this 为 vm 对象
                        ele.value = value;
                    }, that.vm);

                    ele.addEventListener('input', function (e) {
                        that.vm.data[exp] = e.target.value;
                        /* 触发input事件。赋值时触发set
                         * set 触发 dep 的 notify
                         * 触发 watcher 的 run
                         * 调用cb函数, 修改view
                        */
                    })
                })

                // 此处是获取children去解析{{}},事实上应该修改的是text节点
                let child = node.children;
                child = [...child].filter(v => v.innerText.trim().includes("{{"));
                // console.log(child)
                child.forEach(ele => {
                    let txt = ele.innerText; // 记录原始字符串
                    let a = ele.innerText.indexOf("{{"), b = ele.innerText.indexOf('}}');
                    while (a != -1 && b != -1) {
                        let exp = ele.innerText.slice(a + 2, b).trim(); // 获得key

                        new Watcher(exp, function (value, oldVal) {
                            // console.log(exp, value);
                            // 重新对该部分的{{}}进行解析 (有问题)
                            ele.innerText = txt;
                            let a = txt.indexOf("{{"), b = txt.indexOf('}}');
                            while (a != -1 && b != -1) {
                                let exp = ele.innerText.slice(a + 2, b).trim(); // 获得key
                                ele.innerText = ele.innerText.replace(`{{${exp}}}`, that.vm.data[exp]);
                                a = ele.innerText.indexOf("{{"), b = ele.innerText.indexOf('}}');
                            }

                            ele.innerText = ele.innerText.replace(`{{${exp}}}`, value);
                            // console.log('ptext', value);
                        }, that.vm);

                        ele.innerText = ele.innerText.replace(`{{${exp}}}`, that.vm.data[exp]);
                        a = ele.innerText.indexOf("{{"), b = ele.innerText.indexOf('}}');
                    }

                })

            }
        }


        class MyVue {
            constructor({ el, data }) {
                this.$el = document.querySelector(el);
                this.data = data;
                this.comp = new Compile(this);
                this.comp.run()
            }
        }

        let app = new MyVue({
            el: '#app',
            data: {
                text: 'hello',
                test: 'testString'
            }
        })

        document.querySelector('#btn').addEventListener('click', () => { app.data.text = '999' })
        /* v-on解析没写,此处只是用button测试修改text能否同步修改input的value */


    </script>
</body>

</html>

是根据自己对于发布-订阅模式写的,有挺多有问题的地方的。
比如使用childnodes是无法获得直接以textnode结点写入的{{value}}的。
对于input部分的逻辑有了更深的理解。
对于input输入框和v-model,事实上是绑定了input事件。
当触发input事件时:
(1)赋值,触发被劫持的set函数。
(2)set函数中告知dep该值有变化
(3)dep通知watcher即订阅者列表。
(4)watcher执行update函数,通过cb函数实现对view的修改。
而并非触发input事件时执行后三步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值