React16 общий анализ API и принципиальный анализ

React.js

React16 общий анализ API и принципиальный анализ

содержание

  1. Vueа такжеReactГрубое сравнение двух фреймворков
  2. react 16обычная версияapi
  3. reactЖизненный цикл
  4. reactмеханизм события
  5. react.ComponentКак реализовать компонентность и применение компонентов высшего порядка
  6. setStateАсинхронное управление данными очереди
  7. react FiberАнализ архитектуры
  8. react hooks
  9. domизdiffалгоритм
  10. snabbdomКак упростить исходный кодVirtual DOMиз
  11. reduxКак спроектировать архитектуру одностороннего потока данных

Vueа такжеReactГрубое сравнение двух фреймворков

Преимущества Vue включают в себя:

  1. Гибкий выбор шаблонов и функций рендеринга
  2. Простой синтаксис и создание проекта
  3. Более быстрый рендеринг и меньший размер

К сильным сторонам React относятся:

  1. Лучше для больших приложений и лучшей тестируемости
  2. Работает как с веб-приложениями, так и с нативными
  3. Больше поддержки и инструментов из более крупной экосистемы

сходство

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

  • Обе являются библиотеками JavaScript для создания пользовательского интерфейса;
  • Оба быстрые и легкие;
  • Оба имеют компонентную архитектуру;
  • Все используют виртуальный DOM;
  • Все они могут быть помещены в один файл HTML или стать модулями в более сложных настройках веб-пакета;
  • Существуют независимые, но широко используемые библиотеки управления маршрутизаторами и состояниями;
  • Самая большая разница между ними заключается в том, что Vue обычно использует файлы шаблонов HTML, а React — полностью JavaScript. Vue имеет синтаксический сахар двустороннего связывания.

разница

  • Компоненты Vue делятся на глобальную регистрацию и локальную регистрацию.В реакции соответствующие компоненты импортируются, а затем ссылаются на них в шаблоне;
  • Пропсы могут динамически изменяться, а подкомпоненты обновляться в реальном времени.В React официально рекомендуется, чтобы пропсы были как чистые функции, с согласованным вводом и выводом, и не рекомендуется менять представления через пропсы;
  • Дочерний компонент обычно вызывает опцию props явно, чтобы объявить данные, которые он ожидает получить. В реакции нет необходимости, в двух других есть механизм проверки реквизита;
  • Каждый экземпляр Vue реализует интерфейс событий для облегчения связи между родительскими и дочерними компонентами.В небольших проектах нет необходимости внедрять механизм управления состоянием, и react должен реализовать его сам;
  • Распространяйте содержимое с помощью слотов, что позволяет смешивать содержимое родительского компонента с собственным шаблоном дочернего компонента;
  • Существует больше систем инструкций, так что шаблон может выполнять более богатые функции, в то время как React может использовать только синтаксис JSX;
  • Vue добавляет вычисляемые и наблюдаемые синтаксические сахара, но в React для этого нужно написать набор логики;
  • Идея реакции вся в js, html генерируется через js, поэтому jsx проектируется, а js используется для работы css, styled-component, jss сообщества и т. д., а vue — это объединение html, css , js вместе, используйте Для соответствующих методов обработки Vue имеет однофайловый компонент, который может записывать html, css и js в один файл, а html предоставляет механизм шаблонов для его обработки.
  • React делает очень мало вещей, и многие из них оставлены сообществу.Многие вещи в Vue встроены, что действительно удобно писать.Например, combReducer из redux соответствует модулям vuex, а reselect соответствует геттер и vue компоненты vuex.Вычисляемая мутация vuex — это исходные данные, которые изменяются напрямую, а редуктор редукса должен возвращать новое состояние, поэтому редукс объединяет неизменяемые для оптимизации производительности, а vue в этом не нуждается.
  • Общая идея реакции функциональна, поэтому она уважает чистые компоненты, данные неизменяемы и односторонний поток данных.Конечно, это можно сделать и в двусторонних местах.Например, в сочетании с редукционной формой , горизонтальное разделение компонентов обычно выполняется через компоненты высокого порядка. А vue — это изменяемые данные, двусторонняя привязка, декларативное написание, а миксин во многих случаях используется для горизонтального разделения компонентов vue.

общественная деятельность

Из производительности двоих на github (данные взяты от 16 сентября 2019 г.)

react

react

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

По неполной статистике, в том числе Ele.me, Jianshu, AutoNavi, Rare Earth Nuggets, Suning.com, Meituan, Tmall, Lizhi FM, Fangduoduo, Laravel, htmlBurger и другие известные отечественные и зарубежные компании используют vue для разработки новых проекты и фронтенд-рефакторинг старых проектов.

Компании, использующие React facebook, Twitter, INS, Airbnb, Yahoo, ThoughtWorks, Ant Financial, Alibaba, Tencent, Baidu, Koubei, Meituan, Didi Chuxing, Ele.me, JD.com, NetEase и т. д.

экология пользовательского интерфейса

vue react
ПК сторона iview, элемент и т. д. Ant Design, Material-UI и т. д.
терминал h5 Как вант, минтуй и т.д. Муравей Дизайн Мобильный, Weui
Гибридная разработка weexui, буй-weex teaset, реагировать на родные элементы
Апплет WeChat iview, Weapp, zanui iView Weapp, пользовательский интерфейс Таро

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

react16обычная версияapi

Давайте посмотрим на API, выставленный реагированием

const React = {
  Children: {
    map,
    forEach,
    count,
    toArray,
    only
  },

  createRef,
  Component,
  PureComponent,

  createContext,
  forwardRef,

  Fragment: REACT_FRAGMENT_TYPE,
  StrictMode: REACT_STRICT_MODE_TYPE,
  unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
  unstable_Profiler: REACT_PROFILER_TYPE,

  createElement: __DEV__ ? createElementWithValidation : createElement,
  cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
  createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
  isValidElement: isValidElement,

  version: ReactVersion,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals
}

Children

Этот объект предоставляет набор методов, которые помогут вам работать с props.children, поскольку children — это структура данных, подобная массиву, но не массив, и если вы хотите иметь с ним дело, вы можете использовать методы подключаемого модуля React.Children.

createRef

Новое использование ref, от которого React собирается отказаться<div ref="myDiv" />Это использование строки ref, вы можете использовать ref только двумя способами в будущем.

class App extends React.Component {
  constructor() {
    this.ref = React.createRef()
  }

  render() {
    return <div ref={this.ref} />
    // or
    return <div ref={node => (this.funRef = node)} />
  }
}

createContext

createContextЭто официально доработанное контекстное решение. Старые контекстные API, которые мы использовали ранее, - это API, которые React не рекомендует. Теперь новые API выпущены, и официальный представитель подтвердил, что старые API будут удалены в 17-й версии ( старые API). производительность в целом неплохая).

Как использовать новый API:

const { Provider, Consumer } = React.createContext('defaultValue')

const ProviderComp = (props) => (
  <Provider value={'realValue'}>
    {props.children}
  </Provider>
)

const ConsumerComp = () => (
  <Consumer>
    {(value) => <p>{value}</p>}
  </Consumber>
)

жизненный цикл реакции

В настоящее время жизненный цикл React 16.8+ разделен на три этапа: этап монтирования, этап обновления и этап выгрузки.

  • Стадия монтирования:constructor(props): создать экземпляр.
    static getDerivedStateFromPropsотpropsполучено вstate.
    renderоказывать.
    componentDidMount: Завершите монтаж.
  • Этап обновления:static getDerivedStateFromPropsПолучить состояние из реквизита.
    shouldComponentUpdateОпределите, требуется ли перекраска.
    renderоказывать.
    getSnapshotBeforeUpdateПолучите снимок.
    componentDidUpdateОбратный вызов после завершения рендеринга.
  • Этап удаления:componentWillUnmountНасчет удаления.
  • Обработка ошибок:static getDerivedStateFromErrorполучить от ошибкиstate.
    componentDidCatchНайдите ошибку и обработайте ее.
class ExampleComponent extends react.Component {
  // 构造函数,最先被执行,我们通常在构造函数里初始化state对象或者给自定义方法绑定this
  constructor() {}
  //getDerivedStateFromProps(nextProps, prevState)用于替换 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被调用
  // 这是个静态方法,当我们接收到新的属性想去修改我们state,可以使用getDerivedStateFromProps
  static getDerivedStateFromProps(nextProps, prevState) {
    // 新的钩子 getDerivedStateFromProps() 更加纯粹, 它做的事情是将新传进来的属性和当前的状态值进行对比, 若不一致则更新当前的状态。
    if (nextProps.riderId !== prevState.riderId) {
      return {
        riderId: nextProps.riderId
      }
    }
    // 返回 null 则表示 state 不用作更新
    return null
  }
  // shouldComponentUpdate(nextProps, nextState),有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利用此生命周期来优化react程序性能
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.id !== this.props.id
  }
  // 组件挂载后调用
  // 可以在该函数中进行请求或者订阅
  componentDidMount() {}
  // getSnapshotBeforeUpdate(prevProps, prevState):这个方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null,此生命周期必须与componentDidUpdate搭配使用
  getSnapshotBeforeUpdate() {}
  // 组件即将销毁
  // 可以在此处移除订阅,定时器等等
  componentWillUnmount() {}
  // 组件销毁后调用
  componentDidUnMount() {}
  // componentDidUpdate(prevProps, prevState, snapshot):该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,如果触发某些回调函数时需要用到 DOM 元素的状态,则将对比或计算的过程迁移至 getSnapshotBeforeUpdate,然后在 componentDidUpdate 中统一触发回调或更新状态。
  componentDidUpdate() {}
  // 渲染组件函数
  render() {}
  // 以下函数不建议使用
  UNSAFE_componentWillMount() {}
  UNSAFE_componentWillUpdate(nextProps, nextState) {}
  UNSAFE_componentWillReceiveProps(nextProps) {}
}

В React версии 17 будут объявлены устаревшие несколько жизненных циклов API компонентов класса:componentWillMount,componentWillReceivePropsа такжеcomponentWillUpdate.

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

Просто поймите, как React обрабатывает события.React регистрирует события для документирования через addEventListener, когда компоненты загружаются (монтируются) и обновляются (обновляются), а затем будет пул событий, в котором хранятся все события, когда события запускаются, через dispatchEvent для распределения событий. .

ЦитироватьНепонятные моменты для начинающих, чтобы научиться реагировать (2)

  • Способ привязки событий в реакции похож на привязку событий в HTML, использование верблюжьего регистра для указания связываемого свойства onClick — это метод, определенный компонентом {this.handleClick.bind(this)}.
  • Поскольку метод класса не привязывает this по умолчанию, если вы забудете привязать его при вызове, значение this будет неопределенным. Обычно, если он не вызывается напрямую, он должен быть привязан к методу, а контекст функции события должен быть привязан к экземпляру компонента.

Четыре способа привязки событий

class Button extends react.Component {
  constructor(props) {
    super(props)
    this.handleClick1 = this.handleClick1.bind(this)
  }
  //方式1:在构造函数中使用bind绑定this,官方推荐的绑定方式,也是性能最好的方式
  handleClick1() {
    console.log('this is:', this)
  }
  //方式2:在调用的时候使用bind绑定this
  handleClick2() {
    console.log('this is:', this)
  }
  //方式3:在调用的时候使用箭头函数绑定this
  // 方式2和方式3会有性能影响并且当方法作为属性传递给子组件的时候会引起重渲问题
  handleClick3() {
    console.log('this is:', this)
  }
  //方式4:使用属性初始化器语法绑定this,需要babel转义
  handleClick4 = () => {
    console.log('this is:', this)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick1}>Click me</button>
        <button onClick={this.handleClick2.bind(this)}>Click me</button>
        <button onClick={() => this.handleClick3}>Click me</button>
        <button onClick={this.handleClick4}>Click me</button>
      </div>
    )
  }
}

Почему я получаю ошибку при прямом вызове метода

class Foo extends React.Component {
  handleClick() {
    this.setState({ xxx: aaa })
  }

  render() {
    return <button onClick={this.handleClick.bind(this)}>Click me</button>
  }
}

будет преобразован Babel в

React.createElement(
  'button',
  {
    onClick: this.handleClick
  },
  'Click me'
)

«Синтетические события» и «Нативные события»

react реализует слой "синтетических событий" (synthetic event system), что устранило проблемы совместимости событий в разных браузерах. Все события регистрируются на самом верхнем элементе — документе, а «синтетические события» делегируются событиями (event delegation) привязан к верхнему слою компонента, а когда компонент выгружен (unmount) автоматически уничтожает связанное событие.

разработка реактивного компонента

Идея компонентизации React

Полный шаблон для компонента пользовательского интерфейса
import classNames from 'classnames'
class Button extends react.Component {
  //参数传参与校验
  static propTypes = {
    type: PropTypes.oneOf(['success', 'normal']),
    onClick: PropTypes.func
  }
  static defaultProps = {
    type: 'normal'
  }
  handleClick() {}
  render() {
    let { className, type, children, ...other } = this.props
    const classes = classNames(
      className,
      'prefix-button',
      'prefix-button-' + type
    )
    return (
      <span className={classes} {...other} onClick={() => this.handleClick}>
        {children}
      </span>
    )
  }
}

Функциональный компонент

Чистый тип дисплея, не нужно поддерживать состояние и жизненный цикл, предпочтительно использоватьFunction Component

  1. Код стал более лаконичным, сразу видно, что это чисто дисплейный тип, без сложной бизнес-логики
  2. Лучшая возможность повторного использования. Пока реквизиты одной и той же структуры передаются, один и тот же интерфейс может отображаться без учета побочных эффектов.
  3. Небольшой размер упаковки и высокая эффективность исполнения
import react from 'react'
function MyComponent(props) {
  let { firstName, lastName } = props
  return (
    <div>
      <img src="avatar.png" className="profile" />
      <h3>{[firstName, lastName].join(' ')}</h3>
    </div>
  )
}

будет сбежит вавилон как

return React.createElement(
  'div',
  null,
  React.createElement('img', { src: 'avatar.png', className: 'profile' }),
  React.createElement('h3', null, [firstName, lastName].join(' '))
)

Так,React.createElementЧто ты делаешь? Взгляните на соответствующую часть кода:

var ReactElement = function(type, key, ref, self, source, owner, props) {
  var element = {
    // This tag allow us to uniquely identify this as a React Element
    ?typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner
  }
  // ...
  return element
}

ReactElement.createElement = function(type, config, children) {
  // ...
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props
  )
}

React.createElement() для создания элементов React. Он принимает три параметра, первый тип параметра может быть именем метки. Например, div, span или компонент React. Второй параметр props — это входящее свойство. Третий и последующие параметры потомки используются как дочерние компоненты компонента.

createElementФункция обрабатывает специальные реквизиты, такие как ключ и ссылка, и получаетdefaultPropsНазначьте значения свойствам по умолчанию, обработайте входящие дочерние узлы и, наконец, создайтеreactElementобъекты (так называемый виртуальный DOM).reactDOM.renderВизуализировать сгенерированный виртуальный DOM в указанный контейнер, который использует пакетную обработку, транзакции и другие механизмы, выполняет оптимизацию производительности для определенных браузеров и, наконец, преобразует его в настоящий DOM.

ES6 classОпределите чистый компонент (PureComponent)

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

class MyComponent extends react.Component {
  render() {
    let { name } = this.props
    return <h1>Hello, {name}</h1>
  }
}
PureComponent

Component & PureComponentЭти два класса в основном одинаковы, единственная разницаPureComponentНа прототипе есть дополнительный логотип,shallowEqual(поверхностное сравнение), чтобы решить, следует ли обновлять компонент, поверхностное сравнение похоже на поверхностное копирование, сравнивается только первый слой. использоватьPureComponentЭто равносильно пропуску записиshouldComponentUpdateфункция

if (ctor.prototype && ctor.prototype.isPureReactComponent) {
  return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
}

Это суждение, чтобы проверить, нужно ли обновлять компонент.ctor - это наследование, которое вы объявили.Component or PureComponentclass, он определит, наследуете ли вы отPureComponent,如果是的话就 shallowEqualСравните состояние и свойства.

Сравните один в ReactClassComponentНужно ли его обновлять, там всего два места. Сначала посмотрите, есть лиshouldComponentUpdateметод, второй здесьPureComponentсудить

Используйте неизменяемые структуры данныхImmutablejs

Immutable.jsЭто библиотека постоянных структур данных, разработанная Facebook в 2014 году. Постоянство означает, что после создания данных их нельзя изменить. Любая операция модификации, добавления или удаления вернет новуюImmutableобъект. Это может облегчить нам решение таких проблем, как кэширование, откат, обнаружение изменения данных и т. д., и упростить разработку. И предоставляет большое количество методов, похожих на нативный JS, иLazy Operationфункции, полнофункциональное программирование.

import { Map } from 'immutable'
const map1 = Map({ a: { aa: 1 }, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1 !== map2 // true
map1.get('b') // 2
map2.get('b') // 50
map1.get('a') === map2.get('a') // true

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

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

import { fromJS, isImmutable } from 'immutable'
const obj = fromJS({
  a: 'test',
  b: [1, 2, 4]
}) // 支持混合类型
isImmutable(obj) // true
obj.size() // 2
const obj1 = obj.toJS() // 转换成原生 `js` 类型

ImmutableJSДве самые большие особенности:immutable data structures(постоянная структура данных) сstructural sharing(Совместное использование структуры), постоянная структура данных гарантирует, что данные не могут быть изменены после создания.При использовании старых данных для создания новых данных старые данные не изменятся, и работа с новыми данными не повлияет на старые данные, такие как собственный js. Структурное совместное использование означает, что данные, которые не изменились, имеют общую ссылку, что не только снижает потребление производительности при глубоком копировании, но и уменьшает объем памяти.

Например, следующая картинка:

react-tree

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

Компоненты высшего порядка (higher order component)

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

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

Вышеприведенный код представляет собой простое приложение HOC.Функция получает компонент в качестве параметра и возвращает новый компонент.Новый компонент может получать видимые реквизиты и решать, отображать ли видимый в соответствии со значением visible. Наиболее распространенной является функция подключения Redux. Помимо простого совместного использования библиотек и простой композиции, лучший способ сделать HOC — разделить поведение между реагирующими компонентами. Если вы обнаружите, что пишете много кода в разных местах, чтобы делать одно и то же, вам следует подумать о рефакторинге кода в многоразовый HOC. Вот упрощенная реализация подключения:

export const connect = (
  mapStateToProps,
  mapDispatchToProps
) => WrappedComponent => {
  class Connect extends Component {
    static contextTypes = {
      store: PropTypes.object
    }

    constructor() {
      super()
      this.state = {
        allProps: {}
      }
    }

    componentWillMount() {
      const { store } = this.context
      this._updateProps()
      store.subscribe(() => this._updateProps())
    }

    _updateProps() {
      const { store } = this.context
      let stateProps = mapStateToProps
        ? mapStateToProps(store.getState(), this.props)
        : {}
      let dispatchProps = mapDispatchToProps
        ? mapDispatchToProps(store.dispatch, this.props)
        : {}
      this.setState({
        allProps: {
          ...stateProps,
          ...dispatchProps,
          ...this.props
        }
      })
    }

    render() {
      return <WrappedComponent {...this.state.allProps} />
    }
  }
  return Connect
}

Код очень понятен, функция подключения на самом деле делает одну вещь.mapStateToPropsа такжеmapDispatchToPropsОни деконструируются и передаются в исходный компонент, чтобы мы могли использовать их непосредственно в исходном компоненте.propsПолучатьstateтак же какdispatchфункция.

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

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

управление журналом
function logHoc(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      this.start = Date.now()
    }
    componentDidMount() {
      this.end = Date.now()
      console.log(
        `${WrappedComponent.dispalyName} 渲染时间:${this.end - this.start} ms`
      )
      console.log(`${user}进入${WrappedComponent.dispalyName}`)
    }
    componentWillUnmount() {
      console.log(`${user}退出${WrappedComponent.dispalyName}`)
    }
    render() {
      return <WrappedComponent {...this.props} />
    }
  }
}
в наличии, контроль доступа
function auth(WrappedComponent) {
  return class extends Component {
    render() {
      const { visible, auth, display = null, ...props } = this.props
      if (visible === false || (auth && authList.indexOf(auth) === -1)) {
        return display
      }
      return <WrappedComponent {...props} />
    }
  }
}
проверка формы

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

function validateHoc(WrappedComponent) {
  return class extends Component {
    constructor(props) {
      super(props)
      this.state = { error: '' }
    }
    onChange = event => {
      const { validator } = this.props
      if (validator && typeof validator.func === 'function') {
        if (!validator.func(event.target.value)) {
          this.setState({ error: validator.msg })
        } else {
          this.setState({ error: '' })
        }
      }
    }
    render() {
      return (
        <div>
          <WrappedComponent onChange={this.onChange} {...this.props} />
          <div>{this.state.error || ''}</div>
        </div>
      )
    }
  }
}
const validatorName = {
  func: (val) => val && !isNaN(val),
  msg: '请输入数字'
}
const validatorPwd = {
  func: (val) => val && val.length > 6,
  msg: '密码必须大于6位'
}
<HOCInput validator={validatorName} v_model="name"></HOCInput>
<HOCInput validator={validatorPwd} v_model="pwd"></HOCInput>

Недостатки HOC

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

render props

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

<DataProvider render={data => <h1>Hello {data.target}</h1>} />

setStateуправление данными

**НЕ ОБНОВЛЯЙТЕ СТАТУС НАПРЯМУЮ**
// Wrong 此代码不会重新渲染组件,构造函数是唯一能够初始化 this.state 的地方。
this.state.comment = 'Hello'
// Correct 应当使用 setState():
this.setState({ comment: 'Hello' })
В жизненном цикле компонента или привязке события реакции setState обновляется асинхронно, и вызов setState в отложенном обратном вызове или обратном вызове привязки собственного события не обязательно является асинхронным.
  • Несколько вызовов setState() объединяются в один вызов для повышения производительности.
  • this.props и this.state могут обновляться асинхронно и не должны полагаться на их значения для расчета следующего состояния.
// Wrong
this.setState({
  counter: this.state.counter + this.props.increment
})
// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}))
Собственная привязка событий не будет обрабатываться синтетическими событиями, но войдет в поток обработки транзакций обновления. То же самое верно и для `setTimeout`. Когда обратный вызов `setTimeout` выполняется, исходный процесс обновления компонента завершен, и он не будет помещен в `dirtyComponent` для асинхронного обновления, и результат, естественно, будет синхронным.

setStateпринцип

setState не выполняет рендеринг напрямую, а выполняетupdateQueue(очередь асинхронного обновления),

setState( stateChange ) {
    Object.assign( this.state, stateChange );
    //合并接收到的state||stateChange改变的state(setState接收到的参数)
    renderComponent( this );//调用render渲染组件
}

В этой реализации каждый вызовsetStateбудет обновлять состояние и отображать его один раз (не в соответствии с его механизмом оптимизации обновления), поэтому мы должны объединитьsetState.

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

reactРеализация сделки в

быть улучшенным Это выглядит немного запутаннымАнализ исходного кода React (3): подробная транзакция и очередь обновлений Транзакции в React Механизм транзакций React

транзакция транзакция

ErrorBoundary,Suspenseа такжеFragment

Error Boundaries

react 16 предоставляет новый хук для перехвата ошибокcomponentDidCatch(error, errorInfo), который может фиксировать ошибки, возникающие в жизненном цикле дочерних компонентов, и предотвращать глобальный сбой страницы. демоcomponentDidCatchне ловит следующие ошибки

  • Ошибки, выдаваемые механизмом событий (ошибки в событиях не влияют на отрисовку)
  • Ошибки, выдаваемые самими границами ошибок
  • Асинхронно генерируемые ошибки
  • рендеринг на стороне сервера

lazy、SuspenceЛенивая загрузка компонентов

lazyнужно следоватьSuspenceИспользуйте вместе, иначе будет сообщено об ошибке.

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

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

import react, { lazy, Suspense } from 'react'
const OtherComponent = lazy(() => import('./OtherComponent'))
function MyComponent() {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <OtherComponent />
    </Suspense>
  )
}

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

const OtherComponentPromise = import('./OtherComponent')
const OtherComponent = react.lazy(() => OtherComponentPromise)

Fragments(v16.2.0)

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

render() {
  return (
    <>
      <ChildA />
      <ChildB />
      <ChildC />
    </>
  );
}

react FiberАнализ архитектуры

react-fiberЭто сделано для повышения применимости анимации, макета и мобильных жестов. Наиболее важной функцией является оптимизация рендеринга страницы: это позволяет разделить работу рендеринга на несколько сегментов.

react Fiberкакую проблему решает архитектура

react-fiberОн может предоставить нам следующие функции:

  • Установить приоритет задач рендеринга
  • Использование нового алгоритма Diff
  • Использование дизайна виртуального стека позволяет переключаться между задачами рендеринга с более высоким приоритетом и задачами с более низким приоритетом.

FiberКак сделать асинхронный рендерингVirtual Domа такжеDiffалгоритм

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

function fiber(剩余时间) {
  if (剩余时间 > 任务所需时间) {
    做任务
  } else {
    requestIdleCallback(fiber)
    // requestIdleCallback 是浏览器提供的一个 api,可以让浏览器在空闲的时候执行回调,
    // 在回调参数中可以获取到当前帧剩余的时间,fiber 利用了这个参数,
    // 判断当前剩下的时间是否足够继续执行任务,
    // 如果足够则继续执行,否则暂停任务,
    // 并调用 requestIdleCallback 通知浏览器空闲的时候继续执行当前的任务
  }
}

react hooks

До версии 16.7 реакция имела две формы компонентов: компоненты с состоянием (классы) и компоненты без состояния (функции). Официальное объяснение: Hook — это новая функция React 16.8. Он позволяет использовать состояние и другие функции React без написания классов. Личное понимание: пусть традиционный функциональный компонент функциональный компонент имеет функциональную функцию внутреннего состояния состояния, короче говоря, крючки заставляют функциональный компонент иметь состояние, которое может полностью заменить класс.

Затем разберитесь с двумя основными API в Hooks,useStateа такжеuseEffect

useState

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

const [count, setCount] = useState(initialState)

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

function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

useEffect(fn)

Этот хук выполняется после каждого рендера. рассматривать это какcomponentDidMount,componentDidUpdate``、componentWillUnmountколлекция. Так что используйтеuseEffectПреимущества перед предыдущими:

можно избежать вcomponentDidMount,componentDidUpdateписать повторяющийся код; Ассоциативную логику можно записать вuseEffect(Раньше его нужно было записывать в разные жизненные циклы);

Глубокое понимание принципов реагирования

Разбор принципа реагирующего виртуального дома

Процесс рендеринга компонентов реакции

использоватьreact.createElementИли напишите реагирующие компоненты в JSX, фактически весь код JSX в конечном итоге будет преобразован вreact.createElement(...), Babel помог нам с этим процессом преобразования.

Функция createElement обрабатывает специальные реквизиты, такие как ключ и ссылка, и получаетdefaultPropsНазначьте значения свойствам по умолчанию, обработайте входящие дочерние узлы и, наконец, создайтеreactElementобъекты (так называемый виртуальный DOM).

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

Состав виртуального DOM

которыйreactElementelementОбъекты, наши компоненты в конечном итоге будут отображаться в следующую структуру:

`type`:元素的类型,可以是原生 html 类型(字符串),或者自定义组件(函数或 class)
`key`:组件的唯一标识,用于 Diff 算法,下面会详细介绍
`ref`:用于访问原生 dom 节点
`props`:传入组件的 props,chidren 是 props 中的一个属性,它存储了当前组件的孩子节点,可以是数组(多个孩子节点)或对象(只有一个孩子节点)
`owner`:当前正在构建的 Component 所属的 Component
`self`:(非生产环境)指定当前位于哪个组件实例
`_source`:(非生产环境)指定调试代码来自的文件(fileName)和代码行数(lineNumber)

Когда состояние компонента изменяется, реакция автоматически вызывает метод рендеринга компонента для повторного рендеринга всего пользовательского интерфейса компонента. Конечно, если вы действительно работаете с DOM в такой большой области, производительность будет большой проблемой, поэтому react реализуетVirtual DOM, структура DOM компонента сопоставляется с этимVirtual DOMна, реагировать в этомVirtual DOMАлгоритм diff реализован на основе вышеизложенного.Когда компонент должен быть перерендерен, DOM-узел, который нужно изменить, будет найден через diff, а затем модификация будет обновлена ​​​​до фактического DOM-узла браузера, поэтому он на самом деле не отображает все дерево DOM. этоVirtual DOMЭто чистая структура данных JS, поэтому производительность будет намного выше, чем у нативной DOM.

reactкак предотвратитьXSSиз

reactElementобъект имеет другой?typeofсвойство, которое является переменной типа SymbolSymbol.for('react.element'), когда среда не поддерживает Symbol ,?typeofназначается как0xeac7. Эта переменная предотвращает XSS. Если на вашем сервере есть уязвимость, которая позволяет пользователям хранить произвольные объекты JSON, в то время как код на стороне клиента ожидает строку, это может представлять риск для вашего приложения. нельзя хранить в JSONSymbolпеременная типа и реагирует на нее без\$\$typeofИдентифицированные компоненты отфильтровываются.

diffалгоритм

традиционныйdiffАлгоритм сравнивает узлы один раз через рекурсию, и эффективность очень низкая.Сложность алгоритма достигает O(n^3), где n — общее количество узлов в дереве.React формулирует смелую стратегию по снижению сложности O (n^3) Задача трансформируется в задачу сложности O(n).

diffСтратегия:

  1. В веб-интерфейсе есть несколько операций межуровневого перемещения узлов Dom,diffКогда алгоритм сравнивает старые и новые узлы, сравнение будет выполняться только на одном уровне, а не между уровнями.
  2. Два компонента с одним и тем же классом будут генерировать похожие древовидные структуры, а два компонента с разными классами будут генерировать разные древовидные структуры.
  3. Для группы дочерних узлов одного уровня их можно отличить по уникальному ключу.

Основываясь на трех вышеупомянутых стратегиях, Reacttree diff,component diffтак же какelement diffДля оптимизации алгоритма факты также доказывают, что эти три исходные стратегии разумны и точны, что гарантирует производительность всей конструкции интерфейса. Проще говоря:

Для получения подробной информации см.Серия анализа исходного кода React - невероятная реакция diff

  • React преобразует проблемы сложности O (n3) в проблемы сложности O (n), формулируя смелую стратегию сравнения;
  • React использует стратегию иерархической дифференциации,tree diffОптимизация алгоритма;
  • React генерирует похожие древовидные структуры через один и тот же класс, а разные классы генерируют разные древовидные структуры.component diffОптимизация алгоритма;
  • React отвечает стратегии установки уникального ключаelement diffОптимизация алгоритма;

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

исходный код snabdom, как реализовать упрощенный виртуальный DOM

Быть добавленным

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

Уменьшите ненужный рендеринг

В использованииclass ComponentПри разработке мы можем использоватьshouldComponentUpdateЧтобы уменьшить ненужное рендеринг, затем использоватьreact hooksКак после этого реализовать такую ​​функцию?

решение:React.memoа такжеuseMemoВ этой ситуации реакция, конечно, также дает официальное решение, которое заключается в использовании React.memo и useMemo.

React.memo

React.momo на самом деле не является хуком, он фактически эквивалентен PureComponent, но он только сравнивает свойства. Он используется следующим образом (на примере выше):

import React, { useState } from 'react'

export const Count = React.memo(props => {
  const [data, setData] = useState({
    count: 0,
    name: 'cjg',
    age: 18
  })

  const handleClick = () => {
    const { count } = data
    setData({
      ...data,
      count: count + 1
    })
  }

  return <button onClick={handleClick}>count:{data.count}</button>
})
useMemo

Использование useMemo на самом деле немного похоже на useEffects, давайте посмотрим непосредственно на официальный пример.

function Parent({ a, b }) {
  // Only re-rendered if `a` changes:
  const child1 = useMemo(() => <Child1 a={a} />, [a])
  // Only re-rendered if `b` changes:
  const child2 = useMemo(() => <Child2 b={b} />, [b])
  return (
    <>
      {child1}
      {child2}
    </>
  )
}

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

ЦитироватьРеактивные хуки на практике

Используйте shouldComponentUpdate() для предотвращения ненужного повторного рендеринга.

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

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

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

shouldComponentUpdate(nextProps, nextState) {
   return nextProps.next !== this.props.next
}

Анализатор производительности React

В React 16.5 добавлена ​​поддержка нового плагина профилирования DevTools. Этот плагин использует экспериментальный API-интерфейс React Profiler для сбора информации о времени рендеринга каждого компонента, чтобы выявить узкие места производительности в приложениях React. Он будет полностью совместим с нашими будущими функциями разделения времени (временного разделения) и саспенса (зависания).

redux

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

State:StoreОбъект содержит все данные, если вы хотите получить данные в определенный момент времени, вам нужноStoreСоздайте снимок, набор данных на данный момент времени, называемыйState.

Action:Stateизменения приведут к изменениям в представлении. Однако пользователь не может касаться состояния, только представления. Следовательно, изменение состояния должно быть вызвано представлением. Действие — это уведомление, отправленное представлением, указывающее, что состояние должно измениться.

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

Reducer:StoreполучатьActionПозднее появился новыйState, так что представление изменится. этоStateПроцесс расчета называетсяReducer.Reducerэто функция, которая принимает действие и текущийStateВ качестве аргумента возвращает новыйState.

dispatch:ДаViewпроблемаActionединственный способ.

Основы редукса

Затем проходим весь рабочий процесс:

Во-первых, пользователь (черезView)проблемаAction, используется метод выдачиdispatchметод.

Потом,Storeавтоматический вызовReducerи передать два параметра: текущийStateи получилAction,Reducerвернется новыйState

StateКак только происходит изменение,StoreФункция прослушивателя будет вызываться для обновленияView.

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

reduxКак спроектировать архитектуру одностороннего потока данных

быть улучшенным

reduxпромежуточное ПО

Промежуточное программное обеспечение Redux обеспечивает точку расширения до того, как редуктор будет достигнут после инициации действия.Другими словами, исходный поток данных: представление -> действие -> редуктор -> хранилище плюс промежуточное ПО становится представлением -> действие -> промежуточное ПО -> reducer -> store , в этой ссылке мы можем выполнять некоторые операции с «побочными эффектами», такие как асинхронные запросы, печать логов и т. д.

Промежуточное программное обеспечение Redux реализует перехват действия -> редуктора, переписывая метод store.dispatch.Из приведенного выше описания можно более четко понять модель промежуточного программного обеспечения Redux:

中间件A -> 中间件B-> 中间件C-> 原始 dispatch -> 中间件C -> 中间件B ->  中间件A

Это также напоминает нам, что при использовании промежуточных программ мы должны обратить внимание, когда промежуточное программное обеспечение «делает вещи». Например, Redux-Thunk перехватывает действие функции типа перед выполнением следующего (действие), а redux-Saga находится в следующем ( Действие) будет вызвать мониторинг Sagaemitter.emit (Action) и не перехватят существующие действия от достижения редуктора.

Ссылаться на:

  1. Углубленный анализ принципа рендеринга и характеристик виртуального DOM
  2. механизм событий реакции
  3. От миксинов до HOC и хуков
  4. Техническая команда Meituan-Redux от дизайна до исходного кода
  5. Разберите исходный код snabbdom и научитесь реализовывать упрощенную библиотеку Virtual DOM.
  6. Реагировать на анализ исходного кода
  7. Грубое сравнение двух фреймворков Vue и React