个人关于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 已经被重置了。
相关推荐
橙光成长20 分钟前
PDF离线工具箱深度解析:4MB纯前端单文件,40+功能全本地运行,文件绝不上传
前端·pdf·1024程序员节
小poop7 小时前
CSS 基础与实战:从入门到精通网页美化
前端
Csvn8 小时前
运行时性能
前端
Never_every999 小时前
位图转矢量工具哪个好?实测抠抠图AI位图转矢量工具
大数据·前端
paopaokaka_luck9 小时前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
空中湖10 小时前
用 React 写数据大屏:为什么我不建议用图表库
前端·react.js·前端框架
欣欣之王来了12 小时前
原始HTML插值:v-html指令的使用与安全风险
前端·学习·架构·项目·vue3教程
IT_陈寒12 小时前
Redis的LRU淘汰策略坑了我一整天
前端·人工智能·后端
计算机魔术师12 小时前
砍半价格、参数再涨,国产旗舰追到 Opus 鼻尖了
前端
特立独行的猫A12 小时前
10 MB 的桌面 AI 客户端:用仓颉 + 系统 WebView 替代 Electron
前端·harmonyos