공유 구성 요소 라이브러리 우수 사례


12

공유 가능한 React 구성 요소 라이브러리를 작성 중입니다.

라이브러리에는 많은 구성 요소가 포함되어 있지만 최종 사용자는 몇 가지 구성 요소 만 사용해야합니다.

당신이 웹팩 (또는 소포 또는 롤업)와 코드를 묶을 때 그것은 하나 개의 단일 파일 생성 모든 코드를 .

성능상의 이유로 실제로 사용하지 않는 한 브라우저에서 해당 코드를 모두 다운로드하고 싶지는 않습니다. 구성 요소를 묶어서는 안된다고 생각하는 것이 맞습니까? 번들링을 컴포넌트 소비자에게 맡겨야합니까? 구성 요소 소비자에게 다른 물건을 남겨 두어야합니까? JSX를 번역하면 되나요?

동일한 리포지토리에 많은 다른 구성 요소가 포함되어 있으면 main.js에 무엇이 있어야합니까?


1
내가 제대로 질문을 이해하면이 같은 방법을 찾고있는 자신의 소스 코드를 살펴보고 당신은 클라이언트 응용 프로그램은 그 구성 요소 (개별 수입을 사용 할 때 모든 구성 요소뿐만 아니라 개인들과 수출 것을 볼 수 있습니다 webpack은 imported코드에 있던 파일 만 가져와 번들 크기를 줄입니다.
Edward Chopuryan

답변:


5

이 질문은 "가장 좋은 방법"이 몇 줄의 응답보다 복잡하기 때문에 매우 길고 자세한 답변이 필요하기 때문에 매우 긴 답변입니다.

IV는 그 기간에 3.5 년 이상 사내 라이브러리를 유지했습니다. iv'e는 라이브러리를 묶어야한다고 생각하는 두 가지 방법으로 결정합니다. 트레이드 오프는 라이브러리의 크기에 따라 다르며 개인적으로 우리는 소비자.

방법 1 : 노출 된 모든 대상 및 대상 롤업을 입력으로 사용하여 index.ts 파일을 만듭니다. 전체 라이브러리를 단일 index.js 파일과 index.css 파일로 묶습니다. 라이브러리 코드의 중복을 피하기 위해 소비자 프로젝트에서 외부 종속성이 상속됩니다. (구성 예제 하단에 포함 된 요점)

  • 장점 : 프로젝트 소비자가 루트 상대 라이브러리 경로에서 모든 것을 가져올 수 있으므로 사용하기 쉬움 import { Foo, Bar } from "library"
  • 단점 : 이것은 결코 나무를 흔들 수 없습니다. 사람들이 ESM을 사용하여이 작업을 수행하기 전에 트리 쉐이킹이 가능합니다. NextJS는 현재 단계에서 ESM을 지원하지 않으며 많은 프로젝트 설정을 수행하지 않으므로이 빌드를 CJS로 컴파일하는 것이 좋습니다. 누군가가 귀하의 구성 요소 중 하나를 가져 오면 모든 구성 요소에 대한 모든 CSS와 모든 자바 스크립트를 가져옵니다.

방법 2 : 이것은 고급 사용자를위한 것입니다. 모든 내보내기에 대해 새 파일을 만들고 사용하는 CSS 시스템에 따라 "preserveModules : true"옵션을 사용하여 rollup-plugin-multi-input을 사용하십시오. CSS는 단일 파일로 병합되지 않지만 롤업 후 출력 파일 내에 각 CSS 파일 require ( ". css") 문이 남아 있고 해당 CSS 파일이 존재합니다.

  • 장점 : 사용자가 "library / dist / foo"에서 {Foo}를 가져 오면 Foo 용 코드와 Foo 용 CSS 만 가져옵니다.
  • 단점 :이 설정은 NextJS를 사용한 빌드 구성에서 소비자가 node_modules require ( ". css") 문을 처리해야하는 것과 관련이 있으며 이는 next-transpile-modulesnpm 패키지 로 수행 됩니다.
  • 주의 사항 : 우리는 당신이 여기에서 찾을 수있는 우리 자신의 바벨 플러그인을 사용합니다 : 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은 다음과 같이 ATLEAST를 찾습니다.

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

그리고 마지막으로 ATLEAST처럼 보이는 롤업의 외부 장치.

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, 클래스 등의 모든 babel 도우미 함수에서 자동으로 필요합니다 ( ".."). 이는 번들 크기에서 15-25KB를 지우고 objectSpread 도우미 함수가 라이브러리에서 복제되지 않음을 의미합니다 출력 + 소비 프로젝트 번들 출력.
  • 비동기 기능은 여전히 ​​작동합니다
  • externals는 해당 피어 종속 접미사로 시작하는 항목과 일치합니다. 즉, babel-helpers는 babel-helpers / helpers / object-spread에 대해 external과 일치합니다.

마지막으로 단일 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, rollup에 문제가 있거나 번들링 / 라이브러리에 대한 질문이 있으면 알려주십시오.


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

웹팩 파일은 다음과 같습니다

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 App 만들기는 번들로 제공되지 않은 구성 요소와 함께 작동했지만 Next JS는 오류가 발생하고 번들 구성 요소에서만 작동하여 결정을 내 렸습니다.
OTW

나는 최선을 다해 연구했다 :) "새 컴포넌트를 추가 할 때마다 Webpack 설정을 업데이트하고 싶지는 않습니다."-모든 컴포넌트를 나열하지 않는 glob-wildcard를 사용하면 모든 새로운 구성 요소에 대한 웹팩 구성 업데이트 "다음 JS가 오류를 던지고 있습니다"-글쎄, 패키지를 묶으십시오 :) 소비자 프로젝트에서 번들로 묶는 경우에만 원시 패키지가 작동합니다. 번들 버전은 100 % 작동합니다.
Rashad Ibrahimov

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 및 모든 재즈 구성에 대해 걱정할 필요가 없다는 것입니다. 비트는 구성 요소의 버전 관리도 지원합니다. 이 는 제목 목록 반응 구성 요소 보여 주므로 요구 사항을 충족하는지 여부를 확인할 수 있습니다


0

웹팩에는 청크 파일을 작성하기위한 구성이 있습니다. 시작하려면 기본 번들을 여러 청크로 만들어서 필요할 때로드합니다. 프로젝트에 잘 구조화 된 모듈이 있으면 필요하지 않은 코드를로드하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.