jQuery 先后端分离项目总结

文档目录

  • 前言
  • 设计阶段
    1. 文档
    2. Restful api
    3. 技术选型
  • 开发阶段
    1. 本地开发与联调
    2. 路径、接口状态、登陆超时、服务器报错
    • 统一处理返回结果 登陆超时、字符串错误、其余字符串
  • 部署测试阶段
    1. 项目根目录
    2. 自动部署
  • 待解决问题
    1. 路由问题
    2. 状态共享
    3. 接口测试

前言

近两年尝试了一些先后端分离的项目,开发流程和效率上并非顺风顺水,效率与质量没有特别显著的提高,不过大部分是一些旧项目的维护和二期开发,架构上很难按照单页面应用的开发模式实现,期间也遇到不少问题,最近在旧的项目上用jquery作了一个小模块的先后端分离,并作了一下记录和总结。但愿多学习多提高,若是有好的实现方法你们能多多指点。html

设计阶段

1. 文档

在以前的开发与本次的开发,总会遇到的一个问题就是文档,前端用的是淘宝开源的RAP可视化接口管理工具, 搭建在咱们本身的服务器上,前端定义接口是按照页面来划分,一个页面对应的有几个接口所有列在页面名称下,前端

可是通过几个项目的实践下来,发现java团队的分工与前端不一样,是按照功能实现来分的,前端开发一个购买的流程,会分几个页面,第一步,第二步,第三步,再根据每一个页面功能调用接口,编表单、校验等业务逻辑开发,然后端是按照功能划分,好比前端的第一步、第二步、第三步这个三个页面都有获取对应A\B\C列表的接口,开发上仅仅是从库里查一下这个字段再返回给你,那么相似的功能,就会分给一个java工程师来作,对于后端效率高,接口实现上不会太分散。vue

因此致使接口文档有两份,后端用Excel维护一份,前端用RAP维护一份,前端开发开始前要根据后端提供的接口文档按照页面划分接口定义mock数据。开发期间偶尔接口一个字段的变动就须要维护两份,很不方便,效率反而下来了。java

2. Restful api

定义接口要考虑到多种状况,同时要作好规则约束,咱们遇到的仅仅几个,简单列一下。jquery

状态码: 接口报错有不少种状况,数据格式、服务器出错等,要设计好对应提示的信息和状态码。git

提交数据类型 ajax提交数据时通常都为json,属性不能是数组,咱们内部约定,文档可写数组,提交时统一将数组转为字符串。github

属性与数组 文档内全部返回值,标为list的即返回数组,没有标识的即返回属性。ajax

返回数据类型 咱们没有作的特别细,因此返回的类型都是字符串,前端须要统一处理成本身须要用的格式。express

全部入参增长params 见代码apache

$.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);
            }
        }
    });
复制代码

3. 技术选型

考虑到开发完之后要交给java团队本身来维护,咱们以前有用过vue和avalon,可是后端人员维护起来很麻烦,就直接用jquery+jquer.tmpl了。jquer.tmpl学习成本几乎能够忽略不计,另外一方面由于是旧的项目,前端几乎成型,左侧是树菜单,右侧是load进来的jsp页面,咱们就直接使用jquery的load来加载HTML文件了。

开发阶段

1. 本地开发与联调

mock工具备请求拦截的插件,能够直接实现返回数据,RAP官网文档

可是后期联调也须要转发和拦截,并且还有登陆模拟,就没有用,直接用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')}`);
});

复制代码

2. 路径、接口状态、登陆超时、服务器报错

统一项目根路径 添加部分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,服务器出错!')
                }
            }
        }
      
    });
复制代码

部署测试阶段

1. 项目根目录

本地开发和调试与测试服务器的静态资源链目录不一致,页面中有一些图片和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图片路径
    });
复制代码

2. 自动部署

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'
            }));
    })
复制代码

待解决问题

  1. 路由问题 原本想使用jQuery Hashchange或jqueryRouter来实现的,考虑到以前的模式和开发时间,就没有来替换。
  2. 状态共享 目前左右跨页面的状态都放在隐藏域的value和div的attr上,后期考虑引入Mbox作状态管理
  3. 接口测试 前端联调中,一大部分时间作了接口测试的工做,但愿可以找到好的接口测试工具和测试流程。

才疏学浅,若有问题恳请斧正。