Skip to content

Instantly share code, notes, and snippets.

@effrenus
Last active August 14, 2017 07:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save effrenus/7e1748a8e93f8185addcc815444de534 to your computer and use it in GitHub Desktop.
Save effrenus/7e1748a8e93f8185addcc815444de534 to your computer and use it in GitHub Desktop.
  • Error.captureStackTrace(obj, fun)
2017年08月12日
2017年08月11日
2017年08月09日
2017年08月08日
2017年08月07日
2017年08月05日
2017年08月03日
2017年08月02日
2017年08月01日

webpack 2, 3(?) по-умолчанию для web сборки применяет NodeSourcePlugin: если подключается серверный модуль и есть реализация для веба, то она добавляется в бандл. При этом размер сборки может существенно вырасти.

Для отключения добавлена опция в вебпак конфиге: node: false

Другое решение: перенести подключение серверных модулей в другой npm пакет и в package.json для браузера (browser) указать модуль с noop функцией.

  • Только один тред выполняет js и именно в нем работает event loop
  • libuv создает пул тредов (по дефолту их 4), но использует их только если ОС не предоставляет асинхронного I/O АПИ. В линукс это AIO
  • event loop состоит из набора фаз, в которых выполняются специфичные для них таски
  • В ноде нет нативной поддержки для мониторинга производительности event loop'а. Пример кастомных: частота тиком, длина тика, задержка в обработке setTimeout...

О работа event loop от разработчика libuv

Ссылки на статьи о новых возможностях в версии 16 и алгоритме работы нового реконсилера.

В версии 16 появилась документированная возможноть словить ошибки внутри дочеринх эелементов. За это отвечают компоненты (error boundaries) с новым методом componentDidCatch, отлавливаются исключения внутри render, lifecycle методом и конструкторе. При возниконвении ошибки, она передается в ближайший пэрент с методом componentDidCatch. Если ошибка не обработана, то компонент unmount. Рекомендуется использовать небольшое число error boundaries.

Не выбрали try/catch т.к. реакт проповедует декларативный подход, а данная конструкция больше соответствует императивному коду.

31日07月2017年
29日07月2017年

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

Недостатки асинхронных

  1. Вызов не возвращает фактического значения выполнения, оно передается в коллбэк
  2. Своя система обработки ошибок, не работает try/catch блок, а также невозможно использовать во многих конструкциях управления потоком.
  3. Оновная фишка в NodeJS

Частичное решение это promise, async/await. Однако promise лишь немного упрощает жизнь, все равно промис не является результатом выполнения асинхронной операции, для получения значения требуется передать коллбэк в .than(cb)

async/await

  • async функция вовращает промис
  • требуется await для асинхронных функций и объявление внешней через async

async/await - хорошо, но все равно при работе к high order функциями или при повторной испоьзовании цветовое деление проявляется.

Хорошее решение для асинхронных опереций в Go (goroutibe), Ruby, Lua (coroutibe)

24日07月2017年

Если кратко, то о универсальном решении с code-splitting для js, css с поддержкой SSR.

Digging code...

При разработке компонентов следует придерживаться принципа SRP (single respinsibility principle), тогда поддержка и тестирование значительно упростится.

Возможные техники:

  1. Если видите, что рендер слишком большой, следует подумать над вынесением части кода в отдельные методы, но лучше в отдельные компоненты (лучше, если это будут функциональные/стейтлесс)
  2. Другой подход: создание шаблона компонента, в котором есть слоты для дополнительных/внешних компонентов, которые передаются через пропсы. А в качестве детей - основной контент.
class CommentTemplate extends React.Component {
  static propTypes = {
    // Declare slots as type node
    metadata: PropTypes.node,
    actions: PropTypes.node,
  };
  
  render() {
    return (
      <div>
        <CommentHeading>
          <Avatar user={...}/>
          
          // Slot for metadata
          <span>{this.props.metadata}</span>
          
        </CommentHeading>
        <CommentBody/>
        <CommentFooter>
          <Timestamp time={...}/>
          
          // Slot for actions
          <span>{this.props.actions}</span>
          
        </CommentFooter>
      </div>
    );
  }
}
class Comment extends React.Component {
  render() {
    const metadata = this.props.publishTime ?
      <PublishTime time={this.props.publishTime} /> :
      <span>Saving...</span>;
    
    const actions = [];
    if (this.props.isSignedIn) {
      actions.push(<LikeAction />);
      actions.push(<ReplyAction />);
    }
    if (this.props.isAuthor) {
      actions.push(<DeleteAction />);
    }
    
    return <CommentTemplate metadata={metadata} actions={actions} />;
  }
}
  1. Применение High Order Components. Позволяет избавить компонент от лишней логики. Например, отправка статистики, т.к она не имеет отношения к визуальному представлению, то лучше вынести в HoC. см. также react-redux, styled-components, react-intl, recompose. HoC не меняет поведение основного компонента.
function withLinkAnalytics(mapPropsToData, WrappedComponent) {
  class LinkAnalyticsWrapper extends React.Component {
    componentDidMount() {
      ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
    }

    componentWillUnmount() {
      ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick);
    }

    onClick = (e) => {
      if (e.target.tagName === 'A') { // Naive check for <a> elements
        const data = mapPropsToData ? mapPropsToData(this.props) : {};
        sendAnalytics('link clicked', data);
      }
    };
    
    render() {
      // Simply render the WrappedComponent with all props
      return <WrappedComponent {...this.props} />;
    }
  }
  
  return LinkAnalyticsWrapper;
}

При работе со сторонними библиотеками реакт не знает о манипуляциях с дом объектами, поэтому важно избегать обновений в рендер функции компонента. Для этого стараются использовать заглушку без жочерник компонентов: <div />. В библеотеку при этом передается ссылка на дом, полученную из ref функции после componentDidMount: <div ref={el => this.el = el} />. Не стоит забывать про уничтожение this.$el.chosen('destroy') объекта и всех обработчиков при componentWillUnmount для избежания утечек памяти.

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

При работе с данными отличной от концепции redux, flux желательно использовать high order components, чтобы в дальнейшем проще было перейти к другой концепции.

Перед оптимизацией необходимо найти узкие мечта, для таких целей больше всего подходит React Perf addon. Команда Perf.printWasted() выводит данные о производительности.

Основная причина проблем - повторный рендер компонентов при неизменных свойствах и стейте. Для решения проблемы есть shouldComponentUpdate, если она возвращает false, то компонент не ре-рендерится. Чтобы каждый раз не писать проверку существует PureComponent для es6 классов; он проводит поверхостное сравнение свойств/стейта, если фиксирует изменения, то компонент обновляется.

Подводные камни

  • Если какой-то из объектов изменяется, а ссылка не меняется, то при проверке не будет выявлено изменение. Решения: создание нового при изменениях {...oldObject}, immutablejs, immutability-helper
  • Если отображение зависит от внешних источников данных, то при их изменении ре-рендер не произойдет. Решение: перенести данные в свойста/стейт
  • Если при формировании пропсов применяются функции возвращающие кадлый раз новый объект, то дочерний компонент впустую ре-рендрится, пример .filter Решение: кеширование значений (componentWillUpdate), либо reselect
  • Применение стрелочные функций и .bind в пропсах, при каждом ре-рендере создаются новый. Решение: вынести в отдельные методы
  • Избегайте forceUpdate(), т.к при этом не вызывается shouldComponentUpdate
  • Избегайте передачу пропсов через spread оператор, т.к явное лучше неявного. Такой способ лучше оставить для HoC

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

Если у вас большое приложение в котором часто происходит mount/unmount компонентов и важно выстрое отображение, то использвание функциональных компонентов можен существенно помочь.

const Avatar = props => <img src={props.url}

Разработчики реакта говорят, что в будущем возможны оптимизации при работе с такими компонентами: внутреннее представление, сокращение проверок. Пока (на 2017) внутри реакта используется обертка и выигрых не такой заметный. Если функциональные компоненты вызывать как функции, то это существенно ускоряет рендер:

-    <Avatar url={avatarUrl} />
+    {Avatar({ url: avatarUrl })}
-  React.createElement(Avatar, { url: avatarUrl }),
+  Avatar({ url: avatarUrl }),

Таким образом мы избегаем React.createElement и вызова lifecycle events компонента. Тесты из статьи показывают 45% прирост сокрости по сравнению с 6% для обычных функциональных компонентов.

В комметариях отметили, что есть плагин для бабеля, который выполняет, вроде как, подобную оптимизацию: transform-react-inline-elements

  • Реакт - "простой джаваскрипт", так считают многие, что во многом верно. Однако вью включает много магии внутри, что усложняет ментальное представление компонентов.
  • Компоненты в реакте можно представить как функцию, преобразующую данные в элементы. Во вью шаблоны скорее ссылаются на данные в сторе + включает свой DSL v-if, v-for, что несет доп. нагрузку
  • Инструменты. Т.к JSX является подмножеством джаваскрипт, то инструменты с ним работают корректно, но плохо работают с шаблонами во вью
  • Работа с данными. Реакт продвигает иммутабельные данные, вью (вьюх) имеет reactive state, где значения меняются напрямую и детектируются через геттеры/сеттеры.
22日07月2017年
  • Нет самого лучшего решения, каждая команда подстраивает бест практисес под себя
  • Для сборки предпочитает вебпак с вебпак-дев-сервером для доставки статики и сборки/обновения на лету. Хот-релоад не считает особо важным при разработке
  • Чтобы не задавать относительные пути вида import * from '../../../component', можно задать в вебпаке путь для поиска и resolve: { modules: ['src', 'node_modules']} и указывать при импорте только import * from 'component';
  • Файловая структура:
/
-- src (исходники проекта)
-- webpack (конфиги)
-- tests
-- scripts (вспомогательные скрипты для сборки)
  • Компоненты не разделять в папке по типу HOC/functional
  • Компоненты разделяются по категориям
  • Компонент экспортируется как дефолтный, если завязан на редакс то как именованный, а с редаксом дефолтный
export default ReactComponent;
export ReactComponent;
export default connect()(ReactComponent);

Правила для компонентов:

  • В названии нижний регистр, слова разделяются дефисом. Расширение файла .jsx
  • Избегать больших рендер функций. Если она большая, то выделить что-то в отдельные компоненты либо в отдельные функции
  • Лучше больше небольших компонентов, чем меньше, но крупных
  • Функции по работе с данными (бизнес логику) лучше вынести в отдельный файл в другой директории (например: services). Проще тестировать как компоненты, так и отдельные функции
  • Указывать список и тип свойста через prop-types

Redux

  • Автор предпочитает подход Ducks, когда редьюсеры и экшон криеторы в одном файле, уменьшает число файло в импорте

Тесты

  • Файл тестов находится рядом с тестируемым файлом, в названии добавляется суффикс spec. Плюсы: видно где не хатает тестов, упрощает импорт.
  • Тестовый фреймворк: jest. Удобный, быстрый.
  • Для интеграции с другой библиотекой (D3), либо другим не реакт компонентом
  • Для получения доступа к DOM API, например this.input.focus()
  • Запуск анимации у компонента

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

<input
  onChange={event => this.setState({ value: event.target.value })}
  type="text"
  value={this.state.value}
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment