广告
返回顶部
首页 > 资讯 > 精选 >vue双向绑定原理实例分析
  • 101
分享到

vue双向绑定原理实例分析

2023-06-29 06:06:41 101人浏览 薄情痞子
摘要

这篇文章主要介绍了Vue双向绑定原理实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue双向绑定原理实例分析文章都会有所收获,下面我们一起来看看吧。自定义vue类vue最少需要两个参数:模板和data。

这篇文章主要介绍了Vue双向绑定原理实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue双向绑定原理实例分析文章都会有所收获,下面我们一起来看看吧。

vue双向绑定原理实例分析

自定义vue类

  • vue最少需要两个参数:模板和data。

  • 创建Compiler对象,将数据渲染到模板后,挂载到指定跟节点中。

class MyVue {  // 1,接收两个参数:模板(根节点),和数据对象  constructor(options) {    // 保存模板,和数据对象    if (this.isElement(options.el)) {      this.$el = options.el;    } else {      this.$el = document.querySelector(options.el);    }    this.$data = options.data;    // 2.根据模板和数据对象,渲染到根节点    if (this.$el) {      // 监听data所有属性的get/set      new Observer(this.$data);      new Compiler(this)    }  }  // 判断是否是一个dom元素  isElement(node) {    return node.nodeType === 1;  }}

实现数据首次渲染到页面

Compiler

1,node2fragment函数将模板元素提取到内存中,方便将数据渲染到模板后,再一次性挂载到页面中

2,模板提取到内存后,使用buildTemplate函数遍历该模板元素

  • 元素节点

    • 使用buildElement函数检查元素上以v-开头的属性

  • 文本节点

    • 用buildText函数检查文本中有无{{}}内容

3,创建CompilerUtil类,用于处理vue指令和{{}},完成数据的渲染

4,到此就完成了首次数据渲染,接下来需要实现数据改变时,自动更新视图。

class Compiler {  constructor(vm) {    this.vm = vm;    // 1.将网页上的元素放到内存中    let fragment = this.node2fragment(this.vm.$el);    // 2.利用指定的数据编译内存中的元素    this.buildTemplate(fragment);    // 3.将编译好的内容重新渲染会网页上    this.vm.$el.appendChild(fragment);  }  node2fragment(app) {    // 1.创建一个空的文档碎片对象    let fragment = document.createDocumentFragment();    // 2.编译循环取到每一个元素    let node = app.firstChild;    while (node) {      // 注意点: 只要将元素添加到了文档碎片对象中, 那么这个元素就会自动从网页上消失      fragment.appendChild(node);      node = app.firstChild;    }    // 3.返回存储了所有元素的文档碎片对象    return fragment;  }  buildTemplate(fragment) {    let nodeList = [...fragment.childNodes];    nodeList.forEach(node => {      // 需要判断当前遍历到的节点是一个元素还是一个文本      if (this.vm.isElement(node)) {        // 元素节点        this.buildElement(node);        // 处理子元素        this.buildTemplate(node);      } else {        // 文本节点        this.buildText(node);      }    })  }  buildElement(node) {    let attrs = [...node.attributes];    attrs.forEach(attr => {      // v-model="name" => {name:v-model  value:name}      let { name, value } = attr;      // v-model / v-html / v-text / v-xxx      if (name.startsWith('v-')) {        // v-model -> [v, model]        let [_, directive] = name.split('-');        CompilerUtil[directive](node, value, this.vm);      }    })  }  buildText(node) {    let content = node.textContent;    let reg = /\{\{.+?\}\}/gi;    if (reg.test(content)) {      CompilerUtil['content'](node, content, this.vm);    }  }}
let CompilerUtil = {  getValue(vm, value) {    // 解析this.data.aaa.bbb.ccc这种属性    return value.split('.').reduce((data, currenTKEy) => {      return data[currentKey.trim()];    }, vm.$data);  },  getContent(vm, value) {    // 解析{{}}中的变量    let reg = /\{\{(.+?)\}\}/gi;    let val = value.replace(reg, (...args) => {      return this.getValue(vm, args[1]);    });    return val;  },  // 解析v-model指令  model: function (node, value, vm) {    // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值    new Watcher(vm, value, (newValue, oldValue) => {      node.value = newValue;    });    let val = this.getValue(vm, value);    node.value = val;  },  // 解析v-html指令  html: function (node, value, vm) {    // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值    new Watcher(vm, value, (newValue, oldValue) => {      node.innerHTML = newValue;    });    let val = this.getValue(vm, value);    node.innerHTML = val;  },  // 解析v-text指令  text: function (node, value, vm) {    // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值    new Watcher(vm, value, (newValue, oldValue) => {      node.innerText = newValue;    });    let val = this.getValue(vm, value);    node.innerText = val;  },  // 解析{{}}中的变量  content: function (node, value, vm) {    let reg = /\{\{(.+?)\}\}/gi;    let val = value.replace(reg, (...args) => {      // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值      new Watcher(vm, args[1], (newValue, oldValue) => {        node.textContent = this.getContent(vm, value);      });      return this.getValue(vm, args[1]);    });    node.textContent = val;  }}

实现数据驱动视图

Observer

1,使用defineRecative函数对data做Object.defineProperty处理,使得data中的每个数据都可以进行get/set监听

2,接下来将考虑如何在监听到data值改变后,更新视图内容呢?使用观察者设计模式,创建Dep和Wather类。

class Observer {  constructor(data) {    this.observer(data);  }  observer(obj) {    if (obj && typeof obj === 'object') {      // 遍历取出传入对象的所有属性, 给遍历到的属性都增加get/set方法      for (let key in obj) {        this.defineRecative(obj, key, obj[key])      }    }  }  // obj: 需要操作的对象  // attr: 需要新增get/set方法的属性  // value: 需要新增get/set方法属性的取值  defineRecative(obj, attr, value) {    // 如果属性的取值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法    this.observer(value);    // 第三步: 将当前属性的所有观察者对象都放到当前属性的发布订阅对象中管理起来    let dep = new Dep(); // 创建了属于当前属性的发布订阅对象    Object.defineProperty(obj, attr, {      get() {        // 在这里收集依赖        Dep.target && dep.addSub(Dep.target);        return value;      },      set: (newValue) => {        if (value !== newValue) {          // 如果给属性赋值的新值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法          this.observer(newValue);          value = newValue;          dep.notify();          console.log('监听到数据的变化');        }      }    })  }}

使用观察者设计模式,创建Dep和Wather类

1,使用观察者设计模式的目的是:

  • 解析模板,收集data中某个数据在模板中被使用的dom节点集合,当该数据改变时,更新该dom节点集合就实现了数据更新。

  • Dep:用于收集某个data属性依赖的dom节点集合,并提供更新方法

  • Watcher:每个dom节点的包裹对象

    • attr:该dom使用的data属性

    • cb:修改该dom值的回调函数,在创建的时候会接收

2,到这里感觉思路是没问题了,已经是胜券在握了。那Dep和Watcher该怎么使用呢?

  • 为每个属性添加一个dep,用来收集依赖的dom

  • 因为页面首次渲染的时候会读取data数据,这时候会触发该data的getter,所以在此收集dom

  • 具体如何收集呢,在CompilerUtil类解析v-model,{{}}等命令时,会触发getter,我们在触发之前创建Wather,为Watcher添加一个静态属性,指向该dom,然后在getter函数里面获取该静态变量,并添加到依赖中,就完成了一次收集。因为每次触发getter之前都对该静态变量赋值,所以不存在收集错依赖的情况。

class Dep {  constructor() {    // 这个数组就是专门用于管理某个属性所有的观察者对象的    this.subs = [];  }  // 订阅观察的方法  addSub(watcher) {    this.subs.push(watcher);  }  // 发布订阅的方法  notify() {    this.subs.forEach(watcher => watcher.update());  }}
class Watcher {  constructor(vm, attr, cb) {    this.vm = vm;    this.attr = attr;    this.cb = cb;    // 在创建观察者对象的时候就去获取当前的旧值    this.oldValue = this.getOldValue();  }  getOldValue() {    Dep.target = this;    let oldValue = CompilerUtil.getValue(this.vm, this.attr);    Dep.target = null;    return oldValue;  }  // 定义一个更新的方法, 用于判断新值和旧值是否相同  update() {    let newValue = CompilerUtil.getValue(this.vm, this.attr);    if (this.oldValue !== newValue) {      this.cb(newValue, this.oldValue);    }  }}

3,到这里就实现了数据绑定时,视图自动更新,本来想代码一步步实现的,但是发现不好处理,就把完整的class贴出来了。

实现视图驱动数据

其实就是监听输入框的input、change事件。修改CompilerUtil的model方法。具体代码如下

model: function (node, value, vm) {    new Watcher(vm, value, (newValue, oldValue)=>{        node.value = newValue;    });    let val = this.getValue(vm, value);    node.value = val;// 看这里    node.addEventListener('input', (e)=>{        let newValue = e.target.value;        this.setValue(vm, value, newValue);    })},

总结

vue双向绑定原理

vue接收一个模板和data参数。

1,首先将data中的数据进行递归遍历,对每个属性执行Object.defineProperty,定义get和set函数。并为每个属性添加一个dep数组。当get执行时,会为调用的dom节点创建一个watcher存放在该数组中。当set执行时,重新赋值,并调用dep数组的notify方法,通知所有使用了该属性watcher,并更新对应dom的内容。

2,将模板加载到内存中,递归模板中的元素,检测到元素有v-开头的命令或者双大括号的指令,就会从data中取对应的值去修改模板内容,这个时候就将该dom元素添加到了该属性的dep数组中。这就实现了数据驱动视图。在处理v-model指令的时候,为该dom添加input事件(或change),输入时就去修改对应的属性的值,实现了页面驱动数据。

3,将模板与数据进行绑定后,将模板添加到真实dom树中。

如何将watcher放在dep数组中?

在解析模板的时候,会根据v-指令获取对应data属性值,这个时候就会调用属性的get方法,我们先创建Watcher实例,并在其内部获取该属性值,作为旧值存放在watcher内部,我们在获取该值之前,在Watcher原型对象上添加属性Watcher.target = this;然后取值,将讲Watcher.target = null;这样get在被调用的时候就可以根据Watcher.target获取到watcher实例对象。

methods的原理

创建vue实例的时候,接收methods参数

在解析模板的时候遇到v-on的指令。会对该dom元素添加对应事件的监听,并使用call方法将vue绑定为该方法的this:vm.$methods[value].call(vm, e);

computed的原理

创建vue实例的时候,接收computed参数

初始化vue实例的时候,为computed的key进行Object.defineProperty处理,并添加get属性。

关于“vue双向绑定原理实例分析”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“vue双向绑定原理实例分析”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: vue双向绑定原理实例分析

本文链接: https://www.lsjlt.com/news/322928.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • vue双向绑定原理实例分析
    这篇文章主要介绍了vue双向绑定原理实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue双向绑定原理实例分析文章都会有所收获,下面我们一起来看看吧。自定义vue类vue最少需要两个参数:模板和data。...
    99+
    2023-06-29
  • Vue双向数据绑定原理的示例分析
    小编给大家分享一下Vue双向数据绑定原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!双向数据绑定的原理:采用“数据劫持”结合“发布者-订阅者”模式的方式,通过“Object.defineProperty()”方...
    99+
    2023-06-14
  • Vue响应式原理及双向数据绑定示例分析
    目录前言响应式原理双向数据绑定前言 之前公司招人,面试了一些的前端同学,因为公司使用的前端技术是Vue,所以免不了问到其响应式原理和Vue的双向数据绑定。但是这边面试到的80%的同学...
    99+
    2022-11-13
  • vue2和vue3双向绑定实例分析
    这篇文章主要介绍“vue2和vue3双向绑定实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vue2和vue3双向绑定实例分析”文章能帮助大家解决问题。vue2和vue3中双向绑定的区别是:v...
    99+
    2023-06-29
  • vue中数据双向绑定的示例分析
    这篇文章主要介绍了vue中数据双向绑定的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。* Object.definePro...
    99+
    2022-10-19
  • angular双向绑定的示例分析
    这篇文章主要为大家展示了“angular双向绑定的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“angular双向绑定的示例分析”这篇文章吧。双向绑定原理双向绑定将属性绑定与事件绑定结合...
    99+
    2023-06-22
  • Vue双向绑定原理及实现方法
    目录双向绑定示例vue3双向绑定双向绑定 Vue 的双向绑定是通过数据劫持和发布-订阅模式实现的。 当 Vue 实例初始化时,它会对 data 选项中的每个属性使用 Object.d...
    99+
    2023-05-17
    Vue双向绑定 Vue3双向绑定实现
  • vue中view-model双向绑定基础原理解析
    利用Object.defineProperty进行数据劫持 代码如下 <!DOCTYPE html> <html lang="en"> <head>...
    99+
    2022-11-13
    vue中view-model双向绑定 vue双向绑定
  • vue双向数据绑定原理分析、vue2和vue3原理的不同点
    目录Vue数据双向绑定原理(一)Vue2双向数据绑定原理(一)Vue3双向数据绑定原理受疫情影响,今年各行业不景气,各岗位的跳槽形势也不是很高。所以趁此机会好好蓄力,复习面试题吧。现...
    99+
    2022-12-08
    vue双向数据 vue数据绑定原理 vue2和vue3原理
  • Angular10中双向绑定的示例分析
    这篇文章主要介绍了Angular10中双向绑定的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。将利用@Input()和@Output...
    99+
    2022-10-19
  • 实现mvvm-simple双向绑定的示例分析
    这篇文章给大家分享的是有关实现mvvm-simple双向绑定的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。mvvm模式解放DOM枷锁mvvm原理分析JavaScript...
    99+
    2022-10-19
  • Vue 组件上的v-model双向绑定原理解析
    目录组件上的v-model原理v-model编译阶段组件生成阶段之前我们分析了Vue中v-model指令在普通表单元素上的使用原理(点击这里跳转),这一节我们继续分析v-model指...
    99+
    2022-11-13
  • vue2.x双向数据绑定原理解析
    目录前言一、index.html文件二、vue.js文件1.proxy代理发生了什么?2.observer监听数据3.订阅者Watcher4.订阅器Dep5.编译器Compiler三...
    99+
    2023-02-27
    vue2.x双向数据绑定 vue2.x双向绑定
  • Vue组件间的双向绑定示例解析
    目录何为组件间双向绑定示例解析总结何为组件间双向绑定 我们都知道当父组件改变了某个值后,如果这个值传给了子组件,那么子组件也会自动跟着改变,但是这是单向的,使用v-bind的方式,即...
    99+
    2023-03-19
    Vue组件间双向绑定 Vue组件绑定
  • AngularJs中双向绑定机制的示例分析
    这篇文章主要为大家展示了“AngularJs中双向绑定机制的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“AngularJs中双向绑定机制的示例分析”这...
    99+
    2022-10-19
  • JavaScript中双向数据绑定的示例分析
    小编给大家分享一下JavaScript中双向数据绑定的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!双向数据绑定指的是将...
    99+
    2022-10-19
  • 详解Vue中双向绑定原理及简单实现
    目录监听器订阅器双向绑定构造函数编译器(1)Compile类(2)node2Fragment函数(3)compile函数总结效果完整代码代码缺陷监听器 vue实现双向绑定时,首先要实...
    99+
    2023-05-19
    Vue双向绑定原理 Vue实现双向绑定 Vue双向绑定
  • 面试问题Vue双向数据绑定原理
    目录前言具体步骤什么是Object.defineProperty一、访问器属性二、简单的数据双向绑定实现方法三、 实现任务的思路四、DocumentFragment五、初始化数据绑定...
    99+
    2022-11-13
  • 浅谈vue实现双向事件绑定v-model的原理
    目录解释: 总结 补充 与js或者jquery直接改变操作dom不同,vue使用v-model实现数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。 v-model就是v...
    99+
    2022-11-12
  • vue中双向事件绑定的原理是什么
    今天就跟大家聊聊有关vue中双向事件绑定的原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。与js或者jquery直接改变操作dom不同,vue使用v-model实现数据的双向...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作