美文网首页
自己手动实现简单的双向数据绑定 mvvm

自己手动实现简单的双向数据绑定 mvvm

作者: 指尖跳动 | 来源:发表于2020-02-27 15:25 被阅读0次

数据绑定

数据绑定一般就是指的 将数据 展示到 视图上。目前前端的框架都是使用的mvvm模式实现双绑的。大体上有以下几种方式:

1. 发布订阅
2. ng的脏检查
3. 数据劫持

vue的话采用的是数据劫持和发布订阅相结合的方式。 而数据劫持用的是Object.defineProperty来实现的, 可以通过绑定get和set来在获取和设置数据的时候触发相应的函数。

实现

所以我们需要一个监听器Observe来监听数据的变化。在数据发生变化时,我们需要一个Watcher订阅者来更新视图,我们还需要一个指令的解析器compile来解析指令和初始化视图。

  • Observe 监听器: 监听数据的变化, 通知订阅者
  • Watcher 订阅者: 收到数据的变化, 更新视图
  • Compile 解析器: 解析指令,初始化模板,绑定订阅者
Observe

监听数据的每一个属性, 由于可能会有多个watcher,所以需要一个容器来存储。

function Sub() {
        this.subs = [];
    }
    Sub.prototype = {
        add(sub) {
            this.subs.push(sub);
        },
        trigger() {
            this.subs.forEach(sub => {
                sub.update();
            })
        }
    };
    Sub.target = null;

    function observe(data) {
        if (typeof data !== 'object' || !data) return;
        Object.keys(data).forEach(item => {
            let val = data[item];
            let sub = new Sub();
            Object.defineProperty(data, item, {
                enumerable: true,
                configurable: false,
                get() {
                    if (Sub.target) {
                        sub.add(Sub.target);
                    }
                    return val;
                },
                set(newVal) {
                    val = newVal;
                    sub.trigger();
                }
            })
        })
    }
Watcher
function Watcher(vm, prop, callback) {
        this.vm = vm;
        this.prop = prop;
        this.callback = callback;
        Sub.target = this;
        let val = this.vm.$data[prop];
        Sub.target = null;
        this.vaule = val;
    }

    Watcher.prototype.update = function () {
        let newValue = this.vm.$data[this.prop];
        if (this.value !== newValue) {
            this.value = newValue;
            this.callback.call(this.vm, newValue);
        }
    }
Compile

获取到dom中的指令和初始化模板, 添加watcher更新视图。

function Compile(vm) {
        this.vm = vm;
        this.el = vm.$el;
        this.init();
    }
    
    Compile.prototype.init = function () {
        let fragment = document.createDocumentFragment();
        let child = this.el.firstChild;
        while(child) {
            fragment.append(child);
            child = this.el.firstChild;
        }
        let childNodes = fragment.childNodes;
        Array.from(childNodes).forEach(node => {
            if (node.nodeType === 1) {
                let attrs = node.attributes;
                Array.from(attrs).forEach(attr => {
                    let name = attr.nodeName;
                    if (name === 'v-model') {
                        let prop = attr.nodeValue;
                        let value = this.vm.$data[prop];
                        node.value = value;
                        new Watcher(this.vm, prop, val => {
                            node.value = val;
                        });
                        node.addEventListener('input', e => {
                            let newVal = e.target.value;
                            if (value !== newVal) {
                                this.vm.$data[prop] = newVal;
                            }
                        })
                    }
                })
            }
        
            let reg = /\{\{(.*)\}\}/;
            let text = node.textContent;
            if (reg.test(text)) {
                let prop = RegExp.$1;
                let val = this.vm.$data[prop];
                node.textContent = val;
                new Watcher(this.vm, prop, val => {
                    node.textContent = val;
                });
            }
        })
        this.el.appendChild(fragment);
    }

到这里, 基本的思路已经实现完毕, 这里只实现了v-model指令。

最后,结合 Observe Watcher和 Compile, 就可以成为一个完整的mvvm了。

<div id="app">
        <div>{{val}}</div>
        <input type="text" id="input" v-model="val">
    </div>


<script>
     function MyVue(options) {
        this.$options = options;
        this.$el = options.el;
        this.$data = options.data;
        this.init();
    }

    MyVue.prototype.init = function () {
        observe(this.$data);
        new Compile(this);
    };


    new MyVue({
        el: document.getElementById('app'),
        data: {
            val: 123
        }
    })
</script>

当然,这只是简单的实现,没考虑细节,主要是学习思路。

查看效果 (代码直接在页面上)


from: https://www.cnblogs.com/wjyz/p/11419073.html

相关文章

  • 自己手动实现简单的双向数据绑定 mvvm

    数据绑定 数据绑定一般就是指的 将数据 展示到 视图上。目前前端的框架都是使用的mvvm模式实现双绑的。大体上有以...

  • vue双向数据绑定

    剖析Vue原理、实现双向绑定MVVM 几种实现双向绑定的做法 目前几种主流的mvc(vm)框架都实现了单向数据绑定...

  • Vue MVVM 原理实现

    核心原理 MVVM 双向数据绑定, 数据驱动视图 Vue 实现 MVVM 采用 数据劫持 + 发布订阅模式 : ...

  • 双向数据绑定

    双向数据绑定 双向数据绑定基于MVVM框架,vue属于MVVM框架 MVVM:M等于model,V等于view,即...

  • JavaScript实现双向绑定的三种方式

    前端数据的双向绑定方法 前端的视图层和数据层有时需要实现双向绑定(two-way-binding),例如mvvm框...

  • iOS MVVM的理解与分析

    MVVM MVVM:中心思想是数据实现双向绑定,业务逻辑与试图的分离...

  • Vue双向数据绑定原理

    剖析Vue实现原理 - 如何实现双向绑定mvvm 本文能帮你做什么?1、了解vue的双向数据绑定原理以及核心代码模...

  • 关于双向绑定的问题

    剖析Vue实现原理 - 如何实现双向绑定mvvm 本文能帮你做什么?1、了解vue的双向数据绑定原理以及核心代码模...

  • Vue2.0原理与MVVM的实现

    剖析Vue原理&实现双向绑定MVVM vue源码 双向绑定 -- MVVM 目前几种主流的MVC框架都实现了单向数...

  • Vue面试考点之响应式原理

    我们都知道Vue通过MVVM思想实现数据的双向绑定,数据驱动页面视图。那它到底是如何进行双向绑定的呢? Vue数据...

网友评论

      本文标题:自己手动实现简单的双向数据绑定 mvvm

      本文链接:https://www.haomeiwen.com/subject/uezthhtx.html