原项目是在webpack里配置entry为列表,遍历多个活动页面打包。现改造项目拆分为独立的项目,又想能够在需要时遍历所有项目打包,所以在根目录加了一个build命令,并写了一个脚本去执行。
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "name": "@mono/dsp", "version": "0.1.0", "private": true, "type": "module", "scripts": { "build": "npm run build:prod", "build:dev": "cross-env NODE_ENV=production VUE_APP_ENV=dev node build/index.mjs", "build:test": "cross-env NODE_ENV=production VUE_APP_ENV=test node build/index.mjs", "build:uat": "cross-env NODE_ENV=production VUE_APP_ENV=uat node build/index.mjs", "build:prod": "cross-env NODE_ENV=production VUE_APP_ENV=prod node build/index.mjs" }, ... }
|
在根目录新建build目录存放脚本:

功能实现
首先遍历活动目录,拿到所有项目路径。这里用fs和path模块即可。当前路径用__dirname。
1 2 3 4 5 6
| const fs = require('fs') const path = require('path') const getChildFolders = (dir) => { const children = fs.readdirSync(dir); return children.filter(name => fs.statSync(path.resolve(dir, name)).isDirectory()) }
|
然后遍历执行打包命令。这里用到child_process模块,开启子线程进行编译。
1 2 3 4 5 6 7 8 9
| const cp = require('child_process')
const cmd = `cd ${url} && npm run build:${env}` cp.exec(cmd, { cwd: process.cwd() }, function (err, stdout, stderr) { console.log(err || stdout || stderr) resolve() })
|
这里一开始执行就特别的卡,因为同时开了太多子进程去打包。将遍历改为异步。
1 2 3 4 5 6
| const asyncForEach = async (arr, fn) => { for (let item of arr) { await fn(item) } }
|
待打包完,执行部署,将项目的dist目录复制到指定目录并重命名。这里可以用fs来实现,发现有个fs-extra更方便。将fs引入改为fs-extra,包含所有fs的方法,原先fs使用不用更改。
1 2 3 4 5 6 7 8
| const fs = require('fs-extra') const path = require('path') const deployProgram = async (dir, name, dir2) => { const src = path.join(dir, name, 'dist') const target = path.join(dir2, name) fs.copySync(src, target) }
|
到这里,基本功能已经实现,加一些console就能看到打包的状况。
优化
我们再引入chalk对console做一些美化,并打印进度。这里引入progress组件,看进度更方便一些。
此时遇到一个eslint报错,说不能require es模块。原来npm install chalk安装的模板只支持es module。改成import引入chalk,又遇到另一个报错,说不支持在node环境用import。因此,我们把这两个脚本后缀名改成mjs,然后所有require改成import。
这时还有一个报错,说是__dirname未定义,这个变量只能在node环境用。上网找了下解决方案。
1 2 3 4 5
| import { dirname } from "node:path" import { fileURLToPath } from "node:url"
const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);
|
另外node版本在14以下不支持在最外层直接使用await。改用一个async函数包裹下。
最终代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import utils from './utils.mjs' import path from 'path' import fs from 'fs-extra' import ProgressBar from 'progress';
import chalk from 'chalk'; import { dirname } from "node:path" import { fileURLToPath } from "node:url"
const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);
const env = process.env.VUE_APP_ENV const NODE_ENV = 'production'
const activityPath = path.join(__dirname, '../packageActivity') const subpagePath = path.join(__dirname, '../packageSubpage') const deployPath = path.join(__dirname, '../releases') const subpageDeployPath = path.join(__dirname, '../releases/subpage')
console.log(chalk.blue('清空releases目录')) fs.emptyDirSync(deployPath)
const arr1 = utils.getChildFolders(activityPath)
const arr2 = utils.getChildFolders(subpagePath)
console.log(chalk.blue('开始遍历项目')) console.log(`当前环境:${chalk.blue(env)}`)
const total = arr1.length + arr2.length let finish = 0 const bar = new ProgressBar(':bar :current/:total\n', { total: total });
const init = async () => { await utils.asyncForEach(arr1, async item => { await utils.buildProgram(activityPath, item, env) await utils.deployProgram(activityPath, item, deployPath) finish++ bar.tick(1) })
await utils.asyncForEach(arr2, async item => { await utils.buildProgram(subpagePath, item, env) await utils.deployProgram(subpagePath, item, subpageDeployPath) finish++ bar.tick(1) }) if (finish === total) { console.log(chalk.green('全部打包完成,可以部署releases目录到release仓库')) } } init()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import fs from 'fs-extra' import path from 'path' import cp from 'child_process'
import chalk from 'chalk'; import { dirname } from "node:path" import { fileURLToPath } from "node:url"
const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);
const getChildFolders = (dir) => { const children = fs.readdirSync(dir); return children.filter(name => fs.statSync(path.resolve(dir, name)).isDirectory()) }
const buildProgram = (dir, name, env) => { const url = path.join(dir, name) return new Promise((resolve, reject) => { const cmd = `cd ${url} && npm run build:${env}` console.log(`开始打包: ${chalk.yellow(name)}`) console.log(`打包命令:${chalk.yellow(cmd)}`) cp.exec(cmd, { cwd: process.cwd() }, function (err, stdout, stderr) { console.log(`打包结束: ${chalk.yellow(name)}`) console.log(err || stdout || stderr) resolve() }) }) }
const asyncForEach = async (arr, fn) => { for (let item of arr) { await fn(item) } }
const deployProgram = async (dir, name, dir2) => { console.log(`开始部署: ${chalk.yellow(name)}`) const src = path.join(dir, name, 'dist') const target = path.join(dir2, name) fs.copySync(src, target) console.log(`部署完成: ${chalk.yellow(name)}`) }
export default { getChildFolders, buildProgram, asyncForEach, deployProgram, }
|
运行效果


…
