Анализ принципов Vue 3: система обработки данных

внешний интерфейс JavaScript

Это моя первая статья, посвященная принципам 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При выполнении обратного вызова следующая операция ничем не отличается от вышеописанной.

Наконец

Вышеизложенное является объяснением основного процесса ответа данных, Контента не так много, но после прочтения исходного кода это такой процесс.

Если вас интересует исходный код, объедините это со мнойскладДавайте проверим эту статью.

Чтение исходного кода — очень утомительный процесс, но и польза от него огромна. Если у вас возникнут какие-либо вопросы в процессе чтения, вы можете связаться со мной в области комментариев.

Кроме того, написание этой серии требует очень много времени. Вам необходимо поддерживать комментарии к коду. Вы должны написать статью как можно больше, чтобы читатели могли ее понять. Наконец, вы должны добавить рисунки. Если вы считаете, что статья выглядит ладно, пожалуйста, не скупитесь на свои.

Наконец, если вы также заинтересованы в исследовании исходного кода или у вас есть вопросы, вы можете присоединиться к группе для общения.