Реагировать на часто задаваемые вопросы интервью, чтобы разобраться, посмотреть, как ответить? (начальство)

React.js

Некоторое время назад я подготовился к интервью и многое обобщил.Ниже приведены некоторые из резюме, которые я обобщил при подготовке к React-интервью, объединив мой реальный опыт интервью и мои предыдущие статьи по анализу исходного кода.ReactЧастые вопросы на собеседовании.

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

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

Из-за большого количества тем она разделена на верхнюю и нижнюю части.В этой статье мы сначала обсудим следующие 19 тем:

  • ReactКаковы жизненные циклы и какие изменения произошли в жизненном цикле версии 16?

  • setStateЭто синхронно или асинхронно?

  • почему иногда несколько раз подрядsetStateРаботает только один раз?

  • ReactКак реализовать собственный механизм событий?

  • ЗачемReactсобытие, чтобы связать себяthis?

  • Родные события иReactразница событий?

  • ReactДля чего предназначено синтетическое событие?

  • Reactи каков порядок выполнения нативных событий? Можно ли смешать?

  • 虚拟Domчто это такое?

  • 虚拟DomСравнивать普通DomБыстрее?

  • 虚拟Domсередина?typeofКакова роль атрибутов?

  • ReactКаков процесс рендеринга компонента?

  • Почему это должно быть включено в кодReact?

  • ЗачемReactПервая буква компонента должна быть заглавной?

  • Reactрендеринг真实DomКакие оптимизации производительности были сделаны?

  • Что такое компонента более высокого порядка? Как добиться?

  • HOCКаковы практические сценарии применения в бизнес-сценариях?

  • Компоненты высшего порядка (HOC)а такжеMixinКаковы сходства и различия?

  • HookКаковы преимущества?

Каковы жизненные циклы React и какие изменения произошли в жизненном цикле 16-й версии?

15 Жизненный цикл

  • фаза инициализации

    • constructorКонструктор

    • getDefaultProps propsПо умолчанию

    • getInitialState stateПо умолчанию

  • горная сцена

    • componentWillMountВызывается перед инициализацией и визуализацией компонента

    • renderРендеринг компонентов

    • componentDidMountкомпонент монтируется наDOMзвонить после

  • фаза обновления

    • componentWillReceivePropsкомпонент получит новыеpropsпозвонить до

    • shouldComponentUpdateНужно ли обновлять компонент

    • componentWillUpdateВызывается перед обновлением компонента

    • renderРендеринг компонентов

    • componentDidUpdateВызывается после обновления компонента

  • этап удаления

    • componentWillUnmountВызывается перед выгрузкой компонента

16 Жизненный цикл

  • фаза инициализации

    • constructorКонструктор

    • getDefaultProps propsПо умолчанию

    • getInitialState stateПо умолчанию

  • горная сцена

    • staticgetDerivedStateFromProps(props,state)

    • render

    • componentDidMount

getDerivedStateFromProps: Компонент вызывается каждый разrerender, в том числе после сборки компонента (виртуальныйdomПосле этого фактическиdomперед монтажом), каждый раз новыйpropsилиstateпосле; каждый раз, когда получаются новые реквизиты, объект возвращается как новыйstate, возврат null означает, что обновление не требуетсяstate;сотрудничатьcomponentDidUpdate, может покрытьcomponentWillReceivePropsвсе виды использования

  • фаза обновления

    • staticgetDerivedStateFromProps(props,state)

    • shouldComponentUpdate

    • render

    • getSnapshotBeforeUpdate(prevProps,prevState)

    • componentDidUpdate

getSnapshotBeforeUpdate: Время срабатывания:updateкогда это произойдет, вrenderПосле этого в компонентеdomПеред рендерингом возвращает значение какcomponentDidUpdateТретий параметр ;fitcomponentDidUpdate, может покрытьcomponentWillUpdateвсе виды использования

  • этап удаления

    • componentWillUnmount

  • обработка ошибок

    • componentDidCatch

React16Новый жизненный цикл устарелcomponentWillMount、componentWillReceivePorps,componentWillUpdateнедавно добавленныйgetDerivedStateFromProps、getSnapshotBeforeUpdateдля замены трех устаревших функций ловушек.

React16Эти три функции ловушек не удаляются, но их нельзя смешивать с вновь добавленными функциями ловушек.React17Эти три хука-функции будут удалены, и будет добавлена ​​обработка ошибок (componentDidCatch)

Является ли setState синхронным или асинхронным?

  • Жизненный цикл и синтетические события

существуетReactв жизненном цикле и синтетических событиях,Reactвсе еще в своем механизме обновления, независимо от того, сколько раз он вызываетсяsetState, не будет выполнять обновление немедленно, а сохранит обновленное_pendingStateQueue, сохраните компонент, который нужно обновить, вdirtyComponent.

При выполнении последнего механизма обновления, на примере жизненного цикла, все компоненты, то есть компонент верхнего уровняdidmountзатем установит пакетный флаг наfalse. будет вывезенdirtyComponentкомпоненты в и_pendingStateQueueсерединаstateобновить. Это гарантирует, что компонент не будет повторно отображаться несколько раз.

  componentDidMount() {    this.setState({      index: this.state.index + 1    })    console.log('state', this.state.index);  }

Итак, как и в приведенном выше коде, когда мы выполняемsetStateполучить это немедленноstate, в настоящее время не могу получить обновленноеstate, потому что вReactВ пакетном механизме ,stateОн временно сохраняется, и после того, как механизм пакетной обработки завершится, он будет равномерно обновляться.

так.setStateне является асинхронным как таковым, ноReactПакетный механизм создает иллюзию асинхронности.

  • В асинхронном коде и нативных событиях

  componentDidMount() {    setTimeout(() => {      console.log('调用setState');      this.setState({        index: this.state.index + 1      })      console.log('state', this.state.index);    }, 0);  }

Как и в приведенном выше коде, когда мы вызываем асинхронный кодsetStateкогда, согласноJavaScriptАсинхронный механизм сначала временно сохранит асинхронный код и выполнит его после выполнения всего синхронного кода.ReactМеханизм дозирования завершен, флаг обработки установлен наfalse, затем позвонитеsetStateВы можете выполнить обновление немедленно и получить обновленный результат.

Вызывается в родном событииsetStateне уйдетReactМеханизм пакетной обработки, поэтому последние результаты можно получить сразу.

  • Лучшие практики

setStateВторой параметр принимает функцию, котораяReactвызывается после выполнения механизма пакетной обработки, поэтому вы хотите вызватьsetStateЧтобы получить обновленное значение сразу после этого, получите его в этой функции обратного вызова.

   this.setState({ index: this.state.index + 1 }, () => {      console.log(this.state.index);    })

Рекомендуемое чтение:Изучите механизм выполнения setState из практических задач.

Почему иногда setState несколько раз подряд вступает в силу только один раз?

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

  componentDidMount() {    this.setState({ index: this.state.index + 1 }, () => {      console.log(this.state.index);    })    this.setState({ index: this.state.index + 1 }, () => {      console.log(this.state.index);    })  }

Причина в том,Reactбудет группировать несколько сохраненных в механизмеsetStateЧтобы объединить, давайте посмотримReactв исходном коде_assignфункция, похожая наObjectизassign:

 _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);

Если объект передан, он, очевидно, будет объединен один раз, поэтому приведенный выше код выводит один и тот же результат дважды:

Object.assign(  nextState,  {index: state.index+ 1},  {index: state.index+ 1})

Уведомление,assignВ функции выполняется специальная обработка функции.Первым обрабатываемым параметром является функция, и обрабатываются параметры функции.preStateявляется результатом предыдущего слияния, поэтому результат вычисления точен:

  componentDidMount() {    this.setState((state, props) => ({        index: state.index + 1    }), () => {      console.log(this.state.index);    })    this.setState((state, props) => ({        index: state.index + 1    }), () => {      console.log(this.state.index);    })  }

Таким образом, приведенный выше код выводит два разных результата.

  • Лучшие практики

Reactдля нескольких последовательныхsetStateдля слияния, если вы хотите использовать последний раз сразуsetStateПосле результата приступайте к следующему разуsetState, может позволитьsetStateПринимает функцию вместо объекта. Эта функция использует предыдущуюstateВ качестве первого параметра значение, когда было применено это обновление.propsкак второй параметр.

Как React реализует собственный механизм событий?

ReactСобытие не привязано к реальностиDomНа узле, но через прокси событий, в самом внешнемdocumentЕдиное распределение событий.

Когда компоненты смонтированы и обновлены:

  • пройти черезlastProps,nextPropsЧтобы определить, следует ли добавлять или удалять события, вызовите методы регистрации и удаления событий соответственно.

  • передачаEventPluginHubизenqueuePutListenerдля хранения событий

  • Получатьdocumentобъект.

  • По названию события (например,onClick,onCaptureClick), чтобы определить, следует ли всплывать или захватывать.

  • определить, есть лиaddEventListenerметод, в противном случае используйтеattachEvent(совместимо с IE).

  • ДатьdocumentЗарегистрируйте обратный вызов собственного события какdispatchEvent(унифицированный механизм распределения событий).

Инициализация события:

  • EventPluginHubОтветственный за управлениеReactсинтетическое событиеcallback, Так и будетcallbackсохранить вlistenerBank, а также сохраняетPlugin.

  • Получить уникальный идентификатор элемента, привязанного к событиюkey.

  • БудуcallbackУникальная идентификация элемента на основе типа событияkeyсохранить вlistenerBankсередина.

  • listenerBankСтруктура:listenerBank[registrationName][key].

Когда событие срабатывает:

  • вызыватьdocumentРегистрация обратных вызовов для нативных событийdispatchEvent

  • Получить элемент, который запускает событие на самом глубоком уровне

  • Пройдите все родительские элементы этого элемента и обработайте каждый уровень элементов по очереди.

  • Создайте синтетическое событие.

  • Сохраните синтетические события на каждом уровне вeventQueueв очереди событий.

  • траверсeventQueue.

  • пройти черезisPropagationStoppedОпределяет, выполняет ли текущее событие метод предотвращения всплытия.

  • Если всплытие предотвращено, остановите обход, в противном случае пропуститеexecuteDispatchВыполнение синтетических событий.

  • Отпустите событие о завершении обработки.

ReactПереопределено в собственном синтетическом событииstopPropagationметод, будетisPropagationStoppedУстановить какtrue, а затем решить, продолжать ли выполнение в соответствии с этим обходом в процессе обхода каждого уровня событий. ЭтоReactСамореализованный барботажный механизм.

Рекомендуемое чтение:[Подробно о реакции] Механизм событий React

Почему событие React должно связывать это само?

В упомянутом выше потоке обработки событийReactсуществуетdocumentЕдиное распределение событий наdispatchEventМоделируйте всплытие и захват событий, вызывая события на всех уровнях в цикле.

существуетReactВ исходном коде, когда будет вызван конкретный обработчик события, он будет вызванinvokeGuardedCallbackметод.

function invokeGuardedCallback(name, func, a) {  try {    func(a);  } catch (x) {    if (caughtError === null) {      caughtError = x;    }  }}

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

Разница между собственными событиями и событиями React?

  • ReactСобытия названы верблюжьим регистром, а не строчными.

  • пройти черезJSXВы передаете функцию в качестве обработчика события, а не строка.

  • существуетReactв вы не можете вернутьсяfalseчтобы предотвратить поведение по умолчанию. нужно вызывать явноpreventDefault.

Что такое синтетические события React?

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

Обработчик события доставитSyntheticEvent, который представляет собой встроенную кросс-браузерную оболочку событий. Он имеет тот же интерфейс, что и собственные события браузера, в том числеstopPropagation()а такжеpreventDefault(), они работают одинаково во всех браузерах.

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

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

Каков порядок выполнения React и нативных событий? Можно ли смешать?

ReactВсе событияdocumentдля единой раздачи. когда правдаDomПосле запуска события оно всплывает доdocumentПослеReactсобытие обрабатывается.

Таким образом, собственное событие будет выполняться первым, а затем выполнятьсяReactСинтетические события, окончательное исполнение действительно вdocumentсобытия, установленные на

ReactЛучше не смешивать события и нативные события. Если выполняется в нативном событииstopPropagationметод, приведет к другимReactНедействительность события. потому что события для всех элементов не смогут всплывать доdocumentна, ведущий ко всемуReactСобытия не будут запущены. .

Что такое виртуальный дом?

в родномJavaScriptВ программе мы непосредственноDOMсоздавать и изменять, в то время какDOMЭлементы взаимодействуют с нашим приложением через события, которые мы слушаем.

а такжеReactсначала преобразует ваш код вJavaScriptобъект, то этоJavaScriptЗатем объекты превращаются в реальныеDOM. этоJavaScriptобъекты называются виртуальнымиDOM.

Когда нам нужно создать или обновить элементы,ReactСначала сделайте этоVitrualDomобъекты создаются и изменяются, а затемVitrualDomОбъекты визуализируются в реальный DOM.

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

Рекомендуемое чтение:[Углубленное реагирование] Углубленный анализ процесса рендеринга и характеристик виртуального DOM.

Действительно ли виртуальный дом быстрее, чем обычный дом?

Во многих статьях говоритсяVitrualDomЕго можно улучшить, что на самом деле очень налицо.

Прямое управлениеDOMБез сомнения, это очень требовательно к производительности. ноReactиспользоватьVitrualDomТакже не обойтись без операцииDOMиз.

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

VitrualDomПреимущество в том, чтоReactизDiffАлгоритмы и стратегии пакетной обработки,ReactПеред обновлением страницы способ обновления и рендеринга рассчитывается заранее.DOM. По сути, этим процессом расчета мы непосредственно оперируемDOMМожно также судить и осознавать это самим, но это обязательно отнимет много сил и времени, а то, что мы делаем сами, зачастую не так хорошо, какReactOK. Итак, в этом процессеReactПомогли нам "улучшить производительность".

Так что я больше склоняюсь к тому,VitrualDomПомогает нам повысить эффективность разработки, помогает нам рассчитать, как эффективнее обновлять при повторном рендеринге, а неDOMЭксплуатация быстрее.

Какова роль атрибута ?typeof в виртуальном Доме?

ReactElementесть один?typeofАтрибут, ему присвоеноREACT_ELEMENT_TYPE:

var REACT_ELEMENT_TYPE =  (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||  0xeac7;

видимый,?typeofЯвляетсяSymbolПеременная типа, которая предотвращаетXSS.

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

// JSONlet expectedTextButGotJSON = {  type: 'div',  props: {    dangerouslySetInnerHTML: {      __html: '/* put your exploit here */'    },  },};let message = { text: expectedTextButGotJSON };<p>  {message.text}</p>

JSONнельзя хранить вSymbolпеременная типа.

ReactElement.isValidElementфункция для определенияReactЯвляется ли компонент действительным, ниже приводится его конкретная реализация.

ReactElement.isValidElement = function (object) {  return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;};

видимыйReactбудет отображаться без?typeofИдентификация и компоненты, не прошедшие проверку правил, отфильтровываются.

когда ваша среда не поддерживаетSymbolчас,?typeofназначается как0xeac7, почему,ReactРазработчик дал ответ:

0xeac7немного похожеReact.

Каков процесс рендеринга компонентов React?


  • использоватьReact.createElementилиJSXзаписыватьReactкомпоненты, практически всеJSXВ конечном итоге код будет преобразован вReact.createElement(...),BabelПомогли нам в процессе этого преобразования.



  • createElementфункциональная параkeyа такжеrefи т. д. специальныеpropsобработать и получитьdefaultPropsпо умолчаниюpropsНазначьте назначения и обработайте входящие дочерние узлы и, наконец, создайтеReactElementобъект (так называемый виртуальныйDOM).



  • ReactDOM.renderбудет генерировать виртуальныйDOMРендеринг в указанный контейнер, который принимает пакетную обработку, транзакции и другие механизмы и оптимизирует производительность определенных браузеров и, наконец, преобразует его в реальныйDOM.


Почему React нужно внедрять в код?

JSXПросто дляReact.createElement(component,props,...children)Синтаксический сахар, обеспечиваемый данным способом.

всеJSXВ конечном итоге код будет преобразован вReact.createElement(...),BabelПомогли нам в процессе этого преобразования.

так используетсяJSXкод должен быть импортированReact.

Почему компоненты React должны писаться с большой буквы?

babelбудет оцениваться во время компиляцииJSXПервая буква компонента в, когда первая буква строчная, считается нативнойDOMЭтикетка,createElementПервая переменная компилируется в строку, когда первая буква заглавная, считается пользовательским компонентом,createElementПервая переменная компилируется как объект;

Какие оптимизации производительности делает React при рендеринге реального Dom?

существуетIE(8-11)а такжеEdgeВ браузере узел, вставленный в откровение, которое намного эффективнее, чем вставляя полное последовательное дерево узла.

Reactпройти черезlazyTree,существуетIE(8-11)а такжеEdgeВ других браузерах по очереди отображается один узел, а в других браузерах весьDOMСтруктура строится, а затем вставляется в контейнер целиком.

И, при рендеринге узлов по отдельности,Reactтакже считаетсяfragmentи другие специальные узлы, эти узлы не будут вставляться в рендеринг один за другим.

Что такое компонента более высокого порядка? Как добиться?

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

Компоненты высшего порядка (HOC)ДаReactРасширенные методы в , чтобы повторно использовать логику компонента. Но компоненты более высокого порядка сами по себе неReactAPI. Это всего лишь модель, котораяReactНеизбежно возникает его собственная композиционная природа.

function visible(WrappedComponent) {  return class extends Component {    render() {      const { visible, ...props } = this.props;      if (visible === false) return null;      return <WrappedComponent {...props} />;    }  }}

Приведенный выше код представляет собойHOCПростое приложение функции принимает компонент в качестве параметра и возвращает новый компонент, который может получитьvisible props,согласно сvisibleЗначение, определяющее, отображать ли видимый.

Мы можем реализовать компоненты более высокого порядка двумя способами:

Риелтером

Функция возвращает компонент, который мы определяем сами, а затем вrenderвозвращает компонент для переноса, чтобы мы могли проксировать все входящиеprops, и решить, как визуализировать. Фактически, компонент более высокого порядка, сгенерированный таким образом, является родительским компонентом исходного компонента. Вышеприведенная функцияvisibleтолько одинHOCКак реализован прокси свойства.

function proxyHOC(WrappedComponent) {  return class extends Component {    render() {      return <WrappedComponent {...this.props} />;    }  }}

Усовершенствованные элементы по сравнению с родными компонентами:

  • Действует на все входящиеprops

  • Жизненный цикл активного компонента

  • операционные компонентыstaticметод

  • Получатьrefs

обратное наследование

Возвращает компонент, который наследует исходный компонент, вrenderвызвать исходный компонентrender. Поскольку исходный компонент наследуется, к исходному компоненту можно получить доступ через этот生命周期、props、state、renderи т. д., он может манипулировать большим количеством свойств, чем прокси-сервер свойств.

function inheritHOC(WrappedComponent) {  return class extends WrappedComponent {    render() {      return super.render();    }  }}

Усовершенствованные элементы по сравнению с родными компонентами:

  • Действует на все входящиеprops

  • Жизненный цикл активного компонента

  • операционные компонентыstaticметод

  • Получатьrefs

  • оперативныйstate

  • Может отображать угон

Рекомендуемое чтение:[Углубленное реагирование] От Mixin до HOC и Hook

Каковы практические сценарии применения HOC в бизнес-сценариях?

HOCФункции, которые можно реализовать:

  • Рендеринг комбинации

  • условный рендеринг

  • действоватьprops

  • Получатьrefs

  • государственное управление

  • действоватьstate

  • рендеринг угона

HOCПрактические сценарии применения в бизнесе:

  • управление журналом

  • Контроль доступа

  • двусторонняя привязка

  • проверка формы

Для конкретной реализации, пожалуйста, обратитесь к моей статье: https://juejin.cn/post/6844903815762673671

Каковы сходства и различия между компонентами высшего порядка (HOC) и миксинами?

Mixinа такжеHOCможно использовать для решенияReactпроблема повторного использования кода.

Картинка взята из интернета

  • MixinМогут быть взаимозависимыми и связанными, что не способствует сопровождению кода.

  • разныеMixinметоды могут конфликтовать друг с другом

  • MixinОчень часто компонент ощутим и даже нуждается в обработке, что лавинообразно увеличивает сложность кода.

а такжеHOCВозникновение этих проблем можно решить:

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

  • Компоненты более высокого порядка также могут вызывать конфликты, но мы можем избежать такого поведения, придерживаясь соглашений.

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

В чем преимущества крючков?

  • Уменьшите риск повторного использования логики состояния

Hookа такжеMixinЕсть определенные сходства в использовании, ноMixinВведенные логика и состояние могут перекрывать друг друга, и несколькоHookОни не влияют друг на друга, что избавляет нас от необходимости сосредотачиваться на предотвращении конфликтов, избегающих логического повторного использования. использовать без соблюденияHOCТакже могут быть некоторые конфликты, такие какpropsПереопределить и т. д., используйтеHookЭтих проблем можно избежать.

  • Избегайте гнездования в аду

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

  • Сделайте компоненты более понятными

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

  • Используйте функции вместо классов

Вместо функции напишитеclassВозможно, вам потребуется освоить больше знаний, и тем больше моментов, на которые вам нужно обратить внимание, таких какthisУказывать, связывать события и т. д. Кроме того, компьютер понимаетclassБыстрее, чем понять функцию.Hooksтак что вы можетеclassesиспользовать большеReactновые особенности.

Следующее уведомление:

  • ReactDiffКакова стратегия алгоритма?

  • ReactсерединаkeyКакова роль?

  • ReactFiberчто это такое? Зачем вносить?

  • Зачем рекомендовать вcomponentDidMountИнициировать сетевой запрос?

  • ReactОптимизация кода?

  • ReactКаковы принципы проектирования компонентов?

  • ReduxКаков основной принцип?

  • чтоReduxпромежуточное ПО?

  • ReduxconnectСтратегия реализации функции?

  • MoxКаков основной принцип?

  • Reduxа такжеMobxСходства и различия, как выбрать?


перепечатыватьПосмотреть исходный текст