优化 rollup 配置以启用 tree shaking

optimize rollup config to enable tree shaking

我想就如何构建一个易于使用和 tree-shaking 优化的构建提出建议。我正在使用 rollup 打包一个由多个组件组成的 UI 库。

我的架构是:

/src
  /index.js
  /components
    /index.js
    /component1
      /index.js
      /Comp.vue
    ...
    /componentN
      /index.js
      /Comp.vue
  /directives
    /index.js
    /directive1
      /index.js
    ...
    /directiveN
      /index.js

src/components/index.js看起来像

export { default as Comp1 } from './component1
...
export { default as CompN } from './componentN

src/directives/index.js看起来像

export { default as Directive1 } from './directive1
...
export { default as DirectiveN } from './directiveN

每个内部index.js只是为了方便而绑定,比如

import Comp from './Comp.vue'
export default Comp`

最后 src/index.js 将收集所有内容:

import { * as components } from './components'
import { * as directives } from './directives'

export { components, directives }

构建时,汇总配置如下所示:

{
  input: 'src/index.js',
  output: {
    file: 'dist/lib.esm.js',
    format: 'esm',
}

(当然,我正在避免所有转译丑陋的插件,我认为它们会对这个问题造成干扰)

所以这个构建看起来不错,而且可以工作,但是...

  1. 使用起来很不方便:
import { components } from 'lib'
const { Comp1 } = components
  1. 这个构造可能也会在使用时破坏 tree shaking,因为我们导入了完整的 components 对象,而只需要 Comp1

我知道我不应该关心 tree shaking,而是提供一个 能够进行 tree shaking 的 库,这就是它的意义所在。当使用最简单的@vue/cli 模板测试我的构建时,整个库都被导入了,甚至@vue/cli 声称已经启用了开箱即用的 webpack-treeshaking 功能。

我不介意构建单独的文件而不是一个大的 esm 构建,但据我所知,使用 tree shaking 构建一个文件是可能的。我担心构建单独的文件是 CompA 可能在内部需要 CompB,如果用户也需要 CompB,在这种情况下它可能会在构建中被复制(如,一个外部使用版本和一个内部使用版本)。

我不知道如何进行优化。非常欢迎任何指针。

至于现在,我能找到的唯一有效解决方案是在 dist/ 文件夹内的同一树结构中单独构建所有文件。我决定构建文件以提供 Vue 文件格式样式块,而无需最终消费者进一步构建或配置。

构建后的样子:

/src
  /index.js
  /components
    /index.js
    /component1
      /index.js
      /Comp.vue
    ...
    /componentN
      /index.js
      /Comp.vue
  /directives
    /index.js
    /directive1
      /index.js
    ...
    /directiveN
      /index.js

/dist
  /index.js
  /components
    /index.js
    /component1
      /index.js
    ...
    /componentN
      /index.js
  /directives
    /index.js
    /directive1
      /index.js
    ...
    /directiveN
      /index.js

我创建了一个小的递归函数来查找所有 'index.js' 并将此列表与汇总多入口点功能一起使用。希望汇总会创建所有子文件夹,因此不需要检查或 mkdir -p.

// shorthand utility for 'find all files recursive matching regexp (polyfill for webpack's require.context)'
const walk = (directory, regexp) => {
  let files = readdirSync(directory)

  if (directory.includes('/examples'))
    return []

  return files.reduce((arr, file) => {
    let path = `${directory}/${file}`
    let info = statSync(path)

    if (info.isDirectory())
      return arr.concat(walk(path, regexp))
    else if (regexp.test(file))
      return arr.concat([path])
    else
      return arr
  }, [])
}

// ...

const esm = walk(`${__dirname}/src`, /^index\.js$/)
  .map(file => ({
    input: file,

    output: {
      format: 'esm',
      file: file.replace(`${__dirname}/src`, CONFIG.module)
    },
    ...
  }))

流程的最后一部分是 copy/paste package.json 进入 dist/cd 进入 npm publish ......这是集成到我们的 CI 任务中,因为它与汇总或构建没有直接关系,而是与发布相关。

它并不完美,但由于缺乏输入,这是我找到的唯一方法。 我希望它能帮助某人。