Práticas recomendadas da biblioteca de componentes compartilhados

12

Estou criando uma biblioteca de componentes React compartilhável.

A biblioteca contém muitos componentes, mas o usuário final pode precisar usar apenas alguns deles.

Quando você agrupa o código no Webpack (ou Parcel ou Rollup), ele cria um único arquivo com todo o código .

Por motivos de desempenho, não quero que todo esse código seja baixado pelo navegador, a menos que seja realmente usado. Estou certo ao pensar que não devo agrupar os componentes? O pacote deve ser deixado para o consumidor dos componentes? Deixo mais alguma coisa para o consumidor dos componentes? Transpilei o JSX e é isso?

Se o mesmo repositório contiver muitos componentes diferentes, o que deve estar no main.js?

otw
fonte
11
Se eu entendi sua pergunta corretamente, você está procurando uma abordagem como esta um dar uma olhada em seu código fonte e você vai ver que eles exportar todos os componentes, bem como as individuais e quando um aplicativo cliente utiliza componentes (e importações individuais componentes em vez do módulo inteiro) o webpack extrairá apenas os arquivos que estavam importedno código, diminuindo o tamanho do pacote.
Edward Chopuryan 24/01

Respostas:

5

Essa é uma resposta extremamente longa, porque essa pergunta merece uma resposta extremamente longa e detalhada, pois o caminho das "melhores práticas" é mais complicado do que apenas algumas respostas em linha.

Como mantivemos nossas bibliotecas internas por mais de 3,5 anos, decidimos de duas maneiras que eu acho que as bibliotecas deveriam ser agrupadas, as compensações dependem do tamanho da sua biblioteca e, pessoalmente, compilamos as duas maneiras para agradar os dois subconjuntos de consumidores.

Método 1: Crie um arquivo index.ts com tudo o que você deseja expor exportado e o pacote cumulativo de destino nesse arquivo como entrada. Agrupe toda a sua biblioteca em um único arquivo index.js e arquivo index.css; Com dependência externa herdada do projeto do consumidor para evitar a duplicação do código da biblioteca. (essência incluída na parte inferior do exemplo de configuração)

  • Prós: Fácil de consumir, pois os consumidores do projeto podem importar tudo do caminho da biblioteca relativa raiz import { Foo, Bar } from "library"
  • Contras: Isso nunca será tremido em árvores; e antes que as pessoas digam fazer isso com o ESM, será possível fazer uma alteração na árvore. O NextJS não oferece suporte ao ESM no estágio atual e nem muitas configurações de projeto, por isso ainda é uma boa ideia compilar essa compilação apenas para o CJS. Se alguém importar um dos seus componentes, eles obterão todo o css e todo o javascript de todos os seus componentes.

Método 2: Isso é para usuários avançados: crie um novo arquivo para cada exportação e use rollup-plugin-multi-input com a opção "preserveModules: true", dependendo de como o sistema css que você está usando também precisa ter certeza de que seu css NÃO é mesclado em um único arquivo, mas cada arquivo css requer (". css") é deixado dentro do arquivo de saída após o rollup e esse arquivo css existe.

  • Prós: Quando os usuários importam {Foo} da "library / dist / foo", eles obtêm apenas o código do Foo e o css do Foo e nada mais.
  • Contras: Esta configuração envolve que o consumidor precise lidar com instruções node_modules require (". Css") em sua configuração de construção com o NextJS, isso é feito com o next-transpile-modulespacote npm.
  • Advertência: Usamos nosso próprio plug-in babel que você pode encontrar aqui: https://www.npmjs.com/package/babel-plugin-qubic para permitir que as pessoas import { Foo,Bar } from "library"e depois com babel o transformem em ...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"

Temos várias configurações de rollup em que realmente usamos os dois métodos; portanto, para os consumidores da biblioteca que não se importam com a trepidação de árvores, basta "Foo from "library"importar e importar o único arquivo css; e para os consumidores de bibliotecas que se preocupam com a trepidação de árvores e apenas usando CSS crítico, eles podem simplesmente ativar nosso plug-in babel.

Guia de rollup para práticas recomendadas:

se você está usando "rollup-plugin-babel": "5.0.0-alpha.1" texto datilografado ou não, construa SEMPRE com Verifique se o seu .babelrc se parece com isso.

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

E com o plug-in babel em rollup parecido com este ...

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

E o seu package.json parecendo ATLEAST assim:

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

E, finalmente, seus externos em rollup parecendo ATLEAST assim.

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.

Por quê?

  • Isso agrupará sua merda para herdar automaticamente a reação / reação e suas outras dependências de pares / externas do projeto do consumidor, o que significa que elas não serão duplicadas no seu pacote.
  • Isso fará parte do ES5
  • Isso exigirá automaticamente ("..") todas as funções auxiliares do babel para objectSpread, classes etc. DO projeto do consumidor, que limpará outros 15-25 KB do tamanho do pacote e significará que as funções auxiliares do objectSpread não serão duplicadas na sua biblioteca output + os projetos que consomem empacotaram a saída.
  • As funções assíncronas ainda funcionarão
  • externals corresponderá a qualquer coisa que comece com esse sufixo de dependência de pares, ou seja, babel-helpers corresponderá externo a babel-helpers / helpers / object-spread

Finalmente, aqui está uma essência para um exemplo de arquivo de configuração de rollup de saída de arquivo index.js. https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3 Onde o destino src / export / index.ts se parece com isso ...

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

Entre em contato se tiver algum problema com o babel, o pacote cumulativo ou tiver alguma dúvida sobre o empacotamento / bibliotecas.

Shanon Jackson
fonte
3

Quando você agrupa o código no Webpack (ou Parcel ou Rollup), ele cria um único arquivo com todo o código.

Por motivos de desempenho, não quero que todo esse código seja baixado pelo navegador, a menos que seja realmente usado

É possível ter arquivos separados gerados para cada componente. O Webpack tem essa capacidade definindo várias entradas e saídas. Digamos que você tenha a seguinte estrutura de um projeto

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

O arquivo Webpack seria algo parecido com isto

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

Mais informações sobre "divisão de código" estão disponíveis nos documentos do Webpack

Se o mesmo repositório contiver muitos componentes diferentes, o que deve estar no main.js?

Há um único campo no package.jsonarquivo chamado main, é bom colocar seu valor de lib/index.jsacordo com a estrutura do projeto acima. E no index.jsarquivo tem todos os componentes exportados. Caso o consumidor queira usar um único componente, é possível acessá-lo simplesmente

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

Estou certo ao pensar que não devo agrupar os componentes? O pacote deve ser deixado para o consumidor dos componentes? Deixo mais alguma coisa para o consumidor dos componentes? Transpilei o JSX e é isso?

Bem você decide. Descobri que algumas bibliotecas do React são publicadas da maneira original, outras - estão agrupadas. Se você precisar de algum processo de construção, defina-o e exporte a versão em pacote.

Espero que todas as suas perguntas sejam respondidas :)

Rashad Ibrahimov
fonte
Obrigado pela resposta. Não quero atualizar minha configuração do Webpack toda vez que adiciono um novo componente, como no seu exemplo. "depende de você. Descobri que algumas bibliotecas do React são publicadas da maneira original, outras - estão agrupadas." Isso está provando não ser o caso. O Create React App funcionou com meus componentes desagrupados OK, mas o Next JS está lançando um erro e claramente só funciona com componentes empacotados, tomando a decisão de minhas mãos.
otw 29/01
Eu tentei o meu melhor para pesquisar :) "Não quero atualizar minha configuração do Webpack toda vez que adiciono um novo componente" - é possível usar algum curinga global para não listar todos os componentes, pois resolve o problema de atualizando a configuração do webpack para cada novo componente. "O próximo JS está lançando um erro" - bem, empacote seu pacote :) obviamente, o pacote bruto funcionaria se incluído apenas no pacote do projeto do consumidor. A versão empacotada funcionará 100%.
Rashad Ibrahimov
1

Você pode dividir seus componentes como o lodash está fazendo pelos métodos deles.

O que você provavelmente tem são componentes separados que você pode permitir importar separadamente ou através do componente principal.

Então o consumidor pode importar o pacote inteiro

import {MyComponent} from 'my-components';

ou suas partes individuais

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

Os consumidores criarão seus próprios pacotes configuráveis ​​com base nos componentes que importam. Isso deve impedir que todo o pacote seja baixado.

Bojan Bedrač
fonte
1

Você deve dar uma olhada no Bit , acho que é uma boa solução para compartilhar, reutilizar e visualizar componentes.

É muito fácil de configurar. Você pode instalar sua biblioteca de bits ou apenas um componente com:

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

Em seguida, você pode importar o componente no seu aplicativo com:

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

A parte boa é que você não precisa se preocupar em configurar o Webpack e todo esse jazz. Bit ainda suporta o controle de versão de seus componentes. Este exemplo mostra um componente de reação da lista de títulos para que você possa ver se isso atende aos seus requisitos ou não

Vicente
fonte
0

Há uma configuração no webpack para criar arquivos de bloco. Para começar, ele criará o pacote principal em vários blocos e o carregará conforme necessário. se o seu projeto tiver módulos bem estruturados, ele não carregará nenhum código que não seja necessário.

Ireal23
fonte