【问题标题】:Why is my React component library not tree-shakable?为什么我的 React 组件库不可摇树?
【发布时间】:2020-05-15 22:31:35
【问题描述】:

我有一个 React component library 与汇总捆绑在一起。然后我在一个应用程序设置中使用该库,其中 create-react-app 在后台使用 Webpack。我希望 Webpack 能够对组件库进行摇树。在构建应用程序包并对其进行分析后,我发现该库根本没有进行树摇动,或者树摇动在库上不起作用,因为它一开始就不可摇动。为什么摇树不起作用?我做错了什么?

rollup.config.js(React 组件库的捆绑器配置)

import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import autoExternal from 'rollup-plugin-auto-external'
import resolve from 'rollup-plugin-node-resolve'
import reactSvg from 'rollup-plugin-react-svg'
import url from 'rollup-plugin-url'
import string from 'rollup-plugin-string'
import pureanno from 'rollup-plugin-pure-annotation'

import pkg from './package.json'
const { getSVGOConfig } = require('./scripts/getSVGOConfig')

const MAX_INLINE_FILE_SIZE_KB = 100

export default {
  input: 'src/index.js',
  output: [
    {
      file: pkg.module,
      format: 'es',
    },
  ],
  plugins: [
    autoExternal(),
    babel({
      babelrc: false,
      exclude: 'node_modules/**',
      plugins: [
        'external-helpers',
        'babel-plugin-transform-react-jsx',
        'babel-plugin-transform-class-properties',
        'babel-plugin-transform-object-rest-spread',
        'transform-react-remove-prop-types',
        [
          'babel-plugin-root-import',
          {
            'rootPathSuffix': 'src',
          },
        ],
        'babel-plugin-styled-components',
        'transform-decorators-legacy',
        [
          'ramda',
          {
            'useES': true,
          },
        ],
      ],
    }),
    resolve(),
    commonjs(),
    reactSvg({
      svgo: getSVGOConfig(),
    }),
    url({
      limit: MAX_INLINE_FILE_SIZE_KB * 1024,
      include: ['**/*.woff', '**/*.woff2'],
    }),
    string({
      include: '**/*.css',
    }),
    pureanno({
      includes: ['**/*.js'],
    }),
  ],
  watch: {
    chokidar: false,
  },
}

src/index.js的React组件库

export { default as Theme } from './Theme'
export { default as Badge } from './components/Badge'
...

App.js(使用库的应用)

import React from 'react';
import { Theme, Badge } from 'my-react-component-library'

function App() {
  return (
    <Theme>
      <Badge>Hello</Badge>
    </Theme>
  )
}

export default App

package.json 的 React 组件库(相关部分)

{
  "name": "my-react-component-library",
  "version": "1.1.1",
  "main": "dist/index.js",
  "module": "dist/index.es.js",
  "scripts": {
    ...
    "build": "rollup -c",
  },
  "dependencies": {
    ...
  },
  "peerDependencies": {
    "react": "^15.0.0 || ^16.0.0",
    "react-dom": "^15.0.0 || ^16.0.0"
  },
  "devDependencies": {
    ...
  },
  "sideEffects": false
}

package.json 使用该库的应用程序(相关部分)

{
  "name": "my-app",
  "version": "0.1.0",
  "dependencies": {
    "my-react-component-library": "^1.1.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  },
  "scripts": {
    ...
    "analyze": "source-map-explorer build/static/js/*chunk*.js build/static/js/*chunk*.js.map",
    "build": "react-scripts build",
    "serve": "serve -s build"
  },
  "devDependencies": {
    ...
    "serve": "^11.3.0",
    "source-map-explorer": "^2.2.2"
  }
}

index.es.js(捆绑的 react 组件库)

https://gist.github.com/borisdiakur/ae376738955f15fb5079b5acb2ac83ad

【问题讨论】:

    标签: reactjs webpack create-react-app rollup tree-shaking


    【解决方案1】:

    我找到了一种可能的解决方案来解决我的问题。但这与摇树无关。我只是将库拆分为几个独立的块,方法是利用汇总的一个相当新的功能(我必须升级一堆依赖项才能使其工作)并提供一个对象、映射入口点的名称,汇总配置的输入属性。它看起来像这样:

    input: {
        index: 'src/index.js',
        theme: 'src/Theme',
        badge: 'src/components/Badge',
        contentCard: 'src/components/ContentCard',
        card: 'src/elements/Card',
        icon: 'src/elements/Icon',
        ...
    

    这里是汇总的文档:https://rollupjs.org/guide/en/#input

    输出设置到一个目录:

    output: [
      {
        dir: 'dist/es',
        format: 'es',
      },
    ],
    

    然后我在 package.json 中声明入口点如下:

    "module": "dist/es/index.js",
    

    在我的测试应用程序中,我导入组件,好像没有任何改变:

    import React from 'react';
    import { Theme, Badge } from 'my-react-component-library'
    

    到目前为止,这似乎有效,尽管它再次不是 tree-shaking,我仍然想知道如何使我的组件库可 tree-shakable。

    更新:

    事实证明,摇树一直有效!以下是图书馆的“错误”:

    1. Icon 组件导入了所有图标,因此只要您使用了至少一个图标或使用图标的组件,所有 svg 文件就会最终包含在包中。
    2. Theme 组件将字体作为 base-64 字符串内联到包中。

    我通过在需要时动态导入每个图标来解决第一个问题,通过减少 rollup-plugin-url 的 MAX_INLINE_FILE_SIZE_KB 参数来解决第二个问题,以便拆分字体并将其作为资产加载。

    所以,对于那些像我一样开始相信摇树不起作用的人,我的建议是因为捆绑包大得离谱:仔细检查您的捆绑包分析报告(即使用 source-map-explorer),寻找大人物并仔细检查您的进口。

    【讨论】:

    • 请展示你的 'src/index.js' 的样子。 &请回答我的类似问题。 stackoverflow.com/questions/60848502/…你将获得+50赏金
    • @NikhilPatil 完成。
    • 如果您的库没有摇树,请尝试使用此工具进行检查:github.com/Rich-Harris/agadoo 它可能会提供一些关于问题所在的见解。基本上,任何有副作用的代码都可能导致这种情况。尝试注释掉所有组件,然后只取消注释一个并运行该工具。一个接一个,直到找到罪魁祸首。
    【解决方案2】:

    您朝着正确的方向迈出了一步。 Tree Shaking 适用于文件边界,它会丢弃未在您的导入路径中使用的文件并且没有副作用。

    您的第一个示例无法使用摇树,因为您的整个包都捆绑在一个文件中。

    您的第二个示例捆绑到多个文件中,但您的主捆绑包(带有index.js 输入)仍然是一个巨大的文件,它将所有内容捆绑在一起,而不是将imports 单独留在其中。 (这只是基于您发布的构建过程的假设,请检查您的 index.js 捆绑包以验证这一点)。

    您必须:

    • 定义 index.js as externals 的依赖关系。尝试将整个 /src 目录添加为外部目录。

    • 在构建过程中使用 babel 而不是汇总,以保持导出和导入完好无损。

    如果您有这样的问题,只需检查材料 UI build process 和最终构建文件夹(npm i @material-ui/core 并查看 node_modules)。这是解决方案的一个很好的灵感。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-03
      • 2021-04-11
      • 2016-12-28
      • 1970-01-01
      • 2019-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多