个人关于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 已经被重置了。
相关推荐
星语卿34 分钟前
Vuetify:构建优雅Vue应用的Material Design组件库
前端·javascript·vue.js
roman_日积跬步-终至千里1 小时前
【系统架构设计(25)】Web应用服务器与现代架构
前端·架构·系统架构
yshhuang1 小时前
在Windows上搭建开发环境
前端·后端
littleplayer1 小时前
Redux在iOS中的使用
前端
跟橙姐学代码1 小时前
Python里的“管家婆”:带你玩转os库的所有神操作
前端·python·ipython
jingling5551 小时前
uniapp | 快速上手ThorUI组件
前端·笔记·前端框架·uni-app
UrbanJazzerati1 小时前
可拖拽的进度条组件实战:实现思路与Demo
前端·面试
Cache技术分享1 小时前
188. Java 异常 - Java 异常处理规范
前端·后端
不一样的少年_1 小时前
Vue3 后台分页写腻了?我用 1 个 Hook 删掉 90% 重复代码(附源码)
前端·vue.js·设计模式