美文网首页前端
原生JS模拟Vue双向数据绑定

原生JS模拟Vue双向数据绑定

作者: 虚竹梦姑 | 来源:发表于2018-05-05 15:35 被阅读0次

童鞋们都应该知道Vue2.x很重要的特性(数据双向绑定、虚拟DOM),所以在面试的时候就经常有面试官问小白同学说数据双向绑定原理是什么,虚拟DOM是什么,给我实现一个呗。所以给大家展示一个最简的双向绑定的案例,也参考了网上一些资料。至于虚拟DOM的实现后面再说吧,网上资料也很多很全因为毕竟react都出来好久了。(都知道网上资料一大堆,大都不着调。)直接上代码!!!

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="msg">
        <span>{{msg}}</span>
    </div>
    <script src="mvvm/watcher.js"></script>
    <script src="mvvm/observer.js"></script>
    <script src="mvvm/compile.js"></script>
    <script src="mvvm/index.js"></script>
    <script>
        let vm = new Vue({
            el: "app",
            data: {
                msg: 'hello world'
            }
        })
    </script>
</body>
</html>
  • index.js
// 这个文件为入口文件,也就是Vue的构造函数
function Vue(options) {
    // 传递过来的对象
    this.data = options.data;
    this.id = options.el;
    // 1.第一步先将data中所有的数据进行监听
    //   这个函数一般使用递归的方式完成所有属性的监听
    observer(this.data, this);

    // 2.第二步将所有DOM节点的翻译出来,也就是说将v-model,
    //  {{}}等翻译成你想要的数据。其次还有将v-model的数据进行监听,使用观察者模式,完成双向绑定
    getAllNode(document.getElementById(this.id), this);
}
  • observer.js
// 定义一个pubsub,这个作用是将所有的观察者加入其中,
// 并且出发事件
function pubsub() {
    this.subs = [];
}

pubsub.prototype = {
    // 将需要观察的数据加入subs中
    addSub: function(sub){
        this.subs.push(sub);
    },
    // 执行观察的数据上绑定的事件update事件。
    pub: function(){
        console.log(this.subs);
        this.subs.forEach(function(sub){
            sub.update();
        })
    }
}

// 将数据都进行监听
function active(obj, key, val) {
    var pubsub1 = new pubsub();
    Object.defineProperty(obj.data, key, {
        // getter,如果获取数据时,会判断是有需要观察的数据,如果有就添加到subs中,没有不添加
        // Pubsub是一个全局的变量,这个变量必须是全局才能判断是有需要观察的数据
        get() {
            if(Pubsub.target) {
                // 添加订阅
                pubsub1.addSub(Pubsub.target);
            }
            return val;
        },
        set(newVal) {
            // 如果数据被setter,那么就涉及到及时的更新数据,
            // 这时只需要进行发布事件,观察的数据就会执行update函数来执行更新操作
            if(val == newVal) {
                return;
            }
            val = newVal;

            // 发布
            pubsub1.pub();
        }
    })
}

// 监听data中所有的数据
function observer(obj, vm) {
    // obj = data
    // vm  = 实例对象
    for(var key in obj) {
        active(vm, key, obj[key]);
    }
}
  • comiple.js
// 获取到所有节点,并且进行翻译
function getAllNode(node, vm) {
    console.log(vm);
    var length = node.childNodes.length;
    for(var i = 0; i < length; i++) {
        compile(node.childNodes[i], vm)
    }
}

// 翻译
function compile(node, vm) {
    // 匹配到{{}},将其中的值进行观察。
    var reg = /\{\{(.*)\}\}/;
    // 如果节点存在并且节点类型为1时(查看一下为1时一般都是什么节点)
    if(node!=undefined && node.nodeType == 1) {
        // 获取到节点的属性
        var attr = node.attributes;
        if(attr.length) {
            // 对节点的属性循环处理
            for(var i = 0; i < attr.length; i++) {
                // 如果为v-model时,进行处理
                if(attr[i].nodeName == "v-model") {
                    // 获取到v-model里面的写的变量名
                    var name = attr[i].nodeValue;
                    // 给该input增加事件处理,如果内容改变,并及时更新data中的数据
                    node.addEventListener('input', function(e) {
                        vm.data[name] = e.target.value;
                    })

                    // 修改dom上的数据,并移除指令
                    console.log(vm.data[name]);
                    node.value = vm.data[name];
                    node.removeAttribute('v-model')
                }
            }
        }else {
            // 匹配到{{}}的dom节点
            if(reg.test(node.outerText)) {
                var name = RegExp.$1;
                // 拿到变量的名称
                name = name.trim();

                // 将变量加入到watcher中
                new Watcher(vm, node, name)
            }
        }
    }
}
  • watcher.js
// 全局的变量,这个变量来控制是否当前有观察的数据
var Pubsub = {
    target: null
}
// 观察者定义
function Watcher(vm, node, name) {
    Pubsub.target = this;
    this.name = name;
    this.node = node;
    this.vm = vm;
    // 将数据观察时就要进行更新操作一次
    this.update();
    Pubsub.target = null;
}

Watcher.prototype = {
    // 将更新的数据渲染到页面中去
    update() {
        this.node.innerHTML = this.vm.data[this.name];
    }
}

大家想测试,就将五个文件的代码复制下来,运行一下。
运行的同时给大家配一张图,让大家容易理解。(这张是盗图,不知哪位大神用visio画的,这里我就直接引用了)


原理图.png

相关文章

  • Vue框架基础

    原生js与Vue框架的区别 用原生实现双向数据绑定 用Vue实现双向数据绑定 Vue是一个javaScript框架...

  • 原生JS模拟Vue双向数据绑定

    童鞋们都应该知道Vue2.x很重要的特性(数据双向绑定、虚拟DOM),所以在面试的时候就经常有面试官问小白同学说数...

  • Vue之表单双向数据绑定和组件

    三、表单双向数据绑定和组件 目录:双向数据绑定、组件 1.双向数据绑定 1)什么是双向数据绑定Vue.js是一个M...

  • vue.js 笔记

    v-for(循环) //html //vue.js v-model(双向数据绑定) //html //vue.js...

  • Vue.js 双向数据绑定原理分析

    在使用Vue.js自定义组件时,很多时候,我们都期望数据是双向绑定的。 Vue.js实现双向数据绑定的两种方式 1...

  • Vue 中的双向数据绑定

    双向绑定 单向数据流 双向绑定 or 单向数据流 Vue 是单向数据流,不是双向绑定 Vue 的双向绑定是语法糖 ...

  • 双向数据绑定

    原生js模拟双向数据绑定原理:选中元素,利用事件监听键盘事件keyup将target的值拿到,赋值给目标元素 下面...

  • vue 入门

    1.1 vue的引入数据的双向绑定:也就是数据的同步修改 1.1.1. vue与js的对比----js的实现 --...

  • 2 数据绑定

    1 通过以下代码体验Vue.js最核心的功能——数据的双向绑定 双向绑定v-mod...

  • vue.js 双向数据绑定的实现

    vue.js 双向数据绑定的实现 1.首先定义构造函数var Vue = function (params) { ...

网友评论

    本文标题:原生JS模拟Vue双向数据绑定

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