个人关于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 已经被重置了。
相关推荐
andr_gale8 分钟前
02_uniapp自定义上凸效果的底部TabBar
前端·javascript·uni-app·移动端
一心只读圣贤书21 分钟前
AI 驱动前端测试实战:从需求文档到 Playwright 自动化用例
前端·人工智能
拖孩28 分钟前
用 AI 重解千年观音灵签,做了一个微信小程序,每天摇一摇,命运给你回应
前端·后端·微信小程序
難釋懷38 分钟前
Nginx日志
前端·网络·nginx
Web极客码1 小时前
用 Python 搭建工具调用 Agent 的调试过程
java·服务器·前端
2401_881828321 小时前
简易文本处理网页工具|AI 通识课第三次作业
前端·javascript·html
Ai拆代码的曹操1 小时前
Dubbo 2.7.5 的线程上下文陷阱:从版本演进看 AllChannelHandler 的设计之痛
前端·dubbo·safari
障碍的枫子2 小时前
Vue组件导出&渲染
前端·javascript·vue.js
开开心心就好2 小时前
视频播放器完美解码集成三款播放器切换使用
前端·人工智能·智能手机·电脑·音视频·virtualenv·pygame
Sterting2 小时前
组件通信:Props 与 Emit
前端·vue.js