Praktik terbaik perpustakaan komponen bersama


12

Saya membuat pustaka komponen React yang dapat dibagikan.

Perpustakaan berisi banyak komponen tetapi pengguna akhir hanya perlu menggunakan beberapa dari mereka.

Ketika Anda bundel kode dengan Webpack (atau Paket atau Rollup) itu membuat satu file tunggal dengan semua kode .

Untuk alasan kinerja, saya tidak ingin semua kode itu diunduh oleh browser kecuali jika benar-benar digunakan. Apakah saya benar dalam berpikir bahwa saya tidak boleh membundel komponen? Haruskah bundling diserahkan kepada konsumen komponen? Apakah saya menyerahkan hal lain kepada konsumen komponen? Apakah saya hanya mentranspisikan JSX dan hanya itu?

Jika repo yang sama berisi banyak komponen yang berbeda, apa yang seharusnya ada di main.js?


1
Jika aku mengerti pertanyaan Anda dengan benar Anda mencari pendekatan seperti ini satu kita lihat kode sumber mereka dan Anda akan melihat bahwa mereka mengekspor semua komponen maupun yang individual dan ketika aplikasi klien menggunakan komponen mereka (dan impor individual komponen dan bukan seluruh modul) webpack hanya akan menarik file-file yang ada importeddalam kode sehingga mengurangi ukuran bundel.
Edward Chopuryan

Jawaban:


5

Ini adalah jawaban yang sangat panjang karena pertanyaan ini layak mendapatkan jawaban yang sangat panjang dan terperinci karena cara "praktik terbaik" lebih rumit daripada hanya beberapa tanggapan baris.

Iv'e mempertahankan perpustakaan di rumah kami selama 3,5+ tahun pada waktu itu. Saya setuju dengan dua cara. Saya pikir perpustakaan harus dibundel tergantung dari seberapa besar perpustakaan Anda dan secara pribadi kami menyusun kedua cara untuk menyenangkan kedua himpunan bagian dari konsumen.

Metode 1: Buat file index.ts dengan semua yang Anda inginkan diekspor dan rollup target pada file ini sebagai inputnya. Gabungkan seluruh perpustakaan Anda menjadi satu file index.js dan file index.css; Dengan ketergantungan eksternal yang diwarisi dari proyek konsumen untuk menghindari duplikasi kode perpustakaan. (intisari di bagian bawah contoh konfigurasi)

  • Kelebihan: Mudah dikonsumsi karena konsumen proyek dapat mengimpor semuanya dari jalur pustaka relatif root import { Foo, Bar } from "library"
  • Cons: Ini tidak akan pernah menjadi goyah pohon; dan sebelum orang mengatakan melakukan ini dengan ESM dan itu akan berbahaya. NextJS tidak mendukung ESM pada tahap saat ini dan juga tidak banyak pengaturan proyek itu sebabnya masih merupakan ide bagus untuk mengkompilasi build ini menjadi hanya CJS. Jika seseorang mengimpor 1 komponen Anda, mereka akan mendapatkan semua css dan semua javascript untuk semua komponen Anda.

Metode 2: Ini untuk pengguna tingkat lanjut: Buat file baru untuk setiap ekspor dan gunakan rollup-plugin-multi-input dengan opsi "preserveModules: true" tergantung pada bagaimana sistem css apa yang Anda gunakan, Anda juga perlu memastikan bahwa css Anda TIDAK digabung menjadi satu file tetapi setiap file css membutuhkan pernyataan (". css") yang tersisa di dalam file output setelah rollup dan file css itu ada.

  • Pro: Ketika pengguna mengimpor {Foo} dari "library / dist / foo" mereka hanya akan mendapatkan kode untuk Foo, dan css untuk Foo dan tidak lebih.
  • Cons: Setup ini melibatkan konsumen yang harus menangani pernyataan node_modules memerlukan (". Css") dalam konfigurasi build mereka dengan NextJS ini dilakukan dengan next-transpile-modulespaket npm.
  • Peringatan: Kami menggunakan plugin babel kami sendiri yang dapat Anda temukan di sini: https://www.npmjs.com/package/babel-plugin-qubic untuk memungkinkan orang lain import { Foo,Bar } from "library"dan kemudian dengan babel mengubahnya menjadi ...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"

Kami memiliki beberapa konfigurasi rollup di mana kami benar-benar menggunakan kedua metode; jadi bagi konsumen perpustakaan yang tidak peduli dengan guncangan pohon, cukup lakukan "Foo from "library"dan impor file css tunggal; dan untuk konsumen perpustakaan yang peduli terhadap goncangan pohon dan hanya menggunakan css kritis, mereka bisa menyalakan plugin babel kami.

Panduan rollup untuk praktik terbaik:

apakah Anda menggunakan naskah atau SELALU membangun dengan "rollup-plugin-babel": "5.0.0-alpha.1" Pastikan .babelrc Anda terlihat seperti ini.

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

Dan dengan plugin babel dalam tampilan seperti ini ...

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

Dan package.json Anda terlihat ATLEAST seperti ini:

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

Dan akhirnya eksternal Anda dalam rollup tampak ATLEAST seperti ini.

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.

Mengapa?

  • Ini akan membundel omong kosong Anda untuk secara otomatis mewarisi reaksi / reaksi dom dan dependensi rekan / eksternal Anda lainnya dari proyek konsumen yang berarti mereka tidak akan diduplikasi dalam bundel Anda.
  • Ini akan dibundel ke ES5
  • Ini akan secara otomatis membutuhkan ("..") di semua fungsi pembantu babel untuk objectSpread, kelas dll DARI proyek konsumen yang akan menghapus 15-25KB lain dari ukuran bundel Anda dan berarti bahwa fungsi pembantu untuk objekSebar tidak akan digandakan di perpustakaan Anda output + proyek yang dikonsumsi keluaran yang dibundel.
  • Fungsi Async masih akan berfungsi
  • eksternal akan cocok dengan apa pun yang dimulai dengan sufiks peer-dependency yaitu babel-helper akan cocok dengan eksternal untuk babel-helper / helper / penyebaran objek

Akhirnya di sini adalah inti untuk contoh file konfigurasi rollup file index.js tunggal. https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3 Di mana target src / ekspor / index.ts terlihat seperti ini ...

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

Beritahu saya jika Anda mengalami masalah dengan babel, rollup, atau memiliki pertanyaan tentang bundling / pustaka.


3

Ketika Anda bundel kode dengan Webpack (atau Paket atau Rollup) itu membuat satu file tunggal dengan semua kode.

Untuk alasan kinerja, saya tidak ingin semua kode itu diunduh oleh browser kecuali jika benar-benar digunakan

Dimungkinkan untuk membuat file terpisah yang dihasilkan untuk setiap komponen. Webpack memiliki kemampuan seperti itu dengan mendefinisikan banyak entri dan keluaran. Katakanlah Anda memiliki struktur proyek sebagai berikut

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

File webpack akan terlihat seperti ini

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

Info lebih lanjut tentang "pemecahan kode" ada di sini di dokumen Webpack

Jika repo yang sama berisi banyak komponen yang berbeda, apa yang seharusnya ada di main.js?

Ada satu bidang dalam package.jsonfile bernama main, ada baiknya untuk meletakkan nilainya lib/index.jssesuai dengan struktur proyek di atas. Dan dalam index.jsfile semua komponen diekspor. Dalam hal konsumen ingin menggunakan komponen tunggal itu dapat dicapai hanya dengan melakukan

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

Apakah saya benar dalam berpikir bahwa saya tidak boleh membundel komponen? Haruskah bundling diserahkan kepada konsumen komponen? Apakah saya menyerahkan hal lain kepada konsumen komponen? Apakah saya hanya mentranspisikan JSX dan hanya itu?

Ya terserah Anda. Saya telah menemukan bahwa beberapa perpustakaan Bereaksi diterbitkan dengan cara asli, yang lain - dalam cara yang dibundel. Jika Anda memerlukan beberapa proses pembuatan, maka tentukan dan ekspor versi yang dibundel.

Semoga semua pertanyaan Anda terjawab :)


Terima kasih atas tanggapannya. Saya tidak ingin harus memperbarui konfigurasi Webpack saya setiap kali saya menambahkan komponen baru, seperti dalam contoh Anda. "Terserah kamu. Saya telah menemukan bahwa beberapa perpustakaan Bereaksi diterbitkan dengan cara asli, yang lain - dalam cara yang dibundel." Ini terbukti tidak demikian. Buat Aplikasi Bereaksi bekerja dengan komponen yang tidak di-bundel OK, tetapi JS Selanjutnya membuat kesalahan dan jelas hanya bekerja dengan komponen yang dibundel, mengambil keputusan dari tangan saya.
otw

Saya sudah mencoba yang terbaik untuk meneliti :) "Saya tidak mau harus memperbarui konfigurasi Webpack saya setiap kali saya menambahkan komponen baru" - mungkin menggunakan beberapa glob-wildcard untuk tidak mencantumkan semua komponen, itu memecahkan masalah memperbarui konfigurasi webpack untuk setiap komponen baru. "Next JS melempar kesalahan" - well, lalu bundel paket Anda :) paket mentah pasti akan bekerja jika hanya dimasukkan ke dalam bundling dari proyek konsumen. Versi yang dibundel akan bekerja 100%.
Rashad Ibrahimov

1

Anda dapat membagi komponen-komponen Anda seperti yang dilakukan oleh Lodash untuk metode mereka.

Apa yang mungkin Anda miliki adalah komponen terpisah yang dapat Anda izinkan mengimpor secara terpisah atau melalui komponen utama.

Kemudian konsumen dapat mengimpor seluruh paket

import {MyComponent} from 'my-components';

atau bagian individualnya

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

Konsumen akan membuat bundel mereka sendiri berdasarkan pada komponen yang mereka impor. Itu akan mencegah seluruh bundel Anda diunduh.


1

Anda harus melihat Bit , saya pikir ini adalah solusi yang baik untuk berbagi, menggunakan kembali, dan memvisualisasikan komponen.

Sangat mudah untuk diatur. Anda dapat menginstal perpustakaan bit Anda atau hanya sebuah komponen dengan:

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

Kemudian Anda dapat mengimpor komponen di aplikasi Anda dengan:

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

Bagian baiknya adalah Anda tidak perlu khawatir mengkonfigurasi Webpack dan semua jazz itu. Bit bahkan mendukung versi komponen Anda. Contoh ini menunjukkan komponen reaksi daftar-judul sehingga Anda dapat melihat apakah ini memenuhi persyaratan Anda atau tidak


0

Ada konfigurasi di webpack untuk membuat file chunk. Untuk memulainya akan membuat bundel utama menjadi beberapa bongkahan dan membuatnya dimuat seperti yang diperlukan. jika proyek Anda memiliki modul yang terstruktur dengan baik, itu tidak akan memuat kode apa pun yang tidak diperlukan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.