Vue3 responsive Core-reaktive Quellcode-Analyse
1. Reaktiver Quellcode
1. reaktiv
Quellcodepfad: packages/reactivity/src/reactive.ts
#🎜 🎜#
export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. // 是否是只读响应式对象 if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap ) }
packages/reactivity/src/reactive.ts
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any> ) { // 仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。 if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // target is already a Proxy, return it. // exception: calling readonly() on a reactive object if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } // target already has corresponding Proxy const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } // only specific value types can be observed. const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target } const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy }
当我们执行reactive({})
的时候,会执行createReactiveObject
这个工厂方法,返回一个响应式对象。
2、接着看工厂方法createReactiveObject
源码路径:packages/reactivity/src/reactive.ts
if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target }
仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。
if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target }
如果 target 已经是一个代理对象了,那么直接返回 target
const existingProxy = proxyMap.get(target) // 存储响应式对象 if (existingProxy) { return existingProxy }
如果 target 已经有对应的代理对象了,那么直接返回代理对象
const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target }
对于不能被观察的类型,直接返回 target
// getTargetType源码 function getTargetType(value: Target) { return value[ReactiveFlags.SKIP] || !Object.isExtensible(value) // 不可扩展 ? TargetType.INVALID : targetTypeMap(toRawType(value)) } // ReactiveFlags枚举 export const enum ReactiveFlags { // 用于标识一个对象是否不可被转为代理对象,对应的值是 __v_skip SKIP = '__v_skip', // 用于标识一个对象是否是响应式的代理,对应的值是 __v_isReactive IS_REACTIVE = '__v_isReactive', // 用于标识一个对象是否是只读的代理,对应的值是 __v_isReadonly IS_READONLY = '__v_isReadonly', // 用于标识一个对象是否是浅层代理,对应的值是 __v_isShallow IS_SHALLOW = '__v_isShallow', // 用于保存原始对象的 key,对应的值是 __v_raw RAW = '__v_raw' } // targetTypeMap function targetTypeMap(rawType: string) { switch (rawType) { case 'Object': case 'Array': return TargetType.COMMON case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return TargetType.COLLECTION default: return TargetType.INVALID } } // toRawType export const toRawType = (value: unknown): string => { // extract "RawType" from strings like "[object RawType]" return toTypeString(value).slice(8, -1) }
const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers )
创建响应式对象(核心代码)
export const mutableHandlers: ProxyHandler<object> = { get, set, deleteProperty, has, ownKeys }
接下来将重点讲解baseHandlers
这个回调函数。
二、baseHandlers
1、baseHandlers
baseHandlers
是mutableHandlers
, 来自于 baseHandlers
Wenn wir reactive({})
ausführen, wird die Factory-Methode createReactiveObject
ausgeführt und ein reaktives Objekt zurückgegeben.
2. Schauen Sie sich als Nächstes die Factory-Methode createReactiveObject
packages/reactivity/src/reactive.ts
const get = /*#__PURE__*/ createGetter() function createGetter(isReadonly = false, shallow = false) { // 闭包返回 get 拦截器方法 return function get(target: Target, key: string | symbol, receiver: object) { // 如果访问的是 __v_isReactive 属性,那么返回 isReadonly 的取反值 if (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly // 如果访问的是 __v_isReadonly 属性,那么返回 isReadonly 的值 } else if (key === ReactiveFlags.IS_READONLY) { return isReadonly // 如果访问的是 __v_isShallow 属性,那么返回 shallow 的值 } else if (key === ReactiveFlags.IS_SHALLOW) { return shallow // 如果访问的是 __v_raw 属性,那么返回 target } else if ( key === ReactiveFlags.RAW && receiver === (isReadonly ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap ).get(target) ) { return target } // target是否是数组 const targetIsArray = isArray(target) if (!isReadonly) { // 可读 // 如果是数组,并且访问的是数组的一些方法,那么返回对应的方法 /** * Vue3中使用 arrayInstrumentations对数组的部分方法做了处理,为什么要这么做呢? * 对于 push、pop、 shift、 unshift、 splice 这些方法, * 写入和删除时底层会获取当前数组的length属性,如果我们在effect中使用的话, * 会收集length属性的依赖,当使用这些api是也会更改length,就会造成死循环: * */ if (targetIsArray && hasOwn(arrayInstrumentations, key)) { // 返回重写的push、pop、 shift、 unshift、 splice 'includes', 'indexOf', 'lastIndexOf' return Reflect.get(arrayInstrumentations, key, receiver) } // 如果访问的是 hasOwnProperty 方法,那么返回 hasOwnProperty 方法 if (key === 'hasOwnProperty') { return hasOwnProperty } } // 获取 target 的 key 属性值 const res = Reflect.get(target, key, receiver) // 如果是内置的 Symbol,或者是不可追踪的 key,那么直接返回 res if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { return res } // 如果不是只读的,那么进行依赖收集 if (!isReadonly) { track(target, TrackOpTypes.GET, key) } // 如果是浅的,那么直接返回 res if (shallow) { return res } // 如果 res 是 ref,对返回的值进行解包 if (isRef(res)) { // ref unwrapping - skip unwrap for Array + integer key. return targetIsArray && isIntegerKey(key) ? res : res.value } // 如果 res 是对象,递归代理 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 } }
if (targetIsArray && hasOwn(arrayInstrumentations, key)) { // 返回重写的push、pop、 shift、 unshift、 splice 'includes', 'indexOf', 'lastIndexOf' return Reflect.get(arrayInstrumentations, key, receiver) }
Wenn das Ziel bereits ein Proxy-Objekt ist, geben Sie das Ziel direkt zurück.
const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations() function createArrayInstrumentations() { const instrumentations: Record<string, Function> = {} // instrument length-altering mutation methods to avoid length being tracked // which leads to infinite loops in some cases (#2137) ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => { instrumentations[key] = function (this: unknown[], ...args: unknown[]) { // 由于上面的方法会改变数组长度,因此暂停收集依赖,不然会导致无限递归 console.log('----自定义push等入口:this, args, key'); pauseTracking() console.log('----自定义push等暂停收集依赖&执行开始') // 调用原始方法 const res = (toRaw(this) as any)[key].apply(this, args) console.log('----自定义push等暂停收集依赖&执行结束') //复原依赖收集 resetTracking() return res } }) return instrumentations }
Wenn das Ziel bereits über ein entsprechendes Proxy-Objekt verfügt, geben Sie das Proxy-Objekt direkt zurück.#🎜 🎜#
let arr = [1,2,3] let obj = { 'push': function(...args) { // 暂停收集依赖逻辑 return Array.prototype.push.apply(this, [...args]) // 启动收集依赖逻辑 } } let proxy = new Proxy(arr, { get: function (target, key, receiver) { console.log('get的key为 ===>' + key); let res = ''; if(key === 'push') { //重写push res = Reflect.get(obj, key, receiver) } else { res = Reflect.get(target, key, receiver) } return res }, set(target, key, value, receiver){ console.log('set的key为 ===>' + key, value); return Reflect.set(target, key, value, receiver); } }) proxy.push('99')
// 如果是内置的 Symbol,或者是不可追踪的 key,那么直接返回 res if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { return res; }
// 如果不是只读的,那么进行依赖收集 if (!isReadonly) { track(target, "get" /* TrackOpTypes.GET */, key); }
if (shallow) { return res; }
baseHandlers
Diese Rückruffunktion. 2. baseHandlers1. baseHandlersbaseHandlers
ist mutableHandlers
, von < code>baseHandlers-Datei.
// 如果 res 是 ref,对返回的值进行解包 if (isRef(res)) { // 对于数组和整数类型的 key,不进行解包 return targetIsArray && isIntegerKey(key) ? res : res.value; }
(1), Proxy abrufen
// 如果 res 是对象,那么对返回的值进行递归代理 if (isObject(res)) { return isReadonly ? readonly(res) : reactive(res); }
const set = /*#__PURE__*/ createSetter() function createSetter(shallow = false) { // 返回一个set方法 return function set( target: object, key: string | symbol, value: unknown, receiver: object ): boolean { let oldValue = (target as any)[key] // 获取旧值 // 如果旧值是只读的,并且是 ref,并且新值不是 ref if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) { return false } if (!shallow) { // 非shallow // 新值非shallow && 非只读 if (!isShallow(value) && !isReadonly(value)) { // 获取新旧值的原始值 oldValue = toRaw(oldValue) value = toRaw(value) } // 代理对象非数组 & 旧值是ref & 新值非ref if (!isArray(target) && isRef(oldValue) && !isRef(value)) { oldValue.value = value return true } } else { // in shallow mode, objects are set as-is regardless of reactive or not } console.log('----set', target, key, value) // 是数组 & key是整型数字 ? // 如果 key 小于数组的长度,那么就是有这个 key : // 如果不是数组,那么就是普通对象,直接判断是否有这个 key // 数组会触发两次set: index和新增的值 和 'length'和新增之后的数组长度 const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key) // 设置key-value const result = Reflect.set(target, key, value, receiver) // don't trigger if target is something up in the prototype chain of original // 如果目标对象是原始数据的原型链中的某个元素,则不会触发依赖收集 if (target === toRaw(receiver)) { if (!hadKey) {// 如果没有这个 key,那么就是新增了一个属性,触发 add 事件 trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { // // 如果有这个 key,那么就是修改了一个属性,触发 set 事件 trigger(target, TriggerOpTypes.SET, key, value, oldValue) } } // 返回结果,这个结果为 boolean 类型,代表是否设置成功 return result } }
let oldValue = target[key];
kann mit dem verstanden werden folgender Code: // 如果旧值是只读的,并且是 ref,并且新值不是 ref,那么直接返回 false,代表设置失败
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false;
}
In diesem Schritt werden einige spezielle Attribute gefiltert, z. B. native Symboltypattribute wie: Symbol.iterator, Symbol.toStringTag usw. Diese Eigenschaften erfordern keine Abhängigkeitssammlung, da sie integriert sind und sich nicht ändern #, __v_isRef, __isVue usw. Für die Sammlung ist keine Abhängigkeit von Attributen erforderlich. 🎜#rrreee
Dieser Schritt dient der Behandlung der Referenzsituation Beurteilung hier. Wenn es sich um ein Array handelt und der Schlüssel ein ganzzahliger Typ ist, wird das Entpacken nicht durchgeführt. Da Reactive sehr reaktionsfähig ist, muss das Attribut mit ref entpackt werden des Objektsrrreee
#🎜🎜#(2), set Agent#🎜🎜##🎜🎜#rrreee#🎜🎜#Hauptlogik: #🎜🎜##🎜🎜#Alten Wert abrufen #🎜🎜#rrreee#🎜🎜#Beurteilen Sie, ob der alte Wert schreibgeschützt ist#🎜🎜#rrreeeDas obige ist der detaillierte Inhalt vonVue3 responsive Core-reaktive Quellcode-Analyse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

vue3+vite:src verwendet „require“, um Bilder dynamisch zu importieren, und vue3+vite importiert dynamisch mehrere Bilder. Wenn Sie „requireisnotdefined“ verwenden, wird eine Fehlermeldung angezeigt like vue2 like imgUrl:require(' .../assets/test.png') wird importiert, da Typescript Require nicht unterstützt, daher wird Import verwendet. So lösen Sie das Problem: Verwenden Sieawaitimport

tinymce ist ein voll funktionsfähiges Rich-Text-Editor-Plug-in, aber die Einführung von tinymce in Vue ist nicht so reibungslos wie bei anderen Vue-Rich-Text-Plug-ins. Tinymce selbst ist nicht für Vue geeignet, und @tinymce/tinymce-vue muss eingeführt werden. und Es handelt sich um ein ausländisches Rich-Text-Plug-in, das die chinesische Version nicht bestanden hat. Sie müssen das Übersetzungspaket von der offiziellen Website herunterladen (möglicherweise müssen Sie die Firewall umgehen). 1. Installieren Sie die zugehörigen Abhängigkeiten npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2. 3. Führen Sie den Skin und das chinesische Paket ein. Erstellen Sie einen neuen Tinymce-Ordner im öffentlichen Ordner des Projekts und laden Sie ihn herunter

Um das Blog-Frontend mit Vue zu implementieren, müssen Sie die Markdown-Analyse implementieren. Wenn Code vorhanden ist, müssen Sie die Code-Hervorhebung implementieren. Es gibt viele Markdown-Parsing-Bibliotheken für Vue, wie z. B. markdown-it, vue-markdown-loader, markiert, vue-markdown usw. Diese Bibliotheken sind alle sehr ähnlich. Hier wird Markiert verwendet, und highlights.js wird als Code-Hervorhebungsbibliothek verwendet. Die spezifischen Implementierungsschritte lauten wie folgt: 1. Installieren Sie abhängige Bibliotheken. Öffnen Sie das Befehlsfenster unter dem Vue-Projekt und geben Sie den folgenden Befehl ein: npminstallmarked-save//marked, um Markdown in htmlnpmins zu konvertieren

Um eine teilweise Aktualisierung der Seite zu erreichen, müssen wir nur das erneute Rendern der lokalen Komponente (dom) implementieren. In Vue lässt sich dieser Effekt am einfachsten mit der v-if-Direktive erzielen. In Vue2 können wir zusätzlich zur Verwendung der v-if-Anweisung zum erneuten Rendern des lokalen Doms auch eine neue leere Komponente erstellen. Wenn wir die lokale Seite aktualisieren müssen, springen wir zu dieser leeren Komponentenseite und springen dann wieder hinein der beforeRouteEnter-Schutz in der leeren Komponente. Wie in der Abbildung unten gezeigt, wie man in Vue3.X auf die Schaltfläche „Aktualisieren“ klickt, um das DOM im roten Feld neu zu laden und den entsprechenden Ladestatus anzuzeigen. Da der Guard in der Komponente in der scriptsetup-Syntax in Vue3.X nur o hat

Nachdem das vue3-Projekt gepackt und auf dem Server veröffentlicht wurde, wird auf der Zugriffsseite eine leere 1 angezeigt. Der publicPath in der Datei vue.config.js wird wie folgt verarbeitet: const{defineConfig}=require('@vue/cli-service') module.exports=defineConfig({publicPath :process.env.NODE_ENV==='produktion'?'./':'/&

Vorwort Ob Vue oder React: Wenn wir auf mehrere wiederholte Codes stoßen, werden wir darüber nachdenken, wie wir diese Codes wiederverwenden können, anstatt eine Datei mit einer Reihe redundanter Codes zu füllen. Tatsächlich können sowohl Vue als auch React eine Wiederverwendung durch Extrahieren von Komponenten erreichen. Wenn Sie jedoch auf einige kleine Codefragmente stoßen und keine andere Datei extrahieren möchten, kann React im Vergleich dazu verwendet werden Deklarieren Sie das entsprechende Widget in der Datei , oder implementieren Sie es über die Renderfunktion, wie zum Beispiel: constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

Verwenden von Vue zum Erstellen benutzerdefinierter Elemente. WebComponents ist ein Sammelname für eine Reihe webnativer APIs, die es Entwicklern ermöglichen, wiederverwendbare benutzerdefinierte Elemente (Customelements) zu erstellen. Der Hauptvorteil von benutzerdefinierten Elementen besteht darin, dass sie mit jedem Framework verwendet werden können, auch ohne Framework. Sie sind ideal, wenn Sie Endbenutzer ansprechen, die möglicherweise einen anderen Front-End-Technologie-Stack verwenden, oder wenn Sie die endgültige Anwendung von den Implementierungsdetails der verwendeten Komponenten entkoppeln möchten. Vue und WebComponents sind komplementäre Technologien und Vue bietet hervorragende Unterstützung für die Verwendung und Erstellung benutzerdefinierter Elemente. Sie können benutzerdefinierte Elemente in bestehende Vue-Anwendungen integrieren oder Vue zum Erstellen verwenden

Der letzte Effekt besteht darin, die VueCropper-Komponente „garnaddvue-cropper@next“ zu installieren. Wenn es sich um Vue3 handelt oder Sie andere Methoden als Referenz verwenden möchten, besuchen Sie bitte die offizielle npm-Adresse. Es ist auch sehr einfach, es in einer Komponente zu referenzieren und zu verwenden. Sie müssen nur die entsprechende Komponente und ihre Stildatei einführen. Ich verweise hier nicht global, sondern nur auf import{userInfoByRequest}from'../js/api ' in meiner Komponentendatei. import{VueCropper}from'vue-cropper&
