• vue设计原理-带你重走vue诞生路程


    我们首先看下面这个小demo
    在这里插入图片描述

    demo源码:

    DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>重走vuetitle>
      <style>
        div {
          width: 300px;
          padding: 15px;
          background-color: aqua;
          color: #000;
          margin: 50px auto;
          border: #13c159 double;
          border-radius: 20px;
        }
    
        div>p {
          padding: 5px;
          font-weight: 800;
        }
      style>
    head>
    
    <body>
      <div>
        <p id="name">p>
        <p id="age">p>
        <p id="appearance">p>
      div>
      <script>
        let student = {
          name: '林江涛',
          age: '刚满18岁(没多少年)',
          appearance: '江西吴彦祖'
        }
    
        // 显示姓名
        function showName() {
          document.querySelector('#name').textContent = `姓名: ${student.name}`
        }
        //显示年龄
        function showAge() {
          document.querySelector('#age').textContent = `年龄: ${student.age}`
        }
        //显示外貌
        function showAppearance() {
          document.querySelector('#appearance').textContent = `外貌: ${student.appearance}`
        }
        showName()
        showAge()
        showAppearance()
      script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    从上面源码可以看出,这个demo设计为,展示一个对象student的数据,
    在实际开发中,我们往往会需要改变student这个对象中的一些属性,然后在页面上展示
    如下
    在这里插入图片描述
    我们可以看见,改变了student对象的name属性后,页面并没有跟着变化,
    在没有vue的世界里自然是这样,

    我们要想改动了student对象的name属性,就得再去调用对应的渲染函数
    在这里插入图片描述

    那为什么是调用showName()呢?为什么不是调用showAge()或showAppearance()?

    你们看到这可能就得说,你这问的什么智障问题? showName()这个函数不就是用来展示student.name的值吗,

    student.name的值改变了,要想页面跟着变,当然得重新调用 showName()

    好的,如果你已经十分明白的知道了这个,那我们把上面的逻辑用人话整理一下

    为什么student.name值改变了,要想使页面对应变化,需要调用showName()而不是其它函数?

    是因为showName就是用来展示student.name这个属性的值得,showName在运行的过程中用到了student.name这个属性

    换句更加简洁的话来说就是,showName()依赖于student.name这个属性,

    showAge(),showAppearance()或其它函数,没有没有用到student.name这个属性,也就是说没有依赖于这个属性,所以从逻辑上来讲应该调用showName()这个函数,而不是其他函数

    同理.,如果将来student.appearance这个属性变了,就应该去调用showAppearance()这个函数.因为showAppearance()依赖于student.appearance,
    这样页面才会对应变化
    在这里插入图片描述
    那现在看起来一切和谐,没有什么问题,数据的改动,页面也能对应的改动

    看起来没有问题,其实隐藏着一个巨大的问题,因为这个demo仅仅只有三个属性,依赖于它的函数也仅仅有三个,
    但在实际的项目中,往往是拥有成千上万个对象,对象又可能有成千上万个属性,而依赖于此的对象更是数不胜数

    在这种情况下,上面的写法,就完全不适用了你会发现,你改了一个属性,你完全不知道你应该去调用那些函数,或者这些函数根本就没写在同一个文档中,更甚者,你甚至不知道这个属性会在哪里被更改,比如直接控制台更改

    这简直是灾难,

    那能不能有一种方法,当我改动了一个属性后,会自动去调用依赖于它的方法?

    这样的话,那既能避免手动调用有遗漏和不知道应该调用那个函数,又能保证页面和数据是统一的

    那有目标了,就得想办法实现它

    一个对象的属性,被改变了,我们能知道它被改变了,这时候就可以请出我们的defineProperty了
    在这里插入图片描述
    使用Object.defineProperty()修改一下student.name属性,这样每当student.name属性被获取时会调用对应的get函数
    设置时会调用set函数
    在这里插入图片描述

    我们再对这两个函数进行一个小小的修改,加个变量存储student.name的值,这样赋值取值的时候就能有数据,
    再分别给函数加个console.log()这样我们就能知道函数到底是否运行了

    如下,打开控制台可以看到,函数是正常运行了
    在这里插入图片描述

    我们在对student.name进行修改,可以看到,现在程序自己知道我们读取了student.name的值或给它赋值了

    在这里插入图片描述

    知道了这个之后,那想让页面跟着变,自然就是调用对应的函数
    如下
    在这里插入图片描述

    然后我们再修改student.name的值就能发现,页面也会跟着变化了

    在这里插入图片描述
    恭喜,如果你看到这,那我们已经完成阶段性胜利了,因为我们以及初步完成了数据的响应式,
    即,当数据的值发生更改->对应页面也发生更改,

    但是这样还不够,因为这代码写死了,意味着上面的数据变化,页面也对应变化的功能,仅仅只能针对student.name这一个属性,

    	let internalVal = student.user
    	Object.defineProperty(student, 'name', {
    	  get: () => {
    	    console.log('student.name被读取了')
    	    return internalVal
    	  },
    	  set: val => {
    	    internalVal = val
    	    console.log('student.name被赋值了赋值为' + val)
    	    showName()
    	  }
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    那这是肯定不行的,因为我们的目的,是为了让所有的数据变化时,依赖于它的函数都能执行也就是页面都能做出响应的改变

    所以我们就得想个办法,该如何让所有的数据改变时,依赖于他的方法都能执行?

    既然Object.defineProperty这个方法是给某一个属性修改了,那么我们是不是就可以封装一个方法,然后在里面写一个循环,循环遍历传进来的对象给他每一个属性都修改成上面那种样子,不就能监控对象的所有属性了吗

    示例如下

    声明一个函数,这个函数接收一个对象,然后把对象遍历一遍,再使用defineProperty让对象的每一个属性都修改为拥有get和set方法的属性

    	function observe(o) {
    	  for (let k in o) {
    	    let internalVal = o[k]
    	    Object.defineProperty(o, k, {
    	      get: () => {
    	        console.log(`对象的.${k}被读取了`)
    	        return internalVal
    	      },
    	      set: val => {
    	        internalVal = val
    	        console.log(`对象的.${k}被赋值了赋值为` + val)
    	      }
    	    })
    	  }
    	}
    	observe(student)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    效果如下,
    如图,可以看到修改或赋值student的所有属性

    在这里插入图片描述

    修改完之后,问题就来了,我该如何在set里面绑定调用依赖于对应属性的函数呢?

    因为我们在set方法里压根就不清楚会调用那些函数,

    这个时候我们就得回过头想一想,为什么要调用这些函数?因为这些函数依赖于student1,对象的属性
    那也就是说,student[key]的操作,也就是说,会调用get这个方法,

    那就代表着,可以让get收集访问过这个属性的函数名称,然后在set里面就可以使用循环依次调用了

    但是这样还不够,这样只能知道到底get被调用了,不知道到底是谁调用了,

    那怎么办?在家一层代理,这样询问代理就能知道具体是那个函数访问了属性

    代码如下

    	let distribute;
    	let internalVal = student.user
    	function observe(o) {
    	  for (let k in o) {
    	    let internalVal = o[k]
    	    let fnArr = []
    	    Object.defineProperty(o, k, {
    	      get: () => {
    	        if (distribute) {
    	          fnArr.push(distribute)
    	        }
    	
    	        console.log(`对象的.${k}被读取了`)
    	        return internalVal
    	      },
    	      set: val => {
    	        internalVal = val
    	        console.log(`对象的.${k}被赋值了赋值为` + val)
    	        for (let i = 0; i < fnArr.length; i++) {
    	          fnArr[i]()
    	        }
    	
    	      }
    	    })
    	  }
    	}
    	observe(student)
    	function distributeFun(fn) {
    	  distribute = fn;
    	  fn()
    	  distribute = null
    	}
    	distributeFun(showName)
    	distributeFun(showAge)
    	distributeFun(showAppearance)
    	
    	student.name = '吴彦祖'
    	student.age = 100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    效果如下,

    在这里插入图片描述

    嗯,还有一些小bug比如再次调用age依赖的函数依然会加进去,

    这里做一个去重就好
    在这里插入图片描述

    呐,大功告成

  • 相关阅读:
    matlab simulink 四旋翼跟拍无人机仿真
    计算机单位、变量、数据类型、类型转换、转义字符
    关于皕杰小程序分享设置
    贪心算法的高逼格应用——Huffman编码
    TYUT太原理工大学2022需求工程考试简答题
    MongoDB入门与实战-第四章-SpringBoot集成MongoDB
    预训练相关知识
    成都优优聚美团代运营:塑造卓越优势,引领电商新时代
    Sending non-protected broadcast 问题解决
    Android WMS——服务端输入事件处理(十八)
  • 原文地址:https://blog.csdn.net/weixin_44368963/article/details/136491562