深入Vue原理_全面剖析发布订阅模式

深入Vue原理_全面剖析发布订阅模式文章目录发布订阅模式优化优化思路思考理解发布订阅模式 自定义事件 收集更新函数触发更新函数 6 5 总结总结写在最后上节中我们提到下方更新的问题 无法做到精准更新 divid app pv text name pv text age pv text name script letdata script pv text name pv text age pv text name divid app

<div id="app"> <p v-text="name"> 
     p> <p v-text="age"> 
      p> <p v-text="name"> 
       p>  
        div> <script> let data = { 
          name: '小兰同学', age: 18, height: 180 } // 遍历每一个属性 Object.keys(data).forEach((key) => { 
          // key 属性名 // data[key] 属性值 // data 原对象 defineReactive(data, key, data[key]) }) function defineReactive(data, key, value) { 
          Object.defineProperty(data, key, { 
          get() { 
          return value }, set(newVal) { 
          // 数据发生变化,操作dom进行更新 if (newVal === value) { 
          return } value = newVal compile() } }) } // 编译函数 function compile() { 
          let app = document.getElementById('app') // 1.拿到app下所有的子元素 const nodes = app.childNodes // [text, input, text] //2.遍历所有的子元素 nodes.forEach(node => { 
          // nodeType为1为元素节点 if (node.nodeType === 1) { 
          const attrs = node.attributes Array.from(attrs).forEach(attr => { 
          const dirName = attr.nodeName const dataProp = attr.nodeValue console.log( dirName,dataProp) if (dirName === 'v-text') { 
          console.log(`更新了${ 
           dirName}指令,需要更新的属性为${ 
           dataProp}`) node.innerText = data[dataProp] } }) } }) } // 首次渲染 compile()  
         script> 

发布订阅模式优化

优化思路思考

1.数据更新之后实际上需要执行的代码是什么?

node.innerText = data[dataProp] 

为了保存当前的node和dataProp,我们再次设计一个函数执行利用闭包函数将每一次编译函数执行时候的node和dataProp都缓存下来,所以每一次数据变化之后执行的是这样的一个更新函数

() => { 
    node.innerText = data[dataProp] } 

2.一个响应式数据可能会有多个视图部分都需要依赖,也就是响应式数据变化之后,需要执行的更新函数可能不止一个,如下面的代码所示,name属性有俩个div元素都使用了它,所以当name变化之后,俩个div节点都需要得到更新,那属性和更新函数之间应该是一个一对多的关系

<div id="app"> <div v-text="name"> 
     div> <div v-text="name"> 
      div> <p v-text="age"> 
       p> <p v-text="age"> 
        p>  
         div> <script> let data = { 
           name: 'cp', age: 18 }  
          script> 

经过分析我们可以得到下面的存储架构图,每一个响应式属性都绑定了相对应的更新函数,是一个一对多的关系,数据发生变化之后,只会再次执行和自己绑定的更新函数

在这里插入图片描述

理解发布订阅模式(自定义事件)

理解发布订阅,关键是理解一对多

1. 从浏览器事件说起

dom绑定事件的方式,我们学过俩种

  1. dom.onclick = function(){}
  2. dom.addEventListener(‘click’,()=>{})

这俩种绑定方式的区别是,第二种方案可以实现同一个事件绑定多个回调函数,很明显这是一个一对多的场景,既然浏览器也叫作事件,我们试着分析下浏览器事件绑定实现的思路

  1. 首先addEventListenr是一个函数方法,接受俩个参数,分别是事件类型回调函数
  2. 因为是一个事件绑定多个回调函数,那在内存里大概会有这样的一个数据结构
    { 
          click: ['cb1','cb2',....], input: ['cb1','cb2',...] } 
  3. 触发事件执行,浏览器因为有鼠标键盘输入可以触发事件,大概的思路是通过事件名称找到与之关联的回调函数列表,然后遍历执行一遍即可

ok,我们分析了浏览器事件的底层实现思路,那我们完全可以自己模仿一个出来,事件的触发,我们也通过设计一个方法来执行

2. 实现简单的发布订阅

// 增加dep对象 用来收集依赖和触发依赖 const dep = { 
    map: Object.create(null), // 收集 collect(dataProp, updateFn) { 
    if (!this.map[dataProp]) { 
    this.map[dataProp] = [] } this.map[dataProp].push(updateFn) }, // 触发 trigger(dataProp) { 
    this.map[dataProp] && this.map[dataProp].forEach(updateFn => { 
    updateFn() }) } } 

收集更新函数

在编译函数执行的时候,我们把用于更新dom的更新函数收集起来

 // 编译函数 function compile() { 
    let app = document.getElementById('app') // 1.拿到app下所有的子元素 const nodes = app.childNodes // [text, input, text] //2.遍历所有的子元素 nodes.forEach(node => { 
    // nodeType为1为元素节点 if (node.nodeType === 1) { 
    const attrs = node.attributes // 遍历所有的attrubites找到 v-model Array.from(attrs).forEach(attr => { 
    const dirName = attr.nodeName const dataProp = attr.nodeValue console.log(dirName, dataProp) if (dirName === 'v-text') { 
    console.log(`更新了${ 
     dirName}指令,需要更新的属性为${ 
     dataProp}`) node.innerText = data[dataProp] // 收集更新函数 dep.collect(dataProp, () => { 
    node.innerText = data[dataProp] }) } }) } }) } 

触发更新函数

当属性发生变化的时候,我们通过属性找到对应的更新函数列表,然后依次执行即可

function defineReactive(data, key, value) { 
    Object.defineProperty(data, key, { 
    get() { 
    return value }, set(newValue) { 
    // 更新视图 if (newValue === value) return value = newValue // 再次编译要放到新值已经变化之后只更新当前的key dep.trigger(key) } }) } 

6.5 总结

  1. 了解了发布订阅模式的基础形态
  2. 了解发布订阅可以解决什么样的具体问题(精准更新)

总结

  1. 数据响应式的实现无非是对象属性拦截,我们使用Object.defineProperty来实现,在vue3中使用Proxy对象代理方案进行了优化,解决了Object.defineProperty存在的缺陷
  2. observe对象指的是把数据处理成响应式的对象

    watcher指的其实就是数据变化之后的更新函数 (Vue中的watcher有两种,一种是用来更新视图的watcher,一种是通过watch配置项声明的watcher)

    dep指的就是使用发布订阅实现的收集更新函数和触发更新函数的对象

  3. 指令实现的核心无非是通过模板编译找到标识然后把数据绑上去,等到数据变化之后再重新放一次
  4. 发布订阅模式的本质是解决一对多的问题,在vue中实现数据变化之后的精准更新

写在最后

原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下}

? 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!}

⭐️ 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!}

✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!}













本期推荐

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201532.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午9:11
下一篇 2026年3月20日 上午9:11


相关推荐

  • mysql explain ref null_MySQL Explain详解[通俗易懂]

    mysql explain ref null_MySQL Explain详解[通俗易懂]MySQLExplain详解简介执行计划(queryExecutionplan)语法explainselect*fromtableexplain中的列expain出来的信息有10列,分别是id,select_type,table、type,partitions,possible_keys,key,key_len,ref,rows,Extra,下面对这些字段出现的可能进行解释:一、I…

    2026年1月18日
    3
  • vue页面样式修改eleUI样式不生效

    vue页面样式修改eleUI样式不生效问题 在 style 里面写上 element UI 的相关样式 页面更新后没效果解决在 class 前面加上 deep 如 deep container

    2026年3月18日
    2
  • java jts_JTS基本概念和使用

    java jts_JTS基本概念和使用packagecom alibaba autonavi importcom vividsolutio jts geom Coordinate importcom vividsolutio jts geom Geometry importcom vividsolutio jts geom GeometryColl importcom vividsolutio jts ge

    2026年3月17日
    2
  • adb常用命令–安装apk[通俗易懂]

    adb常用命令–安装apk[通俗易懂]方法一:adbpush  adbpushxxxx.apk/system/app(安装到 system/app目录下,有时安装不成功)手机中的系统apk应用(*.apk)位置:/system/app安装新apk到手机 adbpushxxxx.apk/system/app。后面的/system/app就是apk的安装目录。adbpush没有adbinstall保险,

    2022年5月29日
    356
  • AI智能体大赛:星火S计划—讯飞星火智能体挑战赛

    AI智能体大赛:星火S计划—讯飞星火智能体挑战赛

    2026年3月14日
    3
  • 菜鸟实战UML——状态图

    菜鸟实战UML——状态图状态图状态图 StatechartDi 是描述一个实体基于事件反应的动态行为 显示了该实体如何根据当前所处的状态对不同的事件做出反应 通常我们创建一个 UML 状态图是为了以下的研究目的 研究类 角色 子系统 或组件的复杂行为 理解 状态图其实就是用来描述一个特定对象的所有可能状态以及由于各种事件的发生而引起的状态间的转移 状态图的图符 状态 转移 起点 终点状态状态

    2025年11月7日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号