简陋的双向绑定

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <input type="text" v-model="text">
    {{text}}
  </div>
</body>
<script type="text/javascript">

//将结点劫持
  function node2Fragment(node,vm){
    var flag=document.createDocumentFragment();
    var child;
    while(child=node.firstChild){
      //编译每个节点
      compile(child,vm)
      /*appendChild 会把节点从原来结构转移到目标父节点,所以相当于原来的节点被删除*/
      flag.appendChild(child)
    }
    return flag
  }

  /*
  DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。
  因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(reflow)(对元素位置和几何上的计算)。因此,使用文档片段document fragments 通常会起到优化性能的作用
   */

  //编译虚拟dom的辅助函数
  function compile(node,vm){
    /*匹配{{里的内容}}*/
    var reg=/\{\{(.*)\}\}/;

    /*nodeType -1元素节点 3-文本节点*/
    if(node.nodeType===1){
      var attr=node.attributes;
      for(var i=0;i<attr.length;i++){
        if(attr[i].nodeName==‘v-model‘){
          var name=attr[i].nodeValue;
          node.addEventListener(‘input‘,function(e){
            vm[name]=e.target.value;
          })
          node.value=vm[name];
        }
      }
    };
    if(node.nodeType===3){
      if(reg.test(node.nodeValue)){
        var name=RegExp.$1;
        name=name.trim();
        //初始化数据并且触发get函数
        new Watcher(vm,node,name);
      }
    }
  }
  //定义订阅者类
  function Watcher(vm,node,name){
    //Dep.target存储了当前的观察者,使get函数能够存储观察者
    Dep.target=this;
    this.name=name;
    this.node=node;
    this.vm=vm;
    //订阅者执行一次更新视图并把订阅者添加进去
    this.update();
    Dep.target=null;
  }
  Watcher.prototype={
    update:function(){
      //触发set函数
      this.get();
      //更新视图
      this.node.nodeValue=this.value;
    },
    get:function(){
      this.value=this.vm[this.name]//触发对应数据的get方法
    }
  }
  //响应式的数据绑定函数
  function defineReactive(obj,key,val){
    //定义一个主题实例
    var dep=new Dep()
    Object.defineProperty(obj,key,{
      get:function(){
        //添加订阅者watcher到主题对象Dep
        if(Dep.target)dep.addSub(Dep.target)
        return val
      },
      set:function(newVal){
        if(newVal===val)return ;
        val=newVal;
        //实例发出通知(更新所有订阅了这个属性消息的view)
        dep.notify();
      }
    })
  }
  function Dep(){
    //主题的订阅者们
    this.subs=[];
  }
  Dep.prototype={
    //添加订阅者的方法
    addSub:function(sub){
      this.subs.push(sub);
    },
    //发布信息的方法(让订阅者watcher们全部更新view)
    notify:function(){
      this.subs.forEach(function(sub){
        sub.update();
      })
    }
  }
  //监听vm这个对象的obj有的属性
  function observe(obj,vm){
    Object.keys(obj).forEach(function(key){
      console.log(vm)
      defineReactive(vm,key,obj[key])
    })
  }
  //构建Vue对象
  function Vue(options){
    this.data=options.data;
    var id=options.el;
    var data=this.data
    //监听this(即vm)这个对象的data属性
    observe(data,this)
    var dom=node2Fragment(document.getElementById(id),this);
    document.getElementById(id).appendChild(dom);
  }
  var vm=new Vue({
    el:‘app‘,
    data:{
      text:‘hello world‘,
      fuck:‘fuckingboy‘
    }
  })
  console.log(vm);

/*
  使用发布-订阅模式与闭包,很巧妙的实现了双向绑定,
  首先observe调用,遍历数据对象,并给每个属性利用闭包创建私有空间,
  每个私有空间都new出一个Dep对象,试每个空间都有自己的订阅发布对象,
  并在get访问器里添加订阅者,那么每个Watcher对象里设置当前订阅对象利用
  触发get访问器方法可以向当前属性的私有空间添加订阅者,然后只要每次
  设置这个属性的值时就会触发设置器就会直接触发向所有订阅了这个属性订阅者
  接收到发布的消息,那么就可以更新对应视图内容。
 */

</script>
</html>

扒一张图,代表一下。

一直听说vue的双向绑定很巧妙,很小巧,所以就大概学习一下。

一个双向绑定系统是要很严谨的代码实现的,所以代码量一定不少,

要考虑的因素很多,如data深递归,html各种节点的深递归,还有各种

数据对应视图的那些节点位置对应,还有正则的使用,这些种种考虑

就不是一般几天可以搞定的,所以学习一下就可以,没有必要造轮子,

以上的例子无法符合数据的多层嵌套和就实现了一个v-model,其他都没有,

只能加深双向绑定原理,好以后对这方面的使用更顺手。

原文地址:https://www.cnblogs.com/zhangzhicheng/p/8900722.html

时间: 2024-08-02 03:40:17

简陋的双向绑定的相关文章

剖析Vue原理&amp;实现双向绑定MVVM

剖析Vue原理&实现双向绑定MVVM vue.js 双向绑定 javascript 邓木琴居然被盗用了 2016年08月16日发布 推荐 24 推荐 收藏 195 收藏,10.6k 浏览 本文能帮你做什么?1.了解vue的双向数据绑定原理以及核心代码模块2.缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考虑到数组的处理.数据的循环依赖等,也难免存在一些问题,欢迎大家指正.不过这些并不会影响大家的阅读和理解,相信看完

Angular数据双向绑定

Angular数据双向绑定 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS有着诸多特性,最为核心的是:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入等等. 一.什么是数据双向绑定 Angular实现了双向绑定机制.所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更能实时展现到界面. 一个最简单的示例就是这样: <div ng-control

【vue】跟着老马学习vue-数据双向绑定

学习了node.js教程,只能说是有了一定的了解,之前也了解了webpack和es6的核心内容,也看过vue2.0的官网教程,并结合视频看过项目,但是理解和运用仍然存在很多问题,接下来的一段时间,跟着老马学习vue 学习链接:http://aicoder.com/vue/preview/all.html#1 vue最大的特点就在于它的双向绑定,是一个前端的双向绑定类的框架. 一说到vue我们就应该立刻想到以下几部分:1.数据双向绑定:2.列表渲染.条件渲染:3.事件处理:4.生命周期:5.组件化

原生js实现数据双向绑定

最近接触了vue,在谈到vue等等的mvvm框架之前,先了解什么是数据双向绑定以及如何利用原生JS实现数据双向绑定 单向数据绑定 指先把模板写好,然后把模板和数据(数据可能来自后台)整合到一起形成HTML代码,然后把这段HTML代码插入到文档流里 缺点:一旦HTML代码生成就没有办法改变,如果有新数据重新传入,就必须重新把模板和数据整合到一起插入到文档流中 数据双向绑定 数据模型和视图之间的双向绑定,用户在视图上的修改会自动同步到数据模型中,同样的,如果数据模型中的值发生变化,也会同步到视图中去

AngularJS学习笔记(三)数据双向绑定

双向绑定 双向绑定是AngularJS最实用的功能,它节省了大量的代码,使我们专注于数据和视图,不用浪费大量的代码在Dom监听.数据同步上,关于双向更新,可看下图: 下面,我们通过代码来实现.先不要纠结其中不明白的地方,先来体验下数据绑定的效果. 数据-->视图 这里我们只演示有了数据以后,如何绑定到视图上. <!DOCTYPE html> <html ng-app="App"> <head> <script type="tex

Angular指令封装jQuery日期时间插件datetimepicker实现双向绑定

一放假就高产似母猪了. 00.混乱的前端界 Angular1.x确实是个学习成本很高的框架,刚开始实习那会儿,前端啥也不懂,工头说用Angular,我们这群小弟也只能硬着头皮学.在这之前,前端的东西大部分都用的jQuery,而Angular正好是和jQuery的思维是相反的,开发过程中遇到了不少坑.而Angular团队也放弃了1.x开始开发和React神似的2.0版本,唉,真是沧海桑田啊. 01.Angular vs jQuery Angular模块化和解耦的思路确实值得一学,但是相对于成熟的j

双向绑定 使得函数通过依赖管理系统仅仅需要声明需要的协作对象,而不需要知道从哪里来

双向绑定,即MVVM,也就是数据影响视图,视图也会影响数据. 一个M影响V最简单的示例就是这样: <div ng-controller="CounterCtrl"> <span ng-bind="counter"></span> <button ng-click="counter++">increase</button> </div> function CounterCtrl

vue的双向绑定原理及实现

前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图来吸引各位: 代码:                                                                    效果图:   是不是看起来跟vue的使用方式差不多?接下来就来从原理到实现,从简到难一步一步来实现这个SelfVue.由于本文只是为了学习和分享

vue checkbox 双向绑定及初始化渲染

双向绑定可以绑定到同一个数组 <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="Joh