Meilleures pratiques pour la bibliothèque de composants partagés

12

Je crée une bibliothèque de composants React partageable.

La bibliothèque contient de nombreux composants, mais l'utilisateur final n'aura peut-être qu'à en utiliser quelques-uns.

Lorsque vous regroupez du code avec Webpack (ou Parcel ou Rollup), il crée un fichier unique avec tout le code .

Pour des raisons de performances, je ne souhaite pas que tout ce code soit téléchargé par le navigateur à moins qu'il ne soit réellement utilisé. Ai-je raison de penser que je ne devrais pas regrouper les composants? L'emballage doit-il être laissé au consommateur des composants? Dois-je laisser autre chose au consommateur des composants? Dois-je simplement transpiler le JSX et c'est tout?

Si le même référentiel contient de nombreux composants différents, que devrait contenir main.js?

otw
la source
1
Si j'ai bien compris votre question, vous cherchez une approche comme celle- ci, jetez un oeil à leur code source et vous verrez qu'ils exportent tous les composants ainsi que les composants individuels et lorsqu'une application cliente utilise leurs composants (et importe individuellement composants au lieu du module entier) webpack ne tirera que les fichiers qui étaient importeddans le code, diminuant ainsi la taille du bundle.
Edward Chopuryan

Réponses:

5

Il s'agit d'une réponse extrêmement longue car cette question mérite une réponse extrêmement longue et détaillée, car la méthode des "meilleures pratiques" est plus compliquée qu'une simple réponse en quelques lignes.

Iv'e a maintenu nos bibliothèques internes pendant plus de 3,5 ans pendant cette période iv'e a opté pour deux façons, je pense que les bibliothèques devraient être regroupées, les compromis dépendent de la taille de votre bibliothèque et, personnellement, nous compilons les deux façons de satisfaire les deux sous-ensembles de les consommateurs.

Méthode 1: créez un fichier index.ts avec tout ce que vous souhaitez exposer exporté et rollup cible à ce fichier comme entrée. Regroupez votre bibliothèque entière dans un seul fichier index.js et un fichier index.css; Avec des dépendances externes héritées du projet consommateur pour éviter la duplication du code de bibliothèque. (l'essentiel inclus au bas de l'exemple de configuration)

  • Avantages: Facile à consommer car les consommateurs du projet peuvent tout importer depuis le chemin d'accès de la bibliothèque relative à la racine import { Foo, Bar } from "library"
  • Inconvénients: Ce ne sera jamais secouable par les arbres; et avant que les gens ne disent cela, faites avec ESM et ce sera secouable. NextJS ne prend pas en charge ESM à ce stade actuel et pas plus que beaucoup de configurations de projet, c'est pourquoi c'est toujours une bonne idée de compiler cette build pour juste CJS. Si quelqu'un importe 1 de vos composants, il obtiendra tous les css et tous les javascript de tous vos composants.

Méthode 2: c'est pour les utilisateurs avancés: créez un nouveau fichier pour chaque exportation et utilisez rollup-plugin-multi-input avec l'option "preserveModules: true" selon la façon dont le système css que vous utilisez, vous devez également vous assurer que votre css n'est PAS fusionné en un seul fichier mais que chaque fichier css nécessite une instruction (". css") est laissé dans le fichier de sortie après le rollup et ce fichier css existe.

  • Avantages: Lorsque les utilisateurs importent {Foo} à partir de "library / dist / foo", ils n'obtiendront que le code pour Foo, et le CSS pour Foo et rien de plus.
  • Inconvénients: Cette configuration implique que le consommateur doit gérer les instructions node_modules require (". Css") dans sa configuration de construction avec NextJS, cela se fait avec le next-transpile-modulespackage npm.
  • Avertissement: nous utilisons notre propre plugin babel que vous pouvez trouver ici: https://www.npmjs.com/package/babel-plugin-qubic pour permettre aux gens de le import { Foo,Bar } from "library"transformer en babel ...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"

Nous avons plusieurs configurations de cumul où nous utilisons en fait les deux méthodes; Ainsi, pour les consommateurs de bibliothèques qui ne se soucient pas de l'agitation des arbres, il suffit de faire "Foo from "library"et d'importer le fichier CSS unique; et pour les consommateurs de bibliothèques qui se soucient de secouer les arbres et qui n'utilisent que des css critiques, ils peuvent simplement activer notre plugin babel.

Guide de synthèse des meilleures pratiques:

si vous utilisez tapuscrit ou non TOUJOURS construit avec "rollup-plugin-babel": "5.0.0-alpha.1" Assurez-vous que votre .babelrc ressemble à ceci.

{
  "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
    }]
  ]
}

Et avec le plugin babel en rollup qui ressemble à ceci ...

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

Et votre package.json ressemblant à ATLEAST comme ceci:

    "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",
    }

Et enfin, vos externes en résumé ressemblant à ATLEAST comme ceci.

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.

Pourquoi?

  • Cela regroupera votre merde pour hériter automatiquement de react / react-dom et de vos autres dépendances pairs / externes du projet consommateur, ce qui signifie qu'elles ne seront pas dupliquées dans votre bundle.
  • Ce sera groupé à ES5
  • Cela nécessitera automatiquement ("..") dans toutes les fonctions d'assistance babel pour objectSpread, les classes, etc. du projet consommateur qui effacera 15-25 Ko supplémentaires de la taille de votre bundle et signifiera que les fonctions d'assistance pour objectSpread ne seront pas dupliquées dans votre bibliothèque sortie + la sortie groupée des projets consommateurs.
  • Les fonctions asynchrones continueront de fonctionner
  • externals correspondra à tout ce qui commence par ce suffixe de dépendance des pairs, c'est-à-dire que babel-helpers correspondra à external pour babel-helpers / helpers / object-spread

Enfin, voici un aperçu d'un exemple de fichier de configuration de cumul de sortie de fichier index.js unique. https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3 Où le src / export / index.ts cible ressemble à ceci ...

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

Faites-moi savoir si vous rencontrez des problèmes avec babel, rollup ou si vous avez des questions sur le regroupement / bibliothèques.

Shanon Jackson
la source
3

Lorsque vous regroupez du code avec Webpack (ou Parcel ou Rollup), il crée un seul fichier avec tout le code.

Pour des raisons de performances, je ne souhaite pas que tout ce code soit téléchargé par le navigateur, sauf s'il est réellement utilisé.

Il est possible de générer des fichiers séparés pour chaque composant. Webpack a une telle capacité en définissant plusieurs entrées et sorties. Disons que vous avez la structure suivante d'un projet

- 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
    - ...

Le fichier Webpack ressemblerait à quelque chose comme ceci

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'),
  },
};

Plus d'informations sur le "fractionnement de code" sont ici dans les documents Webpack

Si le même référentiel contient de nombreux composants différents, que devrait contenir main.js?

Il y a un seul champ dans le package.jsonfichier nommé main, il est bon de mettre sa valeur en lib/index.jsfonction de la structure du projet ci-dessus. Et dans le index.jsfichier, tous les composants sont exportés. Dans le cas où le consommateur souhaite utiliser un seul composant, il est accessible en faisant simplement

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

Ai-je raison de penser que je ne devrais pas regrouper les composants? L'emballage doit-il être laissé au consommateur des composants? Dois-je laisser autre chose au consommateur des composants? Dois-je simplement transpiler le JSX et c'est tout?

Bien c'est comme tu veux. J'ai constaté que certaines bibliothèques React sont publiées de manière originale, d'autres - sont regroupées. Si vous avez besoin d'un processus de construction, définissez-le et exportez la version groupée.

J'espère que toutes vos questions auront une réponse :)

Rashad Ibrahimov
la source
Merci pour la réponse. Je ne veux pas avoir à mettre à jour ma configuration Webpack à chaque fois que j'ajoute un nouveau composant, comme dans votre exemple. "c'est à vous de décider. J'ai constaté que certaines bibliothèques React sont publiées de manière originale, d'autres - sont regroupées." Cela s'avère ne pas être le cas. Create React App a fonctionné avec mes composants dégroupés OK, mais Next JS génère une erreur et ne fonctionne clairement qu'avec les composants groupés, prenant la décision de mes mains.
OTW
J'ai fait de mon mieux pour faire des recherches :) "Je ne veux pas avoir à mettre à jour ma configuration Webpack à chaque fois que j'ajoute un nouveau composant" - possible d'utiliser un glob-wildcard pour ne pas lister tous les composants, cela résout le problème de mise à jour de la configuration webpack pour chaque nouveau composant. "Le prochain JS génère une erreur" - eh bien, alors regroupez votre package :) évidemment, le package brut fonctionnerait s'il n'était inclus que dans le regroupement du projet consommateur. La version fournie fonctionnera à 100%.
Rashad Ibrahimov
1

Vous pouvez diviser vos composants comme le fait lodash pour leurs méthodes.

Ce que vous avez probablement, ce sont des composants séparés que vous pouvez autoriser à importer séparément ou via le composant principal.

Ensuite, le consommateur pourrait importer l'ensemble du colis

import {MyComponent} from 'my-components';

ou ses parties individuelles

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

Les consommateurs créeront leurs propres offres groupées en fonction des composants qu'ils importent. Cela devrait empêcher le téléchargement de votre ensemble.

Bojan Bedrač
la source
1

Vous devriez jeter un œil à Bit , je pense que c'est une bonne solution pour partager, réutiliser et visualiser les composants.

C'est très simple à installer. Vous pouvez installer votre bibliothèque de bits ou simplement un composant avec:

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

Ensuite, vous pouvez importer le composant dans votre application avec:

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

La bonne partie est que vous n'avez pas à vous soucier de la configuration de Webpack et de tout ce jazz. Bit prend même en charge la gestion des versions de vos composants. Cet exemple montre un composant réagissant à la liste de titres afin que vous puissiez vérifier s'il répond à vos besoins ou non

Vicente
la source
0

Il y a une configuration dans webpack pour créer des fichiers de morceaux. Pour commencer, il créera le paquet principal en plusieurs morceaux et le chargera comme requis. si votre projet a des modules bien structurés, il ne chargera aucun code qui n'est pas requis.

Ireal23
la source