<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. 【OWASP TOP10】2021年常见web安全漏洞TOP10排行

    [2021]常见web安全漏洞TOP10排行 应用程序安全风险 攻击者可以通过应用程序中许多的不同的路径方式去危害企业业务.每种路径方法都代表了一种风险,这些风险都值得关注. 什么是 OWASP TO ...

  2. 【PHP数据结构】栈的相关逻辑操作

    对于逻辑结构来说,我们也是从最简单的开始.堆栈.队列,这两个词对于大部分人都不会陌生,但是,堆和栈其实是两个东西.在面试的时候千万不要被面试官绕晕了.堆是一种树结构,或者说是完全二叉树的结构.而今天, ...

  3. PHP中的PDO操作学习(四)查询结构集

    关于 PDO 的最后一篇文章,我们就以查询结果集的操作为结束.在数据库的操作中,查询往往占的比例非常高.在日常的开发中,大部分的业务都是读多写少型的业务,所以掌握好查询相关的操作是我们学习的重要内容. ...

  4. webpack learn4-1配置css单独分离打包

    1 先安装extract-text-webpack-plugin npm i extract-text-webpack-plugin 2 配置webpack.config.js

  5. python 深度学习 库文件安装出错汇总

    Cython_bbox FairMOT | win10下cython-bbox安装的心酸之路_是阳阳呀的博客-CSDN博客 swig 安装polyiou.py https://blog.csdn.ne ...

  6. nginx 利用return实现301跳转

    第一种: server { location / { rewrite ^/(.*)$ http://www.baidu.com/$1 permanent; } } 第二种: server { loca ...

  7. lua文件修改为二进制文件

    注意:lua编译跟luajit编译的二进制文件是不兼容,不能运行的 如果是使用luajit,请直接使用luajit直接编译二进制 第一种:luajit编译(以openresty为例,跟luac是相反的 ...

  8. verifycode验证码模版

    # -*- coding:utf-8 -*- from django.shortcuts import HttpResponse def verifycode(request): # 引入绘图模块 f ...

  9. 2.1 附录--JVM指令手册

    栈和局部变量操作 将常量压入栈的指令 aconst_null 将null对象引用压入栈 iconst_m1 将int类型常量-1压入栈 iconst_0 将int类型常量0压入栈 iconst_1 将 ...

  10. Java基础之(八):顺序结构与选择结构

    顺序结构 Java的基本结构就是顺序结构 顺序结构是最简单的算法结构 它是任何一个算法都离不开的一种基本算法结构. 例子: public static void main(String[] args) ...