Schreiben Sie nicht dasselbe: Wie die wiederverwendbaren Komponenten von React Front-End-Entwicklern helfen können, Anwendungen schneller zu erstellen


Die gleichen Änderungen an drei oder vier verschiedenen Stellen im JS-Code vorzunehmen, ist eine Kunst, die Aufmerksamkeit erfordert. Wenn mehr Elemente vorhanden sind, wird die Codeunterstützung zu Mehl. Daher sollten Sie für langfristige oder große Projekte Code schreiben, damit er in separaten Komponenten ausgeführt werden kann.

Ich bin seit 10 Jahren in der Front-End-Entwicklung tätig und werde über die Verwendung von Komponenten zum Erstellen von Front-End-Elementen sprechen. Dies vereinfacht das Leben eines Front-End-Entwicklers erheblich.

Geschrieben mit Unterstützung von Mail.ru Cloud Solutions .

Was sind Frontend-Komponenten und warum werden sie benötigt?


HTML-Tags - Bedingung "Null" der Komponenten. Jeder von ihnen hat seine eigenen Funktionen und Zwecke.

CSS-Klassen sind die nächste Abstraktionsebene, die normalerweise beim Erstellen einer kleinen Site erreicht wird. In den Regeln zum Anwenden von Stilen auf eine CSS-Klasse beschreiben wir das Verhalten aller Elemente, die Teil einer bedingten Teilmenge von Elementen sind.

Mit den Regeln, die für CSS-Klassen gelten, sowie mit allen anderen Elementen, z. B. HTML-Tags, können Sie die Regeln für die Anzeige einer beliebigen Anzahl von Elementen desselben Typs zentral festlegen und ändern. Es gibt verschiedene Tools für die Arbeit mit Elementstilen - tatsächlich CSS, Sass, LESS, PostCSS und Methoden zum Anwenden von Stilen - BEM, SMACSS, Atomic CSS, CSS-Module, gestaltete Komponenten.

Eigentlich sind Komponenten :

  • Elemente desselben Typs, die beide dieselben Stile und dasselbe Layout (HTML) und Verhalten (JS) haben;
  • ähnlich in Stil und Verhaltenselementen, die sich geringfügig voneinander unterscheiden.

Es wird eine Web Components-Technologie entwickelt, mit der Sie benutzerdefinierte HTML-Tags erstellen und Vorlagen-Codeteile in das Layout aufnehmen können. Dank moderner Front-End-Entwicklungsframeworks wie Angular, Vue, React sind Komponenten jedoch weit verbreitet. JavaScript-Funktionen erleichtern das Verbinden einer Komponente:

import {Header, Footer} from "./components/common";
render() {
    return (
       ...
   )
}

Alle großen Projekte kommen in ihre Bibliothek mit vorgefertigten Komponenten oder verwenden eine der vorgefertigten. Die Frage, wann Sie vom Kopieren von Code zum Erstellen von Komponenten wechseln müssen, wird individuell entschieden. Es gibt keine eindeutigen Rezepte.

Es lohnt sich, sich nicht nur an das Schreiben des Codes zu erinnern, sondern auch an dessen Unterstützung. Durch einfaches Kopieren / Einfügen desselben Layouts und Isolieren von Stilen in CSS-Klassen kann für einige Zeit eine Anzeige ohne besondere Risiken erstellt werden. Wenn jedoch jedem Element eine in JS geschriebene Verhaltenslogik hinzugefügt wird, werden die Vorteile der Wiederverwendung des Codes buchstäblich von 2-3 Elementen wahrgenommen, insbesondere wenn es darum geht, zuvor geschriebenen Code zu unterstützen und zu ändern.

Wiederverwendbare Reaktionskomponenten


Angenommen, unsere Anwendung ist groß genug geworden, und wir haben beschlossen, eine eigene Komponentenbibliothek zu schreiben. Ich schlage vor, dafür das beliebte Front-End-Entwicklungstool React zu verwenden. Einer seiner Vorteile ist die Möglichkeit, eingebettete Komponenten einfach und effizient zu verwenden. Im folgenden Code verwendet die ältere Komponente der App drei verschachtelte Komponenten: AppHeader, Article, AppFooter:

import React from "react";
import AppHeader from "./components/AppHeader";
import Article from "./components/Article";
import AppFooter from "./components/AppFooter";
export default class App extends React.Component {
    constructor(props) {
        super(props); 
        this.state = {
            title : "My App",
            contacts : "8 800 100 20 30"
           firtsArticleTitle : "Welcome",
           secondArticleTitle : "Let's speak about..."
        }
    };

    render() {
        return (
            <>
                <AppHeader 
                title={this.state.title}
            />
            <Article
                   title={this.state.firstArticleTitle}
               />
               <Article
                   title={this.state.secondArticleTitle}
               />               
               <AppFooter
                   contacts={this.state.contacts}
               />
           </>
       )
   }
}

Bitte beachten Sie : Jetzt ist es nicht erforderlich, das Senior-Wrapping-Tag im Layout zu verwenden - normalerweise war es das div. Modern React bietet das Fragment-Tool an, von dem eine verkürzte Aufzeichnung vorliegt <></>. Innerhalb dieser Tags können Sie eine flache Tag-Hierarchie verwenden, wie im obigen Beispiel.

Wir haben drei Bibliothekskomponenten verwendet, von denen sich eine zweimal in einem Block befindet. Daten aus der übergeordneten Anwendung werden an die Requisiten der Komponente übertragen und sind dort über die Eigenschaft verfügbar this.props. Dieser Ansatz ist typisch für React und ermöglicht es Ihnen, schnell eine Ansicht aus typischen Elementen zusammenzustellen. Insbesondere, wenn Ihre Anwendung viele ähnliche Seiten enthält, die sich nur im Inhalt der Artikel (Modell) und in der Funktionalität unterscheiden.

Möglicherweise müssen wir jedoch die Bibliothekskomponente ändern. Beispielsweise können sich Komponenten mit derselben Funktionalität nicht nur im Textinhalt, sondern auch im Design unterscheiden: Farbe, Einrückungen, Ränder. Es ist auch möglich, verschiedene Funktionen derselben Komponente bereitzustellen.

Der folgende Fall wird im Folgenden betrachtet: Abhängig vom Vorhandensein eines Rückrufs kann unsere Komponente "reagieren" oder einfach eine Ansicht bleiben, um ein Element auf der Seite zu rendern:

// App.js
...
render() {
    return (        
        <Article 
            text={this.state.articleText}
            onClick={(e) => this.bindTap(e)}
           customClass={this.state.mainCustomClass}
        />                
    )
}

// Article.js
import React from "react";

export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
       let cName="default";
       if (this.props.customClass) cName = cName + " " this.props.customClass;
       let bgColor="#fff";
       if (this.props.bgColor) bgColor = this.props.bgColor;
        return (
            {this.props.onClick &&
            <div
                   className={cName}
                onClick={(e) => this.props.onClick(e)}
                   style={{background : bgColor}}
            >
                <p>{this.props.text}<p/>
            </div>
            }
            {!this.props.onClick && 
                <div className={cName}>
                <p>{this.props.text}<p/>
                </div>
           }
        )
    }
} 

In React gibt es eine andere Technik zum Erweitern der Funktionen von Komponenten. In den Aufrufparametern können Sie nicht nur Daten oder Rückrufe übertragen, sondern auch das gesamte Layout:

// App.js
...
render() {
    return (        
        <Article 
            title={this.state.articleTitle}
            text={
               <>
                <p>Please read the article</p>
                <p>Thirst of all, I should say programming React is a very good practice.</p>
               </>
            }
        />                
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.text}
            </div>
        )
    }
}

Das interne Layout der Komponente wird bei der Übertragung vollständig reproduziert props.

Es ist häufig bequemer, zusätzliches Layout mithilfe des Einfüge- und Verwendungsmusters auf die Bibliothekskomponente zu übertragen this.props.children. Dieser Ansatz eignet sich besser zum Ändern der allgemeinen Komponenten, die für die typischen Blöcke einer Anwendung oder Site verantwortlich sind, bei denen verschiedene interne Inhalte angenommen werden: Großbuchstaben, Seitenleisten, Anzeigenblöcke und andere.

// App.js
...
render() {
    return (        
        <Article title={this.state.articleTitle}>
           <p>Please read the article</p>
            <p>First of all, I should say programming React is a very good practice.</p>
       </Article>                          
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.children}
            </div>
        )
    }
} 

Komplette Reaktionskomponenten


Die Komponenten, die nur für View verantwortlich sind, wurden oben berücksichtigt. Höchstwahrscheinlich müssen wir jedoch nicht nur das Mapping, sondern auch die Standarddatenverarbeitungslogik an die Bibliotheken senden.

Schauen wir uns die Telefonkomponente an, mit der eine Telefonnummer eingegeben werden kann. Es kann die eingegebene Nummer mithilfe der Plug-in-Validierungsbibliothek maskieren und die übergeordnete Komponente darüber informieren, dass das Telefon korrekt oder falsch eingegeben wurde:

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : true,
            isWrong : false,
            onceValidated : true
           });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : true
            })
        }
        const validationFail = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : false,
            isWrong : true,
            });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : false
            })
        }
        new Validator({
            element : this.ref.current,
            callbacks : {
            success : validationSuccess,
            fail : validationFail
            }
        });
    }

    render() {
        return (
            <div className="form-group">
            <labeL htmlFor={this.props.name}>
                    <input 
                name={this.props.name}
                id={this.props.name}
                type="tel"
                placeholder={this.props.placeholder}
                defaultValue={this.state.value}
                ref={this.ref}
                />
            </label>
            </div>
        )
    }

} 

Diese Komponente hat bereits einen internen Statusstatus, von dem ein Teil mit dem externen Code geteilt werden kann, der sie aufgerufen hat. Der andere Teil verbleibt innerhalb der Komponente, im obigen Beispiel onceValidated. Somit ist ein Teil der Logik der Komponente vollständig darin eingeschlossen.

Es kann gesagt werden, dass typisches Verhalten unabhängig von anderen Teilen der Anwendung ist. Abhängig davon, ob die Nummer validiert wurde oder nicht, können wir beispielsweise verschiedene Textansagen anzeigen. Wir haben nicht nur die Anzeige, sondern auch die Datenverarbeitungslogik in eine separate wiederverwendbare Komponente aufgenommen.

MV-Komponenten


Wenn unsere Standardkomponente erweiterte Funktionen unterstützt und über eine ausreichend entwickelte Verhaltenslogik verfügt, lohnt es sich, diese in zwei Teile aufzuteilen:

  • "Smart" für die Arbeit mit Daten ( Model);
  • "Dumm" zur Anzeige ( View).

Die Verbindung wird durch Aufrufen einer Komponente fortgesetzt. Jetzt wird es sein Model. Der zweite Teil - View- wird in heißen render()mit props, von denen einige aus der Anwendung kam, und der andere Teil ist bereits Stand der Komponente selbst:

// App.js
...
render() {
    return (        
        <Phone 
            name={this.state.mobilePhoneName}
            placeholder={"You mobile phone"}
        />                
    )
}

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
import PhoneView from "./PhoneView";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            ...
        }
        const validationFail = (formattedValue) => {
            ...
        }
        new Validator({
           element : this.ref.current,
            ...
        });
    }    

   render() {
        return (
            <PhoneView
                name={this.props.name}
            placeholder={this.props.placeholder}
               value={this.state.value}
               ref={this.ref}
            />
        )
    }
}

// PhoneView.js
import React from "react";
const PhoneView = React.forwardRef((props, ref) => (   
    <div className="form-group">
        <labeL htmlFor={props.name}>
            <input 
                name={props.name}
            id={props.name}
            type="tel"
            ref={ref}
            placeholder={props.placeholder}
            value={props.value}                
            />
        </label>
    </div>    
));
export default PhoneView;

Es lohnt sich, auf das Werkzeug zu achten React.forwardRef(). Sie können refeine Komponente erstellen Phone, diese jedoch direkt an Layoutelemente in binden PhoneView. Alle Manipulationen wie gewohnt refsind dann in verfügbar Phone. Zum Beispiel, wenn wir einen Telefonnummernprüfer anschließen müssen.

Ein weiteres Merkmal dieses Ansatzes ist die maximale Vereinfachung der ViewKomponente. Tatsächlich wird dieser Teil ohne seine integrierten Methoden als const definiert. Nur Layout und Datenersetzung aus dem Modell.

Jetzt ist unsere wiederverwendbare Komponente in unterteilt Modelund Viewwir können Geschäftslogik und Layoutcode separat entwickeln. Wir können das Layout auch aus noch kleineren Komponenten zusammensetzen.

Der Status der gesamten Anwendung, die auf Komponenten ausgeführt wird


Es wurde oben gezeigt, dass die Anwendung Komponenten sowohl durch Übergeben von Parametern oder Satz als auch durch Verwenden von Rückrufen verwalten kann.

Damit die Anwendung erfolgreich funktioniert, muss die obere Ebene aussagekräftige Daten zum Status der verschachtelten wiederverwendeten Komponenten erhalten. Dies ist jedoch möglicherweise nicht die höchste Ebene der gesamten Anwendung.

Wenn wir einen Client-Autorisierungsblock und wiederverwendbare Komponenten zur Eingabe eines Logins und eines Kennworts haben, muss die gesamte Anwendung nicht wissen, in welchem ​​Zustand sich diese einfachen Komponenten zu einem bestimmten Zeitpunkt befinden. Vielmehr kann der Berechtigungsblock selbst einen neuen Status basierend auf den Status einfacher wiederverwendeter Komponenten berechnen und weitergeben: Der Autorisierungsblock ist korrekt gefüllt oder nicht.

Bei einer großen Verschachtelung von Komponenten ist es notwendig, die Organisation der Arbeit mit Daten zu überwachen, um immer zu wissen, wo die "Quelle der Wahrheit" liegt. Ich habe bereits

über einige der Schwierigkeiten geschrieben, die mit dem asynchronen Zustandsübergang in React verbunden sind . Wiederverwendbare Komponenten sollten über die Rückrufe immer die Daten weiterleiten, die zur Verwaltung der möglichen Komponentenblöcke erforderlich sind. Sie müssen jedoch keine zusätzlichen Daten übertragen, um keine unnötigen Neuzeichnungen großer Teile des DOM-Baums zu verursachen und den Code für die Verarbeitung von Änderungen an Komponenten nicht zu komplizieren. Ein anderer Ansatz zum Organisieren von Daten besteht darin, einen Komponentenaufrufkontext zu verwenden. Dies ist eine native React-Methode. , verfügbar ab Version 16.3, nicht zu verwechseln mit einer früheren ! ..



createContextReact getChildContext

Dann müssen Sie keine Requisiten durch die „Dicke“ der Komponenten im Baum der Komponentenverschachtelung führen. Oder verwenden Sie spezielle Bibliotheken für die Datenverwaltung und Änderungsbereitstellung, z. B. Redux und Mobx (siehe Artikel zum Mobx + React-Bundle ).

Wenn wir eine Bibliothek wiederverwendbarer Komponenten auf Mobx erstellen, hat jeder dieser Komponententypen einen eigenen Speicher. Das heißt, die "Quelle der Wahrheit" über den Status jeder Instanz der Komponente mit End-to-End-Zugriff von überall in der gesamten Anwendung. Im Fall von Redux und seinem einzigen Data Warehouse sind alle Status aller Komponenten an einem Ort verfügbar.

Einige vorgefertigte Bibliotheken mit React-Komponenten


Es gibt beliebte Bibliotheken mit vorgefertigten Komponenten, die in der Regel ursprünglich interne Projekte von Unternehmen waren:

  1. Material-UI — , Material Design Google.
  2. React-Bootstrap — , . : API , , .
  3. VKUI — «». VK mini apps, (. VK mini apps). VKUI «». «» . vkconnect — iOS Android.
  4. ARUI Feather — React- -. , . open source, .

Alle diese Bibliotheken zielen darauf ab, Layouts von Elementen und deren Gestaltung zu erstellen. Die Interaktion mit der Umgebung wird mithilfe von Rückrufen konfiguriert. Wenn Sie also vollwertige wiederverwendbare Komponenten erstellen möchten, die im dritten und vierten Absatz des Artikels beschrieben sind, müssen Sie dies selbst tun. Vielleicht nehmen wir als Komponenten der Ansicht solcher Komponenten eine der oben vorgestellten populären Bibliotheken.

Dieser Artikel wurde mit Unterstützung von Mail.ru Cloud Solutions verfasst .


All Articles