个人关于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 已经被重置了。
相关推荐
专注API从业者1 小时前
Python + 淘宝 API 开发:自动化采集商品数据的完整流程
大数据·运维·前端·数据挖掘·自动化
烛阴2 小时前
TypeScript高手密技:解密类型断言、非空断言与 `const` 断言
前端·javascript·typescript
样子20183 小时前
Uniapp 之renderjs解决swiper+多个video卡顿问题
前端·javascript·css·uni-app·html
Nicholas683 小时前
flutterAppBar之SystemUiOverlayStyle源码解析(一)
前端
黑客飓风3 小时前
JavaScript 性能优化实战大纲
前端·javascript·性能优化
emojiwoo5 小时前
【前端基础知识系列六】React 项目基本框架及常见文件夹作用总结(图文版)
前端·react.js·前端框架
张人玉5 小时前
XML 序列化与操作详解笔记
xml·前端·笔记
杨荧5 小时前
基于Python的宠物服务管理系统 Python+Django+Vue.js
大数据·前端·vue.js·爬虫·python·信息可视化
YeeWang6 小时前
🎉 Eficy 让你的 Cherry Studio 直接生成可预览的 React 页面
前端·javascript
gnip6 小时前
Jenkins部署前端项目实战方案
前端·javascript·架构