猿进化系列17——实战之一文学会前后端分离套路

时间:2022-07-22
本文章向大家介绍猿进化系列17——实战之一文学会前后端分离套路,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

看完上一个章节,相信你已经掌握了一个高效无误地编写SQL的办法,学会了完成小项目的注册功能,对如何使用之前学过的一些知识有了一定的认识,今天我们继续学习,一起来搞懂前后端分离的套路。

在搞懂MVC框架原理一文中,上面这个MVC的模型图片大家应该有点印象了,客户端发起响应,服务端使用模板技术从当了view的角色,在服务端的应用服务器中渲染html,然后返回给客户端,客户端完成解析。

但实际上,对于浏览器而言,关心的只是html,css,js等元素,对于是谁返回给它的,它并不关心。浏览器解析html,完成样式渲染,加载运行脚本语言。在这个过程中,js是可以去改变页面结构的,也可以再次发起请求。那么问题就来了,js可以改变页面结构,那渲染页面html代码的事情,交给它不就完了?

从这个点考虑,前端脚本只用做两件事情就可以了——获取后端动态数据和改变html的页面结构。后端只用提供改变html的结构所需要的数据就好了。这样子,前端的开发人员可以只用关注前端的事情,后端的开发人员关注返回的数据,职责上明确了,程序的开发方式也就明确了。前端和后端程序的开发相对独立了,也就是所谓的前后端分离了。分离的是职责,是工作方式,具体使用什么技术,做到了就好,而不是框架,不是一堆技术名词……

返回静态页面的事情,可以交给web服务器,在这种静态文件的处理能力上,web服务器比应用服务器强悍多了。浏览器获取到web服务器返回的静态资源,html,css,js……然后在渲染的过程中,js通常发起一个异步请求,到后端获取数据,至于为什么后端应用服务器中的程序返回JSON格式的数据,其实只是JSON是一种比较方便的能够序列化对象数据的格式而已,用其他的也可以,只是JSON格式的数据和javascript的对象和数组可以方便转换,用着比较方便。

这样做了之后,页面还可以保留一些静态节点,即使后端程序挂了,这些静态节点的存在,也不影响页面的展示功能。

动态导航栏

导航栏属于公共头部的一部分,导航栏的内容,实际上属于ul标签下的li标签。想要动态的展示导航栏的内容,需要从数据库里查询分类数据(查询travel_category表),可以使用json的方式返回。页面需要解析json数据,拼接成导航内容的,然后将内容插入到ul标签下即可。

后端代码

package com.pz.route.dao;
 
importjava.util.List;
 
importcom.pz.route.domain.TravelCategory;
 
 
/**
 *
 * @author pangzi
 *
 */
public interface TravelCategoryDao {
    /**
     * 查询所有
     * @return
     */
    public List<TravelCategory>findAllTravelCategory();
}
public class TravelCategoryDaoImpl implements TravelCategoryDao {
 
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
 
    @Override
    public List<TravelCategory>findAllTravelCategory() {
        String sql = "select travel_cid,travel_name fromtravel_category ";
        returntemplate.query(sql,newBeanPropertyRowMapper<TravelCategory>(TravelCategory.class));
    }
}
package com.pz.route.service;
 
import java.util.List;
 
import com.pz.route.domain.TravelCategory;
 
public interface TravelCategoryService {
 
    public List<TravelCategory>findAll();
}
package com.pz.route.service.impl;
 
import java.util.List;
 
import com.pz.route.dao.TravelCategoryDao;
import com.pz.route.dao.impl.TravelCategoryDaoImpl;
import com.pz.route.domain.TravelCategory;
import com.pz.route.service.TravelCategoryService;
 
public class TravelCategoryServiceImpl implements TravelCategoryService {
    //初始化数据访问接口
    private TravelCategoryDao travelCategoryDao= new TravelCategoryDaoImpl();
 
    @Override
    public List<TravelCategory> findAll(){
       
 
        List<TravelCategory> catrgoryList= null;
      
            //调用数据访问接口从数据库查询类目信息
            catrgoryList =travelCategoryDao.findAllTravelCategory();
          
 
        return catrgoryList;
    }
}

编写类目servlet

package com.pz.route.web.servlet;
 
import java.io.IOException;
import java.util.List;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.pz.route.domain.TravelCategory;
import com.pz.route.service.TravelCategoryService;
import com.pz.route.service.impl.TravelCategoryServiceImpl;
//继承之前封装的BaseServlet通配符匹配所有/category/的路径
@WebServlet("/category/*")
public class CategoryServlet extends BaseServlet {
//初始化service接口
    private TravelCategoryService service = new TravelCategoryServiceImpl();
 
    /**
     * 查询所有
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    publicvoidfindAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.调用service查询所有
        List<TravelCategory> cs = service.findAll();
        //返回json数据
       responseJson(cs,response);
 
    }
 
}

前端脚本

$.get("category/findAll",{},function (data) {
             //通过url获取travelCid参数,决定哪个分类被选中
             var reg = new RegExp('(^|&)' + cid + '=([^&]*)(&|$)', 'i');
             var r =window.location.search.substr(1).match(reg);
            
             var cid=null;
             var lis ='';
             cid=getUrlParam("cid");
            
            //[{travelCid:1,travelCname:国内游},{},{}]
            if(cid==null){
                lis= '<liid="indexPage"><a href="index.html">首页</a></li>';
            }else{
                lis= '<liid="indexPage"><a href="index.html">首页</a></li>';
            }
            //遍历数组,拼接字符串(<li>)
            for (var i = 0; i < data.length; i++) {
                var li ='';
                if(cid!=null&&cid==data[i].travelCid){
                        var li = '<liclass="nav-active"><a href="route_list.html?cid='+data[i].travelCid+'">'+data[i].travelCname+'</a></li>';
                }
                else{
                       li= '<li><ahref="route_list.html?cid='+data[i].travelCid+'">'+data[i].travelCname+'</a></li>';
                }
 
                lis += li;
               
            }
           
 
            //拼接收藏排行榜的li,<li><ahref="favoriterank.html">收藏排行榜</a></li>
           
            lis+= '<li id="favlist"><ahref="favoriterank.html">收藏排行榜</a></li>';
 
            //将lis字符串,设置到ul的html内容中
            $("#category").html(lis);
            $(window).trigger("categoryLoaded");
        });
分类列表页面

我们在动态生成的导航栏上加了a标签,点击之后页面会发生跳转到分类列表页面,所以我们需要编写一个新的页面——分类列表页面。页面跳转时,浏览器会发起get请求,将类目的id传过来,所以我们通过分类id来确定列表页面需要展示哪个分类下线路信息的数据,后端程序完成线路数据的查询,返回给列表页。由于线路的数据包含:线路基本信息、线路图片信息、线路商家信息、线路收藏信息、所以一次操作可能涉及多个表的数据。因为某个类型的线路信息可能很多,一次也没必要展示完全,所以列表页下方,有分页标记,我们浏览数据需要像“翻书”一样查看。也就是说,我们需要设定分页的大小,每一次,获取每一页的数据就好了。

后端代码

package com.pz.route.domain;
 
import java.util.List;
 
/**
 * 分页列表对象在list基础长增加分页的参数
 * @author pangzi
 *
 * @param<T>
 */
public class PageList<T> {
 
    private int totalCount;//总记录数
    private int totalPage;//总页数
    private int currentPage;//当前页码
    private int pageSize;//每页显示的条数
 
    private List<T> list;//每页显示的数据集合
 
    publicintgetTotalCount() {
        returntotalCount;
    }
 
    publicvoid setTotalCount(int totalCount) {
        this.totalCount =totalCount;
    }
 
    publicint getTotalPage(){
        returntotalPage;
    }
 
    publicvoid setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }
 
    publicintgetCurrentPage() {
        returncurrentPage;
    }
 
    publicvoidsetCurrentPage(int currentPage) {
        this.currentPage =currentPage;
    }
 
    publicint getPageSize(){
        returnpageSize;
    }
 
    publicvoid setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
 
    public List<T> getList() {
        returnlist;
    }
 
    publicvoidsetList(List<T> list) {
        this.list = list;
    }
}
package com.pz.route.service;
 
import com.pz.route.domain.PageList;
import com.pz.route.domain.TravelRoute;
 
/**
 * 线路Service
 */
public interface TravelRouteService {
 
    /**
     * 根据类别进行分页查询
     * @param travelRouteCid
     * @param currentPage
     * @param pageSize
     * @param travelRouteName
     * @return
     */
    publicPageList<TravelRoute> pageQuery(long travelRouteCid,int currentPage,intpageSize,String travelRouteName);
 
    /**
     * 根据id查询
     * @param rid
     * @return
     */
    public TravelRoutefindTravelRouteById(String travelRouteId);
}
package com.pz.route.service.impl;
 
import java.util.List;
 
import com.pz.route.dao.TravelFavoriteDao;
import com.pz.route.dao.TravelRouteDao;
import com.pz.route.dao.TravelRouteImgDao;
import com.pz.route.dao.TravelSellerDao;
import com.pz.route.dao.impl.TravelFavoriteDaoImpl;
import com.pz.route.dao.impl.TravelRouteDaoImpl;
import com.pz.route.dao.impl.TravelRouteImgDaoImpl;
import com.pz.route.dao.impl.TravelSellerDaoImpl;
import com.pz.route.domain.PageList;
import com.pz.route.domain.TravelRoute;
import com.pz.route.domain.TravelRouteImg;
import com.pz.route.domain.TravelSeller;
import com.pz.route.service.TravelRouteService;
 
public class TravelRouteServiceImpl implements TravelRouteService {
    private TravelRouteDaotravelRouteDao = new TravelRouteDaoImpl();
 
    private TravelRouteImgDaotravelRouteImgDao = new TravelRouteImgDaoImpl();
 
    private TravelSellerDaotravelsellerDao = new TravelSellerDaoImpl();
 
    private TravelFavoriteDaotravelFavoriteDao = new TravelFavoriteDaoImpl();
 
    @Override
    publicPageList<TravelRoute> pageQuery(long travelRouteCid,int currentPage,intpageSize,String travelRouteName) {
        //创建分页bean
        PageList<TravelRoute>page = new PageList<TravelRoute>();
       
        //设置默认当前页,默认为第一页
        if(0==currentPage){
             currentPage=1;
        }
       
        //设置默认分页大小,默认为10条每页
        if(0==pageSize){
             pageSize=10;
        }
       
        //设置当前页码
       page.setCurrentPage(currentPage);
        //设置每页显示条数
        page.setPageSize(pageSize);
       
        //设置线路总数
        int totalCount =travelRouteDao.findTotalCount(travelRouteCid,travelRouteName);
       page.setTotalCount(totalCount);
        //设置当前页显示的数据集合,mysql分页第一个参数,从0开始
        int start = (currentPage -1) * pageSize;
        //返回符合查询条件的线路列表
        List<TravelRoute>list = travelRouteDao.findTravelRouteByPage(travelRouteCid,start,pageSize,travelRouteName);
        page.setList(list);
 
        //计算总页数,需要考虑能除尽和不能除尽的情况,不能除尽的情况下,会多一页
        int totalPage = totalCount% pageSize == 0 ? totalCount / pageSize :(totalCount / pageSize) + 1 ;
       page.setTotalPage(totalPage);
 
 
        return page;
    }
 
    @Override
    public TravelRoutefindTravelRouteById(String travelRouteId) {
        //1.根据线路id查询线路对象
        TravelRoute travelRoute =travelRouteDao.findTravelRouteById(Long.parseLong(travelRouteId));
 
        //2.根据线路id查询线路图片
        List<TravelRouteImg>routeImgList =travelRouteImgDao.findByTravelRouteId(travelRoute.getTravelRouteId());
      
       travelRoute.setRouteImgList(routeImgList);
        //3.根据商家ID查询商家
        TravelSeller seller =travelsellerDao.findTravelSellerById(travelRoute.getTravelRouteSellerId());
       travelRoute.setSeller(seller);
        //4. 查询收藏次数
        int count =travelFavoriteDao.findCountByTravelRouteId(travelRoute.getTravelRouteId());
       travelRoute.setTravelRouteCount(count);
 
 
        return travelRoute;
    }
}
package com.pz.route.dao.impl;
 
import com.pz.route.dao.TravelRouteDao;
import com.pz.route.domain.TravelRoute;
import com.pz.route.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
 
import java.util.ArrayList;
import java.util.List;
 
public class TravelRouteDaoImpl implements TravelRouteDao {
    private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
 
    @Override
    public int findTotalCount(longtravelRouteCid,String travelRouteName) {
        //String sql = "selectcount(*) from tab_route where cid = ?";
        //1.定义sql模板
        String sql = "selectcount(*) from travel_route where 1=1 ";
        StringBuilder sb = newStringBuilder(sql);
 
        List params = newArrayList();//条件们
        //2.判断参数是否有值
        if(travelRouteCid != 0){
            sb.append( " andtravel_route_cid = ? ");
 
           params.add(travelRouteCid);//添加?对应的值
        }
 
        if(travelRouteName != null&& travelRouteName.length() > 0){
            sb.append(" andtravel_route_name like ? ");
 
           params.add("%"+travelRouteName+"%");
        }
 
        sql = sb.toString();
 
 
        returntemplate.queryForObject(sql,Integer.class,params.toArray());
    }
 
    @Override
    public List<TravelRoute>findTravelRouteByPage(long travelRouteCid , int start , int pageSize,StringtravelRouteName) {
        //String sql = "select* from tab_route where cid = ? and rname like ? limit ? , ?";
        String sql = " selecttravel_route_id,travel_route_name,travel_route_price,travel_route_introduce,travel_route_flag,travel_route_date,travel_route_count,travel_route_cid,travel_route_image,travel_route_seller_idfrom travel_route where 1 = 1 ";
        //1.定义sql模板
        StringBuilder sb = newStringBuilder(sql);
 
        List params = newArrayList();//条件们
        //2.判断参数是否有值
        if(travelRouteCid != 0){
            sb.append( " andtravel_route_cid = ? ");
 
           params.add(travelRouteCid);//添加?对应的值
        }
 
        if(travelRouteName != null&& travelRouteName.length() > 0){
            sb.append(" and travel_route_name likeconcat('%',?,'%') ");
 
           params.add(travelRouteName);
        }
        sb.append(" limit ? ,? ");//分页条件
 
        sql = sb.toString();
 
        params.add(start);
        params.add(pageSize);
 
 
      return template.query(sql,newBeanPropertyRowMapper<TravelRoute>(TravelRoute.class),params.toArray());
    }
 
    @Override
    public TravelRoutefindTravelRouteById(long travelRouteId) {
        String sql = "selecttravel_route_id,travel_route_name,travel_route_price,travel_route_introduce,travel_route_flag,travel_route_date,travel_route_count,travel_route_cid,travel_route_image,travel_route_seller_idfrom travel_route where travel_route_id = ?";
        returntemplate.queryForObject(sql,new BeanPropertyRowMapper<TravelRoute>(TravelRoute.class),travelRouteId);
    }
}
package com.pz.route.dao.impl;
 
import com.pz.route.dao.TravelRouteImgDao;
import com.pz.route.domain.TravelRouteImg;
import com.pz.route.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
 
import java.util.List;
 
public class TravelRouteImgDaoImpl implements TravelRouteImgDao {
 
    private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
 
 
    @Override
    publicList<TravelRouteImg> findByTravelRouteId(long travelRouteId) {
        String sql = "selecttravel_route_img_id,travel_route_id,travel_route_img_big_pic,travel_route_img_small_picfrom travel_route_img where travel_route_id = ? ";
        returntemplate.query(sql,newBeanPropertyRowMapper<TravelRouteImg>(TravelRouteImg.class),travelRouteId);
    }
}
package com.pz.route.dao.impl;
 
import com.pz.route.dao.TravelFavoriteDao;
import com.pz.route.domain.TravelFavorite;
import com.pz.route.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
 
import java.util.Date;
 
public class TravelFavoriteDaoImpl implements TravelFavoriteDao {
 
    private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
 
    @Override
    public TravelFavoritefindByTravelRouteIdAndUserId(long travelRouteId, long travelRouteUserId) {
        TravelFavorite favorite =null;
        try {
            String sql = "select travel_route_id,travel_create_date,travel_user_id from travel_favoritewhere travel_route_id = ? and travel_user_id = ?";
            favorite =template.queryForObject(sql, newBeanPropertyRowMapper<TravelFavorite>(TravelFavorite.class),travelRouteId, travelRouteUserId);
        } catch(DataAccessException e) {
            e.printStackTrace();
        }
        return favorite;
    }
 
    @Override
    public intfindCountByTravelRouteId(long travelRouteId) {
        String sql = "SELECTCOUNT(*) FROM travel_favorite WHERE travel_route_id = ?";
 
        return template.queryForObject(sql,Integer.class,travelRouteId);
    }
 
    @Override
    public void add(longtravelRouteId, long travelUserId) {
        String sql = "insertinto travel_favorite values(?,?,?)";
 
       template.update(sql,travelRouteId,new Date(),travelUserId);
    }
}
package com.pz.route.dao.impl;
 
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
 
import com.pz.route.dao.TravelSellerDao;
import com.pz.route.domain.TravelSeller;
import com.pz.route.util.JDBCUtils;
 
public class TravelSellerDaoImpl implements TravelSellerDao {
 
    private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
 
 
 
    @Override
    public TravelSellerfindTravelSellerById(long travelSellerId) {
 
        String sql = "selecttravel_seller_id,travel_seller_name,travel_seller_consphone,travel_seller_address  from travel_seller where travel_seller_id = ?";
        returntemplate.queryForObject(sql,newBeanPropertyRowMapper<TravelSeller>(TravelSeller.class),travelSellerId);
    }
}

前端脚本

$(function () {
           //获取cid的参数值
           var cid = getUrlParam("cid");
            //获取rname的参数值
            var rname = getUrlParam("rname");
            //判断rname如果不为null或者""
            if(rname){
                //url解码
                rname =window.decodeURIComponent(rname);
            }
 
            //当页码加载完成后,调用load方法,发送ajax请求加载数据
            load(cid,null,rname);
        });
 
        function load(cid ,currentPage,rname){
            //发送ajax请求,请求route/pageQuery,传递cid
            $.get("route/pageQuery",{cid:cid,currentPage:currentPage,rname:rname},function (pb) {
                //解析pagebean数据,展示到页面上
 
                //1.分页工具条数据展示
                //1.1 展示总页码和总记录数
                $("#totalPage").html(pb.totalPage);
                $("#totalCount").html(pb.totalCount);
                var lis = "";
 
                var fristPage = '<li onclick="javascipt:load('+cid+',1,''+rname+'')"><ahref="javascript:void(0)">首页</a></li>';
 
                //计算上一页的页码
                var beforeNum = pb.currentPage - 1;
                if(beforeNum <= 0){
                    beforeNum = 1;
                }
 
                var beforePage = '<li onclick="javascipt:load('+cid+','+beforeNum+',''+rname+'')"class="threeword"><a href="javascript:void(0)">上一页</a></li>';
 
                lis += fristPage;
                lis += beforePage;
                //1.2 展示分页页码
                /*
                    1.一共展示10个页码,能够达到前5后4的效果
                    2.如果前边不够5个,后边补齐10个
                    3.如果后边不足4个,前边补齐10个
                */
 
                // 定义开始位置begin,结束位置 end
                var begin; // 开始位置
                var end ; //  结束位置
 
 
                //1.要显示10个页码
                if(pb.totalPage < 10){
                    //总页码不够10页
 
                    begin = 1;
                    end = pb.totalPage;
                }else{
                    //总页码超过10页
                    begin = pb.currentPage - 5;
                    end = pb.currentPage + 4 ;
                    //2.如果前边不够5个,后边补齐10个
                    if(begin < 1){
                        begin = 1;
                        end = begin + 9;
                    }
                    //3.如果后边不足4个,前边补齐10个
                    if(end > pb.totalPage){
                        end = pb.totalPage;
                        begin = end - 9 ;
                    }
                }
 
                for (var i = begin; i <= end ; i++) {
                    var li;
                    //判断当前页码是否等于i
                    if(pb.currentPage == i){
 
                        li = '<lionclick="javascipt:load('+cid+','+i+',''+rname+'')"><ahref="javascript:void(0)">'+i+'</a></li>';
 
                    }else{
                        //创建页码的li
                        li = '<li onclick="javascipt:load('+cid+','+i+',''+rname+'')"><ahref="javascript:void(0)">'+i+'</a></li>';
                    }
                    //拼接字符串
                    lis += li;
                }
 
                var nextPage =null;lastPage=null;
                if(pb.totalPage==1){
                     lastPage = '<li><ahref="javascript:;">末页</a></li>';
                      nextPage = '<li><ahref="javascript:;">下一页</a></li>';
                }else{
                   lastPage = '<li onclick="javascipt:load('+cid+','+pb.totalPage+',''+rname+'')"><ahref="javascript:void(0)">末页</a></li>';
                   nextPage='<li onclick="javascipt:load('+cid+','+(pb.currentPage+1)+',''+rname+'')"><ahref="javascript:void(0)">下一页</a></li>';
                }
 
                lis += nextPage;
                lis += lastPage;
 
                //将lis内容设置到 ul
                $("#pageNum").html(lis);
 
                //2.列表数据展示
                var route_lis = "";
 
                for (var i = 0; i < pb.list.length; i++) {
                    //获取{rid:1,rname:"xxx"}
                    var route = pb.list[i];
 
                    var li = '<li>n' +
                        '                        <divclass="img"><img src="'+route.travelRouteImage+'" style="width:299px;"></div>n' +
                        '                        <divclass="text1">n' +
                        '                            <p>'+route.travelRouteName+'</p>n' +
                        '                            <br/>n' +
                        '                            <p>'+route.travelRouteIntroduce+'</p>n' +
                        '                        </div>n' +
                        '                        <divclass="price">n' +
                        '                            <pclass="price_num">n' +
                        '                               <span>&yen;</span>n' +
                        '                                <span>'+route.travelRoutePrice+'</span>n' +
                        '                                <span>起</span>n' +
                        '                            </p>n' +
                        '                            <p><ahref="route_detail.html?rid='+route.travelRouteId+'">查看详情</a></p>n' +
                        '                        </div>n' +
                        '                    </li>';
                    route_lis += li;
                }
                $("#route").html(route_lis);
 
                //定位到页面顶部
                window.scrollTo(0,0);
            });
 
        }
 
//根据传递过来的参数name获取对应的值
functiongetUrlParam(name) {
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)","i");
    var r = location.search.substr(1).match(reg);
    if (r!=null) return (r[2]); returnnull;
}