Reactjs 共享组件库最佳实践

Reactjs 共享组件库最佳实践,reactjs,npm,webpack,es6-modules,rollup,Reactjs,Npm,Webpack,Es6 Modules,Rollup,我正在创建一个可共享的React组件库 该库包含许多组件,但最终用户可能只需要使用其中的一小部分 当您将代码与Web包(或包裹或汇总)捆绑在一起时,它会创建一个包含所有代码的文件 出于性能方面的原因,我不希望所有这些代码都被浏览器下载,除非它被实际使用。 我不应该捆绑组件的想法对吗?捆绑是否应该留给组件的消费者? 我是否将其他事项留给组件的消费者?我只是传输JSX,就这样吗 如果同一个repo包含许多不同的组件,那么main.js中应该包含什么?您可以像拆分组件的方法一样拆分组件 您可能有单独的

我正在创建一个可共享的React组件库

该库包含许多组件,但最终用户可能只需要使用其中的一小部分

当您将代码与Web包(或包裹或汇总)捆绑在一起时,它会创建一个包含所有代码的文件

出于性能方面的原因,我不希望所有这些代码都被浏览器下载,除非它被实际使用。 我不应该捆绑组件的想法对吗?捆绑是否应该留给组件的消费者? 我是否将其他事项留给组件的消费者?我只是传输JSX,就这样吗


如果同一个repo包含许多不同的组件,那么main.js中应该包含什么?

您可以像拆分组件的方法一样拆分组件

您可能有单独的组件,可以允许单独导入或通过主组件导入

然后消费者可以进口整个包装

import {MyComponent} from 'my-components';
或其个别部分

import MyComponent from 'my-components/my-component';
消费者将根据他们导入的组件创建自己的捆绑包。这将防止您的整个包被下载

当您将代码与Web包(或包裹或汇总)捆绑在一起时,它会创建一个包含所有代码的文件

出于性能方面的原因,我不希望所有的代码都被浏览器下载,除非它被实际使用

可以为每个组件生成单独的文件。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'),
  },
};
有关“代码拆分”的更多信息,请访问

如果同一份回购协议包含许多不同的组件,那么main.js中应该包含什么

package.json
文件中有一个名为
main
的字段,最好根据上面的项目结构将其值放入
lib/index.js
。并在
index.js
文件中导出所有组件。如果消费者想使用单个组件,只需简单地

const componentX = require('my-cool-react-components/lib/componentX');
我不应该捆绑组件的想法对吗?捆绑是否应该留给组件的消费者?我是否将其他事项留给组件的消费者?我只是传输JSX,就这样吗

好吧,这取决于你。我发现有些React库是以原始方式发布的,而其他库是以捆绑方式发布的。若您需要一些构建过程,那个么定义它并导出捆绑版本

希望,您所有的问题都得到了回答:)

您应该看看,我认为这是一个共享、重用和可视化组件的好解决方案

安装起来很容易。您可以安装位库或仅安装具有以下功能的组件:

npm i @bit/bit.your-library.components.buttons
然后,您可以使用以下工具在应用程序中导入组件:

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

好的方面是,您不必担心配置Webpack和所有的jazz。Bit甚至支持组件的版本控制。这显示了一个标题列表react组件,因此您可以查看它是否满足您的要求这是一个非常长的答案,因为这个问题需要非常长和详细的答案,因为“最佳实践”方式比几行回答更复杂

Iv'e维护了我们的内部图书馆3.5年以上,在此期间,Iv'e确定了两种方式,我认为图书馆应该捆绑在一起。取舍取决于您的图书馆有多大,就个人而言,我们采用两种方式来满足两部分消费者

方法1:创建一个index.ts文件,其中包含要公开导出的所有内容,并将此文件的目标汇总作为其输入。将整个库捆绑到单个index.js文件和index.css文件中;使用从使用者项目继承的外部依赖项,以避免库代码的重复。 (示例配置底部包含要点)

  • 优点:易于使用,因为项目使用者可以从根相对库路径导入所有内容
    import{Foo,Bar}从“library”
  • 缺点:这永远不会让树摇晃;在人们说用ESM做这件事之前,它将是树上的。NextJS在当前阶段不支持ESM,许多项目设置也不支持ESM,这就是为什么将此构建编译为CJ仍然是一个好主意。如果有人导入你的一个组件,他们将获得你所有组件的所有css和所有javascript
方法2:这适用于高级用户:为每次导出创建一个新文件,并使用带有选项“preserveModules:true”的汇总插件多输入,具体取决于您使用的css系统,还需要确保您的css没有合并到单个文件中,而是每个css文件都需要(“.css”)语句在汇总后留在输出文件中,并且该css文件存在

  • 优点:当用户从“library/dist/Foo”导入{Foo}时,他们将 仅获取Foo的代码,以及Foo的css,仅此而已
  • 缺点:此设置涉及使用者必须处理节点单元模块 在NextJS的构建配置中需要(“.css”)语句 这是通过
    next transfile modules
    npm包完成的
  • 警告:我们使用我们自己的babel插件,您可以在这里找到:允许人们从“库”导入{Foo,Bar},然后使用babel将其转换为
我们有多个汇总配置,实际使用这两种方法;因此,对于不喜欢树摇动的图书馆用户,只需从“图书馆”执行“Foo”并导入单个css文件;对于喜欢树摇动且只使用关键css的图书馆用户,只需打开我们的babel插件即可

最佳实践汇总指南:

无论您是否使用typescript,都要使用
“rollup plugin babel”进行构建:“5.0.0-alpha.1”
确保你的车是这样的

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

加上rollup中的babel插件
        babel({
            babelHelpers: "runtime",
            extensions,
            include: ["src/**/*"],
            exclude: "node_modules/**",
            babelrc: true
        }),
    "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.

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