Vite多出口打包方案摸索及实践

3,145 阅读2分钟

需求

业务需要根据不同的产品pid打包成多个包,对于vite启动的单页面应用而言,一般一个入口对应一个出口,vite会以根目录index.html作为应用入口和依赖图,解析并打包。对于多出口以官网给出的案例及结合网上案例参考,进行实践。

vite的打包底层是rollup,因此需要参考rollupjs文档的配置项进行开发实践,观察不同配置打包的结果进行分析。

参考

vite 多页面配置

vite2+vue3+typescript按需打包多页面项目

rollupjs#big-list-of-options

vite官方multi-page-app

方案1: 基于官方的配置(多入口配置默认output)

// 读取/src/views pid文件名长度 得到pid名的数组
let requirePidViews = fs.readdirSync('./src/views').filter(pid => pid.length === 32);
// input 入口配置Object|Array
const buildInputConfigs = {};
// 循环遍历
requirePidViews.forEach(pid => {
  // 配置多个入口模板打包
  const htmlUrl = resolve(__dirname, `src/views/${pid}/index.html`);
  // buildInputConfigs.push(htmlUrl); // 数组的形式
  buildInputConfigs[pid] = htmlUrl; // 对象的形式
});
// 配置打包input
defineConfig({
  build: {
    base: './',
    target: 'es2015',
    rollupOptions: {
      input: buildInputConfigs
    }
  }
})

基于官方的配置结果(如下图)

问题:

1.不能分别把不同pid下的assets资源打包到pid/assets下

2.路径多了几层如dist/src/views/pid/index.html

理想的是dist/pid/assets/*,dist/pid/index.html,dist/pid/favicon

解决参考:

vite 多页面配置的root等路径配置,目前未能成功配置

方案2: 基于参考vite2+vue3+typescript按需打包多页面项目的配置(默认input多出口)

// 读取/src/views pid文件名长度 得到pid名的数组
let requirePidViews = fs.readdirSync('./src/views').filter(pid => pid.length === 32);
// output出口配置数组Array
const buildOutputConfigs = [];
// 循环遍历
requirePidViews.forEach(pid => {
  // 配置多出口打包
  buildOutputConfigs.push({
    dir: `dist/${pid}/`,
    assetFileNames: '[ext]/[name]-[hash].[ext]',
    chunkFileNames: 'js/[name]-[hash].js',
    entryFileNames: 'js/[name]-[hash].js'
  });
});
// 配置打包input
defineConfig({
  build: {
    base: './',
    target: 'es2015',
    rollupOptions: {
      output: buildOutputConfigs
    }
  }
})
const fs = require('fs');
const distFiles = fs.readdirSync('dist').filter(item => item !== 'favicon.ico');
distFiles.forEach(pid => {
  fs.copyFileSync('dist/favicon.ico', `dist/${pid}/favicon.ico`);
});

基于基于参考vite2+vue3+typescript按需打包多页面项目的配置结果

本次还试验了同时input为对象、output为对象outputbanneroutputfooter****

defineConfig({
  build: {
    base: './',
    target: 'es2015',
    rollupOptions: {
      input: {
        pid1: 'src/views/pid1/index.html',
        pid2: 'src/views/pid2/index.html'
      },
      output: {
        pid1: 'pid1/',
        pid2: 'pid2/
      }
    }
  }
})

本次还试验了同时input为数组、output为数组

方案3: 基于参考vite2+vue3+typescript按需打包多页面项目的配置(每次打包一个)

const target = 'http://xxx.com';
// 读取/src/views pid文件名长度 得到pid名的数组 
// 来自package.json如{ "buildPid": "vite build --" } 执行时npm run build xxxxx
const pid = process.argv[process.argv.length - 1]; // npm run build xxxxx 中的xxxx
// 根据src/views/*获取列表的pid名数组
const viewsFoldUrl = resolve(__dirname, 'src/views/');
// 过滤是pid的
const pidViews = fs.readdirSync(viewsFoldUrl).filter(pid => pid.length === 32);
// 如果正在打包此pid则删除dist已删除的对应的包
if (pidViews.includes(pid)) {
  fs.rmSync(resolve(__dirname, `dist/${pid}/`), { recursive: true, force: true });
}
// 根据终端分别为开发环境分别配置
export default defineConfig(({ mode }: ConfigEnv) => {
  return pid === '--host'
    ? {
    base: './',
    build: {
      target: 'es2015'
    },
    plugins: [
      vue(),
      Components({
        resolvers: [ElementPlusResolver()]
      }),
      AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
      legacy({
        targets: ['defaults', 'not IE 11'],
        additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
        modernPolyfills: true
      })
    ],
    resolve: {
      alias: [{ find: '@', replacement: resolve(__dirname, 'src') }]
    },
    css: {
      preprocessorOptions: {
        scss: {
          charset: false,
          additionalData: `@import "@/assets/style/variables.scss";@import "@/assets/style/mixins.scss";@import "@/assets/style/common.scss";`
        }
      }
    },
    server: {
      host: 'localhost',
      port: 8848,
      proxy: {
        '/api': {
          target,
          changeOrigin: true,
          rewrite: path => path.replace(/^/api/, '/statice')
        },
        '/plugin': {
          target,
          changeOrigin: true
        }
      }
    }
  }
  : {
    root: `./src/views`, // 基于index.html的路径
    publicDir: `${process.cwd()}/public`, // 
    base: '/',
    build: {
      target: 'es2015',
      emptyOutDir: false, // 每次打包不清空
      outDir: `${process.cwd()}/dist/`, //资源产出路径
      assetsDir: `./${pid}/static/assets/`, // 无法被output识别的其他资源打包路径
      rollupOptions: {
        input: resolve(__dirname, `src/views/${pid}/index.html`), // 打包入口模板
        output: {
          assetFileNames: `${pid}/static/[ext]/[name]-[hash].[ext]`, // 静态资源块
          chunkFileNames: `${pid}/static/js/[name]-[hash].js`, // chunk 块
          entryFileNames: `${pid}/static/js/[name]-[hash].js` // 入口文件块
        }
      }
    },
    plugins: [
      vue(),
      Components({
        resolvers: [ElementPlusResolver()]
      }),
      AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
      legacy({
        targets: ['defaults', 'not IE 11'],
        additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
        modernPolyfills: true,
        renderLegacyChunks: true
      })
    ],
    resolve: {
      alias: [{ find: '@', replacement: resolve(__dirname, 'src') }]
    },
    css: {
      preprocessorOptions: {
        scss: {
          charset: false,
          additionalData: `@import "@/assets/style/variables.scss";@import "@/assets/style/mixins.scss";@import "@/assets/style/common.scss";`
        }
      }
    }
  };
});

基于基于参考vite2+vue3+typescript按需打包多页面项目的配置结果(每次打包一个)

总结

方案1: 单一模板,多个output;assets无法进入到某个pid下,具有路由,某个pid会打包不用页面的代码

方案2: 多模板,单个output,每个pid都是完整的内容,具有路由,某个pid会打包不用页面的代码

方案3: 多模板,单个output,每个pid不再具备路由,某个pid不会打包不用页面的代码

疑惑

outDir,assetsDir,root,base等如何配置,它们之前的配置关系如何相互参照,最终页面如何引入资源,包括开发和生产环境,不是很清楚,在配置的时候比较困难,需要一个一个调整去验证,希望后期能找到合适的资源深入理解。