回顾-admin项目梳理

旧的 admin 项目结构梳理

项目梳理

admin 团队由于业务需要或者职能的转变, 项目经历过几次更迭

  1. old admin 成立以来一直到现在的古老 admin 系统
  2. new admin 新的 admin 系统
  3. mspa 更加独立的 admin 系统
  4. low code 提供高复用方案

old admin 目录结构及运行流程梳理

old admin 技术栈老旧且庞大混杂, 包含了node层(koa) / handlebars / jquery / element-ui 1.x / bootstrap / gulp 混杂 webpack

这里不会写出所有的细节, 类似于 koa-compress / koa-proxy / koa-router 等不会列举出来, 只写出最主要的流程

代码处理

  1. 使用 gulp + webpack 开始编译代码
1
2
gulp dev # for dev hmr
gulp build # for prod
  1. dev / build 命令在 gulpfile.js 文件中定义的, 其中 gulp 会启动 webpack 处理文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gulp.task(
'dev',
[
'conf_gen', // 生成 config 环境url配置
'js:locales', // 处理多语言文件
'css', // 处理css
],
function () {
// ...
// ...
gulp.start('webpack:js:dev'); // 启动 webpack
// ...
}
);
  1. 主要的 webpack 配置
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
// webpack.config.js
// git-revision-webpack-plugin 在代码中注入当前的 commit 信息

module.exports = {
entry: {
index: {}, // 登录, 新的mspa
storage: {
// 每个模块的入口
storage_add: ['pages/storage/storage_add.js'],
// ...
},
setting: {},
act: {},
credit_mgt: {
credit_mgt: ['pages/credit-mgt/main.js'],
},
// ...
},
output: {
chunkFilename: 'js/[name].[chunkhash].js',
filename: 'js/[name].bundle.js',
publicPath: get_public_path_desktop(),
path: __dirname + '/s/dist/desktop/', // 打包到 s/dist 目录下
// /s/dist/desktop/ dev
// //admin.klook.com/s/dist/desktop/ prod
},
module: {}, // 处理 vue / scss / md 等文件的 load
resolve: {
alias: {
// 一些路径的别名
src: 'web/s/src',
desktop: 'src/desktop',
},
},
plugins: [
// 插件
new webpack.ProvidePlugin({
$: 'jquery', // 注入一些全局变量
ADMIN_API: 'admin_api',
}),
],
optimization: {
vendors: {
// extract 3-party modules for better cache
name: 'vendors',
filename: 'js/vendors.bundle.js',
},
iview: {
// iview here
name: 'iview',
filename: 'js/iview.bundle.js',
},
commons: {
// 其他的文件
name: 'commons',
filename: 'js/commons.bundle.js',
chunks: 'initial', //
minChunks: 10,
},
}, // 分chunk
};

运行

  1. dev 时以 app.js 为入口启动 node 服务
1
node app.js --port=1122 # --harmony flag 可以让旧版本的 node 支持 es6
  1. app.js 中的流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var compress = require('koa-compress'); // gzip压缩
var logger = require('koa-logger'); // 打印请求
var koa = require('koa'); // koa
var proxy = require('koa-proxy'); // 请求代理转发
var web_all_init = require('./handlers/web_all.js').init; // 入口方法

// koa 实例
var app = (module.exports = koa());

// 初始化
web_all_init(app);

// 获取 --port=1122 端口参数
var port = argv.p || argv.port || 1122;
app.listen(port);
  1. web_all.js 中的流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var nav_config = require("./nav_config.js").nav_config; // 菜单栏设置
var koa_hbs = require("koa-hbs"); // handle-bars 处理插件

// 入口
function init(app, opt) {
// 静态资源路径
opt.static_root = opt.static_root || path.join(webroot, "s/dist");
// ...
// ...
// 路由, 匹配 mobile.js desktop.js 中配置的路由
app.use(create_web_all(mobile_handler, desktop_handler));
// _render
this._render = function(tpl, locales) {
// ...
// ...
// 注入一些全局变量到 window 下
_session: {},
_permission: {},
this.render(tpl, locals);
}

}
  1. desktop.js 中间件中的流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 匹配路径, 渲染相应的html文件
router.get(
['/api/partner_new_version', '/api/new_partner'],
web_comm.auth_klk, // 公共鉴权
web_comm.auth_klk_roles(
// 角色鉴权
`${role_map['API']['API-PARTNER-BROWSE']}`
),
function* () {
// 返回渲染页面
yield this._render('pages/api/index', {
title: 'api',
});
}
);
  1. 在返回的 html 中, 会用 hbs 手动写入引入的 js 文件, 以 3 中的 pages/api/index 为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{{!< layout }}

<!-- 标题 -->
{{#contentFor "title"}}
<title>{{title}}</title>
{{/contentFor}}

<!-- 主内容 -->
{{#contentFor 'main-content'}} {{{{raw-helper}}}}
<div id="app">
{{#if (eq extra_info [])}}
<div></div>
{{#else}}
<div></div>
<div>
{{/contentFor}}

<!-- handlebars mustache 语法 引入script -->
{{#contentFor "script_self"}}
<script src="{{s '/s/dist/desktop/js/api_index.bundle.js'}}"></script>
{{/contentFor}}
</div>
</div>
  1. 如果是新的 vue spa 流程, 会以 “pages/admin_spa” 为统一入口
1
2
3
4
<div id="app" />
{{#contentFor "script_self"}}
<script src="{{s '/s/dist/desktop/js/admin_spa.bundle.js'}}"></script>
{{/contentFor}}
  1. 其中 bundle : /s/dist/desktop/js/admin_spa.bundle.js 的打包入口在 webpackentry 中指定, 为 admin_spa.js (main.js)
1
2
3
4
5
6
// admin_spa.js 中正常地初始化 vue spa 即可
new Vue({
store,
router,
el: '#app',
});
  1. 每个模块, 如库存/商户/活动/财务, 都将是独立的 spa

部署

1
2
3
# deploy.sh
gulp build
pm2 start app.js

new admin 目录结构

这个工程没什么好说, 基于 vue-admin-template

  1. 目录结构, 其他业务线, 只要在 modules 下新增自己的模块就可以了
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
- build —— 项目构建文件目录
- index.js —— 入口文件
- conf —— 项目配置目录
- dist —— 项目打包输出目录
- docs —— 文档目录
- mock —— mock 服务目录
- node_modules —— npm 包目录
- public —— 公共文件目录
- src —— 开发源文件目录

- modules —— 业务模块

- demo —— demo 模块,展示所有如 业务模版、全局组件 等的效果**_(optional)_**
- layouts
- list
- list_1.vue
- list\_[num].vue
- [business_name]
- components
- [component_name]
- index.vue
- [module_name] (Top-level) —— 一级业务模块

- **\_**************\*\*\*****************\_\*\*
- **_⬇️⬇️⬇️ 开发人员关注 ⬇️⬇️⬇️_**

- modules **_(optional)_**
- [module_name] (Second-level) —— 二级业务模块
- modules
- [module_name] (More-level) —— 更多级业务模块
- (其余同 Top-level 目录)……
- routes —— 路由
- index.js —— 入口文件
- components —— 组件(处于一级模块目录下时,对于一级模块目录下所有子模块来说,是全局组件目录;处于其它层级模块目录下时,为其所在模块的局部组件目录。)**_(optional)_**
- [component_name] —— 任一组件目录
- index.vue —— 任一组件入口文件
- index.css/scss/...... —— 任一组件样式文件
- templates —— 模板**_(optional)_**
- index.html
- pages —— 业务页面
- [page_name].vue
- styles —— 样式**_(optional)_**
- [page_name].css/scss/...... —— vue 对应样式文件
- images —— 图片**_(optional)_**
- [image_name].jpg/jpeg/png/......
- store —— 状态管理(vuex)**_(optional)_**
- index.js —— 入口文件
- getters.js —— getter 定义文件
- mutation-consts.js —— mutation 方法常量文件
- api —— 接口实例**_(optional)_**
- index.js —— 入口文件
- api_url.js —— 接口路径文件
- utils —— 工具函数**_(optional)_**
- index.js —— 入口文件
- conf —— 模块配置**_(optional)_**
- index.js —— 入口文件
- .eslintignore —— eslint 忽略文件(局部)**_(optional)_**
- .eslintrc.js —— eslint 配置文件(局部)**_(optional)_**

- **_⬆️⬆️⬆️ 开发人员关注 ⬆️⬆️⬆️_**
- **\_**************\*\*\*****************\_\*\*

- assets —— 全局资源
- styles —— 样式
- [style_name].css/scss/......
- images —— 图片
- [image_name].jpg/jpeg/png/......
- fonts —— 字体
- [font_name].eot/ttf/woff/woff2/svg/......
- components —— 全局组件
- [component_name] —— 任一全局组件目录
- index.vue —— 任一全局组件入口文件
- index.css/scss/...... —— 任一全局组件样式文件
- index.js 入口文件
- conf —— 开发相关配置
- index.js —— 入口文件
- const.js —— 全局常量定义文件
- ……
- icons —— 图标文件目录(svg)
- layouts —— 业务模版
- components —— 组件目录
- [business_name] —— 任一业务模版目录
- index.vue —— 入口文件
- pages ——  默认提供页面
- 404.vue
- root.vue
- router —— 全局路由
- index.js —— 入口文件
- services —— 服务
- api —— 接口服务
- index.js —— 入口文件
- axios.js —— axios 实例封装文件(interceptors 开发等)
- lang —— 多语言
- index.js —— 入口文件
- [lang].js —— 多语言文案文件
- store —— 全局状态管理(vuex)
- modules —— 模块目录
- index.js —— 入口文件
- getters.js —— getter 定义文件
- mutation-consts.js —— mutation 方法常量文件
- styles —— 全局样式目录
- utils —— 工具函数目录
- index.js —— 入口文件
- [util_module_name].js —— 任一工具函数模块入口文件
- App.vue ——  应用入口 vue 文件
- main.js —— 应用入口 js 文件
- permission.js —— 权限控制文件

- static —— 静态文件目录 **_(optional)_**
- tests —— 项目测试目录
- ********\*\********* 我是分割线 ********\*\*********
- .commitlintrc.js —— commitlint 配置文件
- .editorconfig —— IDE 代码风格配置文件
- .env.development —— 环境脚本文件
- .env.production —— 环境脚本文件
- .env.staging —— 环境脚本文件
- .eslintignore —— eslint 忽略文件(全局)
- .eslintrc.js —— eslint 配置文件(全局)
- .gitignore —— git 忽略文件
- .jsdoc.json —— jsdoc 配置文件
- .travis.yml —— Travis CI 文件
- babel.config.js —— babel 配置文件
- jest.config.js —— jest 单元测试配置文件
- package.json
- postcss.config.js —— postcss 配置文件
- README.md
- tsconfig.json —— typescript 配置文件
- vue.config.js —— vue-cli 配置文件
- ……
  1. 部署, 产线运行目录下的 deploy.sh 即可, deploy.sh包含 checkout/install/build命令 (该项目依赖于 old admin 提供的 node 服务)

mspa (multi single page)

由于 new admin 也集成了各个业务线的代码, 越来越难于维护

并且 admin 团队后续将偏向于成为提供基础能力的团队, 业务开发将分散到各个vertical团队 (各个垂直业务团队, 如租车, hotel, airport transfer, ttd 等等)

于是提供 mspa 的开发模式.

  1. admin 团队提供以下能力:
1
2
3
4
5
6
7
8
9
admin-cli  # vue 项目脚手架 - 类 @vue/cli
admin-cli-service # 项目脚手架工具 - 类 @vue/cli-service
admin-ui # ui 组件库
admin-utils # 常用工具函数库
admin-login # 提供 google 登录功能
admin-permission # 提供 admin 权限验证
admin-layout # 提供统一的布局
....
....
  1. 各个业务团队使用 admin-cli 以及相关的库自行搭建项目
  2. 使用 node 编写接口, 在部署时, 上传每个 approuter, 集成展示
  3. 由于大家都是独立工程, 有需要可以自行部署至单独域名
  4. 统一托管到awscdn

low code

由于 admin 开发, 会出现大量的重复工作, 例如表单+表格的页面模式, 在 admin 占比非常大

于是使用业界方案 sula 或者 baidu/amis , 提供 low code 方案

该方案仅限于普通的通过配置生成界面, 希望做到更高的可拓展性和可拔插性

持续中