<div id="app">
    <input type="text" v-model="username">
    {{username}}
</div>
<script>
function Watcher (vm, node, name, nodeType) {
  this.name = name;
  this.node = node;
  this.vm = vm;
  this.nodeType = nodeType;
  this.update = function() {
    if (this.nodeType == 'text') {
      this.node.nodeValue = this.vm.$data[this.name];
    }
    if (this.nodeType == 'input') {
      this.node.value = this.vm.$data[this.name];
    }
  }
  Dep.target = this;
}
// 主播
function Dep () {
  this.subs = []
  this.addSub = function(sub) {
    this.subs.push(sub);
  },
  this.notify = function() {
    console.log(this.subs)
    this.subs.forEach(function(sub) {
      sub.update();
    });
  }
}
// 保存的所有粉丝
let watchers = [];
function observer(vm)
{
    Object.keys(vm.$data).forEach(key => {
        let dep = new Dep() // 创建主播
        let value = vm.$data[key]
        Object.defineProperty(vm.$data, key,{
            get(){
                // 代码:添加订阅者 watcher 到主题对象 Dep
              // 生活:给主播添加粉丝
              dep.addSub(Dep.target)
              return value;
            },
            set(newValue){
              if(newValue === value) return
              value = newValue
              // 明确:模型中的数据就是主播
              // 然后:视图中使用模型数据就是一个个粉丝
              // 生活中:主播开播 - 通知粉丝进入直播间 弹框提示
              // 代码中:主播更新 - 通知粉丝页面同步显示最新数据
              // 留心:所有节点都遍历性能差
            //   watchers.forEach(watcher=>{
            //       if (watcher.nodeType == 'text') {
            //           watcher.node.nodeValue = watcher.vm.$data[watcher.name]
            //       }
            //       if (watcher.nodeType == 'input') {
            //           watcher.node.value = watcher.vm.$data[watcher.name]
            //       }
            //   })
            dep.notify();
            }
        })
    })
}
//编译解析指令
function compile(node, vm)
{
  let reg = /\{\{(.*)\}\}/g  //正则匹配页面指令
  //元素节点  nodeName 获取标签名、nodeType获取类型
  if(node.nodeType === 1)
  {
    let attr = node.attributes;
    //解析节点的属性
    for(let i = 0;i < attr.length; i++)
    {
      if(attr[i].nodeName == 'v-model')
      {
        let name = attr[i].nodeValue     //  v-model="username" 获取这里面的username
        new Watcher(vm, node, name, 'input')  // 切记:留心位置,因为创建好了之后,下面刚好触发直播get   这样就可以让当前粉丝和主播绑定
        node.value = vm.$data[name]      //  将【对应的】模型数据  给标签的value属性
        node.removeAttribute('v-model'); //  解析完毕不要出现vue指令
        //-------------------------------
        node.addEventListener('input',function(e){
          vm.$data[name] = e.target.value;
        });
        //--------------------------------------------
        //--------------------------------------------
        // 保存粉丝
        // 目的:M到V
        // 代码:node.value = vm.$data[name] // name username
        // watchers.push({ node:node, name: name, nodeType: 'input', vm:vm })
        //--------------------------------------------
      }
    }
  }
  //如果节点类型为text
  if(node.nodeType === 3)
  {
    if(reg.test(node.nodeValue))
    {
      let name = RegExp.$1;//获取匹配到的字符串(注:这句话非常复杂,你想看到得单独找我要视频  
      name = name.trim();  // 面试题js如何取空格  let str = ' a b '    trim, trimLeft, trimRight
                           // str.replace(/\s/g, '')  
                           // function trimAll(data) { return data.replace(/\s/g, '')  }
        new Watcher(vm, node, name, 'text');   // 切记:留心位置
      node.nodeValue = vm.$data[name];
      // ---------------------------
        // 保存粉丝
        // 目的:M到V
        // 代码:node.nodeValue = vm.$data[name] // name username
        // watchers.push({ node:node, name: name, nodeType: 'text', vm:vm })
      // ---------------------------
    }
  }
}
function Vue(options)
{
  //初始化
  this.$el = document.querySelector(options.el)
  this.$data =options.data
    //监听模型数据变化(观察所有模型数据读写操作
    observer(this)
  // 1 剪切到内存,2 剪切过中挨个过滤 将vue模板语法 解析为真实的数据
  //将挂载目标劫持 -> 存到节点容器中(数据处理) -> 再放到挂载目标中
  let flag = document.createDocumentFragment()
  let child
  // let dom = document.querySelector(el);
  let dom = this.$el
  while(child = dom.firstChild){
    compile(child, this)    //挂载目标中的节点挨个过滤(解析指令)
    flag.appendChild(child) //放到DocumentFragment页面渲染就不会显示/劫持
  }
  dom.appendChild(flag)//将DocumentFragment放到挂载目标中
}
let vm = new Vue({
  el:'#app',
  data:{
    username:'webopenfather',
    age: 5
  }
})
</script>

Vue2高级原理的更多相关文章

  1. Vue2.0原理-模板解析

    下面这段代码,vue内部做了什么操作?我去源码里面找找看 new Vue({ el: '#app' }) 入口 vue 的入口文件在 src/core/instance/index.js, 里面一进来 ...

  2. 基于vue2.0原理-自己实现MVVM框架之computed计算属性

    基于上一篇data的双向绑定,这一篇来聊聊computed的实现原理及自己实现计算属性. 一.先聊下Computed的用法 写一个最简单的小demo,展示用户的名字和年龄,代码如下: <body ...

  3. Vue2.0原理-指令

    指令是 模板解析 的续章,本文会尝试从源码的角度来理解 指令 是如何被提取和应用的. 指令的提取 指令的提取过程是在parse阶段进行的,在 parseHTML 方法中,会解析字符串模板为如下的单个a ...

  4. vue3响应式模式设计原理

    vue3响应式模式设计原理 为什么要关系vue3的设计原理?了解vue3构建原理,将有助于开发者更快速上手Vue3:同时可以提高Vue调试技能,可以快速定位错误 1.vue3对比vue2 vue2的原 ...

  5. Vue2和Vue3技术整理3 - 高级篇

    3.高级篇 前言 基础篇链接:https://www.cnblogs.com/xiegongzi/p/15782921.html 组件化开发篇链接:https://www.cnblogs.com/xi ...

  6. Vue2技术整理3 - 高级篇 - 更新完毕

    3.高级篇 前言 基础篇链接:https://www.cnblogs.com/xiegongzi/p/15782921.html 组件化开发篇链接:https://www.cnblogs.com/xi ...

  7. 分布式缓存技术redis学习系列(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

  8. 分布式缓存技术redis学习(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

  9. Mysql高级之权限检查原理

    原文:Mysql高级之权限检查原理 用户进行数据库操作分为两步: 1 是否有权限连接,根据host,name,password: 2 是否有权限进行CURD: 图示解说: 关于用户权限在哪里进行存放? ...

随机推荐

  1. 一起学习PHP中GD库的使用(一)

    又到了一个大家非常熟悉的库了,对于图像图形的处理来说,GD 库是 PHPer 们绕不过去的一道坎.从很早很早的 CMS 或者 Discuz 时代,各类开源软件在安装的时候就会明确地指出 GD 库是它们 ...

  2. 优雅地创建未定义类PHP对象

    在PHP中,如果没有事先准备好类,需要创建一个未定义类的对象,我们可以采用下面三种方式: new stdClass() new class{} (object)[] 首先是stdClass,这个类是一 ...

  3. TP5开启缓存

    https://www.kancloud.cn/manual/thinkphp5/215850 V5.0.6+版本开始,全局请求缓存支持设置排除规则,使用方法如下:config.php文件 'requ ...

  4. Java基础系列(13)- 包机制

    包机制 为了更好的组织类,Java提供了包机制,用于区别类名的命名空间 包语句的语法格式为: package pkg1[. pkg2[. pkg3...]]; 一般利用公司域名倒置作为报名 为了能够使 ...

  5. vue 学习资料

    自学资料地址: https://zhuanlan.zhihu.com/p/26535530项目UI部分1.pc站 UI:(1)考虑自己写成本高,需要花费不少时间,好处是可以自己控制维护!(2)引入第三 ...

  6. javascript traverse object attributes 遍历对象属性

    * for in for (var prop in o) { if (o.hasOwnProperty(prop)) { console.log(o[prop]); } } * Object keys ...

  7. Fiddler修改抓包请求

    hi,说到fiddler的用途,第一时间想到抓包,不过还有一个功能是:支持修改请求. 那么问题来了,怎么做呢?很简单,先定下我们需要修改哪个请求. 这里用F12跟fiddler做演示. 首先我们在F1 ...

  8. 牛客挑战赛48C-铬合金之声【Prufer序列】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/11161/C 题目大意 \(n\)个点加\(m\)条边使得不存在环,每种方案的权值是所有联通块的大小乘积. 求 ...

  9. Docker-Compose的一些常用命令

    一.Docker-Compose简介 1.Docker-Compose简介 Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排. Docker-Co ...

  10. RuntimeError: DataLoader worker (pid 18255) is killed by signal: Killed.

    RuntimeError: DataLoader worker (pid 18255) is killed by signal: Killed. 通过观察内存发现,数据加载过程中内存会被耗尽.