Большое спасибо за все проблемы, которые вы указали в комментариях. Из-за большого количества изменений в прошлом году он был отложен, а проблемы и предложения, на которые указали большие ребята, были исправлены. Пожалуйста, будьте уверены, чтобы поесть! Спасибо~🥳
Я не ожидал, что последняя статья будет настолько популярна у всех, я так взволнована. 🤩. Но это также страх, что также означает ответственность. Многие пункты знаний в следующей части требуют более глубокого изучения и понимания, а блоггеры также ограничены в своем уровне, и они обеспокоены тем, что не могут оправдать всеобщих ожиданий. Но, в конце концов, все же нужно подкорректировать свой менталитет, отпустить эмоции и сосредоточиться на каждом слове, чтобы не потерять ни себя, ни сообщество. Учитесь друг у друга, растите вместе и вдохновляйте друг друга!
В последнее время я был занят делами и ограничен в энергии, и хотя я изо всех сил старался быть строгим и неоднократно пересматривать его, в статье должны быть некоторые упущения. В прошлой статье многие друзья указали на массу проблем, о которых мне тоже глубоко жаль, и я смиренно приму и исправлю ошибки. Я также очень благодарен многим друзьям, которые обсуждали со мной через WeChat или официальный аккаунт, и спасибо за вашу поддержку и поддержку.
введение
Как вы все знаете, React сейчас доминирует в фронтенд-разработке. Отличная производительность и сильная экология делают его неудержимым. Все 5 компаний на лице блогера — это технологические стеки React. Насколько мне известно, большинство крупных фабрик также используют React в качестве основного стека технологий. React также стал неотъемлемой частью интервью.
Средняя часть в основном описывает React со следующих аспектов:
- Fiber
- Жизненный цикл
- SetState
- HOC (компонент высшего порядка)
- Redux
- React Hooks
- SSR
- функциональное программирование
Изначально планировалось, что будет всего две главы, но чем больше я писала, тем больше писала.Из-за ограничения места и для лучшего восприятия чтения пришлось разделить среднюю часть.Надеюсь вы не против. 🙃, Кроме того, в следующей части также есть знания по гибридному приложению/веб-пакету/оптимизации производительности/Nginx, так что следите за обновлениями.
Рекомендуется начать с основ предыдущей статьи~ Существует пошаговый процесс:
Блог Xiaocaiji выпрашивает лайки 🙂blog
продвинутые знания
Фреймворк: Реагировать
React также является самым популярным фреймворком переднего плана на сегодняшний день, а также обязательным для многих крупных фабрик. Хотя React отличается от Vue, это также инфраструктура пользовательского интерфейса.Хотя реализация может отличаться, все же есть сходство в некоторых концепциях, таких как управление данными, компонентизация и виртуальный дом. Вот некоторые концепции, уникальные для React.
1. Fiber
Основной процесс React можно разделить на две части:
- reconciliation (Алгоритм планирования, также известный как визуализация):
- Обновление состояния и реквизита;
- Вызов хуков жизненного цикла;
- создать виртуальный дом;
- Здесь больше подходит название Fiber Tree;
- Алгоритм diff выполняется через старый и новый vdom для получения изменения vdom;
- Определите, требуется ли повторный рендеринг
- commit:
- При необходимости выполните обновление узла dom;
Чтобы понять Fiber, давайте сначала посмотрим, зачем он нам нужен?
-
вопрос: По мере того, как приложение становится все больше и больше, весь процесс обновления и рендеринга становится трудоемким.Большое количество рендеринга компонентов приведет к тому, что основной процесс будет занят в течение длительного времени, что приведет к некоторым анимациям или высокочастотным операциям. И ключевой момент в том, чтосинхронная блокировка. В предыдущем алгоритме планирования React нужно было создать экземпляр каждого компонента класса, сгенерировать дерево компонентов и использоватьсинхронная рекурсияобходной рендеринг, и самая большая проблема этого процесса заключается в том, что он не можетПауза и возобновление.
-
решение: Обычно существует два способа решения блокировки синхронизации:асинхронныйа такжесегментация задач. React Fiber был создан для сегментации задач.
-
Кратко:
- В React V16 был рефакторинг алгоритма планирования, а предыдущий согласовщик стека был преобразован в новую версию согласовщика волокон, которая стала связанным списком и указателем.Алгоритм обхода дерева односвязных списков. Благодаря отображению указателя каждый блок записывает текущий предыдущий шаг и следующий шаг обхода, так что обход можно приостановить и перезапустить.
- Вот это я понимаю какАлгоритм планирования разделения задач, главным образом, чтобы разделить исходные синхронные задачи рендеринга обновлений на независимыенебольшой оперативный блок, в соответствии с разными приоритетами, небольшие задачи распределяются на время простоя браузера для выполнения, полностью используя механизм цикла событий основного процесса.
-
основной:
- Волокно здесь можно представить какструктура данных:
class Fiber { constructor(instance) { this.instance = instance // 指向第一个 child 节点 this.child = child // 指向父节点 this.return = parent // 指向第一个兄弟节点 this.sibling = previous } }
-
алгоритм обхода дерева связанных списков: пройти черезСохранение и сопоставление узлов, он может останавливаться и перезапускаться в любое время, чтобы достичь основной предпосылки разделения задач;
- 1. Во-первых, путем непрерывного обхода дочерних узлов до конца дерева;
- 2. Начать обход одноуровневых узлов через одноуровневых;
- 3. return возвращается к родительскому узлу и продолжает выполнение 2;
- 4. После корневого узла выпрыгнуть из обхода;
-
сегментация задач, обновление рендеринга в React можно разделить на две фазы:
- этап примирения: Сравнение данных vdom является подходящим этапом для разделения.Например, после сравнения части дерева сначала приостановить выполнение вызова анимации, а затем вернуться, чтобы продолжить сравнение после завершения.
- Стадия фиксации: Обновите список изменений до dom, который не подходит для разделения, чтобы синхронизировать данные и пользовательский интерфейс. В противном случае обновление данных может не соответствовать пользовательскому интерфейсу из-за блокировки обновления пользовательского интерфейса.
-
Децентрализованное исполнение: после того, как задача разделена, небольшие блоки задач могут быть распределены на период простоя браузера для постановки в очередь на выполнение, а ключом к реализации являются два новых API:
requestIdleCallback
а такжеrequestAnimationFrame
- задачи с низким приоритетом
requestIdleCallback
Обработка, это функция обратного вызова для периода простоя цикла обработки событий, предоставляемого браузером, требует выполнения поллифилла и имеет параметр крайнего срока для ограничения выполнения событий для продолжения разделения задач; - задачи с высоким приоритетом
requestAnimationFrame
иметь дело с;
- задачи с низким приоритетом
// 类似于这样的方式 requestIdleCallback((deadline) => { // 当有空闲时间时,我们执行一个组件渲染; // 把任务塞到一个个碎片时间中去; while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && nextComponent) { nextComponent = performWork(nextComponent); } });
- приоритетная стратегия: ввод текстового поля > Задачи, которые необходимо выполнить в конце этого расписания > Анимационный переход > Интерактивная обратная связь > Обновление данных > Задачи, которые не будут отображаться, но будут отображаться в будущем
Tips:
Fiber на самом деле можно рассматривать как идею программирования, и существует множество приложений на других языках (Ruby Fiber). Основная идея состоит в том, чтобы разделить и скоординировать задачи, а также активно передать право выполнения основному потоку, чтобы у главного потока было время для обработки других высокоприоритетных задач.
Столкнувшись с проблемой блокировки процесса,сегментация задач,Асинхронный вызова такжестратегия кэшированиятри примечательных решения.
Спасибо @Pengyuan Children's Shoes за указание на несколько основных концепций Fiber в комментариях, спасибо! !
2. Жизненный цикл
В новой версии у React официально появился новый жизненный циклИзменить предложение:
- использовать
getDerivedStateFromProps
заменятьcomponentWillMount
а такжеcomponentWillReceiveProps
; - использовать
getSnapshotBeforeUpdate
заменятьcomponentWillUpdate
; - избегать использования
componentWillReceiveProps
;
На самом деле причина этого изменения именно из-за упомянутого выше Волокна. Прежде всего, из вышеизложенного мы знаем, что React можно разделить на две фазы, согласование и фиксацию, и соответствующий жизненный цикл выглядит следующим образом:
-
reconciliation:
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
-
commit:
componentDidMount
componentDidUpdate
componentWillUnmount
В Fiber фаза согласования разделена на задачи, включающие паузу и перезапуск, поэтому это может привести к уничтожению функций жизненного цикла при согласовании в цикле рендеринга обновления.несколько вызововслучае возникли неожиданные ошибки.
Предлагаемый жизненный цикл новой версии выглядит следующим образом:
class Component extends React.Component {
// 替换 `componentWillReceiveProps` ,
// 初始化和 update 时被调用
// 静态函数,无法使用 this
static getDerivedStateFromProps(nextProps, prevState) {}
// 判断是否需要更新组件
// 可以用于组件性能优化
shouldComponentUpdate(nextProps, nextState) {}
// 组件被挂载后触发
componentDidMount() {}
// 替换 componentWillUpdate
// 可以在更新之前获取最新 dom 数据
getSnapshotBeforeUpdate() {}
// 组件更新后调用
componentDidUpdate() {}
// 组件即将销毁
componentWillUnmount() {}
// 组件已销毁
componentDidUnmount() {}
}
-
Рекомендации:
- существует
constructor
инициализировать состояние; - существует
componentDidMount
мониторинг событий вcomponentWillUnmount
событие отвязки; - существует
componentDidMount
запрос данных вcomponentWillMount
; - Когда вам нужно обновить состояние на основе реквизита, используйте
getDerivedStateFromProps(nextProps, prevState)
;- Старый реквизит нужно хранить отдельно для сравнения;
public static getDerivedStateFromProps(nextProps, prevState) { // 当新 props 中的 data 发生变化时,同步更新到 state 上 if (nextProps.data !== prevState.data) { return { data: nextProps.data } } else { return null1 } }
- допустимый
componentDidUpdate
Прослушивайте изменения свойств или состояния, например:
componentDidUpdate(prevProps) { // 当 id 发生变化时,重新获取数据 if (this.props.id !== prevProps.id) { this.fetchData(this.props.id); } }
- существует
componentDidUpdate
использоватьsetState
При необходимо добавить условие, иначе оно войдет в бесконечный цикл; -
getSnapshotBeforeUpdate(prevProps, prevState)
Последние данные рендеринга можно получить перед обновлением, оно вызывается после рендера и перед обновлением; -
shouldComponentUpdate
: по умолчанию каждый вызовsetState
, обязательно попадет на стадию сравнения, но может бытьshouldComponentUpdate
Крюк жизни возвращаетсяfalse
Чтобы напрямую предотвратить выполнение последующей логики, обычно используется условный рендеринг для оптимизации производительности рендеринга.
- существует
3. setState
в пониманииsetState
Прежде давайте кратко разберемся со структурой упаковки React:Transaction:
-
дела (Transaction):
- это структура вызова в React, используемая для обертывания метода, структура такова:initialize - perform(method) - close. Через транзакцию можно единообразно управлять началом и концом метода, в потоке транзакций это означает, что процесс выполняет какие-то операции;
-
setState
: используется в React для изменения состояния и обновления представления. Он имеет следующие характеристики: -
Асинхронный и синхронный:
setState
Он не является чисто асинхронным или синхронным, он фактически связан с окружением во время вызова:- существуетсинтетическое событиеа такжеКрючки жизненного цикла (кроме componentDidUpdate)середина,
setState
является «асинхронным»;-
причина: Потому что
setState
В реализации есть суждение: когда стратегия обновления находится в выполнении потока транзакций, обновление компонента будет помещено вdirtyComponents
Ожидание выполнения в очереди, в противном случае начать выполнениеbatchedUpdates
обновление очереди;- В вызове ловушки жизненного цикла стратегия обновления предшествует обновлению, компонент все еще находится в потоке транзакций и
componentDidUpdate
После обновления компонент больше не находится в потоке транзакций, поэтому он будет выполняться синхронно; - В синтетических событиях React основан наМеханизм делегирования событий для завершения потока транзакцийРеализация также находится в потоке транзакций;
- В вызове ловушки жизненного цикла стратегия обновления предшествует обновлению, компонент все еще находится в потоке транзакций и
-
вопрос: недоступно
setState
незамедлительно послеthis.state
чтобы получить обновленное значение. -
решать: если вам нужно немедленно синхронизироваться, чтобы получить новые значения,
setState
Фактически, второй параметр может быть передан.setState(updater, callback)
, самое последнее значение можно получить в обратном вызове;
-
причина: Потому что
- существуетродное событиеа такжеsetTimeoutсередина,
setState
Это синхронно, и обновленное значение может быть получено немедленно;- Причина: родное событие является реализацией самого браузера, не имеет ничего общего с потоком транзакций и, естественно, является синхронным;
setTimeout
Помещается в поток таймера для отложенного выполнения, в это время поток транзакций закончился, поэтому тоже синхронизируется;
- Причина: родное событие является реализацией самого браузера, не имеет ничего общего с потоком транзакций и, естественно, является синхронным;
- существуетсинтетическое событиеа такжеКрючки жизненного цикла (кроме componentDidUpdate)середина,
-
Массовое обновление: существуетсинтетическое событиеа такжекрючки жизненного цикласередина,
setState
При обновлении очереди сохраняется то, чтостатус слияния(Object.assign
). Поэтому установленное ранее значение ключа позже будет перезаписано, и в итоге будет выполнено только одно обновление; -
функциональный: из-за проблем с волокном и слиянием официальные рекомендации могут быть переданы вфункцияформа.
setState(fn)
,существуетfn
вернуть новый вstate
объект, напр.this.setState((state, props) => newState);
- Используя функциональный стиль, можно избежать
setState
Логика пакетного обновления, входящая функция будетпоследовательный вызов;
- Используя функциональный стиль, можно избежать
-
Меры предосторожности:
- setState объединяется, и несколько последовательных вызовов в синтетических событиях и ловушках жизненного цикла будут оптимизированы в один;
- Когда компонент был уничтожен, при повторном вызове
setState
, React сообщит об ошибке, обычно есть два решения:- Смонтируйте данные извне и передайте через пропсы, например, в Redux или родителях;
- Поддерживать количество состояний внутри компонента (isUnmounted),
componentWillUnmount
отмечен как истинный вsetState
решение перед
4. HOC (компонент высшего порядка)
HOC (Higher Order Component) — это компонентная модель, сформированная сообществом в рамках механизма React, который является мощным во многих сторонних библиотеках с открытым исходным кодом.
-
Кратко:
- Компоненты более высокого порядка не являются компонентами, ониФункция улучшения, вы можете ввести метакомпонент и вернуть новый расширенный компонент;
- Основная роль компонентов высшего порядка состоит в том, чтобыповторное использование кода,действоватьстатус и параметры;
-
Применение:
-
Реквизит Прокси: возвращает компонент, основанный на обернутом компонентеулучшение функции;
- параметры по умолчанию: Вы можете обернуть слой параметров по умолчанию для компонента;
function proxyHoc(Comp) { return class extends React.Component { render() { const newProps = { name: 'tayde', age: 1, } return <Comp {...this.props} {...newProps} /> } } }
- Извлечь состояние: Состояние в обернутом компоненте может зависеть от внешнего слоя через свойства, например, для компонентов, контролируемых переходом:
function withOnChange(Comp) { return class extends React.Component { constructor(props) { super(props) this.state = { name: '', } } onChangeName = () => { this.setState({ name: 'dongdong', }) } render() { const newProps = { value: this.state.name, onChange: this.onChangeName, } return <Comp {...this.props} {...newProps} /> } } }
Поза следующая, так что очень быстро
Input
Компоненты преобразуются в управляемые компоненты.const NameInput = props => (<input name="name" {...props} />) export default withOnChange(NameInput)
- компоненты упаковки: для обернутого элемента может быть выполнен слой обертывания,
function withMask(Comp) { return class extends React.Component { render() { return ( <div> <Comp {...this.props} /> <div style={{ width: '100%', height: '100%', backgroundColor: 'rgba(0, 0, 0, .6)', }} </div> ) } } }
-
обратное наследование(Инверсия наследования): возвращает компонент,Унаследовано от обернутого компонента, обычно используется для следующих операций:
function IIHoc(Comp) { return class extends Comp { render() { return super.render(); } }; }
-
рендеринг угона (Render Highjacking)
- условный рендеринг: Рендеринг различных компонентов в зависимости от условий
function withLoading(Comp) { return class extends Comp { render() { if(this.props.isLoading) { return <Loading /> } else { return super.render() } } }; }
- Вы можете напрямую изменить дерево элементов React, отображаемое обернутым компонентом.
-
рабочее состояние(Рабочее состояние): вы можете напрямую пройти
this.state
Получите состояние обернутого компонента и работайте с ним. Но такая операция может легко сделать состояние трудным для отслеживания, поддержки и использования с осторожностью.
-
-
-
Сценарии применения:
- Контроль доступа, с помощью абстрактной логики единообразно оценивать разрешения страницы и отображать страницу в соответствии с различными условиями:
function withAdminAuth(WrappedComponent) { return class extends React.Component { constructor(props){ super(props) this.state = { isAdmin: false, } } async componentWillMount() { const currentRole = await getCurrentUserRole(); this.setState({ isAdmin: currentRole === 'Admin', }); } render() { if (this.state.isAdmin) { return <Comp {...this.props} />; } else { return (<div>您没有权限查看该页面,请联系管理员!</div>); } } }; }
- мониторинг производительности, обернуть жизненный цикл компонента и выполнить унифицированное захоронение:
function withTiming(Comp) { return class extends Comp { constructor(props) { super(props); this.start = Date.now(); this.end = 0; } componentDidMount() { super.componentDidMount && super.componentDidMount(); this.end = Date.now(); console.log(`${WrappedComponent.name} 组件渲染时间为 ${this.end - this.start} ms`); } render() { return super.render(); } }; }
- повторное использование кода, повторяющуюся логику можно абстрагировать.
-
Используйте Примечание:
-
- чистая функция: Функция улучшения должна быть чистой функцией, чтобы избежать вторжения и модификации метакомпонентов;
-
- Избегайте загрязнения окружающей среды: В идеале несвязанные параметры и события метакомпонента должны передаваться прозрачно, а использование должно оставаться неизменным, насколько это возможно;
-
- Пространства имен: добавить конкретное имя компонента в HOC, что может облегчить разработку и отладку, а также поиск проблем;
-
-
пройти по ссылке: Если вам нужно передать рефы метакомпонента, вы можете использовать
React.forwardRef
;
-
пройти по ссылке: Если вам нужно передать рефы метакомпонента, вы можете использовать
-
- статический метод: статический метод метакомпонента не может быть передан автоматически, что приведет к сбою вызова бизнес-уровня; решение:
- экспорт функции
- назначение статического метода
-
- перерисовать: Поскольку функция улучшения возвращает новый компонент каждый раз, когда она вызывается, если функция улучшения используется в Render, весь HOC будет каждый раз перерисовываться, а предыдущее состояние будет потеряно;
-
5. Redux
Редукс — этоцентр управления данными, который можно понимать как глобальный экземпляр хранилища данных. Он обеспечивает надежность, прослеживаемость и предсказуемость данных благодаря определенным правилам и ограничениям использования. Он не имеет ничего общего с React и может работать независимо в любой среде JavaScript, тем самым обеспечивая лучший канал синхронизации данных для однородных приложений.
-
основная идея:
- единственный источник правды: все приложение имеет только одно дерево состояний, то есть все состояния в конечном итоге хранятся в хранилище корневого уровня;
-
Статус только для чтения: Чтобы обеспечить управляемость состояния, лучше всего следить за изменениями состояния. Тогда есть два необходимых условия:
- Данные в Redux Store нельзя изменить напрямую;
- Строго контролировать выполнение модификаций;
- чистая функция: указывает, что модификации могут быть описаны только чистой функцией (Reducer);
-
Приблизительная структура данных выглядит следующим образом:
-
Реализация концепции:
-
Store: синглтон Global Store, под каждым приложением Redux есть только один магазин, у него есть следующие методы для использования:
-
getState
: получить состояние; -
dispatch
: триггерное действие, обновление состояния; -
subscribe
: подписываться на изменения данных, регистрировать слушателей;
-
// 创建 const store = createStore(Reducer, initStore)
- Action: он используется как носитель поведения для отображения соответствующего Reducer, и он может стать носителем данных, передавая данные из приложения в хранилище, которое является хранилищем.единственный источник данных;
// 一个普通的 Action const action = { type: 'ADD_LIST', item: 'list-item-1', } // 使用: store.dispatch(action) // 通常为了便于调用,会有一个 Action 创建函数 (action creater) funtion addList(item) { return const action = { type: 'ADD_LIST', item, } } // 调用就会变成: dispatch(addList('list-item-1'))
- Reducer: чистая функция, используемая для описания того, как изменять данные, Action — это имя поведения, а Reducer — суть изменения поведения;
// 一个常规的 Reducer // @param {state}: 旧数据 // @param {action}: Action 对象 // @returns {any}: 新数据 const initList = [] function ListReducer(state = initList, action) { switch (action.type) { case 'ADD_LIST': return state.concat([action.item]) break defalut: return state } }
Уведомление:
- Соблюдайте неизменность данных, не изменяйте состояние напрямую, а возвращайтеновый объект,можно использовать
assign / copy / extend / 解构
и т.д. для создания новых объектов; - Требуется по умолчаниювернуть исходные данные, чтобы избежать очистки данных;
- лучшая обстановкаПервоначальный значение, что удобно для инициализации приложения и стабильности данных;
-
Store: синглтон Global Store, под каждым приложением Redux есть только один магазин, у него есть следующие методы для использования:
-
Передовой:
-
React-Redux: Используйте с React;
-
<Provider>
: передать хранилище в компонент через контекст; -
connect
: компонент более высокого порядка, упрощающий использование Redux в компонентах React;-
- будет
store
пройти черезmapStateToProps
использовать после фильтрацииprops
вводить компоненты
- будет
-
- согласно с
mapDispatchToProps
Метод Create, который используется при вызове компонентаdispatch
активировать соответствующийaction
- согласно с
-
-
-
Разделение и рефакторинг редьюсера:
- По мере роста проекта, если вы напишите редукторы для всех состояний в одной функции, этосложно поддерживать;
- Редуктор можно разделить, т.е.декомпозиция функции, и, наконец, используйте
combineReducers()
выполнить рефакторинг и слияние;
-
Асинхронное действие: Так как Редуктор — строго чистая функция, в Редюсере нельзя запросить данные, нужно сначала получить данные, а потом
dispatch(Action)
То есть вот три разных асинхронных реализации:
-
React-Redux: Используйте с React;
6. React Hooks
Обычно используется в Reactопределение классаилиопределение функцииСоздайте компонент:
В определениях классов мы можем использовать многие функции React, такие как состояние, различные хуки жизненного цикла компонентов и т. д., но в определениях функций мы ничего не можем сделать, поэтому в версии React 16.8 представлена новая функция (React Hooks), благодаря которой , вы можете лучше использовать функции React в компонентах, определяющих функции.
-
выгода:
- 1,Повторное использование компонентов: На самом деле render props/HOC тоже для повторного использования.По сравнению с ними Hooks, как официальный базовый API, самый легковесный, а стоимость трансформации небольшая, что не повлияет на исходную иерархию компонентов и легендарный вложенный ад ;
- 2,Определения классов более сложны:
- Различные жизненные циклы сделают логику разбросанной и запутанной, и ее будет непросто поддерживать и управлять;
- Всегда нужно внимание
this
указание на проблему; - Повторное использование кода обходится дорого, а использование компонентов более высокого порядка часто приводит к раздуванию всего дерева компонентов;
- 3.Состояние изолировано от пользовательского интерфейса: Из-за характеристик хуков логика состояния станет менее детализированной, и ее очень легко абстрагировать в пользовательские хуки, состояние и пользовательский интерфейс в компоненте станут более четкими и изолированными.
-
Уведомление:
- Избегайте вызова хуков в циклах/оценках условий/вложенных функциях, чтобы обеспечить стабильность вызываемой последовательности;
- Только компоненты определения функции и хуки могут вызывать хуки, избегайте их вызова в компонентах класса или обычных функциях;
- не может быть в
useEffect
используется вuseState
, React сообщит об ошибке; - Компоненты класса не будут заменяться или отбрасываться, нет необходимости принудительно преобразовывать компоненты класса, и эти два метода могут сосуществовать;
-
важный крючок*:
-
крючок состояния (
useState
): используется для определения состояния компонента, которое переходит в определение классаthis.state
функция;
// useState 只接受一个参数: 初始状态 // 返回的是组件名和更改该组件对应的函数 const [flag, setFlag] = useState(true); // 修改状态 setFlag(false) // 上面的代码映射到类定义中: this.state = { flag: true } const flag = this.state.flag const setFlag = (bool) => { this.setState({ flag: bool, }) }
-
крючки жизненного цикла (
useEffect
):
В определении класса есть много функций жизненного цикла, и соответствующая функция также предусмотрена в React Hooks (
useEffect
), что можно увидеть здесь какcomponentDidMount
,componentDidUpdate
а такжеcomponentWillUnmount
комбинация.-
useEffect(callback, [source])
принимает два параметра-
callback
: функция обратного вызова ловушки; -
source
: Установите условие запуска, оно будет запускаться только при изменении источника; -
useEffect
Крюк не проходит[source]
Когда параметр установлен, по умолчанию функция, возвращенная в последнем сохраненном обратном вызове, будет вызываться первой каждый раз при рендеринге, а затем обратный вызов будет вызываться снова;
-
useEffect(() => { // 组件挂载后执行事件绑定 console.log('on') addEventListener() // 组件 update 时会执行事件解绑 return () => { console.log('off') removeEventListener() } }, [source]); // 每次 source 发生改变时,执行结果(以类定义的生命周期,便于大家理解): // --- DidMount --- // 'on' // --- DidUpdate --- // 'off' // 'on' // --- DidUpdate --- // 'off' // 'on' // --- WillUnmount --- // 'off'
-
Со вторым параметром мы можем смоделировать несколько общих жизненных циклов:
-
componentDidMount
: входящий[]
, он будет вызван только один раз во время инициализации;
const useMount = (fn) => useEffect(fn, [])
-
componentWillUnmount
: входящий[]
, возвращаемая функция в обратном вызове будет окончательно выполнена только один раз;
const useUnmount = (fn) => useEffect(() => fn, [])
-
mounted
: можно инкапсулировать в смонтированное состояние с высокой степенью повторного использования с помощью useState;
const useMounted = () => { const [mounted, setMounted] = useState(false); useEffect(() => { !mounted && setMounted(true); return () => setMounted(false); }, []); return mounted; }
-
componentDidUpdate
:useEffect
Он будет выполняться каждый раз, фактически после исключения DidMount;
const mounted = useMounted() useEffect(() => { mounted && fn() })
-
-
крючок состояния (
-
Другие встроенные крючки:
-
useContext
: получить объект контекста -
useReducer
: Аналогично реализации идей Redux, но недостаточно заменить Redux, его можно понимать как redux внутри компонента:- Это не постоянное хранилище, и оно будет уничтожено по мере уничтожения компонента;
- Он принадлежит компоненту, и каждый компонент изолирован друг от друга, и он не может обмениваться данными, просто используя его;
- Сотрудничать
useContext
Глобальность , может завершить легкий Redux ;(easy-peasy)
-
useCallback
: Кэшируйте функцию обратного вызова, чтобы входящий обратный вызов не был каждый раз новым экземпляром функции и вызывал повторную визуализацию зависимого компонента, что приводит к оптимизации производительности; -
useMemo
: Используется для кэширования входящих реквизитов, чтобы каждый раз не перерисовывать зависимые компоненты; -
useRef
: Получить реальный узел компонента; -
useLayoutEffect
:- Хук синхронизации обновления DOM. использование с
useEffect
Аналогично, но отличается от точки времени выполнения. -
useEffect
Он относится к асинхронному выполнению и не ждет выполнения DOM после рендеринга, аuseLayoutEffect
Он будет запущен после фактического рендеринга; - Обновленное состояние может быть получено;
- Хук синхронизации обновления DOM. использование с
-
-
пользовательский крючок(
useXxxxx
): Основываясь на том, что хуки могут ссылаться на другие хуки, мы можем написать собственные хуки, такие как приведенные выше.useMounted
. В другом примере нам нужен собственный заголовок для каждой страницы:
function useTitle(title) {
useEffect(
() => {
document.title = title;
});
}
// 使用:
function Home() {
const title = '我是首页'
useTitle(title)
return (
<div>{title}</div>
)
}
7. SSR
ССР, широко известный какрендеринг на стороне сервера(Рендеринг на стороне сервера), человеческая речь: напрямую получать данные на уровне сервера, отображать готовый HTML-файл и напрямую возвращать его в браузер пользователя для доступа.
-
Переднее и заднее разделение: внешний интерфейс изолирован от сервера, и внешний интерфейс динамически получает данные и отображает страницу.
-
Болевые точки:
-
Узкое место в производительности рендеринга первого экрана:
- Пустая задержка: время загрузки HTML + время загрузки/выполнения JS + время запроса + время рендеринга. В это время страница пуста.
-
SEO-проблемы: Поскольку начальное состояние страницы пусто, сканер не может получить какие-либо достоверные данные на странице, поэтому он не удобен для поисковых систем.
- Хотя предложена технология динамического рендеринга краулеров, насколько я знаю, большинство отечественных поисковых систем до сих пор не реализованы.
-
Начальный рендеринг на стороне сервера не имеет этих проблем. Но мы не можем вернуться к основам не только для обеспечения существующего режима независимой разработки фронтенда, но и для рендеринга сервером, поэтому используем React SSR.
-
принцип:
- Node Services: позволяют запускать один и тот же набор кода на переднем и заднем концах.
- Виртуальный дом: пусть интерфейсный код работает вне браузера.
-
условие: средний уровень Node, React/Vue и другие фреймворки. Структура примерно такая:
-
Процесс развития: (Здесь в качестве примера берется React + Router + Redux + Koa)
-
1. В том же проектестроитьПередняя и задняя части, общая конструкция:
- build
- public
- src
- client
- server
-
2. Используйте Koa на серверемониторинг маршрутаДоступ к странице:
import * as Router from 'koa-router' const router = new Router() // 如果中间也提供 Api 层 router.use('/api/home', async () => { // 返回数据 }) router.get('*', async (ctx) => { // 返回 HTML })
- 3. Получив доступ к URL-адресусоответствоватьМаршрутизация страницы интерфейса:
// 前端页面路由 import { pages } from '../../client/app' import { matchPath } from 'react-router-dom' // 使用 react-router 库提供的一个匹配方法 const matchPage = matchPath(ctx.req.url, page)
-
4. Через настройку постраничной маршрутизациисбор информации. Обычно статическая конфигурация, связанная с SSR, может быть добавлена к маршрутизации страниц для абстрактной логики, которая может обеспечить универсальность логики на стороне сервера, например:
class HomePage extends React.Component{ public static ssrConfig = { cache: true, fetch() { // 请求获取数据 } } }
Обычно есть два случая получения данных:
- Также используются промежуточные слои.httpВ настоящее время для получения данных метод выборки может быть разделен между интерфейсом и сервером;
const data = await matchPage.ssrConfig.fetch()
- Средний уровень не использует http, он через некоторыеВнутренний вызов, такие как Rpc или прямое чтение базы данных и т. д. В это время сервер также может напрямую вызывать соответствующий метод для получения данных. Обычно в ssrConfig необходимо настроить определенную информацию, чтобы она соответствовала соответствующему методу сбора данных.
// 页面路由 class HomePage extends React.Component{ public static ssrConfig = { fetch: { url: '/api/home', } } } // 根据规则匹配出对应的数据获取方法 // 这里的规则可以自由,只要能匹配出正确的方法即可 const controller = matchController(ssrConfig.fetch.url) // 获取数据 const data = await controller(ctx)
-
5. Создайте хранилище Redux и сохраните данные
dispatch
Зайти внутрь:
import { createStore } from 'redux' // 获取 Clinet层 reducer // 必须复用前端层的逻辑,才能保证一致性; import { reducers } from '../../client/store' // 创建 store const store = createStore(reducers) // 获取配置好的 Action const action = ssrConfig.action // 存储数据 store.dispatch(createAction(action)(data))
- 6. Инжект Магазин, звоните
renderToString
Рендеринг React Virtual Dom какнить:
import * as ReactDOMServer from 'react-dom/server' import { Provider } from 'react-redux' // 获取 Clinet 层根组件 import { App } from '../../client/app' const AppString = ReactDOMServer.renderToString( <Provider store={store}> <StaticRouter location={ctx.req.url} context={{}}> <App /> </StaticRouter> </Provider> )
-
7. Упакуйте AppString в полный формат файла HTML;
-
8. На данный момент полный файл HTML создан. Но это просто чистая статическая страница, без стиля и взаимодействия. Далее мы собираемся вставить JS и CSS. Мы можем получить доступ к пакету, сгенерированному внешним интерфейсом
asset-manifest.json
файл, чтобы получить соответствующий путь к файлу и вставить его в HTML для справки.
const html = ` <!DOCTYPE html> <html lang="zh"> <head></head> <link href="${cssPath}" rel="stylesheet" /> <body> <div id="App">${AppString}</div> <script src="${scriptPath}"></script> </body> </html> `
- 9. ВыполнитьОбезвоживание данных: Для синхронизации данных, полученных сервером, с интерфейсом. Главное — сериализовать данные, вставить в html и вернуть на фронтенд.
import serialize from 'serialize-javascript' // 获取数据 const initState = store.getState() const html = ` <!DOCTYPE html> <html lang="zh"> <head></head> <body> <div id="App"></div> <script type="application/json" id="SSR_HYDRATED_DATA">${serialize(initState)}</script> </body> </html> ` ctx.status = 200 ctx.body = html
Tips:
Здесь есть два особых момента:
-
использовал
serialize-javascript
Сериализировать хранилище вместоJSON.stringify
, чтобы обеспечить безопасность данных и избежать внедрения кода и XSS-атак; -
Используйте json для передачи, вы можете получить более высокую скорость загрузки;
- 10. Клиентский уровеньданные отстой: при инициализации хранилища используйте обезвоженные данные в качестве данных инициализации для синхронного создания хранилища.
const hydratedEl = document.getElementById('SSR_HYDRATED_DATA') const hydrateData = JSON.parse(hydratedEl.textContent) // 使用初始 state 创建 Redux store const store = createStore(reducer, hydrateData)
-
8. Функциональное программирование
Функциональное программирование – этопарадигма программирования, вы можете понимать это как способ мышления архитектуры программного обеспечения. Он имеет независимый набор теоретических основ и граничных правил, а стремлениеБолее лаконичный, предсказуемый, многократно используемый и легко тестируемый. На самом деле, многие из существующих известных библиотек содержат богатые идеи функционального программирования, такие как React/Redux.
-
Общие парадигмы программирования:
- Императивное программирование (процедурное программирование): больше связано с шагами по решению проблемы, сообщая компьютеру, что делать шаг за шагом, в форме языка;
- Программирование, управляемое событиями: подписка на события и запуск, широко используемые в программировании с графическим интерфейсом;
- Объектно-ориентированное программирование: шаблоны проектирования на основе классов, объектов и методов с тремя основными понятиями: инкапсуляция, наследование и полиморфизм;
- функциональное программирование
- Говоря более продвинутым языком, ориентированным на математическое программирование. Боюсь или нет~🥴
-
Идея функционального программирования:
-
чистая функция(Детерминированная функция): это основа функционального программирования, которая может сделать программу гибкой, расширяемой и удобной в сопровождении;
-
Преимущество:
- Полностью независимый и не связанный с внешним миром;
- Возможность повторного использования, выполнение в любом контексте и на любой временной шкале со стабильными результатами;
- Сильная тестируемость;
-
условие:
- Не изменяйте параметры;
- Не зависит и не изменяет какие-либо данные вне функции;
- Полностью управляемая, параметры одинаковые, возвращаемое значение должно быть одинаковым: например, функция не может содержать
new Date()
илиMath.rando()
такие неконтролируемые факторы; - ссылка на прозрачность;
-
Многие API или служебные функции, которые мы обычно используем, имеют характеристики чистых функций, например:
split / join / map
;
-
-
функциональная композиция: комбинируя несколько функций и вызывая их, вы можете комбинировать функциональные блоки один за другим для достижения конечной цели;
-
сглаженное вложение: В первую очередь надо думать, что самая простая операция объединения функций — это обертка, потому что в JS функции можно использовать и как параметры:
-
f(g(k(x)))
: Вложенный ад, низкая читабельность, когда функция сложная, легко запутать людей; - Идеальная практика:
xxx(f, g, k)(x)
-
-
передача результата: Если вы хотите достичь вышеуказанного метода, то есть
xxx
Что функция должна достичь, так это: выполнение передачи результата выполнения между различными функциями;- На этом этапе мы можем подумать о нативном методе массива:
reduce
, может выполняться последовательно в порядке массива, передавая результат выполнения; - Итак, мы можем реализовать метод
pipe
, для функциональной композиции:
// ...fs: 将函数组合成数组; // Array.prototype.reduce 进行组合; // p: 初始参数; const pipe = (...fs) => p => fs.reduce((v, f) => f(v), p)
- На этом этапе мы можем подумать о нативном методе массива:
-
использовать: Реализовать функцию горбового именования для подчеркивания именования:
// 'Guo DongDong' --> 'guo-dongdong' // 函数组合式写法 const toLowerCase = str => str.toLowerCase() const join = curry((str, arr) => arr.join(str)) const split = curry((splitOn, str) => str.split(splitOn)); const toSlug = pipe( toLowerCase, split(' '), join('_'), encodeURIComponent, ); console.log(toSlug('Guo DongDong'))
-
выгода:
- Скрыть промежуточные параметры, не нужны временные переменные, избежать вероятности ошибок в этой ссылке;
- Просто обратите внимание на стабильность каждой чистой функциональной единицы, больше не нужно обращать внимание на именование, передачу, вызов и т. д.;
- Сильная возможность повторного использования, любой функциональный блок можно повторно использовать и комбинировать произвольно;
- Сильная масштабируемость и низкая стоимость. Например, если вы добавите требование сейчас, вам нужно просмотреть вывод каждой ссылки:
const log = curry((label, x) => { console.log(`${ label }: ${ x }`); return x; }); const toSlug = pipe( toLowerCase, log('toLowerCase output'), split(' '), log('split output'), join('_'), log('join output'), encodeURIComponent, );
Tips:
На некоторые служебные чистые функции можно напрямую ссылаться.
lodash/fp
,Напримерcurry/map/split
Подождите, вам не нужно реализовывать это самостоятельно, как мы сделали выше; -
-
неизменность данных(неизменяемый): это концепция данных и одна из основных концепций функционального программирования:
- Адвокат: Объект нельзя изменить после его создания. Когда значение необходимо изменить, возвращается совершенно новый объект вместо прямого изменения исходного объекта;
- Цель: Обеспечьте стабильность данных. Избегайте неизвестной модификации зависимых данных, что приводит к собственной аномалии выполнения, что может эффективно улучшить управляемость и стабильность;
- не эквивалентен
const
. использоватьconst
После создания объекта его свойства все еще можно изменить; - больше похоже на
Object.freeze
: Заморозить объект, ноfreeze
Еще нет гарантии, что глубинные свойства не будут изменены; -
immutable.js
: библиотека неизменности данных в js, обеспечивающая неизменность данных, широко используется в экосистеме React и значительно повышает производительность и стабильность;-
trie
структура данных:- Структура данных, которая эффективно замораживает объекты, обеспечивая их неизменность;
- совместное использование структуры: Адрес ссылки на память неизменяемых объектов может использоваться совместно, чтобы уменьшить использование памяти и повысить производительность операций с данными;
-
-
Избегайте разницы между различными функциямисовместное использование состояния, передача данных использует копии или новые объекты и придерживается принципа неизменности данных;
-
избегать внутри функцииизменить внешнее состояние, например изменение значения переменной в глобальной или родительской области, может вызвать другие ошибки модуля;
-
Избегайте выполнения некоторых действий внутри функции модуляпобочный эффект, эти операции должны быть разделены на более независимые инструментальные блоки;
- вывод журнала
- читать и писать файлы
- сетевой запрос
- вызов внешнего процесса
- вызвать функцию с побочными эффектами
-
-
Функции высшего порядка: относится к типу функции, которая принимает функцию в качестве аргумента и возвращает новую расширенную функцию, которая обычно используется для:
- выполнять логические действияизолированная абстракция, что удобно для быстрого повторного использования, например обработки данных, совместимости и т.п.;
- функциональная композиция, который объединяет список функций модуля в более мощную функцию;
- улучшение функции, быстро расширить функциональную функцию,
-
Преимущества функционального программирования:
- Функция имеет небольшие побочные эффекты, все функции существуют независимо друг от друга без какой-либо связи, а возможность повторного использования чрезвычайно высока;
- Не обращайте внимания на время выполнения, порядок выполнения, параметры, наименования и т. д., можно сосредоточиться на потоке и обработке данных, эффективно повысить стабильность и надежность;
- Следуйте унификации и детализации, чтобы снизить стоимость реконструкции и преобразования, а также улучшить ремонтопригодность и масштабируемость;
- Проще юнит-тест.
-
Суммировать:
- Функциональное программирование на самом деле является идеей программирования, которая стремится к более тонкой детализации, делит приложение на группы очень маленьких модульных функций, а также объединяет и вызывает потоки рабочих данных;
- Он выступает за чистые функции/композицию функций/неизменяемость данных и осторожно относится к совместному использованию состояния внутри функций/внешних зависимостей/побочных эффектов;
Tips:
На самом деле, нам трудно и не нужно идеально прорабатывать весь набор идей в процессе собеседования, это лишь поверхностный вкус и некое личное понимание. Блогер тоже юниор-новичок, просто оставайся на поверхности, просто хочешь быть всем полезен, слегка брызгай 🤣;
Я лично считаю: противоречия между этими парадигмами программирования нет, у каждой своиПреимущества и недостатки.
понимать и изучать их концепции и преимущества, разумноСлияние дизайна, используя отличные идеи программирования для улучшения наших приложений;
Все дизайнерские идеи, конечная цель должна заключаться в том, чтобы сделать наше приложение болееРаздельная степень детализации, простое расширение, простое тестирование, высокий уровень повторного использования, более эффективная и безопасная разработка;
Есть несколько библиотек, которые позволяют быстро получить доступ и использовать функциональные идеи:
Underscore.js
/Lodash/fp
/Rxjs
Ждать.
Эпилог
На этом этапе, предположительно, все обнаружат, что начали углубляться в некоторые теории и принципы, понять которые не так просто, как в предыдущей статье. Но это также единственный путь, он не может оставаться навсегдаТехники, которые нужно освоить за 5 минутначальство. Не оставайтесь больше на поверхности языка, а поймите более глубокие принципы, шаблоны, архитектуры, причины и следствия, и вы вдруг обнаружите, что вы старший инженер-программист. 😁.
Я надеюсь, что вы можете успокоиться.Хотя некоторые теории и концепции скучны, но, поразмыслив и попробовав их, вы можете прийти к своему собственному пониманию.
Когда вы начинаете брать интервью у старших инженеров, интервьюер перестает сосредотачиваться на том, умеете ли вы писатьstopPropagation
Или это будет середина уровня, но больше озабоченная вашим собственным мышлением и исследовательскими способностями. Демонстрация того, что вы глубоко понимаете результаты исследования, несомненно, произведет впечатление на интервьюера.
Tips:
Свяжитесь со мной по почте 159042708@qq.com или QQ / WeChat: 159042708.
Блогер очень старается писать, если не зазвездишься, то реально заплачешь. ~github. 🤑