本文共 5416 字,大约阅读时间需要 18 分钟。
近两年尝试了一些前后端分离的项目,开发流程和效率上并不是顺风顺水,效率与质量没有特别显著的提升,不过大部分是一些旧项目的维护和二期开发,架构上很难按照单页面应用的开发模式实现,期间也遇到很多问题,最近在旧的项目上用jquery做了一个小模块的前后端分离,并做了一下记录和总结。希望多学习多提升,如果有好的实现方法大家能多多指点。
在之前的开发与本次的开发,总会遇到的一个问题就是文档,前端用的是淘宝开源的, 搭建在我们自己的服务器上,前端定义接口是按照页面来划分,一个页面对应的有几个接口全部列在页面名称下,
但是经过几个项目的实践下来,发现java团队的分工与前端不同,是按照功能实现来分的,前端开发一个购买的流程,会分几个页面,第一步,第二步,第三步,再根据每个页面功能调用接口,编表单、校验等业务逻辑开发,而后端是按照功能划分,比如前端的第一步、第二步、第三步这个三个页面都有获取对应A\B\C列表的接口,开发上仅仅是从库里查一下这个字段再返回给你,那么类似的功能,就会分给一个java工程师来做,对于后端效率高,接口实现上不会太分散。
所以导致接口文档有两份,后端用Excel维护一份,前端用RAP维护一份,前端开发开始前要根据后端提供的接口文档按照页面划分接口定义mock数据。开发期间偶尔接口一个字段的变更就需要维护两份,很不方便,效率反而下来了。
定义接口要考虑到多种情况,同时要做好规则约束,我们遇到的仅仅几个,简单列一下。
状态码: 接口报错有很多种情况,数据格式、服务器出错等,要设计好对应提示的信息和状态码。
提交数据类型 ajax提交数据时一般都为json,属性不能是数组,我们内部约定,文档可写数组,提交时统一将数组转为字符串。
属性与数组 文档内所有返回值,标为list的即返回数组,没有标识的即返回属性。
返回数据类型 我们没有做的特别细,所以返回的类型都是字符串,前端需要统一处理成自己需要用的格式。
所有入参增加params 见代码
$.ajax({ url: "separationCommon/getDic.gsp", type: "POST", dataType: "JSON", baseUrl: true, async: false, data: { "params.dicCode": !code ? "DIC_EB_APPNTPROVINCE": code, "params.searchType": 1 }, success: function (data) { if(data.status == 200) { var ARR = data.data.dicList; window.STR = provinceSelect(ARR, code); } } });复制代码
考虑到开发完以后要交给java团队自己来维护,我们之前有用过vue和avalon,但是后端人员维护起来很麻烦,就直接用jquery+jquer.tmpl了。jquer.tmpl学习成本几乎可以忽略不计,另一方面因为是旧的项目,前端几乎成型,左侧是树菜单,右侧是load进来的jsp页面,我们就直接使用jquery的load来加载HTML文件了。
mock工具有请求拦截的插件,可以直接实现返回数据,
但是后期联调也需要转发和拦截,而且还有登录模拟,就没有用,直接用express+http-proxy-middleware做的本地转发
const express = require('express');const timeout = require('connect-timeout');const proxy = require('http-proxy-middleware');const request = require("request");const cheerio = require("cheerio");const app = express();// 超时时间const TIME_OUT = 30 * 1e3;// 设置端口app.set('port', '8080');// 设置超时 返回超时响应app.use(timeout(TIME_OUT));app.use((req, res, next) => { if (!req.timedout) next();});proxyOption = { target: 'http://ipaddress/mockjsdata/6', // target: 'http://ipaddress:9527/eb/', pathRewrite: { '^/api/' : '/' // 重写请求,api/解析为/ }, changeOrigin: true, headers:{ 'Cookie': '' }};// 静态资源路径app.use('/', express.static('src/page'));request('http://192.168.180.87:9527/eb/syEdor/moniLongin.gsp', function(err,res,body){ // 获取cookie var str = res.headers['set-cookie'][0]; // 设置cookie proxyOption.headers = { 'Cookie':str }; if (!err && res.statusCode == 200) { // 反向代理 app.use('/api/*', proxy(proxyOption)); }else{ console.log('登录失败') }});// 监听端口app.listen(app.get('port'), () => { console.log(`server running @${app.get('port')}`);});复制代码
统一项目根路径 添加部分ajax拦截,判断条件为options.baseUrl;
// 项目路径拦截 $.ajaxPrefilter(function (options) { if(options.baseUrl) { if(options.url.indexOf('/eb/') != -1) { options.url = options.url; } else { options.url = '/eb/' + options.url; } options.crossDomain = false; } }); 复制代码
接口状态、登录超时、服务器报错 因为报错有很多种情况,正常的接口返回的状态码判断,如果非200则统一提示, 登录目前没有用单页token机制,部分还是会跳转到jsp,部分接口报错如500错误也会返回jsp,jsp的返回结果是html,也要增加判断,请求头内会携带session超时信息。
//ajax请求结束 $(document).unbind('ajaxComplete').bind('ajaxComplete', function(e,xhr,opt){ if(opt.baseUrl){ var str = xhr.responseText; try { var obj = JSON.parse(str); if(obj.status != 200) { popUp(obj.msg); } } catch(e) { // 通过XMLHttpRequest取得响应头,sessionstatus var sessionstatus = xhr.getResponseHeader('sessionstatus'); if(sessionstatus == "timeout"){ popUp('登录超时,请重新登录', function(){ window.location.reload(); }); }else if(sessionstatus == "perror"){ popUp('请求的链接地址可能存在非法字符', function(){ window.location.reload(); }); }else{ popUp('404,服务器出错!') } } } });复制代码
本地开发和调试与测试服务器的静态资源链目录不一致,页面中有一些图片和html文件的路径,用gulp打包替换。
gulp.task('prod:narrowImg', ['prod:narrowJs'], function () { return gulp.src(Config.html.htmlProphaseLoad) .pipe(replace(/src=\"\.\.\/images/g, "src=\"\/eb\/static\/images")) .pipe(replace(/\/api\//g, "\/eb\/")) .pipe(replace(/load\(\$\(\"#loadjsp\"\)/g, 'load("\/eb"+$("#loadjsp")')) .pipe(rename({ dirname: ''})) .pipe(gulp.dest(Config.html.distProphaseLoad)); // 替换三期load图片路径 });复制代码
gulp-sftp 很好用,直接配置一个任务就可以了。
gulp.task('ftp', function () { return gulp.src('dist/一二期/三期/load/*.*') .pipe(sftp({ host: 'ipaddress', user: 'user01', pass: 'password', remotePath: '/application/apache9527/apache/htdocs/eb/load' })); })复制代码