Vue数据双向绑定的简单实现

栏目: 编程语言 · 发布时间: 7年前

内容简介:这篇仿Vue数据绑定的简单实现,是我看了一些文章之后,把他们的代码研究懂了之后写出来的。当然,这个是简单版本的,复杂版本的就...如果只是简单实现一下Vue的数据绑定还是很简单的,只要将思路理清楚就可以。写的时候考虑的问题就是2点。问题:

这篇仿Vue数据绑定的简单实现,是我看了一些文章之后,把他们的代码研究懂了之后写出来的。当然,这个是简单版本的,复杂版本的就...

如果只是简单实现一下Vue的数据绑定还是很简单的,只要将思路理清楚就可以。写的时候考虑的问题就是2点。

问题:

1)如何实现将数据层的数据显示到视图上

2)如何在视图上的数据改变后,通知数据层相应的数据发生了变化

3)如何让两者联系起来,当一个数据改变时,视图上的所有数据层的数据进行更新

第一个问题要将数据层的数据显示到视图上,很明显这个需要使用DOM操作,先判断 el 下的子元素上的属性是否有关键字(如: v-model、v-bind),如果有就将其与 data 中的key来进行对比,然后data里的值渲染到页面上。这里需要用到递归,从而使el下的每个层级的元素属性都进行一次筛选。

第二个问题可以通过在相应的元素(input)上进行事件监听,当视图上的数据发生了变化就改变相对应的数据成数据。这两者之间需要一个监听器,从而使当一个改变时,另外一个也跟着改变。

第三个问题实现需要利用发布订阅模式和ES5的Object.defineProperty()。首先要创建一个订阅者,里面有一个方法,使视图层上的值等于数据层的数据。在进行DOM筛选时,将对应的数据、key、元素传入订阅者,在将这个订阅者放在一个对象中,这个对象的key就是data的key。然后通过ES5的Object.defineProperty()来对data里的数据进行数据劫持,当data里的数据被重新设置,会触发set函数,然后触发监听者。

具体代码如下:

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
        <div id="app">
            <input type="text" v-model="name">
            <div>{{name}}</div>
        </div>
    <body>
        <script>
          window.onload = () => {
                new MyVue({
                    el: '#app',
                    data: {
                        name: 123
                    }
                })
            }
            
            function MyVue(option={}) {
                this.$el = document.querySelector(option.el);
                this.$data = option.data;
                this._watchTpl = {}; 
                this._observer(this.$data);
                this._compile(this.$el);
            }
    
            MyVue.prototype._observer = function (data) {
                let _this = this;
                Object.keys(data).forEach( (item, index) => {
                    let value = data[item];
                    this._watchTpl[item] = [];
                    Object.defineProperty( data, item, {
                        enumerable: true,
                        configurable: true,
                        get() {
                            return value;
                        },
                        set(newVal) {
                            if(newVal !== value) {
                                value= newVal;
                                _this._watchTpl[item].forEach((item) => {
                                    item.update();
                                })  
                            }
                        }
                    } )
                } )
            }
            MyVue.prototype._compile = function (el) {
                let nodeList = el.children;
                nodeList = Array.prototype.slice.call(nodeList);
                nodeList.forEach( (node, index) => {
                    if(node.children) {
                        this._compile(node);
                    }
                    if (node.getAttribute('v-model')) {
                        let key = node.getAttribute('v-model');
                        if(!this._watchTpl[key].length) {
                            this._watchTpl[key] = [];
                        }
                        this._watchTpl[key].push(new Watcher(node, key, this, 'value'))
                        node.addEventListener('input', () => {
                            this.$data[key] = node.value;
                        } )
                    }
    
                    let reg = /\{\{\s*([^}]+\S)\s*\}\}/g;
                    // 该正则表达式的含义  \s匹配任何空白字符,\S匹配任何非空白字符,*匹配任何包含零个或多个前导字符串,+匹配任何至少包含一个前导字符串  总的含义就是匹配以{{为开头,后面跟着零或多个空白符,}前面有至少一个非空白字符的以}为结尾的字符串
                    let content = node.textContent;
                    if (reg.test(content)) {
                        // 这篇文章介绍了replace怎么用,我以前没怎么研究过replace这个语法。 https://blog.csdn.net/linbilin_/article/details/57094602
                        content.replace(reg, (matched, placeholder) => {
                            if(!this._watchTpl[placeholder].length) {
                                this._watchTpl[placeholder] = [];
                            }
                            this._watchTpl[placeholder].push(new Watcher(node, placeholder, this, 'innerHTML'))
                        })
                    }
    
                } )
            }
            function Watcher (node, key, vm, type) {
                this.node = node;
                this.key = key;
                this.vm = vm;
                this.type = type;
                this.update();
            }
            Watcher.prototype.update = function () {
                this.node[this.type] = this.vm.$data[this.key];
            }
        </script>
    </body>
    
    </html>

这篇文章主要是参考于 https://segmentfault.com/a/11... ,高难度的那个是 https://www.cnblogs.com/canfo...


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTTP Essentials

HTTP Essentials

Stephen A. Thomas、Stephen Thomas / Wiley / 2001-03-08 / USD 34.99

The first complete reference guide to the essential Web protocol As applications and services converge and Web technologies not only assume HTTP but require developers to manipulate it, it is be......一起来看看 《HTTP Essentials》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具