<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中命名空间是怎样的存在?(三)

    这是与命名空间有关的最后一篇.最后还是两个比较简单的内容,是关于命名空间和全局相关的一些类.函数.常量的使用对比.当然,最后我们还会总结一下命名空间的名称解析规则做为这三篇系列文章的结束. 全局空间 ...

  2. ecshop 首页调用指定分类下的销售排行

    /*首页调用指定分类下的销售排行*/ function get_cats_top10($cat = '') { $sql = 'SELECT cat_id, cat_name ' . 'FROM ' ...

  3. Linux系列(39) - nohup

    nohup 英文全称 no hang up(不挂起),用于在系统后台不挂断地运行命令,退出终端不会影响程序的运行. nohup 命令,在默认情况下(非重定向时),会输出一个名叫 nohup.out 的 ...

  4. 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁的贡献最大 | 百篇博客分析OpenHarmony源码 | v3.05

    百篇博客系列篇.本篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁的贡献最大 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  5. JSP页面属性

    一.JSP指令 <%@指令名属性名=属性值 %> page指令: 定义页面是如何解析 include指令: 静态包含 taglib指令: 在页面引入标签呢库. 1.page指令属性 imp ...

  6. centos8安装MySQL8——通过yum

    centos8上通过yum安装MySQL,过程简单,不易出错 1.检查系统是否已安装MySQL相关,如果有则全部清除干净 #列出MySQL相关的安装包 rpm -qa | grep mysql #依次 ...

  7. 安装 webstorm--->vue

    一.先去官网下载webstorm     https://www.jetbrains.com/ 不论是Mac的还是win得都有相应的版本, 二.再去官网下载git     https://git-sc ...

  8. FastAPI 学习之路(七)字符串的校验

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  9. Linux——Docker安装

    1. 安装Docker i :环境准备:Linux要求内核3.0以上 ii:安装 #1.卸载旧版本 yum remove docker \ docker-client \ docker-client- ...

  10. 2021年1月-第02阶段-前端基础-HTML+CSS进阶-VS Code 软件

    软件安装 VSCode软件 能够安装 VS Code 能够熟练使用 VS Code 软件 能够安装 VS Code 最常用的插件 1. VS Code简介 1.1 VS Code 简介 Visual ...