Интенсивное чтение "Извлечение хуков - исходный код swr"

JavaScript React.js

1. Введение

Выборка — важная часть фронтенд-бизнеса, и она претерпела несколько изменений:

  • fetchСовместимость была достаточно хорошей, чтобы заменить включить$.postРазличные пакеты извлечения, в том числе.
  • Он изначально использовался в течение длительного времени, и было обнаружено, что расширение лучше, и схема изоморфной выборки, поддерживающая ssr, также очень хороша, напримерisomorphic-fetch,axios.
  • Для сценариев, управляемых данными, этого все еще недостаточно: поток данных постепенно инкапсулирует выборку данных, и в то же время осуществляется управление изменением состояния на основе данных.data isLoading errorупаковка.
  • Появление хуков делает компоненты более реактивными, и мы обнаружили, что выборка изящно возвращается к компонентам,swrЭто пример из учебника.

swrПредставлено 29.10.2019, всего за 12 дней было сохранено более 4000 звезд, в среднем 300+ звезд в день! Интенсивное чтение на этой неделе будет посвящено анализу функций и исходного кода этой библиотеки, а также пониманию того, почему, как и что в этой библиотеке извлечения React Hooks.

2 Обзор

Сначала введите функцию swr.

Чтобы отличаться от официальной документации, автор представляет ее в ознакомительном ключе, но все примеры взяты из официальной документации.

2.1 Зачем использовать хуки для получения чисел

Сначала ответьте на фундаментальный вопрос: зачем использовать хуки вместо выборки или потоковой выборки?

потому чтоХуки могут достигать жизненного цикла пользовательского интерфейса, а выборка — это, по сути, часть отображения пользовательского интерфейса или взаимодействия.Форма взятия чисел с помощью Hooks следующая:

import useSWR from "swr";

function Profile() {
  const { data, error } = useSWR("/api/user", fetcher);

  if (error) return <div>failed to load</div>;
  if (!data) return <div>loading...</div>;
  return <div>hello {data.name}!</div>;
}

Первое, что вы видите, это то, что асинхронная логика описана в синхронной записи, потому что рендеринг выполняется дважды.

useSWRПолучите три параметра, первый параметр - числоkey,этоkeyкак второй параметрfetcherПервый параметр передается, в обычных сценариях это URL-адрес, а третий параметр — элемент конфигурации.

Сила хуков заключается не только в этом, приведенные выше короткие строки кода также имеют следующие функции:

  1. Обновляется автоматически.
  2. Когда компонент уничтожается и повторно визуализируется, сначала включается локальный кеш.
  3. Положение полосы прокрутки может автоматически запоминаться при откате браузера на странице списка.
  4. Когда вкладки переключаются, сфокусированная вкладка будет повторно выбрана.

Конечно, автоматическое обновление или повторная загрузка — это не обязательно то, что нам нужно.swrРазрешить пользовательские конфигурации.

2.2 Конфигурация

упомянутый выше,useSWRСуществует также третий параметр в качестве элемента конфигурации.

Автономная конфигурация

Передайте третий параметр для каждогоuseSWRАвтономная конфигурация:

useSWR("/api/user", fetcher, { revalidateOnFocus: false });

Элементы конфигурации могут относиться кДокументация.

Можно настроить следующие параметры: режим приостановки, повторная выборка фокуса, интервал повторной выборки/открытия, необходимость повторной выборки при сбое, время ожидания, функция обратного вызова при успешной/неудачной выборке/повторной попытке и т. д.

Если второй параметр имеет тип объекта, то эффект является элементом конфигурации.Второй сборщик предоставлен только для удобства.Выборщик также может быть настроен в элементе конфигурации объекта.

Глобальная конфигурация

SWRConfigКонфигурация может быть изменена в пакетном режиме:

import useSWR, { SWRConfig } from "swr";

function Dashboard() {
  const { data: events } = useSWR("/api/events");
  // ...
}

function App() {
  return (
    <SWRConfig value={{ refreshInterval: 3000 }}>
      <Dashboard />
    </SWRConfig>
  );
}

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

Самый тяжелый элемент конфигурацииfetcher, который определяет способ получения числа.

2.3 Пользовательский метод получения

Пользовательская логика выборки фактически разделена на несколько абстрактных элементов, таких как настраиваемый URL-адрес выборки или настройка всей функции выборки, в то время какswrПринята относительно промежуточная степень детализации настройкиfetcher:

import fetch from "unfetch";

const fetcher = url => fetch(url).then(r => r.json());

function App() {
  const { data } = useSWR("/api/data", fetcher);
  // ...
}

такfetcherЭто точка расширения сама по себе, мы можем не только настроить функцию выборки номера, настроить логику бизнес-процессинга, но даже настроить протокол выборки номера:

import { request } from "graphql-request";

const API = "https://api.graph.cool/simple/v1/movies";
const fetcher = query => request(API, query);

function App() {
  const { data, error } = useSWR(
    `{
      Movie(title: "Inception") {
        releaseDate
        actors {
          name
        }
      }
    }`,
    fetcher
  );
  // ...
}

Это соответствует причине, по которой первый параметр называется Key, который является описанием синтаксиса в graphql.

На этом этапе мы можем настроить функцию выборки, но мы не можем контролировать, когда выполнять выборку, потому что метод записи хуков объединяет время выборки с временем рендеринга.swrМеханизм условного доступа может решить эту проблему.

2.4 Условный доступ

Так называемое условное число, т.useSWRКогда первый параметр равен нулю, выборка будет прекращена Мы можем использовать тернарный оператор или функцию в качестве первого параметра, чтобы сделать это условие динамическим:

// conditionally fetch
const { data } = useSWR(shouldFetch ? "/api/data" : null, fetcher);

// ...or return a falsy value
const { data } = useSWR(() => (shouldFetch ? "/api/data" : null), fetcher);

В приведенном выше примере, когдаshouldFetchПри значении false номер не будет получен.

Первый параметр выборки рекомендуется использовать в качестве функции обратного вызова, чтобыswrБудет поймать внутреннее исключение, например:

// ... or throw an error when user.id is not defined
const { data, error } = useSWR(() => "/api/data?uid=" + user.id, fetcher);

еслиuserобъект не существует,user.idпотерпит неудачу, и ошибка будет перехвачена и переданаerrorобъект.

Фактически,user.idЭто также сценарий зависимой выборки, когдаuser.idЕго необходимо повторно получить, когда есть изменение.

2.5 Зависимая выборка

Если одна выборка зависит от результата другой выборки, то новая выборка будет запущена только тогда, когда закончатся первые данные, чтоswrОсобого внимания на это обращать не нужно, просто напишите в порядке зависимостиuseSWRТолько что:

function MyProjects() {
  const { data: user } = useSWR("/api/user");
  const { data: projects } = useSWR(() => "/api/projects?uid=" + user.id);

  if (!projects) return "loading...";
  return "You have " + projects.length + " projects";
}

swrЗапросы без зависимостей будут максимально распараллелены, а выборки с зависимостями будут отправляться по одному в порядке зависимости.

Вполне возможно, что при ручном управлении выборкой, когда зависимости сложные, для обеспечения максимального параллелизма выборки часто необходимо тщательно настраивать рекурсивную структуру вложенности выборки.swrВ этой среде вам нужно только писать последовательно, что значительно повышает эффективность. Метод оптимизации подробно описан в разделе интерпретации исходного кода ниже.

Зависимая выборка — это сценарий, в котором выборка автоматически перезапускается.swrРучной запуск повторной загрузки также поддерживается.

2.6 Ручная выборка триггера

triggerИзвлечение может быть запущено вручную с помощью ключа:

import useSWR, { trigger } from "swr";

function App() {
  return (
    <div>
      <Profile />
      <button
        onClick={() => {
          // set the cookie as expired
          document.cookie =
            "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";

          // tell all SWRs with this key to revalidate
          trigger("/api/user");
        }}
      >
        Logout
      </button>
    </div>
  );
}

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

2.7 Оптимистичная выборка

Особенно в сценарии формы изменения данных предсказуемы. В настоящее время решение, управляемое данными, может только ждать, пока серверная часть вернет результат. Фактически, его можно оптимизировать для локального изменения данных, а затем обновить их после возвращается результат бэкенда:

import useSWR, { mutate } from "swr";

function Profile() {
  const { data } = useSWR("/api/user", fetcher);

  return (
    <div>
      <h1>My name is {data.name}.</h1>
      <button
        onClick={async () => {
          const newName = data.name.toUpperCase();
          // send a request to the API to update the data
          await requestUpdateUsername(newName);
          // update the local data immediately and revalidate (refetch)
          mutate("/api/user", { ...data, name: newName });
        }}
      >
        Uppercase my name!
      </button>
    </div>
  );
}

пройти черезmutateВы можете временно изменить ключ локально и вернуть результат, особенно в случае плохой сетевой среды, чтобы ускорить ответ. Оптимистичная выборка означает, что результат выборки является оптимистичным и предсказуемым, поэтому результат можно предсказать и изменить до того, как результат будет возвращен.

2.8 Режим ожидания

В режиме React Suspense все подмодули могут быть загружены лениво, включая код и запросы, которые можно ожидать, пока он включен.suspenseсвойства могут быть:

import { Suspense } from "react";
import useSWR from "swr";

function Profile() {
  const { data } = useSWR("/api/user", fetcher, { suspense: true });
  return <div>hello, {data.name}</div>;
}

function App() {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <Profile />
    </Suspense>
  );
}

2.9 Обработка ошибок

onErrorRetryОшибки могут обрабатываться единообразно, включая повторную выборку после возникновения ошибки и т. д.:

useSWR(key, fetcher, {
  onErrorRetry: (error, key, option, revalidate, { retryCount }) => {
    if (retryCount >= 10) return;
    if (error.status === 404) return;

    // retry after 5 seconds
    setTimeout(() => revalidate({ retryCount: retryCount + 1 }), 5000);
  }
});

  1. локальная мутация
  2. suspense mode
  3. обработка ошибок
// conditionally fetch
const { data } = useSWR(shouldFetch ? "/api/data" : null, fetcher);

// ...or return a falsy value
const { data } = useSWR(() => (shouldFetch ? "/api/data" : null), fetcher);

// ... or throw an error when user.id is not defined
const { data } = useSWR(() => "/api/data?uid=" + user.id, fetcher);

3 Интенсивное чтение

3.1 Глобальная конфигурация

В сценарии Hooks оберните слой настройкиContextГлобальная конфигурация может быть достигнута.

первыйSWRConfigПо сути обычайContext Provider:

const SWRConfig = SWRConfigContext.Provider;

существуетuseSWRОбъедините текущую конфигурацию с глобальной конфигурацией вuseContextПолучите глобальную конфигурацию:

config = Object.assign({}, defaultConfig, useContext(SWRConfigContext), config);

3.2 Некоторые детали использования SWR

Вы можете увидеть более подробную информацию из исходного кода,useSWRДействительно лучше, чем вызывать вручнуюfetchнамного лучше.

совместимость

useSWRкод тела вuseEffect, но чтобы ускорить время запроса, он помещается перед отрисовкой пользовательского интерфейса (useLayoutEffect) и совместим со сценариями на стороне сервера:

const useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect;

неблокирующий

Время запроса — это когда браузер бездействует, поэтому вызывается функция запроса.requestIdleCallbackпакет:

window["requestIdleCallback"](softRevalidate);

softRevalidateдедупликация включенаrevalidate:

const softRevalidate = () => revalidate({ dedupe: true });

То есть повторные выборки с одними и теми же параметрами в течение 2 секунд по умолчанию будут отменены.

оптимизация производительности

из-заswrизdata,isValidatingи т. д. состояние данных используетсяuseStateОтдельно управляются:

let [data, setData] = useState(
  (shouldReadCache ? cacheGet(key) : undefined) || config.initialData
);
// ...
let [isValidating, setIsValidating] = useState(false);

Когда состояние выборки изменяется, частоdataа такжеisValidatingЧтобы обновить вместе, чтобы вызвать только одно обновление, используйтеunstable_batchedUpdatesОбъедините обновления в одно:

unstable_batchedUpdates(() => {
  setIsValidating(false);
  // ...
  setData(newData);
});

На самом деле есть и другие решения, такие как использованиеuseReducerУправление данными также может обеспечить такой же эффект производительности.

3.3 Начальный кеш

При переключении страницы можно временно заменить результат выборки последними данными, то есть данные инициализации берутся из кеша:

const shouldReadCache = config.suspense || !useHydration();

// stale: get from cache
let [data, setData] = useState(
  (shouldReadCache ? cacheGet(key) : undefined) || config.initialData
);

Приведенный выше код находится вuseSWRВо время инициализацииuseHydrationУказывает, является ли это первой загрузкой:

let isHydration = true;

export default function useHydration(): boolean {
  useEffect(() => {
    setTimeout(() => {
      isHydration = false;
    }, 1);
  }, []);

  return isHydration;
}

3.4 Поддержка саспенса

Приостановка разделена на две функции: асинхронная загрузка кода и асинхронная загрузка данных, и теперь упоминается возможность асинхронной загрузки данных.

Приостановка требует, чтобы код был приостановлен, то есть генерировалось перехватываемое исключение Promise и отрисовывался компонент после завершения Promise.

Основным кодом является этот раздел, бросающий обещание числа:

throw CONCURRENT_PROMISES[key];

Подождите, пока номер не будет завершен, а затем вернитесьuseSWRСтруктура, определяемая API:

return {
  error: latestError,
  data: latestData,
  revalidate,
  isValidating
};

без вышеперечисленногоthrowшаг, компонент будет отображаться до завершения выборки, поэтомуthrowОбещание запроса делает эту функцию запроса способной к приостановке.

3.5 Зависимые запросы

Я просмотрел код и не нашел логики для специальной обработки циклических зависимостей.Позже, прочитав официальный документ, я вдруг понял, что он оказался черезtry/catch + onErrorRetryРеализация механизма зависит от выборки.

Посмотрите на следующий код:

const { data: user } = useSWR("/api/user");
const { data: projects } = useSWR(() => "/api/projects?uid=" + user.id);

Как добиться интеллектуального запроса в порядке зависимости? мы видимuseSWRОсновная логика функции выборки:

try {
  // 设置 isValidation 为 true
  // 取数、onSuccess 回调
  // 设置 isValidation 为 false
  // 设置缓存
  // unstable_batchedUpdates
} catch (err) {
  // 撤销取数、缓存等对象
  // 调用 onErrorRetry
}

Можно видеть, что логика выборкиtryживи, тогдаuser.idсуществуетuseSWR("/api/user")Если Готово нет, будет выброшено исключение, и оно автоматически войдетonErrorRetryЛогика, посмотрите в следующий раз, когда номер будет выбранuser.idГотового нет.

Так когда будет следующая очередь считать? Время:

const count = Math.min(opts.retryCount || 0, 8);
const timeout =
  ~~((Math.random() + 0.5) * (1 << count)) * config.errorRetryInterval;

Время повтора в основном растет экспоненциально на 2.

такswrСначала данные будут извлекаться параллельно, а зависимые выборки будут повторяться до тех пор, пока восходящий поток не будет готов. Этот простой режим несколько снижает производительность (не повторяя нисходящий поток вовремя после того, как восходящий поток готов), но это разумное решение, и максимизация параллелизма также делает большинство сцен более производительными, чем рукописные.

4 Резюме

Автор оставляет два вопроса для студентов, внимательно прочитавших эту статью:

  • Что вы думаете о хуках или потоке данных?
  • Есть ли лучший способ улучшить решение swr для зависимой выборки?

Адрес обсуждения:Интенсивное чтение "Извлечение хуков - исходный код swr" · Issue #216 · dt-fe/weekly

Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам. Интерфейс интенсивного чтения — поможет вам отфильтровать надежный контент.

Сфокусируйся наАккаунт WeChat для интенсивного чтения в интерфейсе

Заявление об авторских правах: Бесплатная перепечатка - некоммерческая - не производная - сохранить авторство (Лицензия Creative Commons 3.0)