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

相关推荐
人工智能训练11 小时前
【极速部署】Ubuntu24.04+CUDA13.0 玩转 VLLM 0.15.0:预编译 Wheel 包 GPU 版安装全攻略
运维·前端·人工智能·python·ai编程·cuda·vllm
会跑的葫芦怪11 小时前
若依Vue 项目多子路径配置
前端·javascript·vue.js
xiaoqi92212 小时前
React Native鸿蒙跨平台如何进行狗狗领养中心,实现基于唯一标识的事件透传方式是移动端列表开发的通用规范
javascript·react native·react.js·ecmascript·harmonyos
jin12332213 小时前
React Native鸿蒙跨平台剧本杀组队消息与快捷入口组件,包含消息列表展示、快捷入口管理、快捷操作触发和消息详情预览四大核心功能
javascript·react native·react.js·ecmascript·harmonyos
烬头882114 小时前
React Native鸿蒙跨平台实现二维码联系人APP(QRCodeContactApp)
javascript·react native·react.js·ecmascript·harmonyos
pas13614 小时前
40-mini-vue 实现三种联合类型
前端·javascript·vue.js
摇滚侠14 小时前
2 小时快速入门 ES6 基础视频教程
前端·ecmascript·es6
2601_9498333914 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
珑墨15 小时前
【Turbo】使用介绍
前端
军军君0115 小时前
Three.js基础功能学习十三:太阳系实例上
前端·javascript·vue.js·学习·3d·前端框架·three