Vue-Cli多项目多模块打包——思路记录

93 阅读3分钟

移动端项目往往内嵌着各种各样的H5页面,为了缩小H5打包体积和便于管理前端代码,需要对项目进行分包。 在此,记录一下我的学习思路。

实现一个简单的多模块打包

vue create hello-world

第一步:新建一个vue项目,新建modules目录,在里面新建一个app1文件夹,作为代号为app1的项目。 将默认生成的App.vue、views等文件转移到app1目录中:

image.png

第二步:vue.config.js添加配置

pages对象中存放项目(模块)的信息,Vue-cli官网文件这里有介绍:cli.vuejs.org/zh/config/#…

outputDir这里build时需要用到,需要改成对应的项目(模块)名称

const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
  publicPath: "./",
  outputDir: `dist/app1`, // 输出目录
  pages: {
    app1: {
      // page 的入口
      entry: "src/modules/app1/main.js",
      // 模板来源
      template: "public/index.html",
      // 修改:将 "index.html" 改为 "page1.html",使URL路径与文件名一致
      filename: process.env.NODE_ENV === "production" ? "index.html" : "app1/index.html",
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: "app1",
      // 在这个页面中包含的块,默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ["chunk-vendors", "chunk-common", "app1"],
    }
  },
  transpileDependencies: true,
});

第三步:运行

执行npm run serve,打开浏览器:http://localhost:8080/app1#/

可看到正常运行的网页:

image.png

第四步:接着试试打包

npm run build

可以看到dist目录成功出现了一个app1的目录,也能成功运行

image.png

第五步:新增加一个模块app2

image.png

运行,打开浏览器:http://localhost:8080/app2#/

app2也能正常的预览:

image.png

接着build,但是这里不方便的地方是,要手动的更改vue.config.js中的outputDir配置。

如果需要打包app2,就要更改为dist/app2,同时要注释掉pages中关于app1的部分。

app2打包出来:

image.png

实现命令行动态打包

我们打包的时候,可以在命令行携带参数,例如:npm run build --page=app1

然后,vue-cli配置文件获取到该参数,动态改变outputDir。

在此之前,需要声明一个配置变量/配置文件,用于记录多模块页面的信息。

代码如下:

const { defineConfig } = require("@vue/cli-service");

// 在这里配置模块信息,最好单独一个文件,方便管理
const businessArray = [{
  chunk: 'app1',
  chunkName: 'app1'
}, {
  chunk: 'app2',
  chunkName: 'app2'
}];

const modules = {}

// 打包指定模块,获取命令行 --page参数,例如 npm run build --page=app1
const npm_config_page = process.env.npm_config_page || ''

if (process.env.NODE_ENV === "production") {
  // 获取配置的环境变量
  console.log('打包模块:' + npm_config_page)
  // 生产环境只构建指定模块
  const targetModule = businessArray.find(i => i.chunk === npm_config_page)
  if (targetModule) {
    // 下部分代码不变,后面可以封装成函数
    modules[targetModule.chunk] = {
      entry: `src/modules/${targetModule.chunk}/main.js`,
      template: "public/index.html",
      filename: "index.html",
      title: targetModule.chunkName,
      chunks: ["chunk-vendors", "chunk-common", targetModule.chunk],
    }
  } else {
    throw new Error(`未找到模块: ${npm_config_page},请检查配置`)
  }
} else {
  businessArray.forEach(i => {
    // 下部分代码不变,后面可以封装成函数
    modules[i.chunk] = {
      entry: `src/modules/${i.chunk}/main.js`,
      template: "public/index.html",
      filename: `${i.chunk}/index.html`,
      title: i.chunkName,
      chunks: ["chunk-vendors", "chunk-common", i.chunk],
    }
  })
}

console.log('modules:', modules)

module.exports = defineConfig({
  publicPath: "./",
  outputDir: `dist/` + npm_config_page, // 输出目录
  pages: modules,
  transpileDependencies: true,
});

补充,除了process.env.npm_config_page这个方法外,还可以使用第三方工具commander来解析命令行参数和选项。

打包并压缩成.zip

压缩方案:

  1. 使用webpack插件zip-webpack-plugin
// npm install zip-webpack-plugin --save-dev

module.exports = defineConfig({
  publicPath: "./",
  outputDir: `dist/` + npm_config_page, // 输出目录
  pages: modules,
  transpileDependencies: true,

  configureWebpack: config => {
    if (process.env.NODE_ENV === "production") {
      // 添加zip插件
      config.plugins.push(new (require('zip-webpack-plugin'))({
        path: path.join(__dirname, 'dist'),
        filename: `${npm_config_page}.zip`,
        extension: 'zip'
      }));
    }
  }
});
  1. 使用 npm scripts 后续处理
{
  "scripts": {
    "build": "vue-cli-service build",
    "build:zip": "vue-cli-service build && node scripts/zip-dist.js"
  }
}

scripts/zip-dist.js 是压缩的脚本

使用spawn进行更细致化处理

const { spawn } = require('child_process');

function buildWithSpawn(moduleName) {
  return new Promise((resolve, reject) => {
    // 可以在执行前做更多准备工作
    console.log(`开始构建模块: ${moduleName}`);
    
    const buildProcess = spawn('npm', ['run', 'build', '--', `--page=${moduleName}`], {
      stdio: 'inherit',
      shell: true
    });
    
    buildProcess.on('close', (code) => {
      if (code === 0) {
        console.log(`模块 ${moduleName} 构建成功`);
        // 可以在这里执行后续操作,如压缩、上传等
        resolve();
      } else {
        reject(new Error(`构建失败,退出码: ${code}`));
      }
    });
  });
}

批量构建多个模块

async function buildAllModules() {
  const modules = ['app1', 'app2', 'app3'];
  
  // 串行构建
  for (const module of modules) {
    try {
      await buildWithSpawn(module);
      // 构建完成后可以执行额外操作
      await zipModule(module);
    } catch (error) {
      console.error(`构建模块 ${module} 失败:`, error);
      break;
    }
  }
  
  // 或者并行构建(需要注意资源占用)
  // await Promise.all(modules.map(buildWithSpawn));
}