Vue双向数据绑定的手动实现

class Asarua {

  // 传入一个对象,也就是实例化的时候的参数

  constructor(options) {

    this.$data = options.data;

    // 获取挂载的dom

    this.$el = document.querySelector(options.el);

    this.$methods = options.methods;

    this.$options = options;

    // 定义一个容器,来存放所有的订阅者

    this.observable = {};

    this.initObservable();

    this.addObservable();

    this.observe();

  }

  // 初始化容器

  initObservable() {

    for (let i in this.$data) {

      this.observable[i] = [];

    }

  }

  // 添加订阅

  addObservable() {

    // 获取所有当前节点下面的一级子节点

    const nodes = this.$el.children;

    // 使用for循环,如果是for in会遍历出一些奇怪的东西;

    for (let i = 0; i < nodes.length; i++) {

      // 判断是否存在自定义的a-model属性

      if (nodes[i].hasAttribute("a-model")) {

        // 获取存在a-model属性的元素

        const node = nodes[i];

        // 获取此元素的a-model的值,也就是订阅哪个属性

        const prop = node.getAttribute("a-model");

        // 在初始化后的容器中找到此属性,将一个实例化的观察者添加进容器,同时添加进去的还有一个刷新的方法

        this.observable[prop].push(new Watcher(node, "value", this, prop));

        // 开始监听,如果发生了input事件,则改变this.$data中此属性的值,同时触发拦截器,在拦截器中调用update方法进行更新

        node.addEventListener("input", () => {

          this.$data[prop] = node.value;

        })

      };

      if (nodes[i].hasAttribute("a-text")) {

        const node = nodes[i];

        const prop = node.getAttribute("a-text");

        this.observable[prop].push(new Watcher(node, "innerText", this, prop));

      };

      if (nodes[i].hasAttribute("a-html")) {

        const node = nodes[i];

        const prop = node.getAttribute("a-html");

        this.observable[prop].push(new Watcher(node, "innerHTML", this, prop));

      };

      // 如果有@click,那么为当前元素添加一个事件监听,在点击时调用this.$methods上的方法,同时把作用域指向this.$data,使其可以通过this.来访问$data中的属性

      if (nodes[i].hasAttribute("@click")) {

        const node = nodes[i];

        const prop = node.getAttribute("@click");

        node.addEventListener("click",()=>{

          this.$methods[prop].call(this.$data);

        })

      };

    }

  }

  observe() {

    const _this = this;

    // 遍历容器,给每个属性添加拦截器

    for (let i in this.$data) {

      let _value = this.$data[i];

      // 拦截this.$data中的每一个属性,如果是取值则直接返回,如果是设置,首先判断是否和之前相同,如果不同,将旧值替换为新值,并且调用update方法实时刷新

      Object.defineProperty(this.$data, i, {

        get() {

          return _value;

        },

        set(v) {

          if (_value !== v) {

            _value = v;

            _this.observable[i].forEach(v => v.update())

          }

        }

      })

    }

  }

}

class Watcher {

  // 接收四个参数,当前要更新的元素,要更新的属性,上面类的实例,监听的属性

  constructor(el, prop, any, attr) {

    this.el = el;

    this.prop = prop;

    this.any = any;

    this.attr = attr;

    this.update();

  }

  // update函数,调用时,使当前元素的当前属性的值变为data中的当前属性的值

  update() {

    this.el[this.prop] = this.any.$data[this.attr];

  }

}

// 首先获取所有实例化得到的属性,然后为data选项中的每一项都创建订阅者。

// 其次判断是否存在双向数据绑定的自定义属性,如果存在,则将其添加进其属性值所

// 对应的订阅者容器中。同时将其绑定的dom,其要更改的属性,以及更改后的值,还有一个同步当前dom值和data中的当前属性的值的方法传递进去,以便在调用时可以直接更改dom中的值。

// 然后为其添加一个事件监听,观测动向,如果更改了dom中的值则改变data中的值

// 为data中的所有属性添加拦截器,如果是取值则返回当前data中的值,如果是设置,在将传入的值设置为data的值后,调用容器中的当前属性的所有订阅者的update方法,

// 将更改后的data的值,赋值给所有使用了这个数据的值的dom,更改其属性

这么多东西说起来就两步

第一步,添加将input值变为自身值的订阅者

第二步,添加一个自身值改变之后将所有当前属性订阅者的值进行修改的拦截器

原文地址:https://www.cnblogs.com/asablog/p/11365982.html

时间: 2024-10-25 21:05:55

Vue双向数据绑定的手动实现的相关文章

angular和vue双向数据绑定

angular和vue双向数据绑定的原理(重点是vue的双向绑定) 我在整理javascript高级程序设计的笔记的时候看到面向对象设计那章,讲到对象属性分为数据属性和访问器属性,我们平时用的js对象90%以上都只是用到数据属性;我们向来讲解下数据属性和访问器属性到底是什么? 数据属性:数据属性包含一个数据值的位置,在这个位置可以读取和写入值. 访问器属性:访问器属性不包含数据值;他们包含一对getter和setter函数在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值,在写

vue 双向数据绑定的实现学习(二)- 监听器的实现

废话:上一篇https://www.cnblogs.com/adouwt/p/9928278.html 提到了vue实现的基本实现原理:Object.defineProperty() -数据劫持 和 发布订阅者模式(观察者),下面讲的就是数据劫持在代码中的具体实现. 1.先看如何调用 new一个对象,传入我们的参数,这个Myvue ,做了啥? 上面看到了在实例化一个Myvue 对象的时候,会执行init方法, init 方法做了两个事,调用了observer 方法,和 实例化调用了 compil

React 事件对象、键盘事件、表单事件、ref获取dom节点、react实现类似Vue双向数据绑定

1.案例实现代码 import React, { Component } from 'react'; /** * 事件对象.键盘事件.表单事件.ref获取dom节点.react实现类似Vue双向数据绑定 * 事件对象: 在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件有关的信息 * 表单事件: 获取表单的值 * 1.监听表单的改变事件 ---onChange * 2.在改变的事件里面获取表单输入的值 ---event * 3.把表单输入的值赋值给username

vue双向数据绑定原理探究(附demo)

昨天被导师叫去研究了一下vue的双向数据绑定原理...本来以为原理的东西都非常高深,没想到vue的双向绑定真的很好理解啊...自己动手写了一个. 传送门 双向绑定的思想 双向数据绑定的思想就是数据层与UI层的同步,数据再两者之间的任一者发生变化时都会同步更新到另一者. 双向绑定的一些方法 目前,前端实现数据双向数据绑定的方法大致有以下三种: 1.发布者-订阅者模式(backbone.js) 思路:使用自定义的data属性在HTML代码中指明绑定.所有绑定起来的JavaScript对象以及DOM元

Vue双向数据绑定简易实现

一.vue中的双向数据绑定主要使用到了Object.defineProperty(新版的使用Proxy实现的)对Model层的数据进行getter和setter进行劫持,修改Model层数据的时候,在setter中可以知道对那个属性进行修改了,然后修改View的数据. 二.简易版双向数据绑定 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> &

vue双向数据绑定简易demo

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <meta http-equiv="X-UA-Comp

vue双向数据绑定

父组件引用子组件时,可以使用 v-model="params"  绑定一个变量, 在子组件中,需要使用props去接收一个value变量,即父组件绑定到的params的值 在子组件中,使用this.$emit("input" , someData); 可以触发父组件的input事件,someData是参数,它的值会赋给父组件的params的变量. 这种写法是一种简化的写法,是vue的语法糖.他也可以写成 <v-children v-bind:value=&qu

vue 双向数据绑定原理

采用defineProperty的两个方法get.set 示例 1 <!-- 表单 --> 2 <input type="text" id="input"> 3 <!-- 展示 --> 4 <p id="desc"></p> 1 let obj = {}; 2 let temp = {};//采用临时变量代理obj 3 Object.defineProperty(obj,'name',{

vue双向数据绑定原理

1.实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值通知订阅者2.实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,以及绑定相应的更新函数3.实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图4.mvvm入口,整合以上三者 原文地址:https://blog.51cto.com/13550695/2467904