«React Advanced» — «кровавый случай», вызванный обновлением React-Router.

внешний интерфейс JavaScript React.js
«React Advanced» — «кровавый случай», вызванный обновлением React-Router.

Введение

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

Знания, охватываемые в этом случае, следующие:

  • 1 Спецификации для установки зависимых пакетов в проект.
  • 2 Подписка потребления контекста.
  • 3 react-routerV5.2.0 версия меняется.
  • 4 местных и онлайн-расследования несчастных случаев.

Два проблемных фона

Далее я расскажу о конкретных проблемах.Недавно одноклассник (псевдоним Сяомин) столкнулся с проблемой в разработке, то есть с онлайн-аварией, вызванной использованием React-Router. Источником аварии является то, что он используется в глобальном компонентеReact-RouterКастомные хуки в - useHistory, подробности таковы.

import { useHistory } from 'react-router'
function Index(){
    /* 获取 histroy 对象 */
    const history = useHistory()
    console.log(history)
    return <div>
        {/* 展示 history 里面信息,期望当 history 中 location 信息变化的时候,组件也能更新 */}
    </div>
}

Для СяоминReact-RouterсерединаuseHistoryчтобы получить состояние в объекте истории. и ожидать:

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

Эта функция работает без проблем в проекте. Но недавно Сяо Мин разработал новую функцию, не имеющую ничего общего с текущим компонентом, и выложил ее в сеть.

В результате в сети произошла авария:При изменении маршрутаIndexКомпоненты больше не обновляются, как раньше.

Что еще более невероятно, так это то, что у Сяомина не будет проблем в местной среде. Так что эта проблема также появилась в Интернете. То есть эта проблема возникает только в Интернете.

Этот внезапный вопрос заставил Сяо Мина смутиться и запаниковать. Так что же вызывает это?

7.jpg

Три решения проблемы

Локальный и онлайн разные

Далее давайте поможем Сяомину решить эту проблему. Итак, сначала 🤔 подумайте об этом:Почему возникает несоответствие между локальным и онлайн?

Несоответствие между онлайн и локальным, то в этом случае первое, о чем следует подумать,Это линейно-зависимый пакет и локальная разница?. Затем проверка также очень проста, то есть нужно обновить все локальные пакеты, потому что пакеты, развернутые онлайн, обычноinstallновый пакет. Затем вы можете проверить это следующим образом:

  • скачать локальныйnode_modules;
  • переустановитьnpm install

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

По сути это связано с механизмом установки пакетов npm, то есть, например, если ваш проект зависит от x.x.x версии модуля a, то после того, как деплой выйдет в онлайн, в проекте должна быть установлена ​​x.x.x версия a? Ответ — нет, как работать с npm, будет рассказано позже. В приведенной выше ситуации сначала анализируется, что проблема возникает вReact-RouterБиблиотека, так что смотрите проект Xiao Mingpackage.json

"react-router": "^5.1.2",
  • Как вы можете видеть выше, проект Сяомин использовалreact-routerда5.1.2версия, то проблема с ^ .

механизм установки версии npm

^ вpackage.jsonчто это значит вpackage.jsonсередина^Будет соответствовать последней основной версии пакета зависимостей. Например:

  • Если версия зависимости написана так^1.2.3, что означает установить последнюю версию 1.х.х (не ниже 1.2.3, включая 1.3.0), но не ставить 2.х.х, то есть при установке основной номер версии не меняется.

Тогда Сяомин в проекте^5.1.2написано так, то если есть более поздняя версияreact-routerНапример5.2.x,5.3.x, то последний установочный пакет будет загружен до6.0.0пока (6.0.0 устанавливаться не будет).

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

Например^0.2.3 Тогда диапазон версий, представляющий установку, равен>=0.2.3 <0.3.0.

Соответствие версии зависимостей

символ пример Объем иллюстрировать
^ будет соответствовать последней основной версии пакета зависимостей ^1.2.3 >=1.2.3 Указывает, что установлена ​​последняя версия 1.х.х (не ниже 1.2.3, включая 1.3.0), но не установлена ​​2.х.х, то есть номер основной версии при установке не меняется.
~ Будет соответствовать самой близкой маленькой версии зависимого пакета ~1.2.3 >=1.2.3 <1.3.0 Представляют последнюю версию 1.2.x (не менее 1.2.3), но не устанавливается 1.3.x, не изменяет основной номер версии и устанавливается второстепенный номер версии.
>= >=2.1.0 >=2.1.0 Больше или равно 2.1.0
<= <=2.0.0 <=2.0.0 Меньше или равно 2.0.0
laster -- -- Установите последнюю версию
* -- -- любая версия
- 1.2.3 - 2.3.4 >=1.2.3 <=2.3.4 между двумя версиями

Итак, давайте вернемся к проблеме, с которой столкнулся Сяомин.Теперь, когда мы знаем, что причина в автоматическом обновлении, что, если мы решим эту проблему?

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

"react-router": "5.1.2",

Символ перед номером версии не добавляется, версия фиксирована5.1.2, наиболее фундаментальное и эффективное решение проблемы.

Очевидно это не лучший ответ, в первую очередь следует начать с сути проблемы, почемуreact-routerБольше невозможно подписаться на информацию о маршрутизации через историю использования. Так что же на самом деле изменилось? мы нашлиreact-routerV5.1.2исходный код,

export function useHistory() {
  return useContext(Context).history;
}
  • Как видно вышеuseHistoryПо сути, вызовuseContext, который использует всю библиотеку маршрутизацииContextизhistoryобъект.
  • Вся информация о состоянии маршрутизации хранится в Context.Каждый раз, когда маршрутизация изменяется, компонент маршрутизации уведомляется об отображении соответствующего представления посредством изменения Context. Для студентов, не знакомых с React Router, можно прочитать еще одну статью автора:"Анализ исходного кода" На этот раз я полностью понимаю принцип маршрутизации react-router

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

механизм потребления контекста

useHistory по существу использует useContext, который по существу подписывается на новую версию объекта React Context. Здесь необходимо ввести механизм обновления подписки React Context.

Новая версия объекта Context включает провайдеровProviderи подписчикиConsumer:

  • Provider: передать значение значения контекста.
  • Consumer : использовать значение, предоставленное поставщиком.
  • компонент классаcontextTypeи функциональные компонентыuseContextВы также можете подписаться на потребление значения контекста, и когда значение контекста изменится, они будут повторно отображены и не будут затронутыPureComponent,memo,shouldComponentUpdateВлияние стратегий оптимизации.

Вернемся к проблеме, с которой столкнулся Сяомин: до того, как Сяомин использовал useHistory для подписки на изменения маршрутизации, когдаОбновление маршрута, затем компоненты, использующие useHistory, будут повторно отображаться, потому что предыдущая логика заключается в том, что обновления маршрутизации будут обновлятьсяhistoryобъект . Смоделируем процесс.

const Context = React.createContext()

function useName (){
    return React.useContext(Context).name
}

const Child = ()=>{
    const name = useName()
    return <div>
        {name}
    </div>
}

const Index = memo(function(){
    return <div>
        <p>root 组件 </p>
        <Child/>
    </div>
})

export default function App(){
    const [ value , changeValue   ] = React.useState({ name:'列表' , path:'/list'  })
    return <div>
        <Context.Provider value={value} >
            <Index />
        </Context.Provider>
        <button onClick={()=> changeValue({ name:'首页',path:'/detail' })} >改变 value </button>
    </div>
}

Эффект:

10.gif

  • Переключение маршрутов эквивалентно вызовуchangeValue,измененныйProviderсерединаvalue.
  • Компонент, используемый Xiao Ming, — это Child , аuseHistoryпохожий наuseName.
  • Изменение значения при нажатии кнопки. Ребенок обновляет представление.

Ревизия реактивного маршрутизатора

Механизм обновления контекста подписки известен выше, так почему же текущийuseHistoryТогда новая версияreact-routerЧто изменилось? Позже я проверил журнал обновлений и обнаружил, что вreact-routerВ v5.2.0 был извлечен Контекст истории, и у него уже есть свой Контекст.

ЭтоReleasesЗаписывать:

9.jpg

Затем мы снова посмотрели на исходный код:


export function useHistory() {
  return useContext(HistoryContext);
}

export function useLocation() {
  return useContext(Context).location;
}

Из вышеизложенного вы можете увидеть:

  • useHistory больше не подписанContext, ноHistoryContext.
  • useLocation все еще подписывайтесьContext.
  • Когда мы меняем маршрут, существенно меняетсяContext, поэтому используйтеuseLocationКомпоненты будут обновляться с использованиемuseHistoryкомпоненты не будут обновляться.

В этот момент я вдруг понял, что правда наконец всплыла наружу.

8.jpg

Четыре резюме

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

  • Устранение сетевых и локальных несоответствий.
  • package.jsonПроблема с номером версии.
  • Принцип использования История.
  • Контекст подписывается на процесс обновления.

Я надеюсь, что студенты, которые чувствуют, что они что-то добились, могут лайкнуть + подписаться на автора и продолжать делиться хорошими фронтенд-статьями.

Справочные ресурсы