现有基座及子系统
# 简介
主体上使用分包,再合包机制实现
bdp-tpl-sys
:门户基座;
bdp-tpl-sub
:子系统子模块;
# 原理
通过webpack插件的方式,在主/子系统做相应的不同加载插件处理;
# 各个插件讲解
# umi-plugin-increment
[父子]
主要是验证repository.config.js
仓库路径合法性;
# umi-plugin-build
[父]
这个插件仅做调试用,请不要在生产引用;用于打包某个nodemodules的文件或者包;
- buildUmiPluginLocale;
- buildUmiWebpack;
- buildUmiHistoryWebpack;
# umi-plugin-bdpcloud
[子]
完成三个功能;
- bdpcloud依赖搬迁代码;
- 替换
@/为@/bdpcloud/
; - 移动其他模块moveOtherModules;
- 补充国际化;参考
umi-plugin-locale
的实现;
# umi-plugin-monorepo
# mainSystem参考 (opens new window)
主要做的是:externals公共模块提取;
- api.chainWebpackConfig;
- api.addHTMLHeadScript;
- api.addHTMLLink;
# subSystem参考 (opens new window)
只在生产环境下使用,并结合构建env-react
打包程序配套使用;
子系统关键信息挂载到全局window上;
- routes;
- models;
- monorepo/cloud;
const content = `
${globalStyle}
window.g_umi = window.g_umi || {};
window.g_umi.monorepo = window.g_umi.monorepo = [];
window.g_umi.monorepo.push({
routes: {
tabs: ${tabRoutes},
blanks: ${blankRoutes},
exts: ${extRoutes},
menus:${menusRoutes},
},
models: [${models.join(',')}]
});
if (!window.cloud) {
window.cloud = {};
}
if (!window.cloud.${api.pkg.name}) {
window.cloud.${api.pkg.name} = true;
}
`.trim();
return content;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
及子系统externals处理;
# 远程动态加载
通过loadjs
动态加载远程子系统中路由模块;
import { canRemote, goRemotePage } from '@/utils/remoteUtil';
componentDidMount() {
const { location, reloadAppLocale, dispatch } = this.props;
const { pathname } = location;
if (canRemote(pathname)) {
goRemotePage({
props: this.props,
reloadAppLocale,
});
}
2
3
4
5
6
7
8
9
10
11
import loadjs from 'loadjs';
import { message } from 'antd';
import router from 'umi/router';
import config from '@/utils/config';
import { findAlias, JumpToRouter } from '@/utils/utils';
import BlankLayout from '@/layouts/BlankLayout';
const alias = {
dapweb: 'dap',
};
const getFirstPath = pathname => {
if (!pathname) {
return '';
}
if (pathname.indexOf('/') !== 0) {
pathname = `/${pathname}`;
}
const paths = pathname.split('/');
let result = '';
if (paths.length > 2) {
const first = paths[1];
if (first === 'ext' && paths.length > 3) {
result = paths[2];
} else {
result = paths[1];
}
}
if (alias[result]) {
result = alias[result];
}
return result;
};
const canRemote = pathname => {
let canGetRemote = false;
if (!REMOTE_MODULES) {
return canGetRemote;
}
if (!pathname) {
return canGetRemote;
}
let arrs = REMOTE_MODULES.split('|');
arrs = arrs.filter(value => value !== '');
const module = getFirstPath(pathname);
canGetRemote = arrs.includes(module);
return canGetRemote;
};
const findRootRoute = () => {
let root = null;
window.g_routes.forEach(route => {
const { path, redirect, routes } = route;
if (path === '/' && !redirect) {
root = route;
}
});
return root;
};
const findMenuRoutes = () => {
let tabMenus = null;
let extMenus = null;
let blankMenus = null;
window.g_routes.forEach(route => {
const { path, redirect, routes } = route;
if (path === '/' && !redirect) {
routes.forEach(innerRoute => {
if (innerRoute.path === '/' && !innerRoute.redirect) {
tabMenus = innerRoute;
}
if (innerRoute.path === '/ext') {
extMenus = innerRoute;
}
if (innerRoute.path === '/tobereplace') {
// TODO 动态加入blanklayout会导致页面闪一下问题,暂时在初始化的时候有一个待替换的路由,暂时只支持一个
blankMenus = innerRoute;
}
});
}
});
return {
tabMenus,
extMenus,
blankMenus,
};
};
const showLoading = (dispatch, show) => {
if (dispatch) {
dispatch({
type: 'global/save',
payload: {
remoteLoading: show,
},
});
} else {
if (show) {
message.loading('loading', 0);
} else {
message.destroy();
}
}
};
const load = props => {
const {
location: { pathname },
reloadAppLocale,
intl,
dispatch,
} = props;
const modulename = getFirstPath(pathname);
const modulesContext = 'modules';
const remoteUrl = `${ROUTER_BASE}${modulesContext}/${modulename}/`;
const promise = new Promise(resolve => {
if (!window.loadjsCache) {
window.loadjsCache = {};
}
if (window.loadjsCache[modulename]) {
// 已经加载过
return resolve();
}
showLoading(dispatch, true);
// 远程加载
const timestamp = new Date().getTime();
loadjs([`${remoteUrl}${modulename}-locales.js?t=${timestamp}`], () => {
// 国际化
if (window.g_umi && window.g_umi.locales) {
const locales = {};
(window.g_umi.locales || []).forEach(locale => {
const { name, message: localeMessage } = locale;
locales[name] = localeMessage;
});
if (locales) {
// 把子模块的放在runtime里面
if (typeof window.g_plugins.use === 'function') {
window.g_plugins.use({
locale: { messages: locales },
});
}
// 先全局刷新国际化
if (typeof reloadAppLocale === 'function') {
reloadAppLocale();
}
// 再把国际化丢给子模块依赖的国际化插件
// repo.setIntlObject(intl);
}
}
loadjs(
[
`${remoteUrl}${modulename}.js?t=${timestamp}`,
`${remoteUrl}${modulename}.css?t=${timestamp}`,
],
() => {
showLoading(dispatch, false);
((window.g_umi && window.g_umi.monorepo) || []).forEach(repo => {
const { tabs, blanks, exts } = repo.routes;
let tabsRoutes = [];
let blanksRoutes = [];
let extsRoutes = [];
const { tabMenus, extMenus, blankMenus } = findMenuRoutes();
// 路由
tabs.forEach(route => {
const { routes: moduleRoutes = [] } = route;
if (tabMenus && moduleRoutes.length > 0) {
tabMenus.routes.unshift(route);
tabsRoutes = [...moduleRoutes];
}
});
exts.forEach(route => {
if (extMenus) {
extMenus.routes.unshift(route);
extsRoutes.push(route);
}
});
blanks.forEach(route => {
if (blankMenus) {
blankMenus.path = route.path;
blankMenus.component = BlankLayout;
blankMenus.routes = route.routes;
blanksRoutes.push(blankMenus);
}
});
// 路由 alias,用于子页面权限校验
const configObj = config.get();
const { routeAlias } = configObj;
config.set({
...configObj,
routeAlias: {
...routeAlias,
...findAlias(tabsRoutes),
...findAlias(blanksRoutes),
...findAlias(extsRoutes),
},
});
// model
(repo.models || []).forEach(model => {
if (typeof model === 'object') {
window.g_app.model(model);
}
});
});
// 标识已经加载过,避免重复加载
window.loadjsCache[modulename] = true;
resolve();
}
);
});
});
return promise;
};
const goRemotePage = menuItem => {
const { props } = menuItem;
const { location = {} } = props;
const { pathname, query = {}, ...rest } = location;
// 远程加载
load(props).then(() => {
let realPath = pathname;
if (pathname.charAt(pathname.length - 1) === '/') {
realPath = pathname.substring(0, pathname.length - 1);
}
router.push({
pathname: realPath,
query,
...rest,
});
});
};
const pushRoute = ({ pathname, query, payload, reloadAppLocale }) => {
if (canRemote(pathname)) {
goRemotePage({
props: {
location: {
pathname,
query,
...payload,
},
reloadAppLocale,
},
});
} else {
if (THEME === 'bss') {
JumpToRouter(pathname, {
query,
...payload,
});
} else {
router.push({
pathname,
query,
...payload,
});
}
}
};
export { canRemote, goRemotePage, pushRoute, load };
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# 全局挂载数据
app.js
if (!window.cloud) {
window.cloud = {
loadjs,
publicPath: PUBLIC_PATH,
remoteModules: REMOTE_MODULES,
};
}
2
3
4
5
6
7
# 引入子系统配置
# 分包配置
# 插件上配置
# sys配置
bdp-cloud-react
加载插件:
const plugins = [
[
'umi-plugin-react',
{
antd: true,
dva: {
hmr: true,
},
locale: {
enable: true, // default false
default: LANGUAGE, // default zh-CN
baseNavigator: true, // default true, when it is true, will use `navigator.language` overwrite default
},
dynamicImport: {
loadingComponent: loadingComponent || './components/PageLoading/index',
webpackChunkName: true,
},
dll: false,
},
],
// ***
`${process.cwd()}/node_modules/bdpcloud/config/umi-plugin-increment.js`,
[
`${process.cwd()}/node_modules/bdpcloud/config/umi-plugin-monorepo`,
{
type: 'mainSystem', // 默认值 mainSystem:主系统 subSystem:子系统
},
],
];
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
# sub配置
realtime-react
加载插件:
const plugins = [
[
'umi-plugin-react',
{
antd: process.env.NODE_ENV === 'development',
dva: {
hmr: true,
},
locale: {
enable: true, // default false
default: LANGUAGE, // default zh-CN
baseNavigator: true, // default true, when it is true, will use `navigator.language` overwrite default
},
dynamicImport: {
loadingComponent: loadingComponent || './bdpcloud/components/PageLoading/index',
webpackChunkName: true,
},
dll: false,
},
],
// ***
`${process.cwd()}/node_modules/bdpcloud/config/umi-plugin-increment.js`,
`${process.cwd()}/node_modules/bdpcloud/config/umi-plugin-bdpcloud.js`,
[
`${process.cwd()}/node_modules/bdpcloud/config/umi-plugin-monorepo`,
{
type: 'subSystem', // 默认值 mainSystem:主系统 subSystem:子系统
},
],
];
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
子项目修改依赖门户的方式:采用相关路径引用方式;
//"bdpcloud": "git+ssh://git@gitlab.xx.com:zsmart-xx/bdp-cloud-react.git#V6.5.0",
"bdpcloud": "file:../bdp-cloud-react",
2
路由模式改成非hash模式;要不然构建build会报错;
hash: false, // 加上hash
# 构建相关配置
# 主项目构建
zsmart-rgdp-web
项目;要确保第一次主项目构建成功;
# 子.env修改
# 实时监控系统
branch.realtime=
# 远程加载菜单的模块,多个用竖线|分隔
remoteModules=realtime
2
3
4
5
# ngx上配置
location / {
root /home/xxx/AAWeb/AAportal;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
2
3
4
5
sudo nginx -t
sudo nginx -s reload
2
# Jks配置
执行shell:
cd $WORKSPACE
envName=autorun
moduleName=realtime
rm -rf dist dist.zip
cd ./node
npm install --registry=https://registry.npm.taobao.org/
npm run git addUser xxx xx=
npm run clone env=${envName}
npm install --registry=https://registry.npm.taobao.org/
npm run build env=${envName}
zip -r dist.zip dist
2
3
4
5
6
7
8
9
10
11
12
13
14
getdist.sh
#!/bin/sh
set -e
if [ -f "dist.zip" ]; then
mv dist.zip backweb/dist`date +%Y%m%d%H%M%S`.zip
fi
sleep 1
ftp -niv <<!
open xx.45.47.xx
user xx xxx
binary
cd /var/lib/jenkins/workspace/zsmart-rgdp-web
prompt
get dist.zip
close
bye
!
sleep 2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
reload.sh
不考虑使用,可直接合并到上面脚本中;
#!/bin/bash
rm -rf AAPortal
sleep 1
/usr/bin/unzip dist.zip
mv dist AAPortal
2
3
4
5
# 分包子项目构建
# 项目pkg包名修改
package.json
"name": "realtime",
要不然会报错;
# pkg.lock依赖
package-lock.json
依赖跟门户的一致;- 由于历史原因确保用公司源安装依赖;
npm install --registry=http://npm.iwxxx.com:8081/repository/npm-all/
# umi路由模式
改成非hash模式;
config/config.js
hash: false, // 加上hash
# 子.env修改
# 实时监控系统
branch.realtime=dev
# 远程加载菜单的模块,多个用竖线|分隔
#remoteModules=realtime
2
3
4
5
# 拉资源脚本
目录:/home/xxx/AAWeb/modules
sudo chmod +x getrealtime.sh
#!/bin/sh
sleep 1
ftp -niv <<!
open xx.45.47.xx
user jkxxx xxx
binary
cd /var/lib/jenkins/workspace/zsmart-rgdp-modules-web/.gittmp/realtime
lcd /home/xxx/AAWeb/modules
prompt
get dist.zip
close
bye
!
sleep 2
rm -rf realtime
sleep 1
/usr/bin/unzip dist.zip
mv dist realtime
sleep 2
rm -f dist.zip
echo "解压realtime成功"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ngx上配置
location / {
#root /home/xxx/autorun/AAportal;
root /home/xxx/AAWeb/AAPortal;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# 子模块前端
location ^~ /modules {
root /home/xxx/AAWeb;
}
2
3
4
5
6
7
8
9
10
11
sudo nginx -t
sudo nginx -s reload
2
# Jks配置
执行shell:
cd $WORKSPACE
isNpmT=true # 是否使用npm淘宝源
envName=develop # develop/release
moduleName=realtime #子模块名
isPull=false # 是否git pull模式;加快构建速度:目前要第一次成功后,才能用pull;如果对应的env中模块的版本修改后请下要改回false;
npmSource=http://npm.iwxxx.com:8081/repository/npm-all/
if [ $isNpmT == true ]; then
npmSource=https://registry.npm.taobao.org/
fi
rm -rf dist dist.zip
cd ./node
npm install --registry=${npmSource}
npm run git addUser xxx xxx=
if [ $isPull == true ]; then
npm run clone env=${envName} module=${moduleName} pull=true
else
npm run clone env=${envName} module=${moduleName}
fi
cd ../.gittmp/$moduleName
if [ $isPull != true ]; then
npm install --registry=${npmSource}
fi
npm run build env=${envName}
zip -r dist.zip dist
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
# 构建加速
# nodejs版本
nodejs升级最新版本;
yarn命令安装;
#npm init -y #npm install -S yarn --registry=https://registry.npm.taobao.org/ rm -rf dist dist.zip cd ./node #npm install --registry=http://npm.xxxx.com:8081/repository/npm-all/ #$WORKSPACE/node_modules/.bin/yarn install --registry=https://registry.npm.taobao.org/
1
2
3
4
5
6
7max_old_space_size;
if(orm) pkg.scripts.build = "node --max_old_space_size=5120 ./node_modules/.bin/umi build"; // 统一配置构建内存
1
# 缓存总体
- pull模式仓库;
let cmdStr = `git clone --depth 1 -b ${configBranch} ${url} ${repoTempPath}`;
if (pull) {
cmdStr = `cd ${repoTempPath} && git pull`;
console.log(cmdStr);
}
2
3
4
5
pull模式:完整jks中的配置
cd $WORKSPACE
envName=develop
moduleName=schedule
pull=true # 目前要第一次完全成功后,才能用pull模式,加快构建速度
rm -rf dist dist.zip
cd ./node
npm install --registry=http://npm.iwxxx.com:8081/repository/npm-all/
npm run git addUser xxx xxx=
if [ $pull != true ]; then
npm run clone env=${envName} module=${moduleName}
else
npm run clone env=${envName} module=${moduleName} pull=true
fi
cd ../.gittmp/$moduleName
if [ $pull != true ]; then
npm install --registry=http://npm.iwxxx.com:8081/repository/npm-all/
fi
npm run build env=${envName}
zip -r dist.zip dist
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 依赖第三方缓存;
应该涉及到本地相互包依赖,要做下分级拆开来安装依赖配置处理;比如:realtime依赖门户bdp;
cd ../.gittmp/bdp-cloud-react
echo "=======bdp拷贝之前缓存node_modules依赖包数据======="
mv ../../node_modules0 ./node_modules #利用之前的依赖包,提高速度;
npm install --registry=${npmSource}
#cd ../.gittmp/$moduleName
cd ../$moduleName
if [ $isPull != true ]; then
echo "=======拷贝之前缓存node_modules依赖包数据======="
mv ../../node_modules ./ #利用之前的依赖包,提高速度;
echo "=======installing 依赖安装中======="
npm install --registry=${npmSource}
echo "=======installed 依赖安装成功======="
npm run build env=${envName}
mv ./node_modules ../../
echo "=======bdp缓存node_modules依赖包数据======="
mkdir ../../node_modules0 && mv ../bdp-cloud-react/node_modules/* ../../node_modules0
else
npm run build env=${envName}
fi
zip -r dist.zip dist
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
缓存模式:对应jks中的环境
优化后的处理:支持JKS构建不报错;
注意
1:用mv直接重名方式,可以加快文件移动备份,而不要用cp命令这个慢;
2:还要考虑直接覆盖原有文件,要加-f
;
3:不能用/*
拷贝文件夹,因为/*
识别拷贝不了./bin
等相关隐藏文件;而使用{.,}*
匹配,在linux中有效,mac好像没用,不影响构建;
cd $WORKSPACE
envName=autorun # develop/release
moduleName=realtime #子模块名
modulePName=bdp-cloud-react #门户模块名
isNpmT=true # 是否使用npm淘宝源
isCacheDep=false # 是否缓存node_modules依赖包数据
isPull=false # 是否git pull模式;加快构建速度:目前要第一次成功后,才能用pull;如果对应的env中模块的版本修改后请下要改回false;
# curDir=$WORKSPACE
curDir=$(
cd "$(dirname "$0")"
pwd
)
bakDir=$curDir/node_modules_bak
bakPDir=$bakDir/$modulePName
bakMDir=$bakDir/$moduleName
bakPDirisEmpty=true
bakMDirisEmpty=true
npmSource=http://npm.iwxxx.com:8081/repository/npm-all/
if [ $isNpmT == true ]; then
npmSource=https://registry.npm.taobao.org/
fi
function checkFileEmpty() {
isEmptyFile=true
file=$1
if [ -e $file ]; then
if [ "$(ls -A ${file})" != "" ]; then
isEmptyFile=false
fi
else
mkdir -p $file
fi
echo $isEmptyFile
}
rm -rf dist dist.zip
cd ./node
npm install --registry=$npmSource
npm run git addUser xxx xxx=
if [ $isPull != true ]; then
npm run clone env=$envName module=$moduleName
else
npm run clone env=$envName module=$moduleName pull=true
fi
if [ $isPull == true ]; then
cd ../.gittmp/$moduleName
npm run build env=$envName
else
bakPDirisEmpty=$(checkFileEmpty $bakPDir)
bakMDirisEmpty=$(checkFileEmpty $bakMDir)
cd ../.gittmp/$modulePName
if [ $bakPDirisEmpty == false -a $isCacheDep == true ]; then
echo "=======$modulePName 拷贝之前缓存node_modules依赖包数据======="
rm -rf node_modules && mkdir node_modules && mv -v $bakPDir/{.[!.],}* ./node_modules
fi
echo "=======$modulePName installing 依赖安装中======="
npm install --registry=$npmSource
echo "=======$modulePName installed 依赖安装成功======="
cd ../$moduleName
if [ $bakMDirisEmpty == false -a $isCacheDep == true ]; then
echo "=======$moduleName 拷贝之前缓存node_modules依赖包数据======="
rm -rf node_modules && mkdir node_modules && mv -v $bakMDir/{.[!.],}* ./node_modules
fi
echo "=======$moduleName installing 依赖安装中======="
npm install --registry=$npmSource
echo "=======$moduleName installed 依赖安装成功======="
npm run build env=$envName
if [ $isCacheDep == true ]; then
rm -rf $bakDir && mkdir -p $bakPDir $bakMDir
echo "=======$modulePName 缓存node_modules依赖包数据======="
mv -vf ../$modulePName/node_modules/{.[!.],}* $bakPDir
echo "=======$moduleName 缓存node_modules依赖包数据======="
mv -vf ./node_modules/{.[!.],}* $bakMDir
fi
fi
zip -r dist.zip dist
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
分包构建模式比较:
default模式 =》cache模式=》pull模式
模式 | 说明 | 设置方式 | 局限性 | 构建速度 |
---|---|---|---|---|
default模式 | 正常全模式构建方式; | isCacheDep=false isPull=false | 要全部移除各个模块后再下载重新安装依赖库很费时间; | 普通 |
cache模式 | 要下载各个子模块,可缓存第三方依赖库;要跟pull模式独立以来使用 | isCacheDep=true isPull=false | 还是要下载各个模块,缓存拷贝费一点点时间; | 提速3-5倍 |
pull模式 | 要默认模式成功后,才使用之前的各个子模块及第三方库;要跟cache模式独立以来使用 | isCacheDep=false isPull=true | 修改env后要第一次正常模式成功后再手动设置pull模式要不然默认设置会报错; 对应的env修改分支后要手动设置pull模式要不然项目不能切换对应的版本; | 提速5-8倍 |
各模式构建速度对比:
JKS上5个项目同时构建的情况下;
模块 | default模式用时 | cache模式用时 | pull模式用时 |
---|---|---|---|
实时模块 | 30min左右 ~ 47 | 10min左右 | 5min左右 |
调度模块 | 40min左右 ~ 48 | 16min左右 | 7min左右 |
# 项目内构建
- babel
- happack
# 本地构建工具
# 参考链接
https://v2.umijs.org/zh/plugin/develop.html
https://github.com/umijs/umi-example-monorepo/
https://github.com/umijs/umi/blob/2.x/packages/umi-plugin-locale/src/index.js