解析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 ... 的情况,可见文档

相关推荐
崔庆才丨静觅17 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606117 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了17 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅17 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅18 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅18 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment18 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅19 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊19 小时前
jwt介绍
前端
爱敲代码的小鱼19 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax