看完上一个章节,相信你已经掌握了一个高效无误地编写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 ListfindAllTravelCategory();}
public class TravelCategoryDaoImpl implements TravelCategoryDao { private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); @Override public ListfindAllTravelCategory() { String sql = "select travel_cid,travel_name fromtravel_category "; returntemplate.query(sql,newBeanPropertyRowMapper(TravelCategory.class)); }}
package com.pz.route.service; import java.util.List; import com.pz.route.domain.TravelCategory; public interface TravelCategoryService { public ListfindAll();}
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 ListfindAll(){ List 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= '首页'; }else{ lis= '首页'; } //遍历数组,拼接字符串() for (var i = 0; i < data.length; i++) { var li =''; if(cid!=null&&cid==data[i].travelCid){ var li = ''+data[i].travelCname+''; } else{ li= '+data[i].travelCid+'">'+data[i].travelCname+''; } lis += li; } //拼接收藏排行榜的li,收藏排行榜 lis+= '收藏排行榜'; //将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 */public class PageList { private int totalCount;//总记录数 private int totalPage;//总页数 private int currentPage;//当前页码 private int pageSize;//每页显示的条数 private Listlist;//每页显示的数据集合 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 getList() { returnlist; } publicvoidsetList(Listlist) { 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 */ publicPageListpageQuery(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 publicPageListpageQuery(long travelRouteCid,int currentPage,intpageSize,String travelRouteName) { //创建分页bean PageListpage = new PageList(); //设置默认当前页,默认为第一页 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; //返回符合查询条件的线路列表 Listlist = 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查询线路图片 ListrouteImgList =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 ListfindTravelRouteByPage(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.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.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 publicListfindByTravelRouteId(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.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.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.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 = '首页'; //计算上一页的页码 var beforeNum = pb.currentPage - 1; if(beforeNum <= 0){ beforeNum = 1; } var beforePage = '上一页'; 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 = '+cid+','+i+',\''+rname+'\')">'+i+''; }else{ //创建页码的li li = ''+i+''; } //拼接字符串 lis += li; } var nextPage =null;lastPage=null; if(pb.totalPage==1){ lastPage = '末页'; nextPage = '下一页'; }else{ lastPage = '末页'; nextPage='下一页'; } 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 = '\n' + '
\n' + '
\n' + '
'
+route.travelRouteName+'\n' +
'
\n' +
'
'
+route.travelRouteIntroduce+'\n' +
'
\n' + '
\n' + '
\n'
+
' ¥\n' +
' '+route.travelRoutePrice+'\n' +
' 起\n' +
' \n' +
'
+route.travelRouteId+'">查看详情
\n' +
'
\n' + ' '; 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;}
我建了一个群,诚心邀请喜欢技术的小伙伴们加入讨论。