Skip to content

手写数据双向绑定 #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
LumiereXyloto opened this issue Dec 16, 2019 · 1 comment
Open

手写数据双向绑定 #35

LumiereXyloto opened this issue Dec 16, 2019 · 1 comment

Comments

@LumiereXyloto
Copy link
Collaborator

总的来说数据劫持结合发布者-订阅者模式

通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

vue的数据双向绑定将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。

极简版双向数据绑定:

<body>
    <div id="app">
        <input type="text" id="txt">
        <p id="show"></p>
    </div>
</body>

<script type="text/javascript">
    var obj = {}
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj.text || ''
        },
        set: function (newValue) {
            obj.text = newValue
            document.getElementById('txt').value = newValue
            document.getElementById('show').innerHTML = newValue
        }
    })
    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
    })
</script>
@Reaper622
Copy link
Member

由于Object.defineProperty()对于引用类型的一些不足:

  • Vue 中,Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应,而实际上Object.defineProperty本身是可以监听下标变化的,vue只监听了数组的一些方法。
  • Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
  • Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

双向绑定的Proxy版本:

<body>
    <input type="text" id="model" />
    <p id="word"></p>
    <script>
    const input = document.getElementById('model');
    const word = document.getElementById('word');

    const newObj = new Proxy({}, {
        get: function (target, key, receiver) {
            console.log(`get ${key}`);
            console.log(target, key, receiver);
            return Reflect.get(target, key, receiver);
        },
        set: function (target, key, value, receiver) {
            console.log(`set ${key} = ${value}`);
            console.log(target, key, value, receiver);
            if (key === 'text') {
                input.value = value;
                word.innerText = value;
            }
            return Reflect.set(target, key, value, receiver);
        }
    })    

    input.addEventListener('keyup', function (e) {
        newObj.text = e.target.value;
    })
    </script>
</body>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants