Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • reactivity响应式

    • ref
      • 概览
      • 源码分析
        • ref
        • createRef
        • RefImpl
        • toRaw
        • toReactive
        • shallowRef
        • isRef
        • toRef
        • GetterRefImpl
        • propertyToRef
        • ObjectRefImpl
        • toValue
        • toRefs
        • unref
        • proxyRef
        • customRef
        • triggerRef
    • reactive
    • 基础对象的代理
    • 数组代理的方法
    • 集合对象的代理
    • Reflect和Proxy详解
    • 依赖的收集与触发
    • effectScope解析
    • effect解析
    • reactive响应式依赖的收集与触发监听
    • 批量更新实现
    • ReactiveEffect类介绍
    • computed
  • 5.18源码学习》
  • reactivity响应式
东流
2025-08-13
目录

ref

# 概览

vue3中ref用于将原始数据(如布尔值、数字、字符串等)包裹成为响应式数据。具体实现的文件参见packages\reactivity\src\ref.ts与之相关的有如下api

  • ref: 用于创建响应式数据,当数据发生变化时,会触发视图更新。
  • shallowRef: 用于创建浅层响应式数据,只有数据重新被赋值时,才会触发视图更新。
  • isRef: 用于判断一个数据是否是ref响应式数据。
  • toRef: 用于将一个响应式对象的属性或某数据转换为ref响应式数据。
  • toValue:
  • toRefs:将响应式对象的所有属性转换为ref响应式数据
  • unref: 解绑响应式数据,返回原始数据
  • proxyRefs: 用于为包含ref的对象创建代理
  • customRef: 自定义ref,用于创建一个自定义的ref响应式数据
  • triggerRef:强制触发ref的更新

# 源码分析

# ref

ref的实现如下:

function ref(value) {
  return createRef(value, false);
}
1
2
3

ref的实现就是一个高阶函数,会返回调用createRef函数的结果。此时createRef接受的第二个参数shallow为false,表示ref创建的响应式数据不是一个浅层响应。

# createRef

createRef函数顾名思义就是创建一个响应式数据,其实现如下:

function createRef(rawValue, shallow) {
  if (isRef(rawValue)) {
    return rawValue;
  }
  return new RefImpl(rawValue, shallow);
}
1
2
3
4
5
6

createRef函数接受两个参数,第一个参数rawValue表示原始数据,第二个参数shallow表示是否是浅层响应。首先会调用isRef(后面会讲到isRef的实现)判断原始数据rawValue是不是Ref响应式数据,若是,则直接返回该值;否则实例化RefImpl类,并返回实例对象。

# RefImpl

RefImpl类的实现如下:

class RefImpl {
  constructor(value, __v_isShallow) {
    this.__v_isShallow = __v_isShallow; //是否是浅层响应的标志位
    __publicField(this, "_value"); // 响应式数据的当前值
    __publicField(this, "_rawValue"); // 原始数据
    __publicField(this, "dep",new Dep()); // 依赖收集器
    __publicField(this, "__v_isRef", true); // 标志位,用于判断是否是 Ref 响应式数据
    this._rawValue = __v_isShallow ? value : toRaw(value);
    this._value = __v_isShallow ? value : toReactive(value);
  }
  get value() {
    {
      this.dep.track({
        target: this,
        type: "get",
        key: "value"
      });
    }
    return this._value;
  }
  set value(newVal) {
     const oldValue = this._rawValue;
    const useDirectValue = this["__v_isShallow"] || isShallow(newValue) || isReadonly(newValue);
    newValue = useDirectValue ? newValue : toRaw(newValue);
    if (hasChanged(newValue, oldValue)) {
      this._rawValue = newValue;
      this._value = useDirectValue ? newValue : toReactive(newValue);
      {
        this.dep.trigger({
          target: this,
          type: "set",
          key: "value",
          newValue,
          oldValue
        });
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

RefImpl类在构造函数中会根据参数__v_isShallow判断是否是浅层响应,若是,则直接将参数value赋值给实例的_rawValue和_value;否则,会调用toRaw函数将原始数据转换为原始值,再调用toReactive函数将原始值转换为响应式数据,最后将转换后的数据赋值给实例的_rawValue和_value。

RefImpl类还定义了get value()和set value(newVal)方法,用于获取和设置响应式数据的值。

在get value()方法中,会调用trackRefValue函数进行依赖收集,然后返回实例的_value属性; 在set value(newVal)方法中,会先判断是否直接使用新值,若当前实例是浅层响应(__v_isShallow为true)或者新值的__v_isReadonly或__v_isShallow属性为true,则直接使用新值,否则调用toRaw获取新值的原始值;然后调用hasChanged方法比较新值和实例的原始值是否相等,hasChanged的内部就是通过Object.is进行比较;若不等,则更新实例的_rawValue和_value;最后调用triggerRefValue方法通知所有依赖于实例的副作用进行更新,该方法后续会介绍。

# toRaw

toRaw的作用就是获取响应式数据的原始值,会返回参数observed的__v_raw的值,若该值不存在,则返回参数observed。

function toRaw(observed) {
  const raw = observed && observed["__v_raw"];
  return raw ? toRaw(raw) : observed;
}
1
2
3
4

toRaw实际上是个递归函数,直到返回原始值为止。对于reactive响应式数据而言,读取它的__v_raw,会返回原始值,在BaseReactiveHandler类中的get实现的。

# toReactive

前面提到ref会将原始数据转换为响应式数据,而如果ref接受的是一个对象,则返回的也是响应式对象,这就是在toReactive中实现的。

toReactive的实现如下:

const toReactive = (value) => isObject(value) ? reactive(value) : value;
1

toReactive会判断参数是否是对象,若不是,则直接返回参数;否则,会调用reactive函数将参数转换为响应式对象,并返回该对象。所以若将对象作为参数传给ref,响应式实际上通过reactive函数实现的。

# shallowRef

shallowRef的实现如下:

function shallowRef(value) {
  return createRef(value, true);
}
1
2
3

shallowRef的实现和ref的实现类似,唯一的不同就是调用createRef时,第二个参数是true,表示创建的响应式数据是一个浅层响应式数据。

# isRef

isRef的实现如下:

function isRef(r) {
 return r ? r["__v_isRef"] === true : false;
}
1
2
3

isRef实际就是读取参数r的__v_isRef属性,若该属性为true,则返回true,否则返回false。因为在创建ref响应式数据时,实例的__v_isRef都会设置为true

# toRef

function toRef(source, key, defaultValue) {
  if (isRef(source)) {
    return source;
  } else if (isFunction(source)) {
    return new GetterRefImpl(source);
  } else if (isObject(source) && arguments.length > 1) {
    return propertyToRef(source, key, defaultValue);
  } else {
    return ref(source);
  }
}
1
2
3
4
5
6
7
8
9
10
11

toRef的源码实现实际上是用了typescript的重载,参数会有以下几种情况:

  • 第一个参数是ref响应式数据,直接返回该数据
  • 第一个参数是函数,返回一个GetterRefImpl实例
  • 第一个参数是对象,第二个参数是属性名,返回一个PropertyRefImpl实例
  • 其他情况,返回一个RefImpl实例

# GetterRefImpl

GetterRefImpl的实现如下:

class GetterRefImpl {
  constructor(_getter) {
    this._getter = _getter;
    __publicField(this, "__v_isRef", true);
    __publicField(this, "__v_isReadonly", true);
    __publicField$1(this, "_value");
  }
  get value() {
    return this._value = this._getter();
  }
}
1
2
3
4
5
6
7
8
9
10
11

_getter实际上就是toRef中的第一个参数,它是一个函数,用于获取响应式数据的值,除此之外GetterRefImpl类还会在构造函数中设置实例__v_isRef和__v_isReadonly为true(表示是一个ref响应式数据且只读,因为实例没有setter的实现)

# propertyToRef

propertyToRef的作用就是将对象的属性转换为ref响应式数据,它的实现如下:

function propertyToRef(source, key, defaultValue) {
  const val = source[key];
  return isRef(val) ? val : new ObjectRefImpl(source, key, defaultValue);
}
1
2
3
4

propertyToRef接受三个参数:对象source、属性名key和默认值defaultValue;会先通过isRef判断值是否是响应式数据,若是,则直接返回;否则会实例化ObjectRefImpl类,并返回实例。

# ObjectRefImpl

ObjectRefImpl的实现如下:

class ObjectRefImpl {
  constructor(_object, _key, _defaultValue) {
    this._object = _object;
    this._key = _key;
    this._defaultValue = _defaultValue;
    __publicField(this, "__v_isRef", true);
    __publicField$1(this, "_value");
  }
  get value() {
    const val = this._object[this._key];
    return this._value = val === void 0 ? this._defaultValue : val;
  }
  set value(newVal) {
    this._object[this._key] = newVal;
  }
  get dep() {
    return getDepFromReactive(toRaw(this._object), this._key);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

ObjectRefImpl类用于将响应式对象的一个属性作为独立的ref使用。

# toValue

toValue的作用就是将响应式数据转换为普通数据,它的实现如下:

function toValue(source) {
  return isFunction(source) ? source() : unref(source);
}
1
2
3

toValue的参数source可以是一个getter函数或者响应式数据,若是函数,则调用该函数并返回函数的返回值;否则,调用unref函数并返回unref函数的返回值。

# toRefs

toRefs的作用就是将响应式对象的所有属性转换为ref响应式数据,它的实现如下:

function toRefs(object) {
  const ret = isArray(object) ? new Array(object.length) : {};
  for (const key in object) {
    ret[key] = propertyToRef(object, key);
  }
  return ret;
}
1
2
3
4
5
6
7

toRefs的参数object可以是一个数组或者对象,若为数组,则返回一个数组,数组的每个元素都是一个ref响应式数据;若为对象,则返回一个对象,对象的每个属性都是一个ref响应式数据。

# unref

unref的实现如下:

function unref(ref2) {
  return isRef(ref2) ? ref2.value : ref2;
}
1
2
3

unref会先调用isRef判断参数是否是响应式对象,若为响应式对象,则返回该对象的value属性;否则,直接返回参数。

# proxyRef

proxyRef的相关实现如下

const shallowUnwrapHandlers = {
  // 当访问代理对象的属性时会触发,使用`Reflect.get`获取属性值,再使用`unref`解包
  get: (target, key, receiver) => key === "__v_raw" ? target : unref(Reflect.get(target, key, receiver)),
  // 当设置代理对象的属性时会触发
  set: (target, key, value, receiver) => {
    const oldValue = target[key];

    if (isRef(oldValue) && !isRef(value)) {
      // 若旧值是ref响应式数据,且新值不是ref响应式数据,则直接设置旧值的value属性为新值
      oldValue.value = value;
      return true;
    } else {
      // 若旧值不是ref响应式数据,或新值是ref响应式数据,则直接调用`Reflect.set`设置属性值
      return Reflect.set(target, key, value, receiver);
    }
  }
};
function proxyRefs(objectWithRefs) {
  return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

proxyRef会先判断传入对象是否是响应式的,若是,则直接返回该对象;否则使用shallowUnwrapHandlers作为处理器创建一个新的Proxy代理对象并返回。

# customRef

customRef的本质上就是实例化一个customRefImpl类,并返回该实例。

function customRef(factory) {
  return new CustomRefImpl(factory);
}

class CustomRefImpl {
  constructor(factory) {
    __publicField$1(this, "dep");
    __publicField$1(this, "_get");
    __publicField$1(this, "_set");
    __publicField$1(this, "__v_isRef", true);
    __publicField$1(this, "_value");
    const dep = this.dep = new Dep();
    const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep));
    this._get = get;
    this._set = set;
  }
  get value() {
    return this._value = this._get();
  }
  set value(newVal) {
    this._set(newVal);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

customRefImpl类接受一个工厂函数,该工厂函数接受两个参数函数:track和trigger,分别用于收集依赖和触发更新,并且工厂函数还会返回一个对象,对象会包含get和set的实现,在读取或设置自定义ref实例时,会分别调用get和set。该类的实现给了用户更多的自主权去定制响应式行为。

# triggerRef

triggerRef的实现如下:

function triggerRef(ref2) {
  if (ref2.dep) {
    {
      ref2.dep.trigger({
        target: ref2,
        type: "set",
        key: "value",
        newValue: ref2._value
      });
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

triggerRef会调用triggerRefValue触发更新,triggerRefValue后续会介绍。

编辑 (opens new window)
上次更新: 2025/08/15, 09:55:22
reactive

reactive→

最近更新
01
computed
08-26
02
ReactiveEffect类介绍
08-26
03
批量更新实现
08-25
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式