Это моя первая статья, посвященная принципам Vue 3. В этой статье вы узнаете о содержании, связанном с ответами на данные, и постараетесь сделать как можно больше.Избавьтесь от исходного кода, чтобы понять принцип, чтобы уменьшить сложность обучения для всех.
Информация, связанная со статьей
Текущее состояние Vue 3 на самом деле очень приятно читать, потому что объем кода невелик, а основные функции сильно не изменятся.
Поэтому автор разветвил текущий исходный код и аннотировал его. В то же время, чтобы позаботиться о людях, которые не очень хорошо знакомы с TS, автор также объяснил некоторый основной синтаксис TS.
Этот комментарий — не просто сухое описание того, что делает строка кода, он используется в контексте, чтобы объяснить его полезность. Если вы хотите прочитать исходный код, но боитесь его не понять, вы можете передать мне этоскладучиться.
Предварительное знание
Метод написания кода Vue 3 сильно изменился.Если вам непонятен этот контент, рекомендуется сначала прочитать его.Vue Function-based API RFC
механизм ответа данных
Как мы все знаем, в Vue 3 используетсяProxy
заменил оригиналObject.defineproperty
для достижения ответа данных.
Кроме того, если вы не знакомы с использованием прокси, рекомендуется сначала прочитатьДокументация.
Давайте сначала узнаем, как использовать этот API.
const value = reactive({ num: 0 })
// 需要注意的一点,这个回调中用到了 value.num
// 那么只有当外部给 value.num 赋值才会触发回调
effect(() => {
console.log(value.num)
})
value.num = 7
Очень просто, приведенный выше код реализует реакцию данных и может выполнять соответствующий обратный вызов после изменения данных.
reactive
Код внутреннего ядра упрощен следующим образом:
function reactive(target) {
if (!isObject(target)) {
return target
}
if (!canObserve(target)) {
return target
}
const handlers = collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlers
observed = new Proxy(target, handlers)
return observed
}
Во-первых, определите, можно ли использовать тип входящего параметра для наблюдения.В настоящее время поддерживаются следующие типы:Object|Array|Map|Set|WeakMap|WeakSet
.
Далее оцените конструктор параметра и получите разные значения в зависимости от типа.handlers
. Здесь мы используем унифицированныйbaseHandlers
, потому что это покрыло 99% случаев. ТолькоSet, Map, WeakMap, WeakSet
будет использоватьсяcollectionHandlers
.
заbaseHandlers
Самое главное угнатьget
иset
Поведение, эти два поведения также могут изначально перехватывать поведение изменения значения индексов массива и добавления свойств объектов.Содержимое, связанное с этими двумя поведениями, будет упомянуто ниже.
Наконец, построитьProxy
Объект завершает отзывчивость данных. по сравнению сObject.defineproperty
Для практики рекурсивного обхода всего объекта в начале используйтеProxy
Производительность будет намного лучше.
Далее, когда мы перейдем к использованиюvalue
Когда этот объект используется, внутреннее поведение может быть перехвачено.
Напримерconsole.log(value.num)
вызоветget
функция;value.num = 2
вызоветset
функция.
Ниже представлена основная анатомия этих двух функций:
function get(target: any, key: string | symbol, receiver: any) {
// 获得结果
const res = Reflect.get(target, key, receiver)
track(target, OperationTypes.GET, key)
// 判断是否为对象,是的话将对象包装成 proxy
return isObject(res) ? reactive(res) : res
}
заget
Для функций получение значения, безусловно, является наиболее важным шагом. Далее стоит позвонитьtrack
, это иeffect
Об этом я расскажу позже. Наконец, тип значения суждения, если это объект, по-прежнему упакован какProxy
.
function set(
target: any,
key: string | symbol,
value: any,
receiver: any
): boolean {
const result = Reflect.set(target, key, value, receiver)
if (是否新增 key) {
trigger(target, OperationTypes.ADD, key)
} else if (value !== oldValue) {
trigger(target, OperationTypes.SET, key)
}
return result
}
заset
функция, установка значения является первым шагом, затем вызовtrigger
, что такжеeffect
содержание в .
Проще говоря, еслиeffct
используется в обратном вызовеvalue.num
, то этот обратный вызов будет собран и вызванvalue.num = 2
срабатывает когда.
Итак, как вы собираете этот контент? Это сказатьtargetMap
этот объект. Он используется для хранения зависимостей и имеет следующую структуру, которая будет использоваться в файлах эффектов.
{
target: {
key: Dep
}
}
Давайте сначала объясним, что это за три, это очень важно:
- target — проксируемый объект
- key — это свойство объекта после запуска действия get. Например, counter.num запускает поведение получения, а num является ключом.
- dep — это callback-функция, то есть если в эффекте вызывается counter.num, то этот callback и есть dep, который нужно собрать и использовать в следующий раз
Здесь я выношу это содержимое из исходного кода и рассказываю о процессе.
const counter = reactive({ num: 0 })
effect(() => {
console.log(counter.num)
})
counter.num = 7
Сначала создайтеProxy
объект,targetMap
Этот объект будет собран как ключ.
Когда обратный вызов эффекта вызывается следующим, обратный вызов будет сохранен для следующей коллекции зависимостей. сработает во время звонкаcounter
изget
функция, которая вызывается внутриtrack
функция, которая используетtargetMap
.
Здесь сначала черезtarget
отtargetMap
чтобы получить объект, этот объектtarget
Все зависимости. тогда дляcounter.num
Сказать,num
Это ключ этого объекта (если здесь немного расплывчато, вы можете посмотреть на структуру данных выше), а значение — это коллекция, которая зависит от обратного вызова, потому чтоcounter.num
Может зависеть от нескольких мест.
После выполнения обратного вызова сохраненный обратный вызов будет уничтожен.
когда мы звонимcounter.num = 7
, курокset
функция, вызываемая внутриtrigger
функция, также будет использоватьсяtargetMap
.
также черезtarget
Получите объект, а затем передайте ключ, которыйnum
Чтобы вынуть коллекцию зависимостей и, наконец, пройти по коллекции, чтобы выполнить все функции обратного вызова в ней.
Также дляcomputed
Он также используется внутрьeffect
, за исключением того, что его обратный вызов не будет вызванeffect
Выполняется сразу после, только при срабатыванииget
Обратный вызов будет выполнен, и зависимости будут собраны после поведения, например:
const value = reactive({ num: 0 })
const cValue = computed(() => value.num)
value.num = 1
Для приведенного выше кодаcomputed
Обратный вызов никогда не выполняется, только при использованииcValue.value
При выполнении обратного вызова следующая операция ничем не отличается от вышеописанной.
Наконец
Вышеизложенное является объяснением основного процесса ответа данных, Контента не так много, но после прочтения исходного кода это такой процесс.
Если вас интересует исходный код, объедините это со мнойскладДавайте проверим эту статью.
Чтение исходного кода — очень утомительный процесс, но и польза от него огромна. Если у вас возникнут какие-либо вопросы в процессе чтения, вы можете связаться со мной в области комментариев.
Кроме того, написание этой серии требует очень много времени. Вам необходимо поддерживать комментарии к коду. Вы должны написать статью как можно больше, чтобы читатели могли ее понять. Наконец, вы должны добавить рисунки. Если вы считаете, что статья выглядит ладно, пожалуйста, не скупитесь на свои.
Наконец, если вы также заинтересованы в исследовании исходного кода или у вас есть вопросы, вы можете присоединиться к группе для общения.