解析ElementPlus打包源码(二、buildFullBundle)

runTask('buildFullBundle')

打包出来的结构

我们对应里面有两个并行运行的任务,里面的 minify 就是是否要将打包出来代码进行压缩混淆

scss 复制代码
export const buildFull = (minify: boolean) => async () =>
  Promise.all([buildFullEntry(minify), buildFullLocale(minify)])
  
export const buildFullBundle: TaskFunction = parallel(
  withTaskName('buildFullMinified', buildFull(true)),
  withTaskName('buildFull', buildFull(false))
)

buildFullEntry

php 复制代码
async function buildFullEntry(minify: boolean) {
  const plugins: Plugin[] = [
    ElementPlusAlias(),
    VueMacros({
      setupComponent: false,
      setupSFC: false,
      plugins: {
        vue: vue({
          isProduction: true,
          template: {
            compilerOptions: {
              hoistStatic: false,
              cacheHandlers: false,
            },
          },
        }),
        vueJsx: vueJsx(),
      },
    }),
    nodeResolve({
      extensions: ['.mjs', '.js', '.json', '.ts'],
    }),
    commonjs(),
    esbuild({
      exclude: [],
      sourceMap: minify,
      target,
      loaders: {
        '.vue': 'ts',
      },
      define: {
        'process.env.NODE_ENV': JSON.stringify('production'),
      },
      treeShaking: true,
      legalComments: 'eof',
    }),
    replace({
      'process.env.NODE_ENV': JSON.stringify('production'),
    }),
  ]
  if (minify) {
    plugins.push(
      minifyPlugin({
        target,
        sourceMap: true,
      })
    )
  }

  const bundle = await rollup({
    input: path.resolve(epRoot, 'index.ts'),
    plugins,
    external: await generateExternal({ full: true }),
    treeshake: true,
  })
  await writeBundles(bundle, [
    {
      format: 'umd',
      file: path.resolve(
        epOutput,
        'dist',
        formatBundleFilename('index.full', minify, 'js')
      ),
      exports: 'named',
      name: PKG_CAMELCASE_NAME,
      globals: {
        vue: 'Vue',
      },
      sourcemap: minify,
      banner,
    },
    {
      format: 'esm',
      file: path.resolve(
        epOutput,
        'dist',
        formatBundleFilename('index.full', minify, 'mjs')
      ),
      sourcemap: minify,
      banner,
    },
  ])
}

我们来看对应的代码挨个说明

  • ElementPlusAlias:见上一小结
  • VueMacros:见上一小节
  • nodeResolve:见上一小节
  • commonjs:见上一小节
  • esbuild
    • sourceMap值为minify,也就是说,如果不压缩就不需要sourceMap,因为打包出来的代码是比较好识别的,因为代码没有进行压缩混淆
    • define:这里作用和@rollup/plugin-replace插件作用一样,就是将对应代码进行字符替换。如果注释掉对应的代码,打包的结果中就会出现下面的代码process.env.NODE_ENV。并且替换了之后可以借助treeShaking处理掉为false的代码
    • treeShaking:去除掉不会运行的代码
    • legalComments:JavaScript 中,有些注释具有特殊的法律意义或功能。这个可以防止对应的注释打包被消除
  • replace@rollup/plugin-replace插件,和上面的define配置效果一样
  • 如果有minify是true,需要加入rollup-plugin-esbuild中的minify插件,并且sourceMap需要为true(对应就是例如index.full.min.js.map

我们思考一下全量打包需要替换 process.env.NODE_ENV 为字符串"production"的原因,为啥上一期我们打包模块不需要:因为全量打包是相当于直接生产使用的,所以必须去掉对应的process.env.NODE_ENV,而我们上一期的打包内容是给构建工具使用的,构建工具打包的时候才会去自行处理。例如vite的NODE_ENV如下图,可见官网

然后我们来看这块代码

php 复制代码
const bundle = await rollup({
    input: path.resolve(epRoot, 'index.ts'),
    plugins,
    external: await generateExternal({ full: true }),
    treeshake: true,
})

入口文件就是packages/element-plus/index.ts;plugins就是上面说的各个插件配置;external这个generateExternal中full为true时候就只会排除vue的相关依赖。

我们主要来说下treeshake这个配置,这里的配置是true是rollup的标准树摇模式。

对于treeshake: { moduleSideEffects: false }假定了所有模块都是没有副作用的。可以配合package.json中的sideEffects配置手动设置哪些文件是有副作用的,避免被误删除。例如packages\components\package.json中有配置

treeshake: { moduleSideEffects: false }很高效,并且可以使得打包体积更小,风险就是可能会被错误的扔掉有副作用代码。所以和sideEffects配合使用。

继续看下writeBundles

php 复制代码
  await writeBundles(bundle, [
    {
      format: 'umd',
      file: path.resolve(
        epOutput,
        'dist',
        formatBundleFilename('index.full', minify, 'js')
      ),
      exports: 'named',
      name: PKG_CAMELCASE_NAME,
      globals: {
        vue: 'Vue',
      },
      sourcemap: minify,
      banner,
    },
    {
      format: 'esm',
      file: path.resolve(
        epOutput,
        'dist',
        formatBundleFilename('index.full', minify, 'mjs')
      ),
      sourcemap: minify,
      banner,
    },
  ])

这里两个版本:

  • umd:生成一个兼容性最强的版本
    • 可以<script>标签直接引入,通过name: PKG_CAMELCASE_NAME就可以定义全局变量挂载在window上。也可以CommonJS的require()使用
    • globals: 指定了外部依赖(externals)如何被处理。这里告诉 Rollup:import vue from 'vue' 语句中的 'vue' 这个模块,对应的是全局变量 Vue
  • esm格式,通过import引入,所以没有 nameglobals

对于file、exports、sourceMap和前面讲解的作用一样不多赘述。

对于banner,就是文件头部添加的注释(如版权信息),如下图

buildFullLocale

less 复制代码
async function buildFullLocale(minify: boolean) {
  const files = await glob(`**/*.ts`, {
    cwd: path.resolve(localeRoot, 'lang'),
    absolute: true,
  })
  return Promise.all(
    files.map(async (file) => {
      const filename = path.basename(file, '.ts')
      const name = upperFirst(camelCase(filename))

      const bundle = await rollup({
        input: file,
        plugins: [
          esbuild({
            minify,
            sourceMap: minify,
            target,
          }),
        ],
      })
      await writeBundles(bundle, [
        {
          format: 'umd',
          file: path.resolve(
            epOutput,
            'dist/locale',
            formatBundleFilename(filename, minify, 'js')
          ),
          exports: 'default',
          name: `${PKG_CAMELCASE_LOCAL_NAME}${name}`,
          sourcemap: minify,
          banner,
        },
        {
          format: 'esm',
          file: path.resolve(
            epOutput,
            'dist/locale',
            formatBundleFilename(filename, minify, 'mjs')
          ),
          sourcemap: minify,
          banner,
        },
      ])
    })
  )
}

对于语言包,我们打包入口是packages\locale\lang中的每个ts文件。不是packages\locale\index.ts,因为需要方便按需引入。如果从index.ts引入的话就会全部语言都打包成一个。

其他的参数都类似。exports这里参数是default,适用于只使用 export default ... 的情况,可见文档

相关推荐
hj5914_前端新手3 小时前
深入分析 —— JavaScript 深拷贝
前端·javascript
中微子3 小时前
虚拟列表完全指南:从零到一手写实现
前端
YaeZed3 小时前
TypeScript6(class类)
前端·typescript
织_网3 小时前
UniApp 页面通讯方案全解析:从 API 到状态管理的最佳实践
前端·javascript·uni-app
emojiwoo4 小时前
前端视觉交互设计全解析:从悬停高亮到多维交互体系(含代码 + 图表)
前端·交互
xxy.c4 小时前
嵌入式解谜日志—多路I/O复用
linux·运维·c语言·开发语言·前端
yuehua_zhang4 小时前
uni app 的app端 写入运行日志到指定文件夹。
前端·javascript·uni-app
IT_陈寒5 小时前
SpringBoot 3.x实战:5种高并发场景下的性能优化秘籍,让你的应用快如闪电!
前端·人工智能·后端
麦文豪(victor)5 小时前
自动化流水线
前端