原文链接: http://www.luckyjing.com/posts/front-end/arch-explore.html
回顾大学四年,在学院课程大作业范围内的项目,并没有一次印象深刻的团队开发经历,往往都是三两程序员一聚,便草草开始了。在阿里云实习的几个月里,相比代码本身,学到的最多的便是人力资源的合理利用,工具的效率辅助,年已至末,最后一次大作业,将自己在实习过程中积累的经验运用起来,指定合理规范的项目开发流程,平稳地推进项目的开发,不会让项目边界越来越大以至于收不住边界,特地做此记录分享给大家。
背景
项目背景
本次的项目是一个在线购物商城系统,这个词相信大家已经耳熟能详了,具体细节不必多说,分为三个子系统Customer,Owner,Administrator
,需要将项目分为三个阶段,Release1,2,3
,采用原型开发模式,每个版本给出一个可供交付的产品,总结来说,项目性质为无复杂功能点,但业务量繁多。
成员介绍
团队成员共有20人,其中开发同学共10位,前端同学4位,后端同学6位。首先进行技术调研,和在公司不同,在公司大家技术栈其实差不多,在学校则不是,最终得出结果,前端熟悉React
同学1位,其余3位熟悉jQuery
,后端同学全部熟悉SSH框架
,为保证后端的统一,后端技术栈不作调整,但前端因考虑到自动化构建的工具的接入, 以及用jQuery
前期成本小,后期成本大,难以维护的现状,故全部采用React
技术栈,并对不熟悉的同学进行一次集体培训以及后续答疑解惑。
前期准备
三个子系统之间其实前端关联度不大,可以认为是三个独立的系统,对于前端而言,React全家桶
搭建起来并不容易,涉及到代码目录的规定,Webpack
的配置等等,故前端由我做出基层的脚手架,实现业务同学可以直接写业务,无需关系构件流程的目的,对于后端而言,不同数据之间是共享的,故由一名主力后端同学负责数据库的设计与构建,并且搭建好基础后端目录,其他同学在此基础上进行业务层面的开发。
项目构建历程
基础结构预览
在看过上面这个图以后,项目的整个轮廓已经清晰,为了实现以上架构,我们需要定一些小小目标:
-
前后端采用分离模式开发,通过一份提前给出的
API
文档进行协商,数据则通过以json
作为基础格式的接口进行交互。 -
打造一个
Mock
服务器,实现API文档已给出
,但后端接口还没有写好的时候,前端通过模拟接口可以透明地请求接口了。(注:透明的意思即Mock服务器可以拦截前端的所有请求,达到和真实线上服务调用接口一样的效果) -
动静资源分离,我们只有一台服务器,是很实惠阿里云学生机,所以前后端数据都要放在这台机器上,前端静态资源存储在
nginx
服务器下,后端服务放在tomcat/webapps
目录下,因为为单页应用,后端只需要在那份index.html
里引入前端静态资源就可以了(直接写入绝对路径),最终访问的时候,服务器能够辨别是请求静态资源还是后端服务器,也就是俗称的动静分离
。 - 快速部署,部署的时候如何轻松,快捷,避免配置各种环境,要配也是一个人配,不能让开发同学苦恼。
下面我们来一步步实现。
前端
简述
- 前后端分离开发,所以路由要放到前端,用
React-Router
。 - 前端要自动化构建,用
Webpack
。 -
React+Redux
配置其实挺麻烦的,如果能写好模板,然后自动生成就好了,用redux-cli
。 -
mock2easy
作为mock服务器。
基础目录结构
在项目开发前,我提前将项目的脚手架提前搭建好,项目的大概目录如下所示:
├── README.md
├── blueprints # 这就是上述第三点的模板,大家可以去github上搜 redux-cli
├── build # 构建后的文件
├── dev.js # 类似webpack-dev-server 加入了mock的服务
├── package.json
├── src
└── webpack.config.js
自动化构建命令
使用Webpack
提前写好配置文件,不管webpack.config.js
里有多少步骤,最终呈现出来的只有build
目录下那几个打包好的文件,将相关命令写到package.json
中,前端同学只需执行一个命令就可以启动服务了。
"scripts": {
"dev": "node dev.js",
"pub": "NODE_ENV=production webpack && git add . && git commit -m':grin:发布新功能' && git push origin master"
}
mock服务
mock服务器什么原理,一张图给大家展示一下。
解释一下,webpack-dev-server
简单点就是一个express
的服务器,那么如果我们直接用它的话,直接访问/ajax/test.json
肯定是跑不起来的,因为这个接口404啊,所以我们要想实现如下目标:既能拥有webpack-dev-server
独特的热刷新功能,还可以当请求后缀为xxx.json
的时候,去请求一个mock
服务器去拿数据然后返回,我们这个mock
服务使用的是mock2easy
,大家可以去GitHub
上搜搜看,一切皆透明,于是我们的dev.js
配置如下所示:
// 先实现一个webpack-dev-server
var app = connect();
var compiler = webpack(config);
app.use(require("webpack-dev-middleware")(compiler, config.devServer));
app.use(require("webpack-hot-middleware")(compiler));
// 核心代码 之 mock服务就是一个中间件
app.use(function(req,res,next){
if (req.url.indexOf('.json') == -1) {
return next();
}
// 发现是接口请求,然后去请求mock服务器,然后把数据返回
var _req = http.request(options, function (_res) {
var data = '';
_res.on('data', function (chunk) {
data += chunk;
});
_res.on('end', function () {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(data);
});
});
});
app.listen(config.devServer.port, function () {
console.log('开发环境已经启动: http://127.0.0.1:' + port);
});
// 启动mock服务器
require('mock2easynew')(options, function (app) {
app.listen(options.port, function () {
console.log('mock服务器已经启动,地址:http://localhost:' + options.port);
});
});
优化
- 在开发时,
React
,Redux
就不用被抽离出来了,当然代码也不用压缩了,一切以快为主。 - 在发布时,将
React
,Redux
这些库抽离出来,这样打包出来文件会小,库文件我们可以用免费的CDN
进行引入。
<script src="//cdn.bootcss.com/react/15.4.0/react.min.js"></script>
<script src="//cdn.bootcss.com/react/15.4.0/react-dom.min.js"></script>
<script src="//cdn.bootcss.com/redux/3.6.0/redux.min.js"></script>
实现以上优化,我们需要在我们的webpack.config.js
中加入如下逻辑,即需要拥有区分开发环境的能力。
// 区别环境
var debug = process.env.NODE_ENV !== 'production';
// 第三方资源
var externals = {
"react": 'React',
"react-dom": "ReactDOM",
'redux': 'Redux'
};
var config = {
externals: debug ? {} : externals,
}
经过以上步骤之后,我们的前端开发环境已经搭建好了,开发同学只要先去填写一些mock
的API,然后就可以在前端直接访问到了,所以剩下的只需要和后端进行约定下接口名称就可以了。
# step1
前后端约定API名称为:/ajax/profile.json
# step2
前端(假设是3005端口)在mock服务器(假设是8005端口)添加这个接口 && 后端正常工作...
# step3
访问localhost:3005/ajax/profile.json 成功拿到数据
# step4
前端代码里直接书写 $.ajax('/ajax/profile.json') 即可
后端简介
- 后端基本上尽可能也要自动化,快捷化,所以使用
maven
去进行项目的构建,达到拥有一份源代码便可四处运行的目的。 - 使用
postman
让后端同学进行接口的测试。
服务器部署实战
到目前为止,前后端同学都可以在各自本地进行业务开发了,而且相互不耦合,以一份API文档为准便可以开发,接下来要讨论的就是构建的事情了,我们要达到的目的是:前端打包好后的静态资源可以自动部署到服务器上,后端代码推到仓库后,会自动进行maven构建,打war包,并自动上传到服务器上,重启tomcat服务器。
为了实现以上目的,项目采用阿里云code
进行代码管理,与此同时,使用阿里云CRP
服务实现上述持续化,描述如下:
- CRP服务检测指定的远程仓库
master
分支是否有更新 - 如果有更新,则执行一段脚本(由用户输入,往往为一些构建命令)
- 用户指定需要上传的文件(往往是第二步骤的产出)
- 用户指定需要上传到的服务器以及路径
- 用户指定上传结束后需要执行的一段脚本(往往为一些部署前的命令,比如移动静态资源到指定目录,或关闭tomcat,解压缩包,重启tomcat)
还需要对nginx
做一下简单配置,实现动静分离。
# 由nginx处理静态页面
location ~ .*\.(gif|jpg|png|bmp|swf)$
{
expires 30d; #使用expires缓存模块,缓存到客户端30天
}
location ~ .*\.(html|jsp|js|css)?$
{
expires 1d;
}
# 其余请求全部转到tomcat
location ~ .*$ {
proxy_pass http://127.0.0.1:8080;
}
我们集成后的效果如下,当集成完毕后,可以通知测试同学进行及时测试,提前暴露出问题,保证项目的稳步前进。
总结
全程构建系统的过程中,一直遵循的守则是自动化越高越好(redux-cli
,maven
,持续交付
),细节屏蔽的越高越好(执行命令写到package.json
里),将部署步骤和开发步骤相互隔离,开发同学只需要专注业务的开发即可,这样便可以三个子系统同时前进,开发同学可以有更多的时间去考虑代码本身的质量。
能够让我们放心去构建整个系统的基础,屏蔽底层的不同,多亏了在背后运行的Docker
服务,本次项目中的尝鲜体验已经感受到了好处,将Docker
纳入项目的部署环境,往往会带来惊喜。
路还长,保持新鲜感,不断去构造更加完善的自动化系统。