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

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

内容简介:这篇仿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...


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

查看所有标签

猜你喜欢:

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

零基础学C语言

零基础学C语言

康莉//李宽 / 机械工业 / 2009-4 / 48.00元

《零基础学C语言》的特点是内容全面、翔实,通俗易懂,循序渐进地介绍了C语言各方面的知识,重点突出。《零基础学C语言》含有大量实例,代码短小精炼,紧扣所讲要点的本质,以加深读者的印象,同时结合笔者多年使用C语言的经验,阐述了很多代码编写技巧,读者可将代码复制到自己的机器上进行实验,自行实践和演练。C语言是编程方式灵活多样、功能强大、应用广泛的一种程序设计语言。从程序设计语言的发展历程来看,尽管后来出......一起来看看 《零基础学C语言》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具