Реагировать на обучение - Реагировать Обсуждение

React.js

前端阅读室

система событий

React реализует слой SyntheticEvent (синтетическое событие) на основе Virtual DOM.Определяемый нами процессор будет получать экземпляр объекта SyntheticEvent, который полностью соответствует стандарту W3C и не будет иметь проблем с совместимостью с IE. И оно имеет тот же интерфейс, что и нативное событие браузера, а также поддерживает всплывающий механизм события.Мы можем использовать stopPropagation() и preventDefault(), чтобы прервать его. Если вам нужно получить доступ к собственному объекту события, вы можете использовать свойство nativeEvent.

Как связать синтетические события

События React привязываются аналогичным образом к собственным свойствам прослушивателя событий HTML.

<button onClick={this.handleClick}>Test</button>

Механизм реализации синтетических событий

Под капотом React есть две основные вещи, связанные с синтетическими событиями: делегирование событий и автоматическая привязка.

1. Делегация мероприятия

React не привязывает обработчики событий напрямую к реальным узлам, но привязывает все события к самому внешнему слою структуры, используя унифицированный прослушиватель событий, который поддерживает карту для сохранения всех событий. Прослушиватель событий и функция обработчика внутри компонента. Когда компонент монтируется или размонтируется, он просто вставляет или удаляет какие-то объекты в этот унифицированный прослушиватель событий, когда происходит событие, оно сначала обрабатывается этим унифицированным прослушивателем событий, а затем находит в карте настоящий обработчик событий и вызывает его. (Принцип реализации: привяжите самый внешний контейнер и полагайтесь на механизм всплытия событий для завершения делегирования.) Это упрощает механизм обработки и повторного использования событий, а также значительно повышает эффективность.

2. Автоматическая привязка

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

class App extends Component {
  constuctor() {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }
}

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

class App extends Component {
  handleClick= () => {}
}

Использование нативных событий в React

class NativeEventDemo extends Component {
  componentDidMount() {
    this.refs.button.addEventListener('click', this.handleClick)
  }
  componentWillUnmout() {
    this.refs.button.removeEventListener('click', this.handleClick)
  }
}

Сравнение синтетических событий React с нативными событиями JavaScript

1. Распространение событий и предотвращение распространения событий

Распространение собственных DOM-событий браузера можно разделить на три этапа: этап захвата событий, вызов обработчика событий самого целевого объекта и всплывающая подсказка событий. Обработчик событий захвата может быть зарегистрирован для элемента e, если для третьего параметра e.addEventListener установлено значение true. Захват событий недоступен ниже IE9. Захват событий не имеет большого значения в разработке приложений, React не реализует захват событий в синтетических событиях, а поддерживает только всплытие событий.

Чтобы предотвратить распространение собственного события, вам нужно использовать e.stopPropagation, но для браузеров, которые не поддерживают этот метод (ниже IE9), вы можете использовать только e.cancelBubble = true, чтобы предотвратить это. В синтетических событиях React просто используйте stopPropagation(). Поведение предотвращения всплытия событий React может использоваться только в системе синтетических событий React, и нет никакого способа предотвратить всплывание собственных событий. И наоборот, нативные события предотвращают всплытие, что предотвращает распространение синтетических событий React.

2. Тип события

Типы событий для синтетических событий React являются подмножеством собственных типов событий JavaScript. Он реализует только интерфейс событий DOM уровня 3 и унифицирует проблемы совместимости браузеров. Некоторые события не реализованы React или не могут быть реализованы из-за определенных ограничений, таких как событие изменения размера окна.

3. Метод привязки событий

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

<button onClick={this.handleClick}>Test</button>

4. Объект события

В системе синтетических событий React нет проблем с совместимостью, вы можете получить объект синтетического события.

форма

В React все находится в состоянии, включая данные формы, конечно. Далее поговорим о том, как React обрабатывает формы.

Применение компонентов формы

Все компоненты в html-форме реализованы в JSX React, но они несколько отличаются в использовании, некоторые из них имеют синтаксис JSX, а некоторые из-за некоторых различий в обработке состояния React.

1. Текстовое поле

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      inputValue: '',
      textareaValue: ''
    }
  }

  handleInputChange = (e) => {
    this.setState({
      inputValue: e.target.value
    });
  }

  handleTextareaChange = (e) => {
    this.setState({
      textareaValue: e.target.value
    })
  }

  render() {
    const { inputValue, textareaValue } = this.state;
    return (
      <div>
        <p>
          单行输入框:
          <input type="text" value={inputValue} onChange={this.handleInputChange}/>
        </p>
        <p>
          多行输入框:
          <textarea type="text" value={textareaValue} onChange={this.handleTextareaChange}/>
        </p>
      </div>
    )
  }

}

В HTML значение textarea представлено дочерними элементами, в то время как в реакции для представления значения формы используется свойство value.

2. Радиокнопки и флажки

В HTML переключатели представлены входным тегом типа radio, а флажки представлены входным тегом типа checkbox. Значение этих двух форм обычно не меняется, но используется логическое проверенное свойство, чтобы указать, находится ли оно в выбранном состоянии. Это то же самое в JSX, но есть некоторые различия в использовании.

Пример радиокнопки

import React, { Component } from 'react';

class App extends Component {
  construtor(props) {
    super(props);
    this.state = {
      radioValue: '',
    }
  }

  handleChange = (e) => {
    this.setState(
      radioValue: e.target.value
    )
  }

  render() {
    const { radioValue } = this.state;

    return (
      <div>
        <p>gender:</p>
        <label>
          male:
          <input 
            type="radio"
            value="male"
            checked={radioValue === 'male'}
            onChange={this.handleChange}
          />
        </label>
        <label>
          female:
          <input 
            type="radio"
            value="female"
            checked={radioValue === 'female'}
            onChange={this.handleChange}
          />
        </label>
      </div>
    )
  }
}

Пример кнопки проверки

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props)
    
    this.state = {
      coffee: []
    }
  }

  handleChange = (e) => {
    const { checked, value } = e.target;
    let { coffee } = this.state;

    if (checked && coffee.indexOf(value) === -1) {
      coffee.push(value)
    } else {
      coffee = coffee.filter(i => i !== value)
    }

    this.setState({
      coffee,
    })
  }

  render() {
    const { coffee } = this.state;
    return (
      <div>
        <p>请选择你最喜欢的咖啡</p>
        <label>
          <input 
            type="checkbox"
            value="Cappuccino"
            checked={coffee.indexOf('Cappuccino') !== -1}
            onChange={this.handleChange}
          />
          Cappuccino
        </label>
        <br />
        <label>
          <input 
            type="checkbox"
            value="CafeMocha"
            checked={coffee.indexOf('CafeMocha') !== -1}
            onChange={this.handleChange}
          />
          CafeMocha
        </label>
      </div>
    )
  }
}

3.Выберите компонент

В элементе select HTML есть два вида одиночного выбора и множественного выбора. В синтаксисе JSX вы также можете реализовать раскрывающийся список с множественным выбором, установив несколько={true} тега select.

class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      area: ''
    }
  }

  handleChange = (e) => {
    this.setState({
      area: e.target.value
    })
  }

  render() {
    const { area } = this.state;

    return (
      <select value={area} onChange={this.handleChange}>
        <option value='beijing'>北京</option>
        <option value='shangehai'>上海</option>
      </select>
    )
  }
}

Пример настройки элемента select multiple={true}

class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      area: ['beijing', 'shanghai']
    }
  }

  handleChange = (e) => {
    const { options } = e.target;
    const area = Object.keys(options)
      .filter(i => options[i].selected === true)
      .map(i => options[i].value);

    this.setState({
      area,
    })
  }

  render () {
    const { area } = this.state;

    return (
      <select multiple={true} value={area} onChange={this.handleChange}>
        <option value="北京">北京</option>
        <option value="上海">上海</option>
      </select>
    )
  }
}

Компонент option в HTML нуждается в атрибуте selected для представления выбранного элемента списка по умолчанию, а метод обработки React заключается в добавлении свойства value к компоненту select для представления выбранного параметра, что в определенной степени унифицирует интерфейс.

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

<select multiple={true} onChange={this.handleChange}>
  <option value="beijing" selected={area.indexOf('beijing') !== -1}>北京</option>
  <option value="shanghai" selected={area.indexOf('shanghai') !== -1}>上海</option>
</select>

Управляемые компоненты

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

неконтролируемые компоненты

Если компонент формы не имеет свойства value (или значения checked), его можно назвать неуправляемым компонентом. Соответственно, вы можете использовать реквизиты defaultValue и defaultChecked для представления состояния компонента по умолчанию.

class App extends Compoent {
  constructor(props) {
    super(props)

  }

  handleSubmit = (e) => {
    e.preventDefault();

    const { value } = this.refs.name;
    console.log(value)
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input ref="name" type="text" defaultValue="Hangzhou" />
        <button type="submit">submit</button>
      </form>
    )
  }
}

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

Сравнение контролируемых и неконтролируемых компонентов

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

1. Проблемы с производительностью

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

2. Нужна ли вам привязка событий?

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

Тем не менее, в React по-прежнему рекомендуется использование контролируемых компонентов, потому что это делает общее состояние приложения более контролируемым.

Несколько важных свойств компонентов формы

1. Атрибут статуса

Компонент формы React предоставляет несколько важных свойств для отображения состояния компонента. value: компонент ввода, компонент textarea и компонент выбора типа text используют свойство value для отображения состояния приложения. проверено: компонент типа радио или флажок отображает состояние приложения с помощью проверенной опоры со значением логического типа. selected: Это свойство может воздействовать на параметр под выбранным компонентом. React не рекомендует использовать этот метод. Рекомендуется использовать значение.

2. Свойства события

При изменении свойства состояния срабатывает свойство события onChange. На самом деле событие изменения в контролируемом компоненте больше похоже на событие ввода, представленное в HTML DOM. React поддерживает все события формы, определенные в DOM Level3.

обработка стиля

Основные настройки стиля

React может устанавливать классы для html, устанавливая свойство className, или встроенные стили для компонентов, устанавливая свойство стиля.

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

Мы можем установить имя класса html через библиотеку имен классов.

CSS Modules

Есть много решений модульности CSS, но есть два основных.

  1. Встроенный стиль. Это решение полностью отказывается от CSS и использует JavaScript или JSON для написания стилей, что может обеспечить CSS такой же мощной модульностью, как и JavaScript. Но недостаток также очевиден, он вряд ли может использовать преимущества самого CSS, такого как каскад, медиа-запрос и т. д. С псевдоклассами, такими как :hover и :active, сложнее иметь дело. Кроме того, это решение должно опираться на реализацию фреймворка, среди которых React связан с Radium, jsxstyle и react-style.
  2. CSS-модули. Все еще используя CSS, но используя JavaScript для управления зависимостями стилей. Модули CSS могут максимизировать сочетание существующей экосистемы CSS и возможностей модуляции JavaScript, а их API очень прост. Релиз по-прежнему компилируется в отдельные файлы JavaScript и CSS. Теперь webpack css-loader имеет встроенную функциональность модулей CSS.

1. Какие проблемы с модульностью CSS?

Модульность CSS важна для решения следующих двух проблем: импорта и экспорта стилей CSS. Гибкий импорт по требованию для повторного использования кода и скрытие внутренней области при экспорте, чтобы избежать глобального загрязнения. Sass, Less, PostCSS и т. д. пытаются решить проблему слабой способности программирования на CSS, но не решают проблему модульности. Проблемы, связанные с CSS, необходимые для фактической разработки React:

  1. Глобальное загрязнение: CSS использует механизм глобального селектора для установки стилей, преимущество в том, что стили удобно переписывать. Недостатком является то, что все стили действуют глобально, и стили могут быть перезаписаны по ошибке. Поэтому генерируются очень уродливые !important, даже встроенные !important и сложные таблицы подсчета веса селекторов, что увеличивает вероятность ошибки и стоимость использования. Shadow DOM в стандарте Web Component может полностью решить эту проблему, но он полностью локализует стиль, делая невозможным внешнее переписывание стиля и теряя гибкость.
  2. Путаница в именовании: из-за проблемы глобального загрязнения, чтобы избежать конфликтов стилей во время совместной разработки с несколькими людьми, селекторы становятся все более и более сложными, и легко формировать разные стили именования. именование будет более запутанным.
  3. Неполное управление зависимостями: компоненты должны быть независимы друг от друга, а при импорте компонента должны импортироваться только нужные ему стили CSS. Текущая практика заключается в том, чтобы вводить свой CSS в дополнение к JavaScript, и для Sass/Less сложно компилировать отдельный CSS для каждого компонента, а введение CSS для всех модулей расточительно. Модульность JavaScript очень зрелая, это хорошее решение, позволяющее JavaScript управлять зависимостями CSS, и css-loader webpack обеспечивает эту возможность.
  4. Невозможно совместно использовать переменные: сложные компоненты должны использовать JavaScript и CSS для совместной обработки стилей, в результате чего некоторые переменные являются избыточными в JavaScript и CSS, а предварительно скомпилированные языки не предоставляют такой возможности для совместного использования переменных в JavaScript и CSS.
  5. Сжатие кода не полное: сжатие очень длинных имен классов тут ни при чем.

2. Модульное решение CSS Modules

Модули CSS используют ICSS для решения двух проблем импорта и экспорта стилей, соответствующих двум новым псевдоклассам: import и :export соответственно.

:import("path/to/dep.css") {
  localAlias: keyFromDep;
}

:export {
  exportedKey: exportedValue;
}

Но использовать эти два ключевых слова напрямую в программе слишком громоздко, и они редко используются напрямую в проекте.Что нам нужно, так это возможность управлять CSS с помощью JavaScript. В сочетании с css-загрузчиком webpack вы можете определять стили в CSS и экспортировать их в файлы JavaScript.

Включить CSS-модули

css?modules&localIdentName=[name]__[local]-[hash:base64:5]

Добавьте модули для включения, где localIdentName — для установки правил именования стиля генерации.

Давайте посмотрим, как js представляет CSS:

/* button相关的所有样式 */
.normal {}
import styles from './Button.css'

buttonElm.outerHTML = `<button class=${styles.normal}>Submit</button>`

Окончательный сгенерированный HTML выглядит так

<button class="button--normal-abc5436">Processing...</button>

Это имя класса в основном уникально. Модули CSS обрабатывают имена классов в CSS и используют объекты для хранения соответствия между исходным классом и запутанным классом. С помощью этих простых процессов модули CSS достигают следующих целей:

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

локальный стиль по умолчанию

Использование модулей CSS эквивалентно добавлению :local к имени каждого класса для достижения локализации стиля. Если мы хотим переключиться в глобальный режим, мы можем использовать пакет :global

.normal {
  color: green;
}
/* 与上面等价 */
:local(.normal) {
  color: green;
}
/* 定义全局样式 */
:global(.btn) {
  color: red;
}
/* 定义多个全局样式 */
:global {
  .link {
    color: green;
  }
  .box {
    color: yellow;
  }
}

Используйте композицию для комбинирования стилей

Для повторного использования стилей CSS-модули предоставляют единственный способ работы с композициями.

/* components/Button.css */
.base { /* 所有通用的样式 */ }

.normal {
  composes: base;
  /* normal其他样式 */
}

Также с помощью композиций можно комбинировать стили из внешних файлов.

/* settings.css */
.primary-color {
  color: #f40;
}

/* component/Button.css */
.base { /* 所有通用样式 */ }

.primary {
  composes: base;
  composes: $primary-color from './settings.css'
}

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

умение называть классы

Соглашение об именах для модулей CSS расширено из БЭМ. БЭМ делит имена стилей на 3 уровня

  1. Блок: соответствует имени модуля, например Диалог
  2. Элемент: соответствует имени узла в модуле Confirm Button
  3. Модификатор: соответствует состояниям, связанным с узлом, таким как отключено и выделено. Например, dialog__confirm-button--highlight.

Реализовать совместное использование переменных CSS и JavaScript

Ключевое слово :export может экспортировать переменные в CSS в JavaScript.

$primary-color: #f40;

:export {
  primaryColor: $primary-color;
}
// app.js
import style from 'config.scss'

console.log(style.primaryColor);

Советы по использованию модулей CSS

Рекомендуется придерживаться следующих принципов

  1. Не используйте селекторы, просто используйте имена классов для определения стилей.
  2. Вместо объединения нескольких классов используйте только один класс для определения всех стилей.
  3. Все стили повторно используются в композициях
  4. не вложенный Общая проблема 1. Если вы используете класс с таким же именем в файле стиля? Хотя это может быть случайный код после компиляции, это все равно то же имя. 2. Что делать, если в файле стилей используются селекторы id, псевдоклассы и селекторы тегов? Эти селекторы не трансформируются и отображаются без изменений в скомпилированном CSS. То есть CSS Moudles будут преобразовывать только стили, связанные с именами классов.

Модули CSS сочетают в себе опыт исторических устаревших проектов.

1. Как переопределить локальные стили извне

Поскольку конечное имя класса непредсказуемо, стили не могут быть переопределены универсальными селекторами. Мы можем добавить атрибут data-role к ключевому узлу компонента, а затем переопределить стиль через селектор атрибутов.

// dialog.js
return (
  <div className={styles.root} data-role="dialog-root"></div>
);
// dialog.css
[data-role="dialog-root"] {
  // override style
}

2. Как сосуществовать с глобальными стилями

Изменить конфигурацию веб-пакета

module: {
  loaders: [{
    test: /\.scss$/,
    exclude: path.resolve(__dirname, 'src/views'),
    loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true',
  }, {
    test: /\.scss$/,
    include: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css!sass?sourceMap=true'
  }]
}
/* src/app.js */
import './styles/app.scss';
import Component from './view/Component'

/* src/views/Component.js */
import './Component.scss'

Модули CSS в сочетании с практиками React

import styles from './dialog.css';

class Dialog extends Component {
  render() {
    return (
      <div className={styles.root}></div>
    )
  }
}

Если вы не хотите часто вводить стили**, вы можете использовать react-css-modules

Связь между компонентами

Родительский компонент взаимодействует с дочерним компонентом

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

Дочерний компонент взаимодействует с родительским компонентом

Есть два способа: 1. Использовать функцию обратного вызова. 2. Использование пользовательского механизма событий. Этот метод является более общим, и добавление механизма событий при разработке компонента часто позволяет упростить API компонента.

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

Взаимодействие компонентов между уровнями

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

class ListItem extends Component {
  static contextTypes = {
    color: PropTypes.string,
  }

  render () {
    return (
      <li style={{ background: this.context.color }}></li>
    )
  }
}
class List extends Component {
  static childContextTypes = {
    color: PropTypes.string,
  }

  getChildContext() {
    return {
      color: 'red'
    }
  }
  render() {

  }
}

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

Взаимодействие компонентов без вложенных отношений

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

Нужно обратить внимание в процессе обработки событий: в событии componentDidMount, если компонент смонтирован, подписаться на событие, при размонтировании компонента отменить подписку на событие в событии componentWillUnmount.

Для сценариев, используемых React, EventEmitter нужен только синглтон

import { EventEmitter } from 'events';

export default new EventEmitter();
import emitter from './events';

emitter.emit('ItenChange', entry)
class App extends Component {
  componentDidMount() {
    this.itemChange = emitter.on('ItemChange', (data) => {
      console.log(data)
    })
  }
  componentWillUnmount() {
    emitter.removeListener(this.itemChange)
  }
}

Вообще говоря, в программе происходит многоуровневый перенос или межуровневый перенос, поэтому необходимо пересмотреть, есть ли более разумный способ. Шаблон Pub/Sub также может привести к путанице в логических связях.

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

абстракция между компонентами

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

Инкапсулировать методы миксина

const mixin = function(obj, mixins) {
  const newObj = obj;
  newObj.prototype = Object.create(obj.prototype);

  for (let prop in mixins) {
    if (mixins.hasOwnProperty(prop)) {
      newObj.prototype[prop] = mixins[prop]
    }
  }
}

const BigMixin = {
  fly: () => {
    console.log('fly');
  }
}

const Big = function() {
  console.log('new big');
}

consg FlyBig = mixin(Big, BigMixin)

const flyBig = new FlyBig();
flyBig.fly(); // => 'fly'

Для обобщенного метода миксина метод в объекте миксина монтируется к исходному объекту путем назначения для реализации микширования объекта.

Глядя на приведенную выше реализацию, вы можете подумать о расширении в библиотеке подчеркивания, или о методе assign в библиотеке lodash, или о методе Object.assign() в ES6. Объяснение MDN заключается в том, чтобы скопировать перечисляемые свойства, принадлежащие любому количеству исходных объектов, в целевой объект, а затем вернуть целевой объект.

Использование миксинов в React

React предоставляет свойства миксина при использовании createClass для сборки компонентов, таких как официальный упакованный PureRenderMixin.

import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';

React.createClass({
  mixins: [PureRenderMixin],
  render() {}
})

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

Миксин делает две вещи для компонентов:

  1. инструментальный метод. Если вы хотите совместно использовать некоторые методы служебного класса, вы можете определить их и использовать непосредственно в каждом компоненте.
  2. Наследование жизненного цикла, реквизиты и состояние объединяются, примеси также могут воздействовать на результат getInitialState для слияния состояния, и реквизиты также объединяются таким образом.

Классы ES6 и декораторы

Компоненты создаются как классы ES6, которые не поддерживают примеси. Синтаксический сахар декоратора может реализовывать примеси в классах.

Библиотека core-decorators предоставляет разработчикам несколько полезных декораторов, которые реализуют @mixin, который мы ищем.

import { getOwnPropertyDescriptors } from './private/utils';

const { defineProperty } = Object;

function handleClass(target, mixins) {
  if (!mixins.length) {
    // throw error;
  }

  for(let i = 0, l = mixins.length; i < l; i ++) {
    const descs = getOwnPropertyDescriptors(mixins[i])

    for (const key in descs) {
      if (!(key in target.prototype)) {
        defineProperty(target.prototype, key, descs[key])
      }
    }
  }
}

export default function mixin(...mixins) {
  if (typeof mixins[0] === 'function') {
    return handleClass(mixins[0], [])
  } else {
    return target => {
      return handleClass(target, mixins)
    }
  }
}

Принцип также очень прост: методы каждого примесного объекта накладываются на прототип целевого объекта для достижения цели примеси. Таким образом, вы можете использовать @mixin для объединения нескольких повторно используемых модулей.

const PureRender = {
  shouldComponentUpdate() {}
}

const Theme = {
  setTheme() {}
}

@mixin(PureRender, Theme)
class MyComponent extends Component {
  render() {}
}

Логика миксина очень похожа на простую логику самой ранней реализации, раньше свойство прототипа объекта присваивалось напрямую, но здесь используются два метода getOwnPropertyDescriptor и defineProperty, в чем разница?

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

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

Проблемы с миксинами

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

1. Инкапсуляция поврежденных оригинальных компонентов

Мы знаем, что метод примеси будет смешивать методы, чтобы добавить новые функции в исходные компоненты.Например, в примеси есть метод renderList, который дает нам возможность отображать список, но также может привносить новое состояние и реквизиты. что означает, что компонент имеет какое-то «невидимое» состояние, которое нам нужно поддерживать, но мы не знаем, когда мы его используем. Кроме того, метод в списке рендеринга будет вызывать метод в компоненте, но он, скорее всего, будет перехвачен другими миксинами, что вносит много непознаваемости.

2. Конфликты имен разных примесей

3. Добавьте сложности

Мы разрабатываем компонент и вводим миксин PopupMixin, который вводит в компонент метод жизненного цикла PopupMixin. Когда мы повторно представим HoverMixin, будет введено больше методов. Конечно, мы могли бы еще больше абстрагироваться от TooltipMixin и интегрировать их вместе, но мы обнаружили, что они оба имеют метод compomentDidUpdate. Через некоторое время вы обнаружите, что его логика стала слишком сложной для понимания.

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

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

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

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

Существует два способа реализации компонентов более высокого порядка:

  1. Прокси для реквизита: манипулируйте реквизитом через обернутые компоненты React.
  2. Обратное наследование: наследование от обернутого компонента React

1. Агент по недвижимости

const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      return <WrappedComponent {...this.props} />
    }
  }
}

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

@MyContainer
class MyComponent extends Component {
  render() {}
}

export default MyComponent

Приведенный выше процесс выполнения жизненного цикла похож на вызовы стека: didmount -> HOC didmount -> (HOCs didmount) -> (HOC будут размонтированы) -> HOC будут размонтированы -> размонтированы Функционально компоненты более высокого порядка также могут управлять такими компонентами, как миксины, включая управление свойствами, использование ссылок через ссылки, абстрагирование состояния и обертывание WrappedComponent другими элементами.

1. Контрольный реквизит

Мы можем читать, добавлять, редактировать или удалять реквизиты, переданные из WrappedComponent, но нужно быть осторожным при удалении и редактировании важных реквизитов. Где возможно, мы должны переименовывать реквизиты HOC, чтобы избежать путаницы.

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

const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      const newProps = {
        text: newText,
      };
      return <WrappedComponent {...this.props} {...newProps} />;
    }
  }
}

2. Использование ссылок через refs

const MyContainer = (WrappedComponent) => {
  class extends Component {
    proc(wrappedComponentInstance) {
      wrappedComponentInstance.method();
    }

    render() {
      const props = Object.assign({}, this.props, {
        ref: this.proc.bind(this),
      })
      return <WrappedComponent {...props} />
    }
  }
}

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

const MyContainer = (WrappedComponent) => {
  class extends Component {
    constructor(props) {
      super(props)
      this.state = {
        name: ''
      }

      this.onNameChange = this.onNameChange.bind(this)
    }

    onNameChange(event) {
      this.setState({
        name: event.target.value
      })
    }

    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange,
        }
      }
      return <WrappedComponent {...this.props} {...newProps}>
    }
  }
}

Это эффективно тесно те же состояние операции. 4. Другие элементы завернутые ворчаны Это либо для добавления стиля, вы также можете заказать макет

const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      return (
        <div style={{ display: 'block' }}>
          <WrappedComponent {...this.props} />
        </div>
      )
    }
  }
}

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

const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent{
    render() {
      return super.render()
    }
  }
}

Компонент, возвращаемый компонентом более высокого порядка, наследуется от WrappedComponent, поскольку он пассивно наследует WrappedComponent, все вызовы будут обратными, что является источником этого метода. Поскольку он основан на механизме наследования, порядок вызова HOC такой же, как и в очереди. didmount->HOC didmount->(HOCs didmount)->размонтирует->HOC размонтируется->(HOC размонтируется)

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

Он имеет две характеристики

1. Перехват рендеринга

Компоненты более высокого порядка могут управлять процессом рендеринга WrappedComponent. Может быть прочитан в выводе любого элемента React во время этого процесса; добавлено, изменено. Удалите реквизиты, или прочитайте или измените дерево элементов React, или условно отобразите дерево элементов, или оберните дерево элементов элементами управления стилем.

Если дерево элементов содержит компонент React типа function, вы не можете манипулировать дочерними компонентами компонента.

Пример условного рендеринга

const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent {
    render() {
      if (this.props.loggedIn) {
        return super.render();
      } else {
        return null;
      }
    }
  }
}

Изменить вывод рендера

const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent {
    render() {
      const elementsTree = super.render();
      let newProps;
      if (elementsTree && elementsTree.type === 'input') {
        newProps = { value: 'may the force be with you' }
      }
      const props = Object.assign({}, elementsTree.props, newProps);
      const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children);
      return newElementsTree;
    }
  }
}

2. Контролируйте состояние

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

const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent {
    render() {
      return (
        <div>
          <h2>HOC Debugger Component</h2>
          <p>Props</p><pre>{JSON.stringify(this.props, null, 2)}</pre>
          <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
          {super.render()}
        </div>
      )
    }
  }
}

В этом примере реквизиты и состояние WrappedComponent отображаются для отладки.

именование компонентов

Компоненты более высокого порядка потеряли свое исходное имя diplayName, мы должны назвать компоненты более высокого порядка

HOC.displayName = `HOC(${getDisplayName(WrappedComponent)})`

или

class HOC extends ... {
  static displayName = `HOC(${getDisplayName(WrappedComponent)})`
}
function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName ||
         WrappedComponent.name || 
         'Component'
}

Параметры компонента

function HOCFactoryFactory(...params) {
  return function HOCFactory(WrappedComponent) {
    return class HOC extends Component {
      render() {
        return <WrappedComponent {...this.props} />
      }
    }
  }
}

Практика разработки составных компонентов

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

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

1. Повторное разделение компонентов

Три более мелких компонента SelectInput, SearchInput и List могут быть объединены в многофункциональные компоненты, каждый из которых может быть чистым компонентом, похожим на марионетку.

2. Реферат логики

Та же логика взаимодействия и бизнес-логика в компонентах также должны быть абстрагированы.

// 完成SearchInput与List的交互
const searchDecorator = WrappedComponent => {
  class SearchDecorator extends Component {
    constructor(props) {
      super(props)

      this.handleSearch = this.handleSearch.bind(this)
    }

    handleSearch(keyword) {
      this.setState({
        data: this.props.data,
        keyword,
      })

      this.props.onSearch(keyword)
    }

    render() {
      const { data, keyword } = this.state;
      return (
        <WrappedComponent
          {...this.props}
          data={data}
          keyword={keyword}
          onSearch={this.handleSearch}
        />
      )
    }
  }

  return SearchDecorator;
}

// 完成List数据请求
const asyncSelectDecorator = WrappedComponent => {
  class AsyncSelectDecorator extends Component {
    componentDidMount() {
      const { url, params } = this.props;

      fetch(url, { params }).then(data => {
        this.setState({
          data
        })
      })
    }

    render() {
      return (
        <WrappedComponent
          {...this.props}
          data={this.state.data}
        />
      )
    }
  }

  return AsyncSelectDecorator;
}

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

const FinalSelector = compose(asyncSelectDecorator, searchDecorator, selectedItemDecorator)(Selector)

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

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

чистая функция

Чистые функции состоят из трех принципов

1. При одном и том же вводе он всегда возвращает один и тот же вывод.

2. Процесс не имеет побочных эффектов (мы не можем изменить внешнее состояние)

3. Никаких дополнительных зависимостей состояния

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

PureRender

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

1. Суть PureRender

В первые дни официальный представитель предоставил разработчикам плагин react-addons-pure-render-mixin, принцип которого заключается в повторной реализации метода жизненного цикла shouldComponentUpdate, чтобы текущие входящие реквизиты и состояние поверхностно сравнивались с предыдущими. Если он возвращает false, то компонент не будет выполнять метод рендеринга. (Если вы проводите углубленное сравнение, оно также очень требовательно к производительности)

В исходном коде PuerRender сделано только поверхностное сравнение старого и нового реквизита.Ниже приведен пример кода мелкогоEqual

function shallowEqual(obj, newObj) {
  if (obj === newObj) {
    return true;
  }

  const objKeys = Object.keys(obj);
  const newObjKeys = Object.keys(newObj);
  if (objKeys.length !== newObjKeysl.length) {
    return false;
  }

  return objKeys.every(key => {
    return newObj[key] === obj[key];
  })
}

3. Оптимизируйте PureRender

Если в свойствах или состоянии есть несколько типов случаев, то PureRender активируется в значение true, несмотря ни на что.

3.1 Установка объектов или массивов непосредственно для реквизита

Указанный адрес изменится

<Account style={{ color: 'black' }} />

избежать этой проблемы

const defaultStyle = {};
<Account style={{ this.props.style || defaultStyle }} />

3.2 Установите метод props и привяжите его к элементу через событие

class MyInput extends Component {
  constructor(props) {
    super(props)

    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(e) {
    this.props.update(e.target.value)
  }

  render() {
    return <input onChange={this.handleChange} />
  }

}

3.3 Настройка подкомпонентов

Для компонентов React с установленными дочерними компонентами при вызове shouldComponentUpdate возвращается значение true.

class NameItem extends Component {
  render() {
    return (
      <Item>
        <span>Arcthur</span>
      </Item>
    )
  }
}
<Item 
  children={React.createElement('span', {}, 'Arcthur')}
/>

Как избежать повторного покрытия рендеринга?PureRender выставляем в NameItem, то есть обращаемся к родителю для суждения.

Immutable

Immutable Data можно использовать непосредственно при передаче данных для дальнейшего повышения производительности рендеринга компонента.

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

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

1. НЕИЗМЕНИМЫЕ ДАННЫЕ После создания данные нельзя изменить, изменить неизменяемый объект, добавить или удалить действия, будет возвращен новый неизменяемый объект. Принцип реализации Immutable заключается в постоянной структуре данных, то есть использовать старые данные для создания новых данных, сохранять старые данные одновременно и без изменений, в то же время, во избежание глубокого копирования, все узлы были скопированы, а Immutable использует структуру Совместное использование, то есть, если узел в целевом дереве изменяется, модифицируется только этот узел и затрагиваемый им родительский узел, а другие узлы являются общими.

Преимущества неизменного

1. Уменьшите сложность, вызванную переменной.

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

function touchAndLog (touchFn) {
  let data = { key: '1' }
  touchFn(data);
  console.log(data.key)
}

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

2. Сохранить память

Общая структура, используемая Immutable, максимально повторно использует память. Объекты без ссылок удаляются сборщиком мусора.

import { map } from 'immutable';

let a = Map({
  select: '1',
  filter: Map({ name: '2' }),
});

let b = a.set('select', 'people');

a === b
a.get('filter') === b.get('filter')// true

3. Отмена/повтор, копирование/вставка и даже путешествие во времени — все это легко реализовать.

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

4. Параллельная безопасность

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

5. Используйте функциональное программирование

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

Недостатки использования Immutable

Легкая путаница с нативными объектами — самая большая проблема, возникающая в процессе использования Immutale. Некоторые методы приведены ниже

1. Используйте инструменты проверки статического типа FlowType или TypeScript

2. Соглашенные правила именования переменных, например объекты неизменяемого типа, начинаются с?

3. Используйте Immutable.fromJS вместо Immutable.Map или Immutable.List для создания объектов, что позволяет избежать смешивания между неизменяемыми объектами и собственными объектами.

Immutable.js

Два неизменяемых объекта можно сравнить с помощью ===, что является прямым сравнением адресов памяти и имеет наилучшую производительность. Но он вернет false, даже если значение двух объектов одинаково.

Immutable предоставляет Immutable.is для "сравнения значений". Immutable сравнивает hasCode или valueOf двух объектов. Поскольку Immutable использует внутреннюю структуру данных trie, до тех пор, пока hasCode двух объектов равен, значение будет одинаковым. Такой алгоритм позволяет избежать глубоких сравнений обхода и поэтому работает очень хорошо.

Неизменяемый и курсор

Курсор здесь и курсор в БД — совершенно разные понятия. Поскольку неизменяемые данные, как правило, очень глубоко вложены, чтобы облегчить доступ к глубоким данным, курсор предоставляет ссылку, которая может напрямую обращаться к этим глубоким данным:

let data = Immutable.fromJS({ a: { b: { c: 1 } } });
let cursor = Cursor.from(data, ['a', 'b'], newData => {
  // 当cursor或其子cursor执行更新时调用
  console.log(newData)
})

// 1
cursor.get('c');
cursor = cursor.update('c', x => x + 1)
// 2
cursor.get('c');

Неизменяемый и PureRender

Наиболее часто используемая оптимизация производительности React — shouldComponentUpdate, Использование глубокого копирования и глубокого сравнения — очень дорогой выбор. Использование Immutable.js, === и is — эффективный способ определить, изменились ли данные.

import { is } from 'immutable'

shouldComponentUpdate(nextProps, nextState) {
  const thisProps = this.props || {};
  const thisState = this.state || {};

  if (Object.keys(thisProps).length !== Object.keys(nextProps).length || Object.keys(thisState).length !== Object.keys(nextState).length)  {
    return true;
  }

  for (const key in nextProps) {
    if (nextProps.hasOwnProperty(key) && !is(thisProps[key], nextProps[key])) {
      return true;
    }
  }

  for (const key in nextState) {
    if (nextState.hasOwnProperty(key) && !is(thisState[key], nextState[key])) {
      return true;
    }
  }
}

Неизменяемый и setState

React рекомендует рассматривать this.state как неизменяемый, поэтому вам необходимо сделать глубокую копию перед изменением.

import _ from 'lodash';

class App extends Component {
  this.state = {
    data: { time: 0 }
  }

  handleAdd() {
    let data = _.cloneDeep(this.state.data);
    data.time = data.time + 1;
    this.setState({ data });
  }
}

После использования Immutable операция становится очень простой

import { Map } from 'immutable';

class App extends Component {
  this.state = {
    data: Map({ time: 0 })
  }

  handleAdd() {
    this.setState(({ data }) => {
      data: data.update('times', v => v + 1)
    })
  }
}

Immutable может значительно повысить производительность приложений.

key

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

Если есть два подкомпонента, которые необходимо отобразить, это можно решить, обернув плагин createFragment.

react-addons-perf

react-addons-perf использует API-интерфейсы Perf.start и Perf.stop, чтобы установить начальный и конечный статус для анализа.Он будет подсчитывать время каждого этапа рендеринга компонента, а затем распечатывать таблицу.

Ссылаться на

«Погрузитесь в стек React»

前端阅读室