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


12

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

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

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

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

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


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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.