在项目开发中,有些商户要求用自己的资料注册小程序。这样就会有多个小程序需要开发,他们代码一摸一样只是一些配置参数不一样而已。

管理这种功能和代码一样,只是个别配置参数不一样的小程序,通过NodeJS写给脚本就可以方便管理了。原理很简单,以一个小程序项目为模版,每次开发之需要编辑它。其它小程序都是它的克隆而已。

项目结构

  • template — 模版小程序目录
  • miniprogram — 存放克隆项目的目录
  • common.js — 公用方法
  • create.js — 创建新项目的脚本
  • projects.json — 记录已有小程序的文件
  • update.js — 更新代码

创建新项目的命令:node create.js。它会要求你输入项目必须要填的参数。

更新项目的命令是:node update.js

common.js

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
const fs = require('fs');
const path = require('path');
const ignoreItem = []; // 需要忽略的文件或目录
/**
* 检查目录是否已经存在 是否创建新的目录
* @param {*} dirPath 目标 绝对路径
* @param {*} dir 目标目录名
* @param {*} create 如果不存在是否创建新目录 默认是创建
*/
function checkOrCreateDir(dirPath, dir, create = true) {
let target = path.resolve(dirPath, './' + dir);
let dirStatus = fs.existsSync(target);
if (!dirStatus && create) {
fs.mkdir(target, () => {
console.log(dir, '文件夹创建成功!')
});
return true;
} else {
return dirStatus;
}
}


/**
* 修改 小程序自己的 配置文件
* @param targetPath 配置文件路径
* @param config 配置对象 appid, projectname
*/
function modifyProjectConfig(targetPath, config) {
let item = path.resolve(targetPath, './project.config.json');
fs.readFile(item, (err, data) => {
let projectConfig = data.toString();
projectConfig = JSON.parse(projectConfig);
projectConfig['appid'] = config.appid;
projectConfig['projectname'] = config.projectname;

var str = JSON.stringify(projectConfig);
fs.writeFile(item, str, (err) => {
if (err) {
console.error(err);
}
console.log('project.config.json 修改成功!');
})
})
};

/**
* 修改 新建项目 config 配置文件 该文件用来保存 项目通用的全局变量 比如:shopid
* @param {*} targetPath 目标项目 绝对路径
* @param shopid 配置对象 shopid
*/
function modifyConfig(targetPath, config) {
let item = path.resolve(targetPath, './config.js');
fs.writeFile(item, `export const shop_id = ${config.shopid};export const qqMapKey = 'SWLBZ-WKWCJ-SKLFP-FBAGY-2M3WE-SBFAI';`, (err) => {
if (err) {
console.error(err);
}
console.log('config.js 操作成功!');
})
}



/**
* 循环遍历 模板目录 拷贝到新项目中
* templatePath 模板 绝对路径路径
* targetPath 目标 绝对路径
*/
function checkTemplate(templatePath, targetPath) {
let arr = fs.readdirSync(templatePath);
for (let i = 0, l = arr.length; i < l; i++) {
let itemPath = path.resolve(templatePath, './' + arr[i]); // 模板 文件或目录 路径
let targetItemPath = path.resolve(targetPath, './' + arr[i]); // 目标项目 文件或目录 路径
let fileStatus = fs.statSync(itemPath).isFile();
if (ignoreItem.includes(arr[i])) continue;
if (fileStatus) {
copyFile(itemPath, targetItemPath)
} else {
let status = checkOrCreateDir(targetPath, arr[i], true);
if (status) {
checkTemplate(itemPath, targetItemPath)
};
}
}
}

/**
* 拷贝文件
* @param {*} src 源文件路径
* @param {*} target 目标文件路径
*/
function copyFile(src, target) {
fs.copyFileSync(src, target);
console.log(target, '文件创建成功!')
}
const templateFolder = './template'; // 模版文件目录
const targetFolder = './miniprogram'; // 存放克隆项目的目录

module.exports = {
checkOrCreateDir,
modifyProjectConfig,
modifyConfig,
checkTemplate,
copyFile,
templateFolder,
targetFolder
}

create.js

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
const readline = require('readline'); // 获取用户命令行输入数据
const fs = require('fs');
const path = require('path');
const {
checkOrCreateDir,
modifyProjectConfig,
modifyConfig,
checkTemplate,
copyFile,
templateFolder,
targetFolder
} = require('./common.js')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '>'
})
const tmptProject = 'wx_17904'; // 测试用的
const config = {
shopid: '',
appid: '',
projectname: ''
};

var projects = fs.readFileSync('./projects.json'); // 获取以后克隆项目信息
projects = projects.toString();
projects = JSON.parse(projects);
rl.prompt();
rl.question("请输入shopid: ", (answer) => {
rl.prompt();
config.shopid = answer;
rl.question("请输入 appid: ", (answer) => {
config.appid = answer;
rl.prompt();
rl.question("请输入 projectname: ", (answer) => {
config.projectname = answer;
rl.close();
});
});
});
rl.on('close', (input) => {
init();
});
/**
* 项目初始化
*/
function init() {
if (!!projects[config.projectname]) {
rl.prompt();
rl.question(config.projectname + "已存在 请重新输入个 projectname: ", (answer) => {
config.projectname = answer;
rl.close();
});
} else {
// 检查存放克隆项目的目录是否存在。
checkOrCreateDir(path.resolve(__dirname), targetFolder);
// 检查新建的克隆项目是否存在。
checkOrCreateDir(path.resolve(__dirname, targetFolder), config.projectname);
let templateFolderPath = path.resolve(__dirname, templateFolder);
let targetFolderPath = path.resolve(__dirname, targetFolder, './' + config.projectname);
checkTemplate(templateFolderPath, targetFolderPath);
modifyProjectConfig(targetFolderPath, config);
modifyConfig(targetFolderPath, config);
projects[config.projectname] = {
"shopid": config.shopid,
"appid": config.appid,
"projectname": config.projectname
};
let str = JSON.stringify(projects);
fs.writeFile('./projects.json', str, (err) => {
if (err) {
console.error(err);
}
console.log('project.config.json 修改成功!');
})
}
}

这里用NodeJS readline模块获取用户在命令行输入的信息。

update.js

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
const {
checkOrCreateDir,
modifyProjectConfig,
modifyConfig,
checkTemplate,
copyFile,
templateFolder,
targetFolder
} = require('./common.js')
const fs = require('fs');
const path = require('path');

var projects = fs.readFileSync('./projects.json'); // 获取已有项目的信息
projects = projects.toString();
projects = JSON.parse(projects);

// 在克隆一遍
for (let key of Object.keys(projects)) {
checkOrCreateDir(path.resolve(__dirname, targetFolder), key);
let templateFolderPath = path.resolve(__dirname, templateFolder);
let targetFolderPath = path.resolve(__dirname, targetFolder, './' + key);
checkTemplate(templateFolderPath, targetFolderPath);
let config = projects[key];
modifyProjectConfig(targetFolderPath, config);
modifyConfig(targetFolderPath, config);
}

这里是读取已有项目的信息,在克隆一遍。这是比较笨的办法,更好的办法是监听模版项目文件的变化,定时更新克隆项目对应的文件。