个人关于vue响应式系统的理解

前言

前置知识

Object.defineProperty(obj, prop, descriptor)

基本语法:

css 复制代码
Object.defineProperty(obj, prop, descriptor);
参数 说明js
obj 需要定义属性的目标对象
prop 属性名(字符串)
descriptor 属性描述符(定义属性行为)

descriptor 具体说明

选项 类型 含义描述 默认值
value 任意类型 属性的值(静态定义值时用) undefined
writable Boolean 属性的值是否可修改(必须搭配 value 使用) false
enumerable Boolean 属性是否可以被遍历(for...in false
configurable Boolean 属性能否删除、重新定义 false
get Function 属性被访问时调用函数 undefined
set Function 属性被修改时调用函数 undefined

特别注意:

  • value/writable 与 get/set 不能同时存在。
  • 如果定义了 get 或 set,则不能定义 value 或 writable。

🌰 三、简单易懂的例子(快速理解):

实例1:定义普通属性(value / writable):

javascript 复制代码
const person = {};
Object.defineProperty(person, 'name', {
    value: '张三',
    writable: true,  // 能否修改
    enumerable: true,  // 可否被遍历
    configurable: true // 可否删除
});
​
console.log(person.name); // 输出:"张三"
​
person.name = "李四";
console.log(person.name); // 输出:"李四"

实例2(getter/setter):

javascript 复制代码
const user = {
    _name: '张三'
};
​
Object.defineProperty(user, 'name', {
    enumerable: true,
    configurable: true,
    get() {
        console.log('getter触发了');
        return this._name;
    },
    set(val) {
        console.log('setter触发了,值为', val);
        this._name = val;
    }
});
​
user.name = '李四'; // 触发setter
console.log(user.name); // 触发getter

输出:

objectivec 复制代码
setter触发了,值为 李四
getter触发了
李四

主要三大模块

主要分为一个类两个方法,先来个最小demo实现后面讲他们怎么合作

Dep:

perl 复制代码
class Dep{
  constructor() {
    this.subs = []
  }
  addSub (sub) {
    this.subs.push(sub)
  }
  notify() {
    this.subs.forEach(item => {
      item.update()
    })
  }
}

Watcher:

javascript 复制代码
class Watch {
  constructor() {
    Dep.target = this
  }
  update() {
    //更新视图
  }
}
Dep.target = null 

Observer

javascript 复制代码
function defineReactive(obj, key, val){
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get: function reactiveGetter() {
      dep.addSub(Dep.target)
      return val
    },
    set: function reactiveGetter(newVal) {
      if(newVal === val) return
      dep.notify() //更新所有和这个数据有关的视图
    }
  })
}

分工

vue内部大概是这样的

kotlin 复制代码
class Vue {
  constructor(options) {
    this.data = options.data
    observer(this.data) //为数据构造一个Dep,存储Watcher
    new Watcher() //新建一个Watcher用来更新视图
  }
}

简述:

当dep.target为watcher时,watcher如果get了obj,那么就会被dep记录,反之则不会,每次创建vue实例都会有一个watcher被数据对象的dep.target监测

扩展与疑问:全局的Dep.target不会被多个watcher抢占吗?

假设有两个组件视图的 Watcher:

  • Watcher A (组件A)
  • Watcher B (组件B)

它们依次初始化时:

ini 复制代码
// 组件A初始化:
Dep.target = WatcherA;
renderA(); // 此刻只有 WatcherA 在进行依赖收集(调用getter)
Dep.target = null;
​
// 然后才会到组件B初始化:
Dep.target = WatcherB;
renderB(); // 此刻只有 WatcherB 在进行依赖收集(调用getter)
Dep.target = null;

可以看出,每次依赖收集过程是串行而非并发的

  • WatcherA 收集依赖时,不可能被 WatcherB 抢占。
  • WatcherB 收集时,WatcherA 的依赖收集早已完成,Dep.target 已经被重置了。
相关推荐
paopaokaka_luck1 小时前
基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
前端·javascript·vue.js·spring boot·后端·小程序·uni-app
患得患失9491 小时前
【前端】【vscode】【.vscode/settings.json】为单个项目配置自动格式化和开发环境
前端·vscode·json
飛_1 小时前
解决VSCode无法加载Json架构问题
java·服务器·前端
YGY Webgis糕手之路4 小时前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
90后的晨仔4 小时前
🚨XSS 攻击全解:什么是跨站脚本攻击?前端如何防御?
前端·vue.js
Ares-Wang4 小时前
JavaScript》》JS》 Var、Let、Const 大总结
开发语言·前端·javascript
90后的晨仔4 小时前
Vue 模板语法完全指南:从插值表达式到动态指令,彻底搞懂 Vue 模板语言
前端·vue.js
德育处主任5 小时前
p5.js 正方形square的基础用法
前端·数据可视化·canvas
烛阴5 小时前
Mix - Bilinear Interpolation
前端·webgl
90后的晨仔5 小时前
Vue 3 应用实例详解:从 createApp 到 mount,你真正掌握了吗?
前端·vue.js