Рекомендации по использованию библиотеки общих компонентов

12

Я создаю разделяемую библиотеку компонентов React.

Библиотека содержит много компонентов, но конечному пользователю может понадобиться использовать только некоторые из них.

Когда вы связываете код с помощью Webpack (или Parcel или Rollup), он создает один файл со всем кодом .

Из соображений производительности я не хочу, чтобы весь этот код загружался браузером, если он фактически не используется. Правильно ли я считаю, что не следует связывать компоненты? Должна ли комплектация быть оставлена ​​потребителю компонентов? Оставляю ли я что-нибудь еще потребителю компонентов? Должен ли я просто перенести JSX и все?

Если в одном репозитории много разных компонентов, что должно быть в main.js?

OTW
источник
1
Если я правильно понял ваш вопрос , вы ищете подход , как этот один взглянуть на их исходном коде , и вы увидите , что они экспортируют все компоненты, а также индивидуальные и когда клиентское приложение использует их компоненты (и импорт отдельного компоненты вместо всего модуля) Webpack будет тянуть только те файлы, которые были importedв коде, таким образом уменьшая размер пакета.
Эдвард Чопурян

Ответы:

5

Это очень длинный ответ, потому что этот вопрос заслуживает очень длинного и подробного ответа, поскольку способ «наилучшей практики» является более сложным, чем ответ в несколько строк.

Iv'e поддерживал наши собственные библиотеки в течение 3,5 с лишним лет, и тогда я остановился на двух способах, которые, по моему мнению, должны быть связаны между собой библиотеками. Компромиссы зависят от того, насколько велика ваша библиотека, и лично мы компилируем оба способа, чтобы удовлетворить оба подмножества потребители.

Способ 1. Создайте файл index.ts со всем, что вы хотите, чтобы он был экспортирован, и целевым накоплением для этого файла в качестве входных данных. Объедините всю свою библиотеку в один файл index.js и файл index.css; С внешней зависимостью, унаследованной от потребительского проекта, чтобы избежать дублирования библиотечного кода. (Гист включен в нижней части примера конфигурации)

  • Плюсы: легко использовать, так как пользователи проекта могут импортировать все из корневого пути относительно библиотеки import { Foo, Bar } from "library"
  • Минусы: это никогда не будет шататься по дереву; и, прежде чем люди скажут, сделайте это с ESM, и это будет с треском. NextJS не поддерживает ESM на данном текущем этапе и не поддерживает множество настроек проекта, поэтому все еще хорошая идея скомпилировать эту сборку только для CJS. Если кто-то импортирует 1 из ваших компонентов, он получит все css и весь javascript для всех ваших компонентов.

Метод 2: Это для опытных пользователей: создайте новый файл для каждого экспорта и используйте rollup-plugin-multi-input с опцией «preserveModules: true» в зависимости от того, какую систему css вы используете, вам также нужно убедиться, что ваш css НЕ объединен в один файл, но для каждого css-файла требуется оператор (". css"), который остается внутри выходного файла после свертывания, и этот css-файл существует.

  • Плюсы: когда пользователи импортируют {Foo} из "library / dist / foo", они получают только код для Foo и css для Foo и ничего более.
  • Минусы: эта настройка подразумевает, что потребитель должен обрабатывать операторы node_modules require (". Css") в своей конфигурации сборки с помощью NextJS, это делается с next-transpile-modulesпомощью пакета npm.
  • Предостережение: Мы используем наш собственный плагин Babel, который вы можете найти здесь: https://www.npmjs.com/package/babel-plugin-qubic, чтобы позволить людям, import { Foo,Bar } from "library"а затем с Babel преобразовать его в ...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"

У нас есть несколько конфигураций накопления, где мы фактически используем оба метода; поэтому для пользователей библиотеки, которые не заботятся о дрожании дерева, можно просто сделать "Foo from "library"и импортировать один файл CSS; и для потребителей библиотек, которые заботятся о тряске деревьев и используют только критические CSS, они могут просто включить наш плагин babel.

Сводное руководство для лучшей практики:

Используете ли вы машинопись или нет ВСЕГДА "rollup-plugin-babel": "5.0.0-alpha.1" собирать с Убедитесь, что ваш .babelrc выглядит так.

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

И с плагином Babel в свертке выглядит вот так ...

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

И ваш package.json выглядит как минимум так:

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

И, наконец, ваши внешности в свертке выглядят как минимум такими.

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.

Почему?

  • Это автоматически связывает ваше дерьмо с тем, чтобы унаследовать реагировать / реагировать на дом и ваши другие равноправные / внешние зависимости от потребительского проекта, что означает, что они не будут дублироваться в вашем пакете.
  • Это будет в комплекте с ES5
  • Это автоматически потребует ("..") во всех вспомогательных функциях babel для objectSpread, классов и т. Д. От потребительского проекта, который уничтожит еще 15-25 КБ от размера вашего пакета и означает, что вспомогательные функции для objectSpread не будут дублироваться в вашей библиотеке. выходные данные + связанные проекты.
  • Асинхронные функции все еще будут работать
  • externals будет соответствовать всему, что начинается с этого суффикса зависимости от сверстников, т.е. babel-helpers будет совпадать с external для babel-helpers / helpers / object-spread

И наконец, вот пример файла конфигурации выходного файла с единственным index.js. https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3 Где целевой src / export / index.ts выглядит следующим образом ...

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

Дайте мне знать, если у вас возникли проблемы с babel, накопительным пакетом или у вас есть вопросы по комплектации / библиотекам.

Шенон Джексон
источник
3

Когда вы связываете код с помощью Webpack (или Parcel или Rollup), он создает один файл со всем кодом.

Из соображений производительности я не хочу, чтобы весь этот код загружался браузером, если он фактически не используется

Можно создать отдельные файлы для каждого компонента. Webpack имеет такую ​​возможность, определяя несколько входов и выходов. Допустим, у вас есть следующая структура проекта

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

Файл Webpack будет выглядеть примерно так

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

Более подробная информация о «разделении кода» находится в документации по Webpack.

Если в одном репозитории много разных компонентов, что должно быть в main.js?

В package.jsonфайле есть одно поле с именем main, и его значение следует lib/index.jsуказывать в соответствии с приведенной выше структурой проекта. И в index.jsфайле все компоненты экспортируются. В случае, если потребитель хочет использовать один компонент, это можно сделать, просто выполнив

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

Правильно ли я считаю, что не следует связывать компоненты? Должна ли комплектация быть оставлена ​​потребителю компонентов? Оставляю ли я что-нибудь еще потребителю компонентов? Должен ли я просто перенести JSX и все?

Ну, решать тебе. Я обнаружил, что некоторые библиотеки React публикуются оригинальным способом, другие - в комплекте. Если вам нужен процесс сборки, определите его и экспортируйте в комплекте версию.

Надеюсь, на все ваши вопросы ответили :)

Рашад Ибрагимов
источник
Спасибо за ответ. Я не хочу обновлять конфигурацию Webpack каждый раз, когда добавляю новый компонент, как в вашем примере. «Это зависит от вас. Я обнаружил, что некоторые библиотеки React публикуются оригинальным образом, а другие - в комплекте». Это оказывается не так. Приложение «Создать React» работало с моими разделенными компонентами, все в порядке, но Next JS выдает ошибку и явно работает только со связанными компонентами, принимая решение из моих рук.
От
Я старался изо всех сил исследовать :) «Я не хочу обновлять свою конфигурацию Webpack каждый раз, когда добавляю новый компонент» - возможно использовать какой-то глобальный подстановочный знак, чтобы не перечислить все компоненты, это решает проблему обновление конфигурации веб-пакета для каждого нового компонента. «Далее JS выдает ошибку» - ну, тогда свяжите ваш пакет :), очевидно, сырой пакет будет работать, если он будет включен только в пакет из потребительского проекта. В комплекте версия будет работать на 100%.
Рашад Ибрагимов
1

Вы можете разделить ваши компоненты, как это делает lodash для их методов.

Вероятно, у вас есть отдельные компоненты, которые вы можете разрешить импортировать отдельно или через основной компонент.

Тогда потребитель может импортировать всю упаковку

import {MyComponent} from 'my-components';

или его отдельные части

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

Потребители будут создавать свои собственные пакеты на основе компонентов, которые они импортируют. Это должно предотвратить загрузку всего вашего пакета.

Боян Бедрач
источник
1

Вы должны взглянуть на Bit , я думаю, что это хорошее решение для обмена, повторного использования и визуализации компонентов.

Это очень легко настроить. Вы можете установить свою битовую библиотеку или просто компонент с:

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

Затем вы можете импортировать компонент в ваше приложение с помощью:

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

Хорошая часть заключается в том, что вам не нужно беспокоиться о настройке Webpack и всего этого джаза. Bit даже поддерживает управление версиями ваших компонентов. В этом примере показан компонент реакции списка заголовков, поэтому вы можете посмотреть, соответствует ли он вашим требованиям или нет

Висенте
источник
0

В веб-пакете есть конфигурация для создания чанков. Для начала создадим основной пакет на несколько кусков и загрузим его по мере необходимости. если ваш проект имеет хорошо структурированные модули, он не будет загружать код, который не требуется.

Ireal23
источник