广告
返回顶部
首页 > 资讯 > 精选 >如何实现一个vue双向绑定
  • 542
分享到

如何实现一个vue双向绑定

2023-06-29 09:06:39 542人浏览 安东尼
摘要

这篇文章主要讲解了“如何实现一个Vue双向绑定”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现一个vue双向绑定”吧!开始开局一张图从图上可以看出new Vue()分为了两步走代理监听

这篇文章主要讲解了“如何实现一个Vue双向绑定”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现一个vue双向绑定”吧!

如何实现一个vue双向绑定

开始

开局一张图

如何实现一个vue双向绑定

从图上可以看出new Vue()分为了两步走

  • 代理监听所有数据,并与Dep进行关联,通过Dep通知订阅者进行视图更新。【相关推荐:vuejs视频教程

  • 解析所有模板,并将模板中所用到的数据进行订阅,并绑定一个更新函数,数据发生改变时Dep通知订阅者执行更新函数。

接下里就是分析如何去实现,并且都需要写什么,先看一段vue的基础代码,我们从头开始分析

<div id="app">  <input v-model="message" />  <p>{{message}}</p></div>
let app = new Vue({    el:"#app",    data:{      message:"测试这是一个内容"    }})

从上面代码我们可以看到new Vue的操作,里面携带了eldata属性,这算是最基础的属性,而在html代码中我们知道<div id="app">是vue渲染的模板根节点,所以vue要渲染页面就要去实现一个模板解析的方法Compile类,解析方法中还需要去处理{{ }}v-model两个指令,除了解析模板之后我们还需要去实现数据代理也就是实现Observer

实现 Vue 类

如下代码所示,这就写完了Vue类,够简单吧,如果对class关键字不熟悉的,建议先去学习一下,从下面我们可能看到,这里实例化了两个类,一个是代理数据的类,一个是解析模板的类。

class Vue {  constructor(options) {    // 代理数据    new Observer(options.data)    // 绑定数据    this.data = options.data    // 解析模板    new Compile(options.el, this)  }}

接着往下我们先写一个Compile类用于解析模板,我们再来分析一波,解析模板要做什么事

  • 我们要解析模板不可能直接对dom继续操作,所以我们要创建一个文档片段(虚拟dom),然后将模板DOM节点复制一份到虚拟DOM节点中,对虚拟DOM节点解析完成之后,再将虚拟DOM节点替换掉原来的DOM节点

  • 虚拟节点复制出来之后,我们要遍历整个节点树进行解析,解析过程中会对DOM的atrr属性进行遍历找到Vue相关的指令,除此之外还要对 textContent节点内容进行解析,判断是否存在双花括号

  • 将解析出来所用到的属性进行一个订阅

实现模板解析 Compile 类

下面我们将逐步实现

  • 构建Compile类,先把静态节点和Vue实例获取出来,再定义一个虚拟dom的属性用来存储虚拟dom

class Compile {  constructor(el, vm) {    // 获取静态节点    this.el = document.querySelector(el);    // vue实例    this.vm = vm     // 虚拟dom    this.fragment = null     // 初始化方法    this.init()  }}
  • 实现初始化方法init(),该方法主要是用于创建虚拟dom和调用解析模板的方法,解析完成之后再将DOM节点替换到页面中

class Compile {   //...省略其他代码  init() {    // 创建一个新的空白的文档片段(虚拟dom)    this.fragment = document.createDocumentFragment()  // 遍历所有子节点加入到虚拟dom中    Array.from(this.el.children).forEach(child => {      this.fragment.appendChild(child)    })    // 解析模板    this.parseTemplate(this.fragment)    // 解析完成添加到页面    this.el.appendChild(this.fragment);  }}
  • 实现解析模板方法parseTemplate,主要是遍历虚拟DOM中的所有子节点并进行解析,根据子节点类型进行不同的处理。

class Compile {   //...省略其他代码  // 解析模板   parseTemplate(fragment) {    // 获取虚拟DOM的子节点    let childnodes = fragment.childNodes || []    // 遍历节点    childNodes.forEach((node) => {      // 匹配大括号正则表达式       var reg = /\{\{(.*)\}\}/;      // 获取节点文本      var text = node.textContent;      if (this.isElementNode(node)) { // 判断是否是html元素        // 解析html元素        this.parseHtml(node)      } else if (this.isTextNode(node) && reg.test(text)) { //判断是否文本节点并带有双花括号        // 解析文本        this.parseText(node, reg.exec(text)[1])      }      // 递归解析,如果还有子元素则继续解析      if (node.childNodes && node.childNodes.length != 0) {        this.parseTemplate(node)      }    });  }}
  • 根据上面的代码我们得出需要实现两个简单的判断,也就是判断是否是html元素和文字元素,这里通过获取nodeType的值来进行区分,不了解的可以直接看一下 传送门:Node.nodeType,这里还扩展了一个isVueTag方法,用于后面的代码中使用

class Compile {   //...省略其他代码// 判断是否携带 v-  isVueTag(attrName) {    return attrName.indexOf("v-") == 0  }  // 判断是否是html元素  isElementNode(node) {    return node.nodeType == 1;  }  // 判断是否是文字元素  isTextNode(node) {    return node.nodeType == 3;  }}
  • 实现parseHtml方法,解析html代码主要是遍历html元素上的attr属性

class Compile {  //...省略其他代码  // 解析html  parseHtml(node) {    // 获取元素属性集合    let nodeAttrs = node.attributes || []    // 元素属性集合不是数组,所以这里要转成数组之后再遍历    Array.from(nodeAttrs).forEach((attr) => {      // 获取属性名称      let arrtName = attr.name;      // 判断名称是否带有 v-       if (this.isVueTag(arrtName)) {        // 获取属性值        let exp = attr.value;        //切割 v- 之后的字符串        let tag = arrtName.substring(2);        if (tag == "model") {          // v-model 指令处理方法          this.modelCommand(node, exp, tag)        }      }    });  }}
  • 实现modelCommand方法,在模板解析阶段来说,我们只要把 vue实例中data的值绑定到元素上,并实现监听input方法更新数据即可。

class Compile {//...省略其他代码     // 处理model指令  modelCommand(node, exp) {    // 获取数据    let val = this.vm.data[exp]    // 解析时绑定数据    node.value = val || ""    // 监听input事件    node.addEventListener("input", (event) => {      let newVlaue = event.target.value;      if (val != newVlaue) {        // 更新data数据        this.vm.data[exp] = newVlaue        // 更新闭包数据,避免双向绑定失效        val = newVlaue      }    })  }}
  • 处理Text元素就相对简单了,主要是将元素中的textContent内容替换成数据即可

class Compile {//...省略其他代码    //解析文本  parseText(node, exp) {    let val = this.vm.data[exp]    // 解析更新文本    node.textContent = val || ""  }}

至此已经完成了Compile类的初步编写,测试结果如下,已经能够正常解析模板

如何实现一个vue双向绑定

下面就是我们目前所实现的流程图部分

如何实现一个vue双向绑定

坑点一:

  • 在第6点modelCommand方法中并没有实现双向绑定,只是单向绑定,后续要双向绑定时还需要继续处理

坑点二:

  • 第7点parseText方法上面的代码中并没有去订阅数据的改变,所以这里只会在模板解析时绑定一次数据


实现数据代理 Observer 类

这里主要是用于代理data中的所有数据,这里会用到一个Object.defineProperty方法,如果不了解这个方法的先去看一下文档传送门:

文档:

https://developer.mozilla.org/zh-CN/docs/WEB/javascript/Reference/Global_Objects/Object/defineProperty

Observer类主要是一个递归遍历所有data中的属性然后进行数据代理的的一个方法

defineReactive中传入三个参数data, key, val

datakey都是Object.defineProperty的参数,而val将其作为一个闭包变量供Object.defineProperty使用

// 监听者class Observer {  constructor(data) {    this.observe(data)  }  // 递归方法  observe(data) {    //判断数据如果为空并且不是object类型则返回空字符串    if (!data || typeof data != "object") {      return ""    } else {      //遍历data进行数据代理      Object.keys(data).forEach(key => {        this.defineReactive(data, key, data[key])      })    }  }  // 代理方法  defineReactive(data, key, val) {    // 递归子属性    this.observe(data[key])    Object.defineProperty(data, key, {      configurable: true,  //可配置的属性      enumerable: true, //可遍历的属性      get() {        return val      },      set(newValue) {        val = newValue      }    })  }}

下面我们来测试一下是否成功实现了数据代理,在Vue的构造函数输出一下数据

class Vue {  constructor(options) {    // 代理数据    new Observer(options.data)    console.log(options.data)    // 绑定数据    this.data = options.data    // 解析模板    new Compile(options.el, this)  }}

结果如下,我们可以看出已经实现了数据代理。

如何实现一个vue双向绑定

对应的流程图如下所示

如何实现一个vue双向绑定

坑点三:

  • 这里虽然实现了数据代理,但是按照图上来说,还需要引入管理器,在数据发生变化时通知管理器数据发生了变化,然后管理器再通知订阅者更新视图,这个会在后续的填坑过程过讲到。


实现管理器 Dep 类

上面我们已经实现了模板解析到初始化视图,还有数据代理。而下面要实现的Dep类主要是用于管理订阅者和通知订阅者,这里会用一个数组来记录每个订阅者,而类中也会给出一个notify方法去调用订阅者的update方法,实现通知订阅者更新功能。这里还定义了一个target属性用来存储临时的订阅者,用于加入管理器时使用。

class Dep {  constructor() {    // 记录订阅者    this.subList = []  }  // 添加订阅者  addSub(sub) {    // 先判断是否存在,防止重复添加订阅者    if (this.subList.indexOf(sub) == -1) {      this.subList.push(sub)    }  }  // 通知订阅者  notify() {    this.subList.forEach(item => {      item.update() //订阅者执行更新,这里的item就是一个订阅者,update就是订阅者提供的方法    })  }}// Dep全局属性,用来临时存储订阅者Dep.target = null

管理器实现完成之后我们也就实现了流程图中的以下部分。要注意下面几点

  • Observer通知Dep主要是通过调用notify方法

  • Dep通知Watcher主要是是调用了Watcher类中的update方法

如何实现一个vue双向绑定


实现订阅者 Watcher 类

订阅者代码相对少,但是理解起来还是有点难度的,在Watcher类中实现了两个方法,一个是update更新视图方法,一个putIn方法(我看了好几篇文章都是定义成 get 方法,可能是因为我理解的不够好吧)。

  • update:主要是调用传入的cb方法体,用于更新页面数据

  • putIn:主要是用来手动加入到Dep管理器中。

// 订阅者class Watcher {  // vm:vue实例本身  // exp:代理数据的属性名称  // cb:更新时需要做的事情  constructor(vm, exp, cb) {    this.vm = vm    this.exp = exp    this.cb = cb    this.putIn()  }  update() {    // 调用cb方法体,改变this指向并传入最新的数据作为参数    this.cb.call(this.vm, this.vm.data[this.exp])  }  putIn() {    // 把订阅者本身绑定到Dep的target全局属性上    Dep.target = this    // 调用获取数据的方法将订阅者加入到管理器中    let val = this.vm.data[this.exp]    // 清空全局属性    Dep.target = null  }}

坑点四:

  • Watcher类中的putIn方法再构造函数调用后并没有加入到管理器中,而是将订阅者本身绑定到target全局属性上而已

埋坑

通过上面的代码我们已经完成了每一个类的构建,如下图所示,但是还是有几个流程是有问题的,也就是上面的坑点。所以下面要填坑

如何实现一个vue双向绑定

埋坑 1 和 2

完成坑点一和坑点二,在modelCommandparseText方法中增加实例化订阅者代码,并自定义要更新时执行的方法,其实就是更新时去更新页面中的值即可

modelCommand(node, exp) {    // ...省略其他代码    // 实例化订阅者,更新时直接更新node的值  new Watcher(this.vm, exp, (value) => {    node.value = value  })}parseText(node, exp) {    //  ...省略其他代码    // 实例化订阅者,更新时直接更新文本内容  new Watcher(this.vm, exp, (value) => {    node.textContent = value  })}

埋坑 3

完成坑点三,主要是为了引入管理器,通知管理器发生改变,主要是在Object.defineProperty set方法中调用dep.notify()方法

// 监听方法defineReactive(data, key, val) {  // 实例化管理器--------------增加这一行  let dep = new Dep()    // ...省略其他代码      set(newValue) {      val = newValue      // 通知管理器改变--------------增加这一行      dep.notify()    }}

埋坑 4

完成坑点四,主要四将订阅者加入到管理器中

defineReactive(data, key, val) {  // ...省略其他代码    get() {      // 将订阅者加入到管理器中--------------增加这一段      if (Dep.target) {        dep.addSub(Dep.target)      }      return val    },  // ...省略其他代码}

完成了坑点四可能就会有靓仔疑惑了,这里是怎么加入的呢Dep.target又是什么呢,我们不妨从头看看代码并结合下面这张图

如何实现一个vue双向绑定

至此我们已经实现了一个简单的双向绑定,下面测试一下

如何实现一个vue双向绑定

感谢各位的阅读,以上就是“如何实现一个vue双向绑定”的内容了,经过本文的学习后,相信大家对如何实现一个vue双向绑定这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 如何实现一个vue双向绑定

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

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

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

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

下载Word文档
猜你喜欢
  • 如何实现一个vue双向绑定
    这篇文章主要讲解了“如何实现一个vue双向绑定”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现一个vue双向绑定”吧!开始开局一张图从图上可以看出new Vue()分为了两步走代理监听...
    99+
    2023-06-29
  • Vue如何实现双向绑定
    这篇文章主要介绍Vue如何实现双向绑定,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!原理当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍...
    99+
    2022-10-19
  • vue如何实现数据双向绑定
    Vue.js是一个流行的前端框架,它提供了多种方式来实现数据双向绑定。数据双向绑定是Vue.js最重要的特性之一,它使得开发者能够根据数据的变化自动更新视图,同时也能根据视图的变化自动更新数据。因此,Vue.js的数据双向绑定也成为前端面试...
    99+
    2023-05-14
  • vue实现双向数据绑定
    本文实例为大家分享了vue实现双向数据绑定的具体代码,供大家参考,具体内容如下 vue中数组与对象采用了不同的绑定方式 1.vue对象数据绑定 (1)数据侦测 在js中,我们使用Ob...
    99+
    2022-11-12
  • vue实现双向绑定有哪几个方法
    本教程操作环境:windows7系统、vue3版,DELL G3电脑。Vue 实现双向绑定的几种方法1、v-model 指令<ChildComponent v-model="pageTitle" /> &l...
    99+
    2023-05-14
    数据双向绑定 Vue.js
  • Vue数据的双向绑定如何实现
    本篇内容介绍了“Vue数据的双向绑定如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!实现组件双向数据绑定我们先来看看抛弃 .sync ...
    99+
    2023-07-04
  • 如何使用ES6的class模仿Vue写一个双向绑定
    小编给大家分享一下如何使用ES6的class模仿Vue写一个双向绑定,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!具体如下:最终...
    99+
    2022-10-19
  • vue双向数据绑定怎么实现
    Vue的双向数据绑定是通过v-model指令来实现的。1. v-model是Vue提供的一个指令,用于在表单元素上实现双向数据绑定。...
    99+
    2023-08-09
    vue
  • Vue 3.0 中怎么实现双向绑定
    本篇文章为大家展示了Vue 3.0 中怎么实现双向绑定,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。双向绑定由两个单向绑定组成:模型 &mdash;>...
    99+
    2022-10-19
  • vue实现简单数据双向绑定
    本文实例为大家分享了vue实现简单数据双向绑定的具体代码,供大家参考,具体内容如下 这里是简单的实现,有助于新手理解消化,当然vue要实现双向数据代理不可缺少,期待后续在更新 vue...
    99+
    2022-11-12
  • vue 数据双向绑定怎么实现
    这篇文章给大家分享的是有关vue 数据双向绑定怎么实现的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1. 前言实现简易版的数据双向绑定2. 代码实现2.1 目的分析本文要实现的效果如下图所示:本文用到的HTML和...
    99+
    2023-06-06
  • 怎么使用ES6的class实现一个双向绑定
    这篇文章主要介绍“怎么使用ES6的class实现一个双向绑定”,在日常操作中,相信很多人在怎么使用ES6的class实现一个双向绑定问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用ES6的class实现...
    99+
    2023-07-04
  • 聊聊Vue中如何实现数据双向绑定
    在我们使用vue的时候,当数据发生了改变,界面也会跟着更新,但这并不是理所当然的,我们修改数据的时候vue是如何监听数据的改变以及当数据发生改变的时候vue如何让界面刷新的?当我们修改数据的时候vue是通过es5中的Object.defin...
    99+
    2022-11-24
    Vue vue.js
  • VUE-Table中如何绑定Input通过render实现双向绑定数据
    这篇文章将为大家详细讲解有关VUE-Table中如何绑定Input通过render实现双向绑定数据,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。效果HTML的Table&...
    99+
    2022-10-19
  • Vue中怎么实现数据双向绑定
    这篇文章主要介绍了Vue中怎么实现数据双向绑定的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue中怎么实现数据双向绑定文章都会有所收获,下面我们一起来看看吧。在我们使用vue的时候,当数据发生了改变,界面也会...
    99+
    2023-07-04
  • Vue 中怎么实现数据双向绑定
    本篇文章给大家分享的是有关 Vue 中怎么实现数据双向绑定,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。MVVM 架构的优点低耦合。视图(Vi...
    99+
    2022-10-19
  • Vue双向绑定原理及实现方法
    目录双向绑定示例vue3双向绑定双向绑定 Vue 的双向绑定是通过数据劫持和发布-订阅模式实现的。 当 Vue 实例初始化时,它会对 data 选项中的每个属性使用 Object.d...
    99+
    2023-05-17
    Vue双向绑定 Vue3双向绑定实现
  • vue中双向数据绑定怎么实现
    vue中双向数据绑定是通过数据劫持结合发布订阅模式的方式来实现的,其核心方法是Object.defineProperty()方法。实现双向数据绑定的示例:<!DOCTYPE html><html><h...
    99+
    2022-10-11
  • 如何使用vue代码实现数据双向绑定
    这篇文章主要介绍“如何使用vue代码实现数据双向绑定”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何使用vue代码实现数据双向绑定”文章能帮助大家解决问题。代码如下:* Object.d...
    99+
    2023-07-04
  • 怎么在vue中实现数据双向绑定
    怎么在vue中实现数据双向绑定?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。vue是什么Vue是一套用于构建用户界面的渐进式JavaScript框架,Vue与其它大型框架的...
    99+
    2023-06-14
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作