Это почти то же самое, картинка немного клоунская, а также можно посмотреть чужие более полные картинки.@mxin
предисловие
давно нет связиVue
Теперь, когда я смотрел прямую трансляцию Йоды несколько дней назад, я говорил о看源码
Некоторые из представлений предназначены для лучшего начала работы.vue
Или хотите изучить внутреннюю структуру мышления?
Внутренний интерфейс: интервью, интервью спросят.
В большой среде кажется, что пока вы являетесь разработчиком, вы должны изучить исходный код, будь вы стажером, новичком или старым фронтендером с многолетним опытом.
Если вы застопоритесь и не будете следовать броску, внезапное давление сокрушит вас, и вам может быть трудно выжить в броске, даже если вы правы.
Если вам интересно, вы можете прочитатьоползни @Nuggetsнаписал боссШкала самооценки тревоги программиста.
Кажется, я слишком много наговорил не по теме, вместо того, чтобы жаловаться, лучше успокоиться и учиться вместе.Reactivity
Некоторые основные принципы этого, я полагаю, что после прочтения статьи выvue 3
Реагирование на данные имеет более глубокое понимание.
И почему выбирают
Reactivity
Для модуля это связано с тем, что его степень связи низкая, и онvue3.0
Один из основных модулей, стоимость исполнения очень высока.
Основы
Прежде чем начать, если вы не понимаетеES6
некоторые, которые появляются高阶api
,Такие как,Proxy
, Reflect
, WeakMap
, WeakSet
,Map
, Set
И так далее, вы можете прочитать главу ресурса самостоятельно, и лучше всего сначала понять необходимые очки знаний и просмотреть ее снова.
Proxy
существует@vue/reactivity
середина,Proxy
Это краеугольный камень всего планирования.
пройти черезProxy
прокси-объект, который можно использовать вget
, set
В методе выполняются следующие действия, например依赖收集
,effect
,track
, trigger
Операции и т. д. здесь подробно описываться не будут и будут подробно описаны позже.
Если есть ученики, которые не могут ждать, плюс талант и ум,
ES6
Если у вас есть определенная основа, вы можете сразу перейти к основной главе для просмотра и размышлений.
Давайте напишем простойProxy
. в этомhandleCallback
написано наset
, get
Для перехвата данных мониторинга текущего изменения значения атрибута используются два метода. Сначала код:
const user = {
name: 'wangly19',
age: 22,
description: '一名掉头发微乎其微的前端小哥。'
}
const userProxy = new Proxy(user, {
get(target, key) {
console.log(`userProxy: 当前获取key为${key}`)
if (target.hasOwnProperty(key)) return target[key]
return {
}
},
set(target, key, value) {
console.log(`userProxy: 当前设置值key为${key}, value为${value}`)
let isWriteSuccess = false
if (target.hasOwnProperty(key)) {
target[key] = value
isWriteSuccess = true
}
return isWriteSuccess
}
})
console.log('myNaame', userProxy.name)
userProxy.age = 23
Когда мы назначаем, изменяем и печатаем значение, мы запускаем текущийset
а такжеget
метод.
Это очень важно, о некоторых других свойствах и способах использования я не буду здесь вдаваться в подробности.
Reflect
Reflect
Не класс, а встроенный объект. Об этом должен знать каждый, а не прямо实例化(new)
используя, его функция сравнивает иProxy
изhandles
Это немного похоже, но к этому добавлено гораздо большеObject
Методы.
Здесь мы не углубляемся
Reflect
Если вы хотите понять функции студентов, соответствующий адрес можно найти в следующих ресурсах для обучения. В этой главе представленыReflect
Объект безопасной эксплуатации.
Следующее правильноuser
некоторые из объектов修改
За примером операции вы можете обратиться к нему, который может быть использован в будущем.
const user = {
name: 'wangly19',
age: 22,
description: '一名掉头发微乎其微的前端小哥。'
}
console.log('change age before' , Reflect.get(user, 'age'))
const hasChange = Reflect.set(user, 'age', 23)
console.log('set user age is done? ', hasChange ? 'yes' : 'no')
console.log('change age after' , Reflect.get(user, 'age'))
const hasDelete = Reflect.deleteProperty(user, 'age')
console.log('delete user age is done?', hasDelete ? 'yes' : 'none')
console.log('delete age after' , Reflect.get(user, 'age'))
Принципы
Когда вы усвоите некоторые из предыдущих знаний, пора начинать@vue/reactivity
Глава анализа исходного кода завершена. Далее начнем с простой идеи по реализации базовогоreactivity
, когда вы поймете его основные принципы, вы@vue/reactivity
из依赖收集(track)
а также触发更新(trigger)
,так же как副作用(effect)
Какая именно работа.
reactive
reactive
даvue3
используется для создания引用类型
изapi
.
const user = reactive({
name: 'wangly19',
age: 22,
description: '一名掉头发微乎其微的前端小哥。'
})
Затем загляните внутрь функции,reactive
Что именно делает метод?
Внутренне выполняетtarget
Решение только для чтения, если вы пройдетеtarget
Если это прокси только для чтения, он вернется напрямую. для нормальногоreactive
затем он возвращаетсяcreateReactiveObject
значение метода.
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
createReactiveObject
существуетcreateReactiveObject
, что делается дляtarget
добавить одинproxy
играет роль. Это его ядро,reactive
Наконец-то получилproxy
прокси, ссылкаProxy
Простой пример главы может быть известенreactive
Как это работает, так что давайте посмотримcreateReactiveObject
сделал что-то.
Сначала определите токtarget
Тип , если он не соответствует требованиям, сразу выдается предупреждение и возвращается исходное значение.
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
Во-вторых, оценивается, был ли текущий объект проксирован и не доступен ли он только для чтения, затем он сам является прокси-объектом, тогда нет необходимости снова проксировать, и он напрямую возвращается как возвращаемое значение, чтобы избежать повторного проксирования.
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
Для этих кодов суждений их не очень сложно прочитать, обратите внимание наif ()
В состоянии суда посмотрите, какое действие оно совершает. а такжеcreateReactiveObject
Самое главное, что нужно сделать, это создатьtarget
изproxy
, и поместите его вMap
записано в.
И интереснее, где входящийtarget
называется разнымproxy handle
. Тогда давайте посмотримhandles
Что вы делали в .
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
тип ручек
В типе объекта укажитеObject
а такжеArray
а такжеMap
,Set
, WeakMap
,WeakSet
дифференцированный. они называют разныеProxy Handle
.
-
baseHandlers.ts
:Object
&Array
будет вызывать под этим файломmutableHandlers
объект какProxy Handle
. -
collectionHandlers.ts
:Map
,Set
,WeakMap
,WeakSet
будет вызывать под этим файломmutableCollectionHandlers
объект какProxy Handle
.
/**
* 对象类型判断
* @lineNumber 41
*/
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
}
}
Будет вnew Proxy
вернулся в соответствии сtargetType
судить.
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
Из-за ограниченного места ниже приведены только примеры.
mutableHandlers
в качестве ориентира для анализа. когда понялmutableHandlers
после дляcollectionHandlers
Просто вопрос времени.
Proxy Handle
Как было сказано выше, по разнымType
называй разныеhandle
, тогда давайте посмотримmutableHandlers
Что именно сделал.
В основе все мы знаемProxy
может получить объект конфигурации, который мы демонстрируемget
а такжеset
метод собственности. а такжеmutableHandlers
вещи одного и того же значения, отдельно определенные внутреннеget
, set
, deleteProperty
, has
, oneKeys
Дождитесь нескольких параметров атрибута, если вы не знаете, что это значит, вы можете посмотреть на этоProxy Mdn
. Здесь нужно понять被监听的数据
пока это происходит增查删改
После этого большая часть из них попадет в соответствующий канал поступления.
Здесь мы используем простойget
, set
для простого примера моделирования.
function createGetter () {
return (target, key, receiver) => {
const result = Reflect.get(target, key, receiver)
track(target, key)
return result
}
}
const get = /*#__PURE__*/ createGetter()
function createSetter () {
return (target, key, value, receiver) => {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (result && oldValue != value) {
trigger(target, key)
}
return result
}
}
существуетget
выполнитtrack
сбор зависимостей, в то время какset
срабатывает, когдаtrigger
спусковой механизм. существуетvue3
,а такжеtrigger
а такжеtrack
слова все в насeffect.ts
указано в нем, то взгляните на следующий依赖收集
а также响应触发
Что-то сделал.
Effect
Весь модуль эффектов разделен на три части:
-
effect
: Функция побочного эффекта -
teack
: коллекция зависимостей, вproxy
данные проксиget
когда звонят -
trigger
: реакция триггера, вproxy
Вызывается при изменении данных прокси.
effect
Давайте посмотрим на примерeffect
использовать и понимать, что его главный аргумент — это функция. Внутри функция поможет вам выполнить регистрацию некоторых побочных эффектов и оценку функций.
effect(() => {
proxy.user = 1
})
приди и посмотриvue
изeffect
Что ты сделал?
Здесь сначала определите текущие параметрыfn
этоeffect
, если да, тоraw
Хранится вfn
сделать замену. затем сделай это сноваcreateReactiveEffect
генерировать.
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
if (isEffect(fn)) {
fn = fn.raw
}
const effect = createReactiveEffect(fn, options)
if (!options.lazy) {
effect()
}
return effect
}
существуетcreateReactiveEffect
будем ли мыeffect
затолкнуть вeffectStack
Поместите операцию стека в , а затем используйтеactiveEffect
получить доступ к выполняемому в данный моментeffect
, который будет выполняться после выполнения出栈
. заменить заодноactiveEffect
для новой вершины стека.
пока вeffect
будет срабатывать во время выполненияproxy handle
потомtrack
а такжеtrigger
Две ключевые функции.
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(): unknown {
if (!effect.active) {
return options.scheduler ? undefined : fn()
}
if (!effectStack.includes(effect)) {
cleanup(effect)
try {
enableTracking()
effectStack.push(effect)
activeEffect = effect
return fn()
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect
effect.id = uid++
effect.allowRecurse = !!options.allowRecurse
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
посмотреть короткую версиюeffect
, помимо большей части кодового багажа, следующий код не намного понятнее.
function effect(eff) {
try {
effectStack.push(eff)
activeEffect = eff
return eff(...argsument)
} finally {
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
}
трек (коллекция зависимостей)
существуетtrack
, будет выполнен известный сбор зависимостей, а текущийactiveEffect
добавить вdep
Внутри, и говоря об этом типе отношений. Это будет отношение один-ко-многим-ко-многим.
Это также очень ясно из кода.Прежде всего, у нас будет общийtargetMap
этоWeakMap
,key
даtarget(代理的对象)
, value
ЯвляетсяMap
, назови этоdepsMap
, который используется для управления текущимtarget
каждый изkey
изdeps
Также известна как зависимость от побочных эффектов, также известная какdepend
. существуетvue3
черезSet
приходи и уходи.
Первый шаг — опираться на текущийtarget
ПолучатьtargetMap
серединаdepsMap
, если он не существует, продолжайтеtargetMap.set(target, (depsMap = new Map()))
объявление инициализации, за которым следуетdepsMap
Возьмите текущийkey
изdeps
, если не найдено, также используйтеdepsMap.set(key, (dep = new Set()))
Сделайте объявление инициализации и, наконец, установите текущийactiveEffect
затолкнуть вdeps
, для сбора зависимостей.
-
- существует
targetMap
найти вtarget
- существует
-
- существует
depsMap
найти вkey
- существует
-
- Буду
activeEffect
Сохранитьdep
в.
- Буду
В этом случае будет сформирован шаблон структуры «один-ко-многим-ко-многим», в котором хранятся всеproxy
Захваченные зависимости.
function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
return
}
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
if (__DEV__ && activeEffect.options.onTrack) {
activeEffect.options.onTrack({
effect: activeEffect,
target,
type,
key
})
}
}
}
триггер (ответ на триггер)
существуетtrigger
Когда это происходит, то фактически запускает выполнение текущей зависимости ответа.
Во-первых, вам нужно получить текущийkey
по всем каналамdeps
, так что вы увидите, что естьeffects
а такжеadd
Функция, которую он делает, очень проста, заключается в том, чтобы судить о текущем входящемdepsMap
Нужно ли добавлять недвижимостьeffects
Внутри вот такое состояниеeffect
не может быть текущимactiveEffect
а такжеeffect.allowRecurse
, чтобы гарантировать, что текущийset key
Зависимости выполняются.
const effects = new Set<ReactiveEffect>()
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
Следующие хорошо известные сценарии позволяют судить о некоторых текущих входящих изменениях.trigger
будет доставлен вTriggerOpTypes
поведение, а затем выполнитьadd
метод, который позволит квалифицироватьeffect
добавить вeffects
иди среди, здесь@vue/reactivity
Сделайте много данных, чтобы изменить поведение, напримерlength
Разнообразие.
Затем по разнымTriggerOpTypes
провестиdepsMap
Данные вынимаются и, наконец, вводятсяeffects
. затем черезrun
метод преобразует текущийeffect
выполнить, черезeffects.forEach(run)
выполнить.
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
depsMap.forEach(add)
} else if (key === 'length' && isArray(target)) {
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if (key !== void 0) {
add(depsMap.get(key))
}
// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// new index added to array -> length changes
add(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
add(depsMap.get(ITERATE_KEY))
}
break
}
}
а такжеrun
Что ты опять сделал?
Во-первых, определить текущийeffect
серединаoptions
Есть лиscheduler
, если есть, используйтеschedule
Чтобы обработать выполнение, в противном случае напрямую выполнить его напрямуюeffect()
.
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
Чтобы немного сократить, чтобы посмотреть на логику обработки, она на самом деле изtargetMap
Переписка в Китаеkey
зависимость.
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
const dep = depsMap.get(key)
if (dep) {
dep.forEach((effect) => {
effect()
})
}
Ref
Как мы все знаем,ref
даvue3
Объявление реактивных данных для общих типов. и получитьref
нужно передать значениеref.value
способ получения, многие думают, чтоref
простойreactive
Но это не так.
В исходном кодеref
в конце концов позвонитеcreateRef
метод, который внутренне возвращаетRefImpl
пример. это сProxy
разница в том,ref
Сбор зависимостей и триггеры ответа находятся вgetter/setter
Среди них это может относиться к фигуреdemo
форма, адрес ссылкиgettter/setter.
export function ref<T extends object>(value: T): ToRef<T>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
return createRef(value)
}
function createRef(rawValue: unknown, shallow = false) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
как показано на рисунке,vue
существуетgetter
нейтральныйproxy中的get
тот же звонокtrack
Соберите зависимости, вsetter
в ходе выполнения_value
Вызывается после изменения значенияtrigger
вызывать.
class RefImpl<T> {
private _value: T
public readonly __v_isRef = true
constructor(private _rawValue: T, public readonly _shallow = false) {
this._value = _shallow ? _rawValue : convert(_rawValue)
}
get value() {
track(toRaw(this), TrackOpTypes.GET, 'value')
return this._value
}
set value(newVal) {
if (hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal
this._value = this._shallow ? newVal : convert(newVal)
trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
}
}
}
Тогда вы должны знать:
-
proxy handle
даreactive
принцип, иref
Принципgetter/setter
. - существует
get
называется, когдаtrack
,set
называется, когдаtrigger
-
effect
Это ядро ответа данных.
Computed
computed
Как правило, есть два распространенных способа использования: один — это передача объекта с внутреннимset
а такжеget
метод, который принадлежитComputedOptions
форма.
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
)
А внутри будетgetter / setter
две переменные для сохранения.
когдаgetterOrOptions
Когда это функция, она будет назначена иgetter
.
когдаgetterOrOptions
Когда это объект, он будетset
а такжеget
назначить наsetter
,getter
.
который затем создается как параметрComputedRefImpl
класс и вернуть его как возвращаемое значение.
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
ТакComputedRefImpl
что ты сделал?
Исходный код вычисляемого свойства, на самом деле большая его часть зависит от лицевой стороныeffect
некоторое понимание.
Во-первых, мы все знаем, чтоeffect
может пройти函数
с одним对象options
.
здесь будетgetter
передается как параметр функции, т.е.副作用
, пока вoptions
настроен вlazy
а такжеscheduler
.
lazy
выражатьeffect
выполняется не сразу иscheduler
вtrigger
определит, прошли ли выscheduler
, который выполняется после передачиscheduler
метод.
пока вcomputed scheduler
Среди них нынешний_dirty
Этоfalse
, если да, то будет_dirty
Установить какtrue
и выполнитьtrigger
триггерный ответ.
class ComputedRefImpl<T> {
private _value!: T
private _dirty = true
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true;
public readonly [ReactiveFlags.IS_READONLY]: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')
}
}
})
this[ReactiveFlags.IS_READONLY] = isReadonly
}
пока вgetter/setter
Центральная встреча_value
выполнять разные операции.
Первый вget value
, судить о текущем._dirty
Этоtrue
, если это так, выполните кешированныйeffect
и сохранить возвращенный результат в_value
и выполнитьtrack
Выполните сбор зависимостей.
Во-вторых, вset value
, затем позвоните_setter
способ сброса значения.
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
if (self._dirty) {
self._value = this.effect()
self._dirty = false
}
track(self, TrackOpTypes.GET, 'value')
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
ссылка на ресурс
Ниже приведены некоторые справочные ресурсы, заинтересованные друзья могут взглянуть
- WeakMap серии ES6
- Прокси и отражение
- Vue Mastery
- Vue Docs
- React представляет @vue/reactivity Vue3 для реализации адаптивного управления состоянием.
Суммировать
если вы используетеvue
Настоятельно рекомендую себяdebug
Прочитав эту статью, она определенно поможет вам в написании кода.vue3
Полным ходом уже есть команды, работающие над производственной средой для разработки проекта, и экология сообщества потихоньку развивается.
@vue/reactivity
Сложность чтения невелика, есть много качественных туториалов, если есть определенная рабочая база и знание кода, можно шаг за шагом разобраться.
Лично мне не нужно разбираться в нем досконально и понимать значение каждой строки кода, а нужно понимать его основные идеи, изучать концепции фреймворка и некоторые идеи разработчиков фреймворка по написанию кода. Это все знания, которые можно позаимствовать и усвоить как свои собственные.
для того, кто пошел в
React
Для фронтенда под экосистему читайтеVue
Исходный код на самом деле больше для обогащения ваших знаний в размышлениях, а не для чтения его для интервью. Так же, как ваше одобрение не для экзаменов, а для получения знаний. В нынешних условиях это делать сложно, лучше успокоиться и сконцентрироваться на понимании какого-то знания, чем заучивать несколько сутр лицом к лицу.