Когда любой проект развивается до определенной сложности, он неизбежно сталкивается с проблемой повторного использования логики. существуетReactОбычно существуют следующие способы реализации логического мультиплексирования:Mixin,高阶组件(HOC),修饰器(decorator),Render Props,Hook. В этой статье в основном анализируются преимущества и недостатки вышеуказанных методов, чтобы помочь разработчикам сделать методы более подходящими для бизнес-сценариев.
Mixin
Может быть, это просто отVueпеременаReactПервый способ, который могут придумать разработчики.MixinОн широко используется в различных объектно-ориентированных языках,Его роль заключается в создании эффекта, аналогичного множественному наследованию для языков с одинарным наследованием.. Хотя сейчасReactбыл заброшен, ноMixinдействительно былReactШаблон проектирования для реализации совместного использования кода.
Обобщенный метод примеси заключается в монтировании методов объекта примеси к исходному объекту путем присваивания для реализации смешивания объектов, подобно функции Object.assign() в ES6. Принцип заключается в следующем:
const mixin = function (obj, mixins) {
const newObj = obj
newObj.prototype = Object.create(obj.prototype)
for (let prop in mixins) {
// 遍历mixins的属性
if (mixins.hasOwnPrototype(prop)) {
// 判断是否为mixin的自身属性
newObj.prototype[prop] = mixins[prop]; // 赋值
}
}
return newObj
};
Использование миксинов в React
Предположим, в нашем проекте нескольким компонентам необходимо установить значение по умолчанию.nameсвойства, использованиеmixinЭто может избавить нас от необходимости писать несколько одинаковых компонентов в разных компонентах.getDefaultPropsметод, мы можем определитьmixin:
const DefaultNameMixin = {
getDefaultProps: function () {
return {
name: "Joy"
}
}
}
чтобы использоватьmixin, который необходимо добавить в компонентmixinsсвойства, а затем пишемmixinоберните его в массив, используйте его какmixinsСтоимость свойства:
const ComponentOne = React.createClass({
mixins: [DefaultNameMixin]
render: function () {
return <h2>Hello {this.props.name}</h2>
}
})
написаноmixinМожет быть повторно использован в других компонентах.
из-заmixinsЗначение свойства представляет собой массив, то есть мыВ одном компоненте можно сделать несколько вызововmixin. Небольшое изменение в приведенном выше примере дает:
const DefaultFriendMixin = {
getDefaultProps: function () {
return {
friend: "Yummy"
}
}
}
const ComponentOne = React.createClass({
mixins: [DefaultNameMixin, DefaultFriendMixin]
render: function () {
return (
<div>
<h2>Hello {this.props.name}</h2>
<h2>This is my friend {this.props.friend}</h2>
</div>
)
}
})
мы можем дажеmixinсодержит другиеmixin.
например написать новыйmixin``DefaultPropsв том числе вышеDefaultNameMixinа такжеDefaultFriendMixin :
const DefaultPropsMixin = {
mixins: [DefaultNameMixin, DefaultFriendMixin]
}
const ComponentOne = React.createClass({
mixins: [DefaultPropsMixin]
render: function () {
return (
<div>
<h2>Hello {this.props.name}</h2>
<h2>This is my friend {this.props.friend}</h2>
</div>
)
}
})
Пока что мы можем сделать вывод, чтоmixinКак минимум иметь следующие преимущества:
-
Вы можете использовать одно и то же в нескольких компонентах
mixin; -
Можно использовать несколько в одном компоненте
mixin; -
может быть в том же
mixinнесколько вложенныхmixin;
Но в разных сценариях преимущества могут также превратиться в недостатки:
-
Уничтожьте пакет исходного компонента, возможно, потребуется сохранить новый
stateа такжеpropsсостояние ожидания; -
разные
mixinИменование является агностическим и очень склонным к конфликту; - Может вызывать проблемы с рекурсивными вызовами, увеличивая сложность проекта и сложность обслуживания.;
Помимо,mixinОн имеет собственную логику обработки для таких проблем, как конфликты состояний, конфликты методов и последовательность вызовов нескольких методов жизненного цикла. Заинтересованные студенты могут обратиться к следующим статьям:
компоненты более высокого порядка
из-заmixinВышеупомянутые дефекты существуют, поэтомуReactраздетыйmixin, используйте вместо高阶组件заменить его.
高阶组件По сути, это функция, которая принимает компонент в качестве параметра и возвращает новый компонент..
ReactЧиновники также используют его при реализации некоторых публичных компонентов.高阶组件,Напримерreact-routerсерединаwithRouter,так же какReduxсерединаconnect. здесь сwithRouterНапример.
По умолчанию он должен бытьRouteСуществуют только компоненты, маршруты которых соответствуют рендерингу.this.props, иметь только路由参数, использовать函数式导航выполнение письмаthis.props.history.push('/next')Перейти на страницу соответствующего маршрута.高阶组件серединаwithRouterЭффект заключается в преобразованииRouteНаправьте обернутый компонент, обернув егоRouteвнутрь, чтобыreact-routerтри объектаhistory,location,matchв компонентpropsсвойства, так что это может быть достигнуто函数式导航跳转.
withRouterПринцип реализации:
const withRouter = (Component) => {
const displayName = `withRouter(${Component.displayName || Component.name})`
const C = props => {
const { wrappedComponentRef, ...remainingProps } = props
return (
<RouterContext.Consumer>
{context => {
invariant(
context,
`You should not use <${displayName} /> outside a <Router>`
);
return (
<Component
{...remainingProps}
{...context}
ref={wrappedComponentRef}
/>
)
}}
</RouterContext.Consumer>
)
}
Используйте код:
import React, { Component } from "react"
import { withRouter } from "react-router"
class TopHeader extends Component {
render() {
return (
<div>
导航栏
{/* 点击跳转login */}
<button onClick={this.exit}>退出</button>
</div>
)
}
exit = () => {
// 经过withRouter高阶函数包裹,就可以使用this.props进行跳转操作
this.props.history.push("/login")
}
}
// 使用withRouter包裹组件,返回history,location等
export default withRouter(TopHeader)
из-за高阶组件Суть в том获取组件并且返回新组件的方法, поэтому теоретически это также может быть похоже наmixinТа же реализация множественной вложенности.
Например:
Напишите функцию высшего порядка, которая позволяет петь
import React, { Component } from 'react'
const widthSinging = WrappedComponent => {
return class HOC extends Component {
constructor () {
super(...arguments)
this.singing = this.singing.bind(this)
}
singing = () => {
console.log('i am singing!')
}
render() {
return <WrappedComponent />
}
}
}
Напишите функцию высшего порядка, которая позволяет танцевать
import React, { Component } from 'react'
const widthDancing = WrappedComponent => {
return class HOC extends Component {
constructor () {
super(...arguments)
this.dancing = this.dancing.bind(this)
}
dancing = () => {
console.log('i am dancing!')
}
render() {
return <WrappedComponent />
}
}
}
Используйте вышеуказанные компоненты более высокого порядка
import React, { Component } from "react"
import { widthSing, widthDancing } from "hocs"
class Joy extends Component {
render() {
return <div>Joy</div>
}
}
// 给Joy赋能唱歌和跳舞的特长
export default widthSinging(withDancing(Joy))
Из вышеизложенного видно, что просто используя функции высокого порядка для простой упаковки, оригинальную Джой можно превратить в маленького принца ночного клуба, который может и петь, и танцевать!
Конвенции для использования HOC
В использованииHOCВ то время существуют некоторые жесткие соглашения:
- Передайте несвязанные реквизиты компоненту-оболочке (передайте реквизиты, не связанные с их конкретным содержимым);
- Пошаговая композиция (избегая объединения разных форм вызовов HOC);
- Включите отображаемое отображаемое имя для облегчения отладки (каждый HOC должен соответствовать обычному отображаемому имени);
- Уходите
renderИспользуйте компоненты более высокого порядка в функции (каждый рендеринг более высокого порядка возвращает новый компонент, который влияет на производительность diff); - Статические методы должны быть скопированы (новый компонент, возвращаемый более высоким порядком, не содержит статических методов исходного компонента);
- Избегайте использования ref (ref не будет передан);
Преимущества и недостатки ХОС
Пока что мы можем подвести итог高阶组件(HOC)Преимущества:
-
HOCэто чистая функция, простая в использовании и обслуживании; - также из-за
HOCЭто чистая функция, которая поддерживает передачу нескольких параметров для расширения области применения; -
HOCТо, что возвращается, является компонентом, который можно комбинировать и вкладывать, и он обладает высокой гибкостью;
КонечноHOCЕсть и некоторые проблемы:
- когда несколько
HOCПри вложенности невозможно напрямую судить о подкомпоненте.propsиз которогоHOCответственный за доставку; - Когда родительский и дочерний компоненты имеют одинаковое имя
props, приведет к тому, что родительский компонент переопределит дочерний компонент с тем же именемpropsпроблема, иreactОб ошибках не сообщается, а восприятие разработчика низкое; - Каждый
HOCВсе возвращают новый компонент, что приводит к большому количеству бесполезных компонентов и в то же время углубляет иерархию компонентов, что затрудняет устранение неполадок;
修饰器а также高阶组件Он относится к той же модели и здесь обсуждаться не будет.
Render Props
Render PropsЭто очень гибкий и повторно используемый шаблон, который может инкапсулировать определенное поведение или функцию в компонент и предоставлять его другим компонентам, чтобы позволить другим компонентам иметь такие возможности..
Термин «реквизит рендеринга» относится к методу совместного использования кода между компонентами React с использованием реквизита, значением которого является функция.
ЭтоReactофициальный дляRender PropsОпределение , переведенное на местный язык, звучит так: «Render PropsреализуетсяReact ComponentsТехника совместного использования кода между компонентами,propsсодержитfunctionСвойство типа, которое может вызывать компонентpropsсвойства для реализации внутренней логики рендеринга компонента».
Официальный пример:
<DataProvider render={(data) => <h1>Hello {data.target}</h1>} />
Как указано выше,DataProviderКомпонент имеетrender(можно называть другими именами)propsсвойство, свойство является функцией, и эта функция возвращаетReact Element, вызывая эту функцию внутри компонента для завершения рендеринга, то этот компонент используетсяrender propsТехнология.
Читатель может задаться вопросом: «Зачем нам нужно вызыватьpropsсвойств для достижения внутреннего рендеринга компонента без рендеринга непосредственно в компоненте"?Reactофициальный ответ,render propsне каждыйReactНавыки, которыми должны овладеть разработчики, и даже вы, возможно, никогда не воспользуетесь этим методом, но его существование дает разработчикам еще один выбор, когда они думают о совместном использовании кода компонентов.
Render Propsсцены, которые будут использоваться
Нам может понадобиться часто использовать всплывающие окна при разработке проекта. Пользовательский интерфейс всплывающих окон может постоянно меняться, но функции схожи, а именно打开а также关闭. кantdНапример:
import { Modal, Button } from "antd"
class App extends React.Component {
state = { visible: false }
// 控制弹窗显示隐藏
toggleModal = (visible) => {
this.setState({ visible })
};
handleOk = (e) => {
// 做点什么
this.setState({ visible: false })
}
render() {
const { visible } = this.state
return (
<div>
<Button onClick={this.toggleModal.bind(this, true)}>Open</Button>
<Modal
title="Basic Modal"
visible={visible}
onOk={this.handleOk}
onCancel={this.toggleModal.bind(this, false)}
>
<p>Some contents...</p>
</Modal>
</div>
)
}
}
Выше самое простоеModelИспользуя пример, даже если это простое использование, нам все равно нужно обратить внимание на его состояние отображения и реализовать его метод переключения. Но на самом деле разработчики хотят сосредоточиться только на бизнес-логике, связанной сonOk, идеальное использование должно быть таким:
<MyModal>
<Button>Open</Button>
<Modal title="Basic Modal" onOk={this.handleOk}>
<p>Some contents...</p>
</Modal>
</MyModal>
в состоянии пройтиrender propsДля достижения вышеуказанного использования:
import { Modal, Button } from "antd"
class MyModal extends React.Component {
state = { on: false }
toggle = () => {
this.setState({
on: !this.state.on
})
}
renderButton = (props) => <Button {...props} onClick={this.toggle} />
renderModal = ({ onOK, ...rest }) => (
<Modal
{...rest}
visible={this.state.on}
onOk={() => {
onOK && onOK()
this.toggle()
}}
onCancel={this.toggle}
/>
)
render() {
return this.props.children({
Button: this.renderButton,
Modal: this.renderModal
})
}
}
Таким образом, мы завершили состояние и основную функциюModal, мы используем это на других страницахModal, вам нужно только сосредоточиться на конкретной бизнес-логике.
Как видно из вышеизложенного,render propsнастоящийReactкомпонент вместо чего-то вродеHOCтакой же только одинФункция, которая может возвращать компонент, что также означает использованиеrender propsне какHOCТа же проблема вложенности на уровне компонентов, не беспокойтесь об этомpropsУстранение проблем, возникающих из-за конфликтов имен.
render propsограничения на использование
существуетrender propsследует избегать в箭头函数, так как это влияет на производительность.
Например:
// 不好的示例
class MouseTracker extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
)
}
}
Плохо так писать, потому чтоrenderметод можно отображать несколько раз, используйте箭头函数, из-за чего каждый раз при рендеринге будет передаватьсяrenderЗначение будет отличаться, когда на самом деле нет никакой разницы, что может привести к вопросам производительности.
Так что лучший способ написать это было бы передатьrenderФункции в определены как методы экземпляра, так что даже если мы визуализируем несколько раз, привязка всегда будет одной и той же функцией.
// 好的示例
class MouseTracker extends React.Component {
renderCat(mouse) {
return <Cat mouse={mouse} />
}
render() {
return (
<Mouse render={this.renderTheCat} />
)
}
}
render propsПреимущества и недостатки
-
преимущество
- Имя реквизита можно изменить, взаимного переопределения нет;
- понятный источник реквизита;
- Не будет многоуровневой вложенности компонентов;
-
недостаток
-
сложно писать;
-
невозможно
returnдоступ к данным вне оператора; -
Легко сгенерировать вложенность обратного вызова функции;
Следующий код:
const MyComponent = () => { return ( <Mouse> {({ x, y }) => ( <Page> {({ x: pageX, y: pageY }) => ( <Connection> {({ api }) => { // yikes }} </Connection> )} </Page> )} </Mouse> ) }
-
Hook
ReactЯдром является составляющая, следовательно,ReactВсегда работайте над оптимизацией и совершенствованием способа объявления компонентов. с самого раннего类组件, затем к函数组件, у каждого есть преимущества и недостатки.类组件Он может предоставить нам полный жизненный цикл и состояние (состояние), но очень громоздок в написании, и函数组件Несмотря на то, что он очень краткий и легкий, его ограниченияДолжна быть чистой функцией, не может содержать состояние и не поддерживает жизненные циклы.,следовательно类组件не заменяет函数组件.
а такжеReactкоманда чувствуетКомпоненты лучше всего писать как функции, а не классы, в результате чегоReact Hooks.
Целью разработки React Hooks является улучшение функциональных компонентов, и вы можете написать полнофункциональный компонент, вообще не используя «классы»..
зачем говорить类组件«Громоздкий», чтобы позаимствоватьReactОфициальный пример гласит:
import React, { Component } from "react"
export default class Button extends Component {
constructor() {
super()
this.state = { buttonText: "Click me, please" }
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(() => {
return { buttonText: "Thanks, been clicked!" }
})
}
render() {
const { buttonText } = this.state
return <button onClick={this.handleClick}>{buttonText}</button>
}
}
Выше показан простой компонент кнопки, включая самое основное состояние и метод щелчка.После нажатия кнопки состояние изменяется.
Это очень простой функциональный компонент, но для его реализации требуется много кода. из-за函数组件не содержит состояния, поэтому мы не можем использовать函数组件объявить компонент с вышеуказанными функциями. Но мы можем использоватьHookреализовать:
import React, { useState } from "react"
export default function Button() {
const [buttonText, setButtonText] = useState("Click me, please")
function handleClick() {
return setButtonText("Thanks, been clicked!")
}
return <button onClick={handleClick}>{buttonText}</button>
}
В сравнении,HookКажется светлее в непосредственной близости函数组件При этом он сохраняет собственное состояние.
Первый хук представлен в приведенном выше примере.useState(),Помимо,ReactЧиновник также предоставилuseEffect(),useContext(),useReducer()Подождите крючок. Конкретные хуки и подробности их использования см.официальный.
HookГибкость также ложь, в дополнение к официальным базовым крючкам, мы также можем использовать эту базу пакета и пользовательский крюк к крючкому, что обеспечивает более простое повторное использование кода.
КРЮК выступает
- преимущество
- Легче повторно использовать код;
- Чистый стиль кода;
- меньше кода;
- недостаток
- Асинхронное состояние (функции выполняются независимо, каждая функция имеет отдельную область видимости)
- нужно более рациональное использование
useEffect - Степень детализации мала, и необходимо абстрагироваться от многих сложных логических элементов.
hook
Суммировать
КромеMixinПомимо того, что он немного отстает из-за собственных очевидных недостатков, для高阶组件,render props,react hook, это никак нельзя назвать最佳方案, все они имеют преимущества и недостатки. даже самый популярныйreact hook, хотя каждыйhookЭто выглядит таким коротким и освежающим, но в реальном бизнесе обычно одна бизнес-функция соответствует несколькимhook, а это значит, что при изменении бизнеса необходимо поддерживать несколькоhookизменения, а не сохранениеclassДругими словами, умственная нагрузка может сильно увеличиться. Только так, как подходит вашему бизнесу最佳方案.
Справочная документация: