<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. .NET 6 中的HTTP 3支持

    dotnet团队官方博客发布了一篇HTTP3的文章:HTTP/3 support in .NET 6:https://devblogs.microsoft.com/dotnet/http-3-supp ...

  2. Shell系列(20)- 字符截取命令cut

    前言 grep是按行提取:cut默认是通过制表符,按列提取,不能识别用空格作为分隔符 语法 cut [选项] [文件] 选项 -f :列号,截取哪几列数据,多个列号用逗号隔开:列与列之间默认用TAB制 ...

  3. python列表底层实现原理

    Python 列表的数据结构是怎么样的? 书上说的是:列表实现可以是数组和链表.顺序表是怎么回事?顺序表一般是数组. 列表是一个线性的集合,它允许用户在任何位置插入.删除.访问和替换元素.列表实现是基 ...

  4. Blazor Webassembly多标签页开发

    最近准备用Blazor Webassembly做后台开发要用到多标签页,找了半天发现绝大多数都是Blazor Server的多标签没有Webassembly.没办法只能自己想办法造轮子了. 查了许多资 ...

  5. CAD/DWG图Web网页可视化技术之栅格和矢量瓦片

    背景 在上一篇博文中CAD图DWG解析WebGIS可视化技术分析总结提到,实现CAD/DWG图形Web展示的思路一般为解析AutoCAD图形格式,然后转成html5所能绘制的格式如svg,geojso ...

  6. WIFI Deauth攻击-爬坑笔记

    这里用Aircrack这款工具进行介绍: 准备工作:无线网卡连接电脑或者虚拟机(免驱的最好),如需驱动请自行安装驱动 1.将无线网卡接入测试电脑Linux虚拟机(装有Aircrack-ng) 2.测试 ...

  7. Verilog的数据流、行为、结构化与RTL级描述

    Verilog语言可以有多种方式来描述硬件,同时,使用这些描述方式,又可以在多个抽象层次上设计硬件,这是Verilog语言的重要特征. 在Verilog语言中,有以下3种最基本的描述方式: 数据流描述 ...

  8. 盘点一下Redis中常用的Java客户端,或者咱们手写一个?

    Java中那些Redis的客户端 前面我们的所有操作都是基于redis-cli来完成的,那么我们要在Java中操作Redis,怎么做呢?首先我们先来了解一下Redis Serialization Pr ...

  9. 项目优化之v-if

    前言: 在vue项目中,由于功能比较多,需要各种条件控制某个功能.某个标签.表格中的某一行是否显示等,需要使用大量的v-if来判断条件. 例如: <div v-if="isShow(a ...

  10. 【二食堂】Beta - 事后分析

    事后分析 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? Beta阶段我们首先要对文本标注方式进行优化,其次时添加好友系统,实现邀请好友共同标注的功能. ...