<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. 为什么不推荐Python初学者直接看项目源码

    无论是有没有其他语言的经验,入门Python都很简单.Python拥有简单直观的语法,方便的语法糖,以及丰富的第三方库.只要一个基础的Python教程,大家基本上都能无障碍的入门.在入门之后,很多人对 ...

  2. 如何创建 Office LTSC 2021 VL(批量许可)版本的安装 ISO

    Office LTSC 2021 发布 2021 年 9 月 16 日,微软正式发布了支持 Office 2021 的部署工具(Office Deployment Tool),这意味着 Office ...

  3. Hystrix配置实战及feign超时配置失效

    一.feign超时配置失效 最近项目上遇见feign超时配置总是失效.导致feign调用超过2s之后就会超时,会进行自动重试,重复调用两次服务,并且还是指定接口.这就更加奇怪.最后通过观察以及源码调试 ...

  4. shell脚本中 /dev/null 的用途

    /dev/null 是一个特殊的设备文件,它丢弃一切写入其中的数据 可以将它 视为一个黑洞, 它等效于只写文件, 写入其中的所有内容都会消失, 尝试从中读取或输出不会有任何结果,同样,/dev/nul ...

  5. phpstorm 配置Psr4 风格代码

    http://www.cnblogs.com/xp796/p/6441700.html

  6. sunny 内网穿透使用。

    启动方法:

  7. Loj#116-[模板]有源汇有上下界最大流

    正题 题目链接:https://loj.ac/p/116 题目大意 \(n\)个点\(m\)条边的一张图,每条边有流量上下限制,求源点到汇点的最大流. 解题思路 先别急着求上面那个,考虑一下怎么求无源 ...

  8. 深入浅出WPF-09.Command(命令)

    命令 1)命令系统的基本元素 命令(Command),WPF的命令实际上就是实现了ICommand接口的类,平时使用最多的是RoutedCommand类 命令源(Command Source),即命令 ...

  9. 前端规范之CSS规范(Stylelint)

    代码规范是软件开发领域经久不衰的话题,几乎所有工程师在开发过程中都会遇到或思考过这一问题.而随着前端应用的大型化和复杂化,越来越多的前端团队也开始重视代码规范.同样,前段时间,笔者所在的团队也开展了一 ...

  10. IO流基本概念

    IO流主要分为两类 节点流:直接能够进行数据写入或读取的I0流.可以单独执行读写操作,但是功能比较单一,只能进行一些基本 的操作.例如:FileInputStream FileInputStream ...