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


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.