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

相关推荐
怕浪猫2 小时前
FDE 如何正确解决现场项目工程性的问题
前端·后端·ai编程
西瓜太郎12343 小时前
外层模型卡片与分组详情成功率不一致,应该怎么排查?
前端·typescript·go·软件工程·react
GreenTea3 小时前
vLLM 与 SGLang KV Cache 底层实现机制深度调研报告
前端·后端·算法
毕业设计7036 小时前
(免费领源码) 基于微信小程序的预制菜商城的设计与实现25172-java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
vue.js·python·mysql·微信小程序·pycharm·微信开发者工具·推荐算法
xifangge20256 小时前
AGENTS.md 怎么写?涵盖 Java、Python、Vue、Go 的 8 套开箱即用模板
java·vue.js·python
几何心凉6 小时前
没有万能模型:我用 AiiOnly Token Plan,把多款大模型的长处拼进同一个项目
前端
kyriewen6 小时前
Claude Code 的额度今天缩水了17%——官方公告上写的是"永久提高25%"
前端·ai编程·claude
雪芽蓝域zzs7 小时前
vue解构平铺VS对象包裹
前端·javascript·vue.js
风骏时光牛马7 小时前
从零到实战,完整AI学习路线
前端
三十而立洋8 小时前
深入理解 Monorepo:子包安装依赖
前端·前端工程化