Best Practices für gemeinsam genutzte Komponentenbibliotheken


12

Ich erstelle eine gemeinsam nutzbare React-Komponentenbibliothek.

Die Bibliothek enthält viele Komponenten, aber der Endbenutzer muss möglicherweise nur einige davon verwenden.

Wenn Sie Code mit Webpack (oder Parcel oder Rollup) bündeln, wird eine einzige Datei mit dem gesamten Code erstellt .

Aus Leistungsgründen möchte ich nicht, dass der gesamte Code vom Browser heruntergeladen wird, es sei denn, er wird tatsächlich verwendet. Habe ich Recht, wenn ich denke, dass ich die Komponenten nicht bündeln sollte? Sollte die Bündelung dem Verbraucher der Komponenten überlassen werden? Überlasse ich dem Verbraucher der Komponenten noch etwas? Transpiliere ich nur die JSX und das wars?

Wenn dasselbe Repo viele verschiedene Komponenten enthält, was sollte in main.js enthalten sein?


1
Wenn ich Ihre Frage richtig verstanden suchen Sie einen Ansatz wie dieser ein einen Blick auf den Quellcode nehmen und Sie werden sehen , dass sie alle die Komponenten sowie einzelne exportieren und wenn eine Client - Anwendung ihre Komponenten verwendet (und Importe Einzel Komponenten anstelle des gesamten Moduls) Webpack zieht nur die Dateien, die importedim Code enthalten waren, wodurch die Bundle-Größe verringert wird.
Edward Chopuryan

Antworten:


5

Dies ist eine extrem lange Antwort, da diese Frage eine extrem lange und detaillierte Antwort verdient, da die "Best Practice" -Methode komplizierter ist als nur eine Antwort in wenigen Zeilen.

Ich habe unsere internen Bibliotheken in dieser Zeit mehr als 3,5 Jahre lang unterhalten. Ich habe mich auf zwei Arten entschieden. Ich denke, Bibliotheken sollten gebündelt werden. Die Kompromisse hängen davon ab, wie groß Ihre Bibliothek ist. Persönlich stellen wir beide Möglichkeiten zusammen, um beide Untergruppen von zu befriedigen Verbraucher.

Methode 1: Erstellen Sie eine index.ts-Datei mit allem, was exportiert werden soll, und einem Ziel-Rollup für diese Datei als Eingabe. Bündeln Sie Ihre gesamte Bibliothek in eine einzige Datei index.js und index.css. Mit externen Abhängigkeiten, die vom Consumer-Projekt geerbt wurden, um Doppelarbeit im Bibliothekscode zu vermeiden. (Inhalt unten in der Beispielkonfiguration enthalten)

  • Vorteile: Einfach zu konsumieren, da Projektkonsumenten alles aus dem Pfad der relativen Stammbibliothek importieren können import { Foo, Bar } from "library"
  • Nachteile: Dies wird niemals baumschüttelnd sein; und bevor die Leute sagen, tun Sie dies mit ESM und es wird baumschüttelnd sein. NextJS unterstützt ESM derzeit nicht und viele Projekt-Setups auch nicht. Deshalb ist es immer noch eine gute Idee, diesen Build nur für CJS zu kompilieren. Wenn jemand eine Ihrer Komponenten importiert, erhält er das gesamte CSS und das gesamte Javascript für alle Ihre Komponenten.

Methode 2: Dies ist für fortgeschrittene Benutzer: Erstellen Sie für jeden Export eine neue Datei und verwenden Sie Rollup-Plugin-Multi-Input mit der Option "keepModules: true", je nachdem, welches CSS-System Sie verwenden, müssen Sie dies auch sicherstellen Ihr CSS wird NICHT in eine einzelne Datei zusammengeführt, aber dass jede CSS-Datei die Anweisung (". css") erfordert, bleibt nach dem Rollup in der Ausgabedatei und diese CSS-Datei ist vorhanden.

  • Vorteile: Wenn Benutzer {Foo} aus "library / dist / foo" importieren, erhalten sie nur den Code für Foo und das CSS für Foo und nichts weiter.
  • Nachteile: Bei diesem Setup muss der Consumer in seiner Build-Konfiguration mit NextJS die Anweisungen node_modules require (". CSS") verarbeiten next-transpile-modules Paket npm.
  • Vorsichtsmaßnahme: Wir verwenden unser eigenes Babel-Plugin, das Sie hier finden: https://www.npmjs.com/package/babel-plugin-qubic , damit die Leute es import { Foo,Bar } from "library"mit Babel transformieren können ...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"

Wir haben mehrere Rollup-Konfigurationen, bei denen wir tatsächlich beide Methoden verwenden. Für Bibliothekskonsumenten, die sich nicht für das Schütteln von Bäumen interessieren, können sie einfach "Foo from "library"die einzelne CSS-Datei importieren. und für Bibliothekskonsumenten, die sich um das Schütteln von Bäumen kümmern und nur kritisches CSS verwenden, können sie einfach unser Babel-Plugin aktivieren.

Rollup-Anleitung für Best Practice:

ob Sie Typoskript verwenden oder nicht IMMER mit "rollup-plugin-babel": "5.0.0-alpha.1" erstellen Stellen Sie sicher, dass Ihre .babelrc so aussieht.

{
  "presets": [
    ["@babel/preset-env", {
      "targets": {"chrome": "58", "ie": "11"},
      "useBuiltIns": false
    }],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "absoluteRuntime": false,
      "corejs": false,
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "version": "^7.8.3"
    }],
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-classes",
    ["@babel/plugin-proposal-optional-chaining", {
      "loose": true
    }]
  ]
}

Und mit dem Babel Plugin im Rollup sieht es so aus ...

        babel({
            babelHelpers: "runtime",
            extensions,
            include: ["src/**/*"],
            exclude: "node_modules/**",
            babelrc: true
        }),

Und Ihr package.json sieht mindestens so aus:

    "dependencies": {
        "@babel/runtime": "^7.8.3",
        "react": "^16.10.2",
        "react-dom": "^16.10.2",
        "regenerator-runtime": "^0.13.3"
    },
    "peerDependencies": {
        "react": "^16.12.0",
        "react-dom": "^16.12.0",
    }

Und schließlich sehen Ihre externen Geräte im Rollup mindestens so aus.

const makeExternalPredicate = externalArr => {
    if (externalArr.length === 0) return () => false;
    return id => new RegExp(`^(${externalArr.join('|')})($|/)`).test(id);
};

//... rest of rollup config above external.
    external: makeExternalPredicate(Object.keys(pkg.peerDependencies || {}).concat(Object.keys(pkg.dependencies || {}))),
// rest of rollup config below external.

Warum?

  • Dadurch wird Ihre Scheiße automatisch gebündelt, um React / React-Dom und Ihre anderen Peer- / externen Abhängigkeiten vom Consumer-Projekt zu erben, was bedeutet, dass sie nicht in Ihrem Bundle dupliziert werden.
  • Dies wird auf ES5 gebündelt
  • Dies erfordert automatisch ("..") in allen Babel-Hilfsfunktionen für objectSpread, Klassen usw. aus dem Consumer-Projekt, wodurch weitere 15-25 KB von Ihrer Bundle-Größe gelöscht werden und die Hilfsfunktionen für objectSpread nicht in Ihrer Bibliothek dupliziert werden Ausgabe + die verbrauchenden Projekte gebündelte Ausgabe.
  • Asynchrone Funktionen funktionieren weiterhin
  • Externe Daten stimmen mit allem überein, was mit diesem Peer-Abhängigkeits-Suffix beginnt, dh Babel-Helfer stimmen mit externen für Babel-Helfer / Helfer / Objektverbreitung überein

Schließlich finden Sie hier eine Übersicht über eine Beispiel-Rollup-Konfigurationsdatei für die Ausgabe einer einzelnen index.js-Datei. https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3 Wo das Ziel src / export / index.ts so aussieht ...

export { Button } from "../components/Button/Button";
export * from "../components/Button/Button.styles";

export { Checkbox } from "../components/Checkbox/Checkbox";
export * from "../components/Checkbox/Checkbox.styles";

export { DatePicker } from "../components/DateTimePicker/DatePicker/DatePicker";
export { TimePicker } from "../components/DateTimePicker/TimePicker/TimePicker";
export { DayPicker } from "../components/DayPicker/DayPicker";
// etc etc etc

Lassen Sie mich wissen, wenn Sie Probleme mit Babel, Rollup oder Fragen zu Bundling / Bibliotheken haben.


3

Wenn Sie Code mit Webpack (oder Parcel oder Rollup) bündeln, wird eine einzige Datei mit dem gesamten Code erstellt.

Aus Leistungsgründen möchte ich nicht, dass der gesamte Code vom Browser heruntergeladen wird, es sei denn, er wird tatsächlich verwendet

Es ist möglich, für jede Komponente separate Dateien zu generieren. Webpack verfügt über eine solche Fähigkeit, indem mehrere Einträge und Ausgaben definiert werden. Angenommen, Sie haben die folgende Struktur eines Projekts

- my-cool-react-components
  - src // Folder contains all source code
    - index.js
    - componentA.js
    - componentB.js
    - ...
  - lib // Folder is generated when build
    - index.js // Contains components all together
    - componentA.js
    - componentB.js
    - ...

Die Webpack-Datei würde ungefähr so ​​aussehen

const path = require('path');

module.exports = {
  entry: {
    index: './src/index.js',
    componentA: './src/componentA.js',
    componentB: './src/componentB.js',
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'lib'),
  },
};

Weitere Informationen zum Thema "Code-Aufteilung" finden Sie hier in den Webpack-Dokumenten

Wenn dasselbe Repo viele verschiedene Komponenten enthält, was sollte in main.js enthalten sein?

Es gibt ein einzelnes Feld in der package.jsonDatei mit dem Namen main. Es ist gut, den Wert lib/index.jsentsprechend der obigen Projektstruktur zu setzen . Und in index.jsDatei haben alle Komponenten exportiert. Wenn der Verbraucher eine einzelne Komponente verwenden möchte, ist dies einfach zu erreichen

const componentX = require('my-cool-react-components/lib/componentX');

Habe ich Recht, wenn ich denke, dass ich die Komponenten nicht bündeln sollte? Sollte die Bündelung dem Verbraucher der Komponenten überlassen werden? Überlasse ich dem Verbraucher der Komponenten noch etwas? Transpiliere ich nur die JSX und das wars?

Nun, es liegt an dir. Ich habe festgestellt, dass einige React-Bibliotheken auf originelle Weise veröffentlicht werden, andere auf gebündelte Weise. Wenn Sie einen Erstellungsprozess benötigen, definieren Sie ihn und exportieren Sie die gebündelte Version.

Hoffe, alle deine Fragen sind beantwortet :)


Danke für die Antwort. Ich möchte meine Webpack-Konfiguration nicht jedes Mal aktualisieren müssen, wenn ich eine neue Komponente hinzufüge, wie in Ihrem Beispiel. "Es liegt an Ihnen. Ich habe festgestellt, dass einige React-Bibliotheken auf originelle Weise veröffentlicht werden, andere auf gebündelte Weise." Dies ist nicht der Fall. Create React App funktionierte mit meinen entbündelten Komponenten OK, aber Next JS gibt einen Fehler aus und funktioniert eindeutig nur mit gebündelten Komponenten, wodurch die Entscheidung aus meinen Händen genommen wird.
otw

Ich habe mein Bestes gegeben, um zu recherchieren :) "Ich möchte meine Webpack-Konfiguration nicht jedes Mal aktualisieren müssen, wenn ich eine neue Komponente hinzufüge" - es ist möglich, einen Glob-Wildcard zu verwenden, um nicht alle Komponenten aufzulisten. Dies löst das Problem von Aktualisieren der Webpack-Konfiguration für jede neue Komponente. "Next JS wirft einen Fehler" - nun, dann bündeln Sie Ihr Paket :) Offensichtlich würde das Rohpaket funktionieren, wenn es nur in die Bündelung aus dem Consumer-Projekt einbezogen würde. Die gebündelte Version funktioniert zu 100%.
Rashad Ibrahimov

1

Sie können Ihre Komponenten wie lodash aufteilen für ihre Methoden tut.

Was Sie wahrscheinlich haben, sind separate Komponenten, die Sie separat oder über die Hauptkomponente importieren können.

Dann könnte der Verbraucher das gesamte Paket importieren

import {MyComponent} from 'my-components';

oder seine Einzelteile

import MyComponent from 'my-components/my-component';

Verbraucher erstellen ihre eigenen Bundles basierend auf den von ihnen importierten Komponenten. Das sollte verhindern, dass Ihr gesamtes Bundle heruntergeladen wird.


1

Sie sollten einen Blick darauf werfen Bit . Ich denke, dies ist eine gute Lösung, um Komponenten zu teilen, wiederzuverwenden und zu visualisieren.

Es ist sehr einfach einzurichten. Sie können Ihre Bitbibliothek oder nur eine Komponente installieren mit:

npm i @bit/bit.your-library.components.buttons

Anschließend können Sie die Komponente in Ihre App importieren mit:

import Button3 from '@bit/bit.your-library.components.buttons';

Das Gute daran ist, dass Sie sich nicht um die Konfiguration von Webpack und all dem Jazz kümmern müssen. Bit unterstützt sogar die Versionierung Ihrer Komponenten. Dieses Beispiel zeigt eine Reaktionskomponente in der Titelliste, damit Sie überprüfen können, ob diese Ihren Anforderungen entspricht oder nicht


0

In Webpack gibt es eine Konfiguration zum Erstellen von Blockdateien. Zunächst wird das Hauptpaket in mehrere Blöcke unterteilt und bei Bedarf geladen. Wenn Ihr Projekt über gut strukturierte Module verfügt, wird kein Code geladen, der nicht benötigt wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.