Передовий досвід спільної бібліотеки компонентів


12

Я створюю спільну бібліотеку компонентів React.

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

Коли ви з'єднуєте код за допомогою Webpack (або посилок, або згуртування), він створює один єдиний файл із усім кодом .

З міркувань продуктивності я не хочу, щоб весь цей код завантажувався браузером, якщо він фактично не використовується. Я маю рацію, думаючи, що не слід купувати компоненти? Чи слід упаковку залишати споживачеві компонентів? Чи залишаю я щось інше споживачеві комплектуючих? Я просто перекладаю 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: Це для досвідчених користувачів. Створіть новий файл для кожного експорту та використовуйте збірний плагін-мультивхід з опцією "saveModules: true", залежно від того, яку систему css ви використовуєте, також переконайтесь, що ваш css НЕ об'єднаний в один файл, але кожен файл css вимагає (". css") оператора залишається всередині вихідного файлу після скручування, і цей файл css існує.

  • Плюси: Коли користувачі імпортують {Foo} з "library / dist / foo", вони отримають лише код для Foo, а css для Foo та більше нічого.
  • Мінуси: ця налаштування передбачає, що споживач повинен обробляти node_modules вимагає (". Css") операторів у своїй конфігурації збірки з NextJS, це робиться з next-transpile-modulesпакетом npm.
  • Caveat: Ми використовуємо наш власний плагін Babel, який ви можете знайти тут: https://www.npmjs.com/package/babel-plugin-qubic, щоб дозволити людям, import { Foo,Bar } from "library"а потім за допомогою вавило перетворити його на ...
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
  • Це автоматично зажадає ("..") у всіх функціях помічників для вафелі для objectSpread, класів тощо від споживчого проекту, який видалить ще 15-25 КБ від вашого розміру пакета і означатиме, що допоміжні функції для objectSpread не будуть дублюватися у вашій бібліотеці вихід + споживані проекти, що комплектуються.
  • Функції асинхронізації все ще працюватимуть
  • зовнішні відповідатимуть будь-чому, що починається з цього суфікса одноліткової залежності, тобто babel-helpers відповідатиме зовнішньому для 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 (або посилкою або згуртованим пакетом), він створює один єдиний файл із усім кодом.

З міркувань продуктивності я не хочу, щоб весь цей код завантажувався браузером, якщо він фактично не використовується

Можна створити окремі файли для кожного компонента. 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 публікуються оригінально, інші - у комплекті". Це доводиться не так. OK React App працював з моїми нерозподіленими компонентами Добре, але Next JS видає помилку і, очевидно, працює лише з пакетними компонентами, приймаючи рішення з моїх рук.
— otw

Я намагався зробити все можливе, щоб досліджувати :) "Я не хочу оновлювати конфігурацію Webpack кожного разу, коли я додаю новий компонент" - можливо використовувати деякий глобальний символ, щоб не перелічити всі компоненти, це вирішує проблему оновлення конфігурації вебпакету для кожного нового компонента. "Наступний JS видає помилку" - ну, тоді зв'язуйте свій пакет :) Очевидно, необроблений пакет спрацював би, якби він був включений у пакет з споживчого проекту. У комплекті версія працюватиме 100%.
— Рашад Ібрагімов

1

Ви можете розділити свої компоненти, як лодаш робить для своїх методів.

Можливо, у вас є окремі компоненти, які ви можете дозволити імпортувати окремо або через основний компонент.

Тоді споживач міг імпортувати весь пакет

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 і всього цього джазу. Біт навіть підтримує версію ваших компонентів. Цей приклад показує компонент реакції титульного списку, щоб ви могли подивитися, чи відповідає це вашим вимогам чи ні


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.