vue3의 반응성()에 대해 자세히 이야기해 봅시다.
vue3 개발에서 반응형은 반응형 데이터를 구현하는 방법을 제공합니다. 일상적인 개발에서 자주 사용되는 API입니다. 이 기사에서 저자는 내부 작동 메커니즘을 탐구합니다. 제가 초보라서 서투른 글을 이해해 주시기 바랍니다.
디버그 버전은 3.2.45
3.2.45
-
什么是reactive?
reactive是Vue3中提供实现响应式数据的方法.
在Vue2中响应式数据是通过defineProperty来实现的.
而在Vue3响应式数据是通过ES6的Proxy来实现的
-
reactive注意点
reactive参数必须是对象(json/arr)
如果给reactive传递了其他对象,默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值的方式。【相关推荐:vuejs视频教程、web前端开发】
<script setup> import {reactive} from 'vue' const data = reactive({ //定义对象 name:'测试', age:10 }) const num = reactive(1)//定义基本数据类型 console.log(data)//便于定位到调试位置 </script> <template> <div> <h1 id="nbsp-data-name-nbsp">{{ data.name }}</h1> </div> </template> <style scoped></style>
设置断点
开始调试
接下来我们可以开始调试了,设置好断点后,只要重新刷新页面就可以进入调试界面。
复杂数据类型
我们先调试简单的基本数据类型
1.
/*1.初始进来函数,判断目标对象target是否为只读对象,如果是直接返回*/ function reactive(target) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target; } //创建一个reactive对象,五个参数后续会讲解 return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap); } /*2.判断是来判断target是否为只读。*/ function isReadonly(value) { return !!(value && value["__v_isReadonly" /* ReactiveFlags.IS_READONLY */]); } /*3.创建一个reactive对象*/ /*createReactiveObject接收五个参数: target被代理的对象, isReadonl是不是只读的, baseHandlers proxy的捕获器, collectionHandlers针对集合的proxy捕获器, proxyMap一个用于缓存proxy的`WeakMap`对象*/ function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers, proxyMap) { //如果target不是对象则提示并返回 /*这里会跳转到如下方法 判断是否原始值是否为object类型 const isObject = (val) => val !== null && typeof val === 'object'; */ if (!isObject(target)) { if ((process.env.NODE_ENV !== 'production')) { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; } // 如果target已经是proxy是代理对象则直接返回. if (target["__v_raw" /* ReactiveFlags.RAW */] && !(isReadonly && target["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */])) { return target; } // 从proxyMap中获取缓存的proxy对象,如果存在的话,直接返回proxyMap中对应的proxy。否则创建proxy。 const existingProxy = proxyMap.get(target); if (existingProxy) { return existingProxy; } // 并不是任何对象都可以被proxy所代理。这里会通过getTargetType方法来进行判断。 const targetType = getTargetType(target); //当类型值判断出是不能代理的类型则直接返回 if (targetType === 0 /* TargetType.INVALID */) { return target; } //通过使用Proxy函数劫持target对象,返回的结果即为响应式对象了。这里的处理函数会根据target对象不同而不同(这两个函数都是参数传入的): //Object或者Array的处理函数是collectionHandlers; //Map,Set,WeakMap,WeakSet的处理函数是baseHandlers; const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers); proxyMap.set(target, proxy); return proxy; }
getTargetType
方法调用流程
//1.进入判断如果value有__v_skip属性且为true或对象是可拓展则返回0,否则走类型判断函数 function getTargetType(value) { //Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。 return value["__v_skip" /* ReactiveFlags.SKIP */] || !Object.isExtensible(value) ? 0 /* TargetType.INVALID */ : targetTypeMap(toRawType(value)); } //2.这里通过Object.prototype.toString.call(obj)来判断数据类型 const toRawType = (value) => { // extract "RawType" from strings like "[object RawType]" return toTypeString(value).slice(8, -1); }; const toTypeString = (value) => objectToString.call(value); //3.这里rawType是为'Object'所以会返回1 function targetTypeMap(rawType) { switch (rawType) { case 'Object': case 'Array': return 1 /* TargetType.COMMON */; case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return 2 /* TargetType.COLLECTION */; default: return 0 /* TargetType.INVALID */;//返回0说明除前面的类型外其他都不能被代理,如Date,RegExp,Promise等 } }
在createReactiveObject
方法中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
这一条语句中,第二个参数判断target是否为Map或者Set类型。从而使用不同的handler来进行依赖收集。
在调试的文件node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js
中,我们从reactive
函数的createReactiveObject
函数调用的其中两个参数mutableHandlers
和mutableCollectionHandlers
开始往上查询
mutableHandlers
的实现
const mutableHandlers = { get,// 获取值的拦截,访问对象时会触发 set,// 更新值的拦截,设置对象属性会触发 deleteProperty,// 删除拦截,删除对象属性会触发 has,// 绑定访问对象时会拦截,in操作符会触发 ownKeys// 获取属性key列表 }; function deleteProperty(target, key) { // key是否是target自身的属性 const hadKey = hasOwn(target, key); // 旧值 const oldValue = target[key]; // 调用Reflect.deleteProperty从target上删除属性 const result = Reflect.deleteProperty(target, key); // 如果删除成功并且target自身有key,则触发依赖 if (result && hadKey) { trigger(target, "delete" /* TriggerOpTypes.DELETE */, key, undefined, oldValue); } return result; } // function has(target, key) { //检查目标对象是否存在此属性。 const result = Reflect.has(target, key); // key不是symbol类型或不是symbol的内置属性,进行依赖收集 if (!isSymbol(key) || !builtInSymbols.has(key)) { track(target, "has" /* TrackOpTypes.HAS */, key); } return result; } /*ownKeys可以拦截以下操作: 1.Object.keys() 2.Object.getOwnPropertyNames() 3.Object.getOwnPropertySymbols() 4.Reflect.ownKeys()操作*/ function ownKeys(target) { track(target, "iterate" /* TrackOpTypes.ITERATE */, isArray(target) ? 'length' : ITERATE_KEY); return Reflect.ownKeys(target); }
get
方法实现
const get = /*#__PURE__*/ createGetter(); /*传递两个参数默认都为false isReadonly是否为只读 shallow是否转换为浅层响应,即Reactive---> shallowReactive,shallowReactive监听了第一层属性的值,一旦发生改变,则更新视图;其他层,虽然值发生了改变,但是视图不会进行更新 */ function createGetter(isReadonly = false, shallow = false) { return function get(target, key, receiver) { //1.是否已被reactive相关api处理过; if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) { return !isReadonly; } //2.是否被readonly相关api处理过 else if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) { return isReadonly; } else if (key === "__v_isShallow" /* ReactiveFlags.IS_SHALLOW */) { return shallow; } //3.检测__v_raw属性 else if (key === "__v_raw" /* ReactiveFlags.RAW */ && receiver === (isReadonly ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target)) { return target; } //4.如果target是数组,且命中了一些属性,则执行函数方法 const targetIsArray = isArray(target); if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) { return Reflect.get(arrayInstrumentations, key, receiver); } //5.Reflect获取值 const res = Reflect.get(target, key, receiver); //6.判断是否为特殊的属性值 if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { return res; } if (!isReadonly) { track(target, "get" /* TrackOpTypes.GET */, key); } if (shallow) { return res; } //7.判断是否为ref对象 if (isRef(res)) { // ref unwrapping - skip unwrap for Array + integer key. return targetIsArray && isIntegerKey(key) ? res : res.value; } //8.判断是否为对象 if (isObject(res)) { // Convert returned value into a proxy as well. we do the isObject check // here to avoid invalid value warning. Also need to lazy access readonly // and reactive here to avoid circular dependency. return isReadonly ? readonly(res) : reactive(res); } return res; }; }
检测
__v_isReactive
属性,如果为true,表示target已经是一个响应式对象了。依次检测
__v_isReadonly
和__v_isShallow
属性,判断是否为只读和浅层响应,如果是则返回对应包装过的target。检测
__v_raw
属性,这里是三元的嵌套,主要判断原始数据是否为只读或者浅层响应,然后在对应的Map里面寻找是否有该目标对象,如果都为true则说明target已经为响应式对象。如果target是数组,需要对一些方法(针对
includes
、indexOf
、lastIndexOf
、push
、pop
、shift
、unshift
、splice
)进行特殊处理。并对数组的每个元素执行收集依赖,然后通过Reflect获取数组函数的值。Reflect
获取值。判断是否为特殊的属性值,
symbol
,__proto__
,__v_isRef
,__isVue
, 如果是直接返回前面得到的res
,不做后续处理;如果为
ref
对象,target
不是数组的情况下,会自动解包。如果
res
是Object
,进行深层响应式处理。从这里就能看出,Proxy
是懒惰式的创建响应式对象,只有访问对应的key
,才会继续创建响应式对象,否则不用创建。
set
方法实现
例子:data.name='2'
const set = /*#__PURE__*/ createSetter(); //shallow是否转换为浅层响应,默认为false function createSetter(shallow = false) { //1.传递四个参数 return function set(target, key, value, receiver) { let oldValue = target[key]; //首先获取旧值,如果旧值是ref类型,且新值不是ref类型,则不允许修改 if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) { return false; } //2.根据传递的shallow参数,来执行之后的操作 if (!shallow) { if (!isShallow(value) && !isReadonly(value)) { oldValue = toRaw(oldValue); value = toRaw(value); } if (!isArray(target) && isRef(oldValue) && !isRef(value)) { oldValue.value = value; return true; } } //3.检测key是不是target本身的属性 const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); //利用Reflect.set()来修改值,返回一个Boolean值表明是否成功设置属性 //Reflect.set(设置属性的目标对象, 设置的属性的名称, 设置的值, 如果遇到 `setter`,`receiver`则为`setter`调用时的`this`值) const result = Reflect.set(target, key, value, receiver); // 如果目标是原始原型链中的某个元素,则不要触发 if (target === toRaw(receiver)) { //如果不是target本身的属性那么说明执行的是'add'操作,增加属性 if (!hadKey) { trigger(target, "add" /* TriggerOpTypes.ADD */, key, value); } //4.比较新旧值,是否触发依赖 else if (hasChanged(value, oldValue)) { //5.触发依赖 trigger(target, "set" /* TriggerOpTypes.SET */, key, value, oldValue); } } return result; }; }
1、以data.name='2'
这段代码为例,四个参数分别为:
target
:目标对象,即target={"name": "测试","age": 10}
(此处为普通对象)
key
:修改的对应key,即key: "name"
value
:修改的值,即value: "2"
receiver
:目标对象的代理。即receiver=Proxy {"name": "测试","age": 10}
- 반응형이란 무엇입니까?🎜🎜reactive는 다음에서 제공되는 반응형 구현입니다. Vue3 Data 방식입니다.🎜🎜Vue2에서는 defineProperty를 통해 반응형 데이터를 구현합니다.🎜🎜그리고 Vue3에서는 ES6의 Proxy🎜를 통해 반응형 데이터를 구현합니다.
- 🎜Reactive에 대한 참고사항🎜🎜Reactive 매개변수는 반드시 Object(json/arr)여야 합니다🎜🎜다른 객체가 Reactive로 전달되면 해당 객체는 기본적으로 수정되며 인터페이스는 자동으로 업데이트되지 않습니다. 업데이트하고 싶다면 다시 할당하면 됩니다. [관련 권장사항: vuejs 비디오 튜토리얼, 웹 프론트엔드 개발]🎜
//判断参数是否为string类型,是则返回true const isString = (val) => typeof val === 'string'; //如果参数是string类型并且不是'NaN',且排除-值(排除负数),然后将 key 转换成数字再隐式转换为字符串,与原 key 对比 const isIntegerKey = (key) => isString(key) && key !== 'NaN' && key[0] !== '-' && '' + parseInt(key, 10) === key;
중단점 설정
🎜

디버깅 시작
🎜다음으로 디버깅을 시작할 수 있습니다. 중단점을 설정한 후 페이지를 새로 고쳐 디버깅 인터페이스로 들어갑니다. 🎜복잡한 데이터 유형
🎜간단한 기본 데이터 유형부터 디버깅해 보겠습니다🎜🎜
//Object.is()方法判断两个值是否是相同的值。 const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
getTargetType
메서드 호출 process🎜<script setup> import { reactive } from "vue"; const data = reactive({ name: "测试", age: 10, }); data.name='1'//这里并未收集依赖,在处理完 createSetupContext 的上下文后,组件会停止依赖收集,并且开始执行 setup 函数。具体原因有兴趣的读者可以自行去了解 const testClick = ()=>{ data.name='test' } </script> <template> <div> <h1 id="nbsp-data-name-nbsp">{{ data.name }}</h1> <el-button @click="testClick">Click</el-button> </div> </template> <style scoped></style>
createReactiveObject < /code><code>const Proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
메소드에서 이 명령문에서 두 번째 매개변수는 대상 여부를 결정합니다. Map 또는 Set 유형입니다. 따라서 종속성 수집에는 다양한 처리기가 사용됩니다. 🎜🎜디버깅된 파일 node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js
에서 reactive
의 createReactiveObject
에서 시작합니다. code> 함수 >함수 호출의 두 매개변수인 mutableHandlers
및 mutableCollectionHandlers
는 위쪽으로 쿼리를 시작합니다🎜mutableHandlers
if (!isObject(target)) { if ((process.env.NODE_ENV !== 'production')) { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; }
get
메소드 구현
<script> const data = reactive({ name: "测试", age: 10, }); const num = reactive(data)//定义一个已经是响应式对象 </script>
- < li>🎜
- 🎜
__v_isReadonly
및__v_isShallow
속성을 순서대로 확인하여 읽기 전용이고 얕은 응답인지 확인하세요. 그렇다면 해당하는 응답을 반환하세요. 포장된 대상. 🎜 - 🎜
__v_raw
속성을 감지합니다. 여기에는 주로 원본 데이터가 읽기 전용인지 인지 확인하기 위한 삼항 중첩이 있습니다. 얕은 응답 을 선택한 다음 해당 맵에서 대상 개체를 찾습니다. 둘 다 true인 경우 대상이 이미 반응형 개체라는 의미입니다. 🎜 - 🎜 target이 배열인 경우 일부 메소드(
includes
,indexOf
,lastIndexOf< / code>, <code>push
,pop
,shift
,unshift
,splice
) 특수 처리 . 그리고 배열의 각 요소에 대해 컬렉션 종속성을 수행한 후 Reflect를 통해 배열 함수의 값을 가져옵니다. 🎜 - 🎜
반영
가치를 얻으세요. 🎜 - 🎜특수 속성 값인지 확인,
symbol
,__proto__
,__v_isRef
,__isVue< /code code>, 이전에 얻은 <code>res
가 후속 처리 없이 바로 반환되는 경우 🎜 - 🎜
ref
객체인 경우,target< /code>가 배열이 아니면 자동으로 압축이 풀립니다. 🎜</li><li>🎜<code>res
가Object
인 경우 심층 응답 처리를 수행합니다. 여기에서프록시
는 해당키
에 액세스할 때만 반응형 개체를 계속 생성하고 그렇지 않으면 생성할 필요가 없다는 것을 알 수 있습니다. . 🎜
__v_isReactive
속성을 감지합니다. true인 경우 대상이 이미 반응형 개체라는 의미입니다. 🎜set
메소드 구현
🎜예: data.name='2'</ code>🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>function reactive(target) {
// if trying to observe a readonly proxy, return the readonly version.
if (isReadonly(target)) {
return target;
}
return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
}</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div>🎜1. <code>data.name='2'
를 예로 들어 보겠습니다. 4개의 매개변수는 다음과 같습니다. 🎜🎜target
: 대상 개체, 즉 < code >target={"name": "Test","age": 10}(일반 객체입니다)🎜🎜key
: 수정하려는 해당 키, 즉, 키: "이름"
🎜🎜값
: 수정된 값, 즉 값: "2"
🎜🎜수신기</code >: 대상 객체 에이전트. 즉, <code>receiver=Proxy {"name": "test","age": 10}
🎜2、shallow
为false的时候。
第一个判断:如果新值不是浅层响应式并且不是readonly,新旧值取其对应的原始值。
第二个判断:如果target不是数组并且旧值是ref类型,新值不是ref类型,直接修改oldValue.value为value
3.检测key
是不是target本身的属性。这里的hadKey
有两个方法,isArray
就不解释,就是判断是否为数组
isIntegerKey
:判断是不是数字型的字符串key值
//判断参数是否为string类型,是则返回true const isString = (val) => typeof val === 'string'; //如果参数是string类型并且不是'NaN',且排除-值(排除负数),然后将 key 转换成数字再隐式转换为字符串,与原 key 对比 const isIntegerKey = (key) => isString(key) && key !== 'NaN' && key[0] !== '-' && '' + parseInt(key, 10) === key;
4.比较新旧值,如果新旧值不同,则触发依赖进行更新
hasChanged
方法
//Object.is()方法判断两个值是否是相同的值。 const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
5.触发依赖,这里太过复杂,笔者也没搞懂,如果有兴趣的读者可自行去调试
<script setup> import { reactive } from "vue"; const data = reactive({ name: "测试", age: 10, }); data.name='1'//这里并未收集依赖,在处理完 createSetupContext 的上下文后,组件会停止依赖收集,并且开始执行 setup 函数。具体原因有兴趣的读者可以自行去了解 const testClick = ()=>{ data.name='test' } </script> <template> <div> <h1 id="nbsp-data-name-nbsp">{{ data.name }}</h1> <el-button @click="testClick">Click</el-button> </div> </template> <style scoped></style>
基本数据类型
const num = reactive(2)
这里比较简单,在createReactiveObject
函数方法里面:
if (!isObject(target)) { if ((process.env.NODE_ENV !== 'production')) { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; }
因为判断类型不是对象,所以会在控制台打印出警告,并且直接返回原数据
proxy对象
<script> const data = reactive({ name: "测试", age: 10, }); const num = reactive(data)//定义一个已经是响应式对象 </script>
1.调试开始进来reactive
函数,然后会经过isReadonly
函数,这里跟前面不同的是,target是一个proxy对象,它已经被代理过有set
,get
等handler。所以在isReadonly
函数读取target
的时候,target
会进行get
函数的读取操作。
function reactive(target) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target; } return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap); }
2.可以看到get
传入的参数有个key="__v_isReadonly"
,这里的isReadonly
返回是false,接下来进入createReactiveObject
函数
这里说明下,在本次调试中常见的vue里面定义的私有属性有:
__v_skip
:是否无效标识,用于跳过监听__v_isReactive
:是否已被reactive相关api处理过__v_isReadonly
:是否被readonly相关api处理过__v_isShallow
:是否为浅层响应式对象__v_raw
:当前代理对象的源对象,即target
3.在createReactiveObject
函数中,经过target["__v_isReactive"]
的时候会触发target
的get函数,这时候get
函数传入的参数中key='__v_raw'
if (target["__v_raw" /* ReactiveFlags.RAW */] && !(isReadonly && target["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */])) { return target; }
由上图可知我们检测target
即已定义过的proxy对象,被reactive
api处理过就会有__v_raw
私有属性,然后再进行receiver
的判断,判断target
是否为只读或浅层响应。如果都不是则从缓存proxy的WeakMap
对象中获取该元素。最后直接返回target
的原始数据(未被proxy代理过)。
最后回到之前的判断,由下图可知,target
的__v_raw
属性存在,isReadonly
为false,__v_isReactive
的值为true,可以说明reactive
函数需要处理的对象是一个被reactive
API处理过的对象,然后直接返回该对象的原始数据。
ref类型
经过ref
函数处理,其本质也是一个对象,所以使用reactive
函数处理ref
类型就跟处理复杂数据类型一样过程。对于ref
函数,如果大家有兴趣可以阅读这篇文章vue3——深入了解ref()。有些内容跟这里差不多,也有对此补充,如果觉得不错请各位帮忙点个赞
(开发中应该不会有这种嵌套行为吧,这里只是为了测试多样化)。
<script setup> import { reactive,ref } from "vue"; const data = reactive({ name: "测试", age: 10, }); const numRef = ref(1) const dataRef = ref({ name: "测试2", age: 20, }) const num = reactive(numRef) const dataReactive = reactive(dataRef) console.log('data',data) console.log('numRef',numRef) console.log('num',num) console.log('dataRef',dataRef) console.log('dataReactive',dataReactive) </script>
Map类型和Set类型
Map
类型是键值对的有序列表,而键和值都可以是任意类型。Set
和Map
类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set
中,没有重复的key。
<script setup> import { reactive } from "vue"; const mapData = new Map(); mapData.set('name','张三') const setData = new Set([1,2,3,1,1]) console.log(mapData) console.log(setData) const mapReactive = reactive(mapData) console.log(mapReactive) </script>
由上图可知Map结构和Set结构使用typeof
判断是object
,所有流程前面会跟复杂数据类型一样,知道在createReactiveObject
函数的getTargetType()
函数开始不同。
在getTargetType
函数里面toRawType()
判断数据类型所用方法为Object.prototype.toString.call()
const targetType = getTargetType(target); function getTargetType(value) { return value["__v_skip" /* ReactiveFlags.SKIP */] || !Object.isExtensible(value) ? 0 /* TargetType.INVALID */ : targetTypeMap(toRawType(value)); } function targetTypeMap(rawType) {//rawType="Map",这里返回值为2 switch (rawType) { case 'Object': case 'Array': return 1 /* TargetType.COMMON */; case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return 2 /* TargetType.COLLECTION */; default: return 0 /* TargetType.INVALID */; } }
这时候targetType=2
,在createReactiveObject
的函数中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
的三元表达式中可得知,这里的handler
为collectionHandlers
。
网上查找可在reactive
函数中return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
这条语句找到,当rawType=1
时handler
是用mutableHandlers
,rawType=1
时是用mutableCollectionHandlers
。
mutableCollectionHandlers
方法:
const mutableCollectionHandlers = { get: /*#__PURE__*/ createInstrumentationGetter(false, false) }; //解构createInstrumentations const [mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations] = /* #__PURE__*/ createInstrumentations(); //传入两个参数,是否为可读,是否为浅层响应 function createInstrumentationGetter(isReadonly, shallow) { const instrumentations = shallow ? isReadonly ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly ? readonlyInstrumentations : mutableInstrumentations; return (target, key, receiver) => { if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) { return !isReadonly; } else if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) { return isReadonly; } else if (key === "__v_raw" /* ReactiveFlags.RAW */) { return target; } return Reflect.get(hasOwn(instrumentations, key) && key in target ? instrumentations : target, key, receiver); }; }
//篇幅问题以及这方面笔者并未深入,所以就大概带过 function createInstrumentations() { //创建了四个对象,对象内部有很多方法,其他去掉了,完整可自行去调试查看 const mutableInstrumentations = { get(key) { return get$1(this, key); }, get size() { return size(this); }, has: has$1, add, set: set$1, delete: deleteEntry, clear, forEach: createForEach(false, false) }; ................. //通过createIterableMethod方法操作keys、values、entries、Symbol.iterator迭代器方法 const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]; iteratorMethods.forEach(method => { mutableInstrumentations[method] = createIterableMethod(method, false, false); readonlyInstrumentations[method] = createIterableMethod(method, true, false); shallowInstrumentations[method] = createIterableMethod(method, false, true); shallowReadonlyInstrumentations[method] = createIterableMethod(method, true, true); }); return [ mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations ]; }
后续比较复杂,加上笔者技术力还不够,如果想继续深入的读者,可以阅读这篇文章:Vue3响应式原理
总结:关于reactive
的源码调试就到这了,这只是其中一小部分的源码,希望有兴趣的读者可以以此深入,输出文章,共同进步成长。最后,如果这篇文章对你有所收获,请点个赞,如果有写的不对的地方,请大佬们指出(* ̄︶ ̄)。
위 내용은 vue3의 반응성()에 대해 자세히 이야기해 봅시다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











PHP와 Vue: 프론트엔드 개발 도구의 완벽한 조합 오늘날 인터넷이 빠르게 발전하는 시대에 프론트엔드 개발은 점점 더 중요해지고 있습니다. 사용자가 웹 사이트 및 애플리케이션 경험에 대한 요구 사항이 점점 더 높아짐에 따라 프런트 엔드 개발자는 보다 효율적이고 유연한 도구를 사용하여 반응형 및 대화형 인터페이스를 만들어야 합니다. 프론트엔드 개발 분야의 두 가지 중요한 기술인 PHP와 Vue.js는 함께 사용하면 완벽한 도구라고 볼 수 있습니다. 이 기사에서는 독자가 이 두 가지를 더 잘 이해하고 적용할 수 있도록 PHP와 Vue의 조합과 자세한 코드 예제를 살펴보겠습니다.

프론트엔드 개발 인터뷰에서 일반적인 질문은 HTML/CSS 기초, JavaScript 기초, 프레임워크 및 라이브러리, 프로젝트 경험, 알고리즘 및 데이터 구조, 성능 최적화, 크로스 도메인 요청, 프론트엔드 엔지니어링, 디자인 패턴, 새로운 기술 및 트렌드. 면접관 질문은 후보자의 기술적 능력, 프로젝트 경험, 업계 동향에 대한 이해를 평가하기 위해 고안되었습니다. 따라서 지원자는 자신의 능력과 전문성을 입증할 수 있도록 해당 분야에 대한 충분한 준비를 갖추어야 합니다.

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

Django는 빠른 개발과 깔끔한 방법을 강조하는 Python으로 작성된 웹 애플리케이션 프레임워크입니다. Django는 웹 프레임워크이지만 Django가 프런트엔드인지 백엔드인지에 대한 질문에 답하려면 프런트엔드와 백엔드의 개념에 대한 깊은 이해가 필요합니다. 프론트엔드는 사용자가 직접 상호작용하는 인터페이스를 의미하고, 백엔드는 HTTP 프로토콜을 통해 데이터와 상호작용하는 서버측 프로그램을 의미합니다. 프론트엔드와 백엔드가 분리되면 프론트엔드와 백엔드 프로그램을 독립적으로 개발하여 각각 비즈니스 로직과 인터랙티브 효과, 데이터 교환을 구현할 수 있습니다.

프론트엔드 ESM이란 무엇입니까? 프론트엔드 개발에서 ESM은 ECMAScript 사양을 기반으로 한 모듈식 개발 방법인 ECMAScriptModules를 참조합니다. ESM은 더 나은 코드 구성, 모듈 간 격리, 재사용성과 같은 많은 이점을 제공합니다. 이 기사에서는 ESM의 기본 개념과 사용법을 소개하고 몇 가지 구체적인 코드 예제를 제공합니다. ESM의 기본 개념 ESM에서는 코드를 여러 모듈로 나눌 수 있으며 각 모듈은 다른 모듈에 대한 일부 인터페이스를 노출합니다.

JavaScript에서 HTTP 상태 코드를 얻는 방법 소개: 프런트 엔드 개발에서 우리는 종종 백엔드 인터페이스와의 상호 작용을 처리해야 하며 HTTP 상태 코드는 매우 중요한 부분입니다. HTTP 상태 코드를 이해하고 얻는 것은 인터페이스에서 반환된 데이터를 더 잘 처리하는 데 도움이 됩니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. HTTP 상태 코드란 무엇입니까? HTTP 상태 코드는 브라우저가 서버에 요청을 시작할 때 서비스가

빠르고 효율적인 프로그래밍 언어인 Go 언어는 백엔드 개발 분야에서 널리 사용됩니다. 그러나 Go 언어를 프런트엔드 개발과 연관시키는 사람은 거의 없습니다. 실제로 프런트엔드 개발에 Go 언어를 사용하면 효율성이 향상될 뿐만 아니라 개발자에게 새로운 지평을 열어줄 수도 있습니다. 이 기사에서는 프런트엔드 개발에 Go 언어를 사용할 수 있는 가능성을 살펴보고 독자가 이 영역을 더 잘 이해할 수 있도록 구체적인 코드 예제를 제공합니다. 전통적인 프런트엔드 개발에서는 사용자 인터페이스를 구축하기 위해 JavaScript, HTML, CSS를 사용하는 경우가 많습니다.

Django: 프론트엔드와 백엔드 개발을 모두 처리할 수 있는 마법의 프레임워크! Django는 효율적이고 확장 가능한 웹 애플리케이션 프레임워크입니다. MVC, MTV를 포함한 다양한 웹 개발 모델을 지원할 수 있으며 고품질 웹 애플리케이션을 쉽게 개발할 수 있습니다. Django는 백엔드 개발을 지원할 뿐만 아니라 프런트엔드 인터페이스를 빠르게 구축하고 템플릿 언어를 통해 유연한 뷰 표시를 구현할 수 있습니다. Django는 프론트엔드 개발과 백엔드 개발을 완벽한 통합으로 결합하므로 개발자가 전문적으로 학습할 필요가 없습니다.
