自动化打包微前端多个项目
接手了一个微前端项目,使用 iframe 技术,有一个主框架项目,其余每一个页面就是一个项目,主框架通过 iframe 引入每一个子页面。
但构建打包都是在本地进行的,再提交到 git
这样当两个人同时开发一个页面的时候,提交代码的时候肯定会冲突
就想写一个自动化打包的脚本,开发人员只提交源代码,再由一个人执行自动化打包的脚本,这样就不会出现冲突了
# 自动化打包所有项目
这个脚本的主要功能是自动打包所有的项目
所以使用 node,递归遍历当前文件夹下的所有文件夹,直到找到有 package.json 的文件夹,然后执行 npm install 和 npm run build
在项目根目录添加 install-and-build.js, 内容为:
const fs = require('fs')
const path = require('path')
const { exec } = require('child_process')
// 递归遍历目录
function findAndProcessDirectories(dir) {
fs.readdir(dir, { withFileTypes: true }, (err, entries) => {
if (err) {
console.error(`无法读取目录 ${dir}: ${err}`)
return
}
// 遍历目录中的每个条目
entries.forEach(entry => {
const fullPath = path.join(dir, entry.name)
// 排除 node_modules 目录
if (entry.isDirectory() && entry.name !== 'node_modules') {
// 检查是否包含 package.json
const packageJsonPath = path.join(fullPath, 'package.json')
fs.access(packageJsonPath, fs.constants.F_OK, err => {
if (!err) {
console.log(`发现 package.json 在 ${fullPath}`)
// 执行 npm install 和 npm run build
exec('npm install && npm run build', { cwd: fullPath }, (error, stdout, stderr) => {
if (error) {
console.error(`执行命令失败:${error}`)
return
}
if (stderr) {
console.error(`标准错误:${stderr}`)
}
console.log(`标准输出:${stdout}`)
})
} else {
// 继续递归遍历子目录
findAndProcessDirectories(fullPath)
}
})
}
})
})
}
// 起始目录
const startDir = process.cwd() // 使用当前工作目录
findAndProcessDirectories(startDir)
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
然后在根目录执行 node install-and-build.js
即可
代码解释:
- fs.readdir 用于读取目录内容。
- fs.access 用于检查 package.json 是否存在。
- exec 用于执行 shell 命令。
fs.access 是 Node.js 的 fs 模块中的一个方法,用于检查文件或目录的存在性和权限。具体来说:
fs.access(path, mode, callback) 方法的作用是检查给定路径的文件或目录是否存在,并且你是否有权限访问它。
- path 是要检查的文件或目录的路径。
- mode 是检查权限的类型,fs.constants.F_OK 是一个常量,表示只检查文件是否存在(即,不关心是否有读取或写入权限)。
- callback 是一个回调函数,在检查完成后执行。如果文件或目录存在,err 参数为 null;如果不存在,则 err 参数会包含错误信息。
# 通过 git 提交记录,获取有哪些文件夹下的文件改动了
当我们的项目太多的时候,打包所有项目是很耗时的,所以我们需要通过 git 提交记录,获取有哪些文件夹下的文件改动了,只打包改动了的项目
我们可以使用 Git 的 diff 命令来列出更改的文件夹:
get-changed-dirs.sh
#!/bin/bash
# 获取上一次提交的哈希
LAST_COMMIT=$(git rev-parse HEAD)
# 获取更改的文件
CHANGED_FILES=$(git diff --name-only $LAST_COMMIT^ $LAST_COMMIT)
# 提取文件夹路径并去重
CHANGED_DIRS=$(echo "$CHANGED_FILES" | grep '/' | sed 's|/[^/]*$||' | sort -u)
# 输出更改的目录
echo "$CHANGED_DIRS"
2
3
4
5
6
7
8
9
10
11
12
13
这里着重解释一下这段代码:
# 提取文件夹路径并去重
CHANGED_DIRS=$(echo "$CHANGED_FILES" | grep '/' | sed 's|/[^/]*$||' | sort -u)
2
1 echo "$CHANGED_FILES"
echo "$CHANGED_FILES"
:输出变量CHANGED_FILES
的内容,CHANGED_FILES
是一个包含所有更改文件路径的字符串
2 grep '/'
grep '/'
:筛选出包含斜杠/
的行。因为文件夹路径包含斜杠,而根目录的文件名则不包含,所以这个步骤只保留路径中包含斜杠的行,即只保留文件夹路径(不包括根文件夹)。
3 sed 's|/[^/]*$||'
sed 's|/[^/]*$||'
:使用sed
命令对每一行进行替换处理。s|/[^/]*$||
是一个sed
替换命令:s|pattern|replacement|
语法用于将匹配pattern
的部分替换为replacement
。/[^/]*$
是一个正则表达式:/
匹配斜杠字符。[^/]*
匹配一个或多个非斜杠字符(即文件名)。$
表示行尾。
s|/[^/]*$||
的作用是删除行尾的文件名,仅保留文件夹路径。- 例如,对于
/path/to/file.txt
,它会变成/path/to/
。
- 例如,对于
4 sort -u
sort -u
:对前面的结果进行排序,并去除重复项。-u
选项表示唯一(unique),即删除重复的行。
这段代码的目的是从所有更改的文件路径中提取出唯一的文件夹路径。具体步骤是:
- 从更改的文件中筛选出包含斜杠的路径(即文件夹路径)。
- 去除文件名部分,只保留文件夹路径。
- 对路径进行排序并去除重复项,得到唯一的文件夹路径列表。
最终,CHANGED_DIRS
变量将包含所有唯一的、更改过的文件夹路径。
更新 install-and-build.js
以前是传入根目录,从根目录往下查找的,而现在使用 get-changed-dirs.sh 后,获取的是全路径了,需要从给定的全路径开始,向上逐级查找 package.json 文件,直到找到为止或者到达根目录。
同时也需要考虑避免对已经处理过的目录重复执行操作
const fs = require('fs')
const path = require('path')
const { exec } = require('child_process')
// 用于记录已经处理过的目录
const processedDirs = new Set()
// 执行 shell 脚本获取更改的文件夹
function getChangedDirectories(callback) {
exec('./get-changed-dirs.sh', (error, stdout, stderr) => {
if (error) {
console.error(`执行 shell 脚本失败:${error}`)
return
}
if (stderr) {
console.error(`标准错误:${stderr}`)
}
// 返回目录列表
callback(stdout.split('\n').filter(dir => dir.trim() !== ''))
})
}
// 查找包含 package.json 的目录
function findPackageJsonDir(dir, callback) {
const packageJsonPath = path.join(dir, 'package.json')
// 检查当前目录是否包含 package.json
fs.access(packageJsonPath, fs.constants.F_OK, err => {
if (!err) {
// 找到 package.json,执行回调
callback(dir)
} else {
// 如果找不到 package.json,向上查找
const parentDir = path.dirname(dir)
if (parentDir !== dir) {
// 确保不向上移动到根目录
findPackageJsonDir(parentDir, callback)
}
}
})
}
// 处理目录
function processDirectory(dir) {
findPackageJsonDir(dir, foundDir => {
if (processedDirs.has(foundDir)) {
console.log(`目录 ${foundDir} 已经处理过,跳过。`)
return
}
processedDirs.add(foundDir)
console.log(`发现 package.json 在 ${foundDir}`)
// 执行 npm install 和 npm run build
exec('npm install && npm run build', { cwd: foundDir }, (error, stdout, stderr) => {
if (error) {
console.error(`执行命令失败:${error}`)
return
}
if (stderr) {
console.error(`标准错误:${stderr}`)
}
console.log(`标准输出:${stdout}`)
})
})
}
// 修改后的 findAndProcessDirectories 函数
function findAndProcessDirectories(dirs) {
dirs.forEach(dir => {
processDirectory(dir)
})
}
// 执行主逻辑
getChangedDirectories(changedDirs => {
findAndProcessDirectories(changedDirs)
})
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
如果遇到“permission denied”错误,通常是因为脚本没有执行权限
可以使用以下命令来设置权限:
chmod +x get-changed-dirs.sh
# npm 包
上面代码已经实现了,但是我们还需要生成一个自动化打包的 npm 包,这样就不需要在根目录创建 install-and-build.js 了,以后再有这种微前端项目也可以自动化打包
但还有两个问题:
- Shell 脚本和跨平台兼容性:由于 get-changed-dirs.sh 是一个 Bash 脚本,它在 Windows 上可能无法直接执行。如果需要支持跨平台,需要使用 Node.js 实现
- 执行权限:在发布之前确保脚本有正确的执行权限,并在发布后的文档中提供如何使用的说明。
# 使用 node 实现 shell 脚本
所以我们还是用 node 实现一下吧
get-changed-dirs.js
const { exec } = require('child_process')
// 获取更改的文件夹
function getChangedDirectories(callback) {
exec('git diff --name-only HEAD^ HEAD', (error, stdout, stderr) => {
if (error) {
console.error(`执行命令失败:${error}`)
return
}
if (stderr) {
console.error(`标准错误:${stderr}`)
}
// 提取目录路径并去重
const changedDirs = stdout
.split('\n')
.filter(file => file.includes('/'))
.map(file => file.substring(0, file.lastIndexOf('/')))
.filter((value, index, self) => self.indexOf(value) === index)
callback(changedDirs)
})
}
module.exports = getChangedDirectories
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
然后在 install-and-build.js 中调用这个新脚本:
const getChangedDirectories = require('./get-changed-dirs')
// 执行主逻辑
getChangedDirectories(changedDirs => {
findAndProcessDirectories(changedDirs)
})
2
3
4
5
6
这样就可以了
创建一个 npm 包还是很简单的,网上有一大堆资料,也可以参考这个打造属于自己的项目脚手架工具,不过就是这个例子复杂了一点
这里就简单写一下了:
# 创建项目文件夹
multiple-build
# 初始化 npm 包
执行 npm init -y
# 创建一个主文件入口
在项目根目录中,创建一个 index.js 文件,作为 npm 包的入口文件。将 install-and-build.js 代码添加到 index.js 中:
#!/usr/bin/env node
// install-and-build.js
2
3
# 更新 package.json
在 package.json 文件中,添加一个 bin 字段,将 index.js 文件指定为命令行工具的入口
# 本地安装
使用 npm link
将当前 npm 包链接到全局执行环境,这样在任何目录都可以执行 multiple-build
命令来自动化打包所有项目
使用 multiple-build
测试没问题就可以发布到 npm 了
# 发布到 npm
运行 npm publish
来发布到 npm
# 搭配 Jenkins 实现自动化打包部署
由于 Jenkins 由公司统一管理,所以一切基本配置均已完成,我们只需要少量配置即可
所以在构建步骤中添加命令即可:
npm i multiple-build -g && multiple-build
如需完整流程,请参考 搭配 Jenkins 实现自动化打包微前端多个项目
- 01
- 搭配 Jenkins 实现自动化打包微前端多个项目09-15
- 02
- el-upload 直传阿里 oss 并且显示自带进度条和视频回显封面图06-05
- 03
- version 1.2506-03