页面静态化+前后端分离
- 常用技术AngularJS、Vue.js
- 优点:利用浏览器的缓存
上面的方法比较复杂,我们只是用jQuery来模拟实现页面静态化
将页面直接缓存到用户的浏览器上面,好处:用户访问数据的时候,不用去请求服务器,直接在本地缓存中取得需要的页面缓存。
-
未作页面静态化: 请求某一个页面,访问缓存,查看缓存中是否有,缓存中有直接返回,缓存中没有的话,将数据渲染到html页面再存到缓存,再将整个html页面返回给客户端显示。
-
做了页面静态化: 第一次是去请求后台要渲染好的html页面,之后的请求都是直接访问用户本地浏览器的缓存的html页面 ,静态资源,然后前端通过Ajax来访问后端,只去获取页面需要显示的数据返回即可。
商品详情页静态化
在先前的操作中我们将商品详情页面进行了redis缓存,因为这个接口只是展示相应产品详情和秒杀倒计时等信息,只要显示几个关键信息即可,所以其他的都可以实现静态化。这种技术,实际就是前端分为两部分,一部分是不改变的html块,还有一块就是数据,它只要后端传数据到前端即可,用到ajax技术来请求数据。
为了在浏览器端进行缓存,以及控制缓存时间,在application.properties里面添加配置静态资源配置项:
#static 静态资源配置,设置静态文件路径css,js,图片等等
#spring.mvc.static-path-pattern=/static
@Setter
@Getter
public class GoodsDetailVo {
// 秒杀状态量初始值
private int status = 0;
// 开始时间倒计时
private int remailSeconds = 0;
private GoodsVo goodsVo;
private MiaoshaUser user;
改造商品列表页面(goods_list.html)里面的跳转逻辑,修改跳转请求
之前的请求路径:
<td><a th:href="'/goods/to_detail/'+${goods.id}">详情</a></td>
现在的请求路径:
<td><a th:href="'/goods_detail.htm?goodsId='+${goods.id}">详情</a></td>
而我们现在是不需要再去服务端请求跳转,直接去客户端静态资源里面请求商品详情静态页面goods_detail.htm,这里注意一下修改一下后缀名称
修改之前的商品详情页面,删除thymeleaf具有的标签,让它成为最初始的html静态页面(现在静态页面不需要thymeleaf),修改使其都使用id来标识各个组件。
- 刚转到goods_detail.htm页面时,js初始化,执行下面方法:
$(function(){
getDetail();
});
getDetail方法:
function getDetail(){
//getQueryString获取请求路径中的参数
var goodsId=getQueryString("goodsId");
//alert(goodsId);
$.ajax({
url:"/goods/detail/"+goodsId,
type:"GET",
success:function(data){
if(data.code==0){
//使用render方法渲染页面
render(data.data);
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("请求有误!");
}
});
}
getDetail()方法使用ajax异步请求后台取得该页面需要的数据,然后拿到数据之后调用render()方法渲染我们的静态页面,注意在getDetail中第一步做的是去获取商品的goodsId,因为list页面的商品详情请求是:
<td><a th:href="'/goods_detail.htm?goodsId='+${goods.id}">详情</a></td>
商品详情点击链接里面是带有我们的商品的goodsId的,那么我们通过方法getQueryString来获取请求路径url里面参数的值
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
获取到参数之后就可以去请求后端接口,获取数据去渲染:
function render(detail) {
//alert(detail.status);
var miaoshaStatus = detail.status;
var remailSeconds = detail.remailSeconds;
var goods = detail.goodsVo;
var user = detail.user;
if (user != null) {
//展示相应的
$("#userTip").hide();
}
$("#goodsName").text(goods.goodsName);
$("#goodsImg").attr("src", goods.goodsImg); //.text();
$("#startTime").text(goods.startDate); //还没有格式化
$("#remailSeconds").val(remailSeconds);
$("#goodsId").val(goods.id);
$("#goodsPrice").text(goods.goodsPrice); //goodsStock----- stockCount
$("#miaoshaPrice").text(goods.miaoshaPrice);
$("#stockCount").text(goods.goodsStock);
countDown();
}
倒计时countDown()方法:
function countDown(){
//获取秒杀倒计时进行判断,0-->正在进行秒杀,-1-->秒杀结束,remailSeconds>0-->代表倒计时
var remailSeconds=$("#remailSeconds").val();
//alert("remailSeconds:"+remailSeconds);
var timeout;
if(remailSeconds>0){//秒杀还没有开始,进行倒计时功能
$("#buyButton").attr("disabled",true);
$("#miaoshaTip").html("秒杀倒计时:"+remailSeconds+"秒");
//倒计时
timeout=setTimeout(function(){
$("#countDown").text(remailSeconds-1);
$("#remailSeconds").val(remailSeconds-1);//remailSeconds这是input
countDown();
},1000);//一秒钟之后回调函数
}else if(remailSeconds==0){//正在进行秒杀
$("#buyButton").attr("disabled",false);
if(timeout){//如果timeout有值的情况
clearTimeout(timeout);
}
//将文案修改 df1fab4272a24cdf9432adb9fd69cb38
$("#miaoshaTip").html("秒杀进行中");
}else{
//小于0的情况,秒杀结束,将秒杀按钮设置为不可点击
$("#buyButton").attr("disabled",true);
$("#miaoshaTip").html("秒杀结束");
}
}
- 将后端GoodsController里面接收商品详情页的请求的接口修改为下面代码:
@RequestMapping(value="/detail/{goodsId}") //produces="text/html"
@ResponseBody
public Result<GoodsDetailVo> toDetail_staticPage(Model model,MiaoshaUser user,
HttpServletRequest request,HttpServletResponse response,@PathVariable("goodsId")long goodsId) {//id一般用snowflake算法
System.out.println("页面静态化/detail/{goodsId}");
model.addAttribute("user", user);
GoodsVo goodsVo=goodsService.getGoodsVoByGoodsId(goodsId);
model.addAttribute("goods", goodsVo);
//既然是秒杀,还要传入秒杀开始时间,结束时间等信息
long start=goodsVo.getStartDate().getTime();
long end=goodsVo.getEndDate().getTime();
long now=System.currentTimeMillis();
//秒杀状态量
int status=0;
//开始时间倒计时
int remailSeconds=0;
//查看当前秒杀状态
if(now<start) {//秒杀还未开始,--->倒计时
status=0;
remailSeconds=(int) ((start-now)/1000); //毫秒转为秒
}else if(now>end){ //秒杀已经结束
status=2;
remailSeconds=-1; //毫秒转为秒
}else {//秒杀正在进行
status=1;
remailSeconds=0; //毫秒转为秒
}
model.addAttribute("status", status);
model.addAttribute("remailSeconds", remailSeconds);
GoodsDetailVo gdVo=new GoodsDetailVo();
gdVo.setGoodsVo(goodsVo);
gdVo.setStatus(status);
gdVo.setRemailSeconds(remailSeconds);
gdVo.setUser(user);
//将数据填进去,传至页面
return Result.success(gdVo);
}
- goods_detail.htm完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>商品详情</title>
<!-- jquery -->
<!-- <script type="text/javascript" src="@{/js/jequery.min.js}"></script> -->
<script type="text/javascript" src="/jquery-validation/lib/jquery-1.11.1.js"></script>
<link type="text/css" rel="stylesheet" href="/bootstrap/css/bootstrap.css"/>
<script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
<!-- layer -->
<script type="text/javascript" src="/layer/layer.js"></script>
</head <body>
<div class="panel panel-default">
<div class="panel-heading">秒杀商品详情</div>
<div class="panel-body">
<span id="userTip">您还没有登录,请登录后再操作</span>
<span>没有收货地址的提示。。。</span>
<input type="hidden" id="goodsId" > <!-- $("#goodsId").val(goods.id); -->
</div>
<table class="table" id="goodslist">
<tr>
<td>商品名称</td>
<td colspan="3" id="goodsName"></td>
</tr>
<tr>
<td>商品图片</td>
<td colspan="3"><img id="goodsImg" width="80" height="60"></img></td>
</tr>
<tr>
<td>秒杀开始时间</td>
<td id="startTime"></td>
<td id="">
<!-- 先取得这个时间 -->
<input type="hidden" id="remailSeconds"></input>
<span id="miaoshaTip"></span>
</td>
<td>
<form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
<button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
<input type="hidden" name="goodsId" id="goodsId"></input>
</form>
</td>
</tr>
<tr>
<td>商品原价</td>
<td colspan="3" id="goodsPrice"></td>
</tr>
<tr>
<td>秒杀价</td>
<td colspan="3" id="miaoshaPrice"></td>
</tr>
<tr>
<td>库存数量</td>
<td colspan="3" id="stockCount"></td>
</tr>
</table>
</div>
</body>
<script type="text/javascript"> //获取秒杀地址 function getMiaoshaPath(){ var goodsId=$("#goodsId").val(); } //做轮询 function getMiaoshaResult(goodsId){ $.ajax({ url:"/miaosha/result", type:"GET", data:{ goodsId:$("#goodsId").val() }, success:function(data){ if(data.code==0){ var result=data.data; if(result<0){ layer.msg("抱歉,秒杀失败!"); }else if(result==0){ //继续轮询 setTimeout(function(){ getMiaoshaResult(goodsId); },50);//50ms之后继续轮询 layer.msg(data.msg); }else{ layer.confirm("恭喜你,秒杀成功!查看订单?",{btn:["确定","取消"]}, function(){ //秒杀成功,跳转详情页面 window.location.href="order_detail.htm?orderId="+result; }, function(){ layer.closeAll(); }); } //轮询 //getMiaoshaResult($("#goodsId").val()); }else{ layer.msg(data.msg); } }, error:function(){ layer.msg("请求有误!"); } //token如果cookie里面有,会自己带过去 }); } function doMiaosha(){ alert("秒杀!"); $.ajax({ url:"/miaosha/do_miaosha_ajaxcache", type:"POST", data:{ goodsId:$("#goodsId").val() }, success:function(data){ if(data.code==0){ //秒杀成功,跳转详情页面 //window.location.href="order_detail.htm?orderId="+data.data.id; //轮询 getMiaoshaResult($("#goodsId").val()); }else{ layer.msg(data.msg); } }, error:function(){ layer.msg("请求有误!"); } //token如果cookie里面有,会自己带过去 }); } $(function(){ //countDown(); getDetail(); }); //渲染页面--------5-17 function render(detail){ //alert(detail.status); var miaoshaStatus=detail.status; var remailSeconds=detail.remailSeconds; var goods=detail.goodsVo; var user=detail.user; if(user!=null){ //展示相应的 $("#userTip").hide(); } $("#goodsName").text(goods.goodsName); $("#goodsImg").attr("src",goods.goodsImg); //.text(); $("#startTime").text(goods.startDate); //还没有格式化 $("#remailSeconds").val(remailSeconds); $("#goodsId").val(goods.id); $("#goodsPrice").text(goods.goodsPrice); //goodsStock----- stockCount $("#miaoshaPrice").text(goods.miaoshaPrice); $("#stockCount").text(goods.goodsStock); countDown(); } function getQueryString(name){ var reg=new RegExp("(^|&)"+name+"=([^&]*)(&|$)"); var r=window.location.search.substr(1).match(reg); if(r!=null){ return unescape(r[2]); } return null; } function getDetail(){ var goodsId=getQueryString("goodsId"); //alert(goodsId); $.ajax({ url:"/goods/detail/"+goodsId, type:"GET", success:function(data){ if(data.code==0){ render(data.data); }else{ layer.msg(data.msg); } }, error:function(){ layer.msg("请求有误!"); } }); } function countDown(){ //获取秒杀倒计时进行判断,0-->正在进行秒杀,-1-->秒杀结束,remailSeconds>0-->代表倒计时 var remailSeconds=$("#remailSeconds").val(); //alert("remailSeconds:"+remailSeconds); var timeout; if(remailSeconds>0){//秒杀还没有开始,进行倒计时功能 $("#buyButton").attr("disabled",true); $("#miaoshaTip").html("秒杀倒计时:"+remailSeconds+"秒"); //倒计时 timeout=setTimeout(function(){ $("#countDown").text(remailSeconds-1); $("#remailSeconds").val(remailSeconds-1);//remailSeconds这是input countDown(); },1000);//一秒钟之后回调函数 }else if(remailSeconds==0){//正在进行秒杀 $("#buyButton").attr("disabled",false); if(timeout){//如果timeout有值的情况 clearTimeout(timeout); } //将文案修改 df1fab4272a24cdf9432adb9fd69cb38 $("#miaoshaTip").html("秒杀进行中"); }else{ //小于0的情况,秒杀结束,将秒杀按钮设置为不可点击 $("#buyButton").attr("disabled",true); $("#miaoshaTip").html("秒杀结束"); } } </script>
</html>