什么是静态页面
最早的时候,网站内容是通过在主机空间中放置大量的静态网页实现的,静态网页最大的缺点就是每个人看到的一样的。以静态网页为主的网站最大的困难在于网站内容的更新,每次都需要更改完重新上传覆盖
什么是动态页面
动态页面就是由网页的框架和内容本身抽象分离出来的,动态页面是通过执行asp、php、jsp、.net等程序,访问数据库,再生成客户端网页代码的网页;动态页面通常可以通过网站后台管理系统对网站的内容进行更新管理
进行页面静态化的好处
进行页面静态化后在页面上的体现为:访问速度加快,用户体验感明显提升;在后台体现为:访问脱离数据库,减轻了数据库访问压力
如何将动态页面静态化
动态页面非常便于管理,但是访问网页时还需要程序先处理一遍,所以导致访问速度相对较慢;而静态页面访问速度快,却又不便于管理,那么动态页面静态化可以将两种页面的好处集中到一起。
页面静态化的方案:
1、使用文件读写功能生成静态页面
2、使用nosql从内存中读取内容(其实这个已经不算静态化了而是缓存),比如说redis,虽然没有纯静态化那么快,但是要远比查数据库快的多
静态化方案需要注意的问题:
1、静态页面中动态(即时)数据问题(通过ajax可以解决)
2、静态化内容,一旦改动,都需要重新生成静态页面
什么是页面伪静态化
页面伪静态化是相对真实静态来讲的,通常我们为了增强搜索引擎的友好面,都将文章内容生成静态页面,但是有的朋友为了实时的显示一些信息,或者还想运用动态脚本解决一些问题,不能用静态的方式来展示网站内容,但是这就损失了对搜索引擎的友好面。怎么样在两者之间找个中间方法呢,这就产生了伪静态技术:展示出来的页面是将HTML一类的静态页面作为URL,但其实是用ASP一类的动态脚本来处理的。
- 从URL结构以及页面名称看,伪静态和静态页面是一样的;伪静态的页面后缀可以是HTML、HTM或者是目录格式
- 伪静态只是改变了URL的表现形式,实际上还是动态页面
- 静态页面可以节省服务器资源,而伪静态严格说是增加服务器资源消耗的
页面伪静态化在商城秒杀项目中的使用
本项目中只是简单的实现页面静态化:即将页面直接缓存到用户的浏览器上面
好处:用户访问数据的时候,不用去请求服务器,直接在本地缓存中取得需要的页面缓存
未作页面静态化时的请求过程:请求某一个页面,访问缓存,查看缓存中是否有,缓存中有直接返回,缓存中没有的话,将数据渲染到HTML页面再存到缓存,再将整个HTML页面返回给客户端显示
做了页面静态化的请求过程:第一次是去请求后台渲染好的HTML页面,之后的请求都是直接访问用户本地浏览器缓存的HTML页面 和静态资源,然后前端通过ajax来访问后端,只去获取页面需要显示的数据返回即可
对商品详情页面进行静态化
在先前的操作中我们将商品详情页面进行了redis缓存,因为这个接口只是展示相应产品详情和秒杀倒计时等信息,只要显示几个关键信息即可,所以其他的都可以实行静态化。这种技术,实际就是将前端分为两部分,一部分是不改变的HTML块,还有一块就是数据,它只要后端传数据到前端即可,用到ajax技术来请求数据
- 为了在浏览器端进行缓存,以及控制缓存时间,在application.properties里面添加配置静态资源配置项:
#static 静态资源配置,设置静态文件路径css、js、图片等
spring.resources.add-mappings=true
spring.resources.cache.period=3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
- 在static资源文件夹下面新建静态资源页面文件(以.htm格式结尾),这里为什么不能用.html文件结尾是因为如果不修改继续用.html后缀,系统会默认去找templates下面的同名文件,所以修改它以示区别:
- 定义一个GoodsDetailVo类来专门给页面传值:
package com.javaxl.miaosha_05.vo;
import com.javaxl.miaosha_05.domain.MiaoshaUser;
public class GoodsDetailVo {
//秒杀状态量初始值
private int miaoshaStatus = 0;
//开始时间倒计时
private int remainSeconds = 0;
private GoodsVo goods;
private MiaoshaUser user;
public int getMiaoshaStatus() {
return miaoshaStatus;
}
public void setMiaoshaStatus(int miaoshaStatus) {
this.miaoshaStatus = miaoshaStatus;
}
public int getRemainSeconds() {
return remainSeconds;
}
public void setRemainSeconds(int remainSeconds) {
this.remainSeconds = remainSeconds;
}
public GoodsVo getGoods() {
return goods;
}
public void setGoods(GoodsVo goods) {
this.goods = goods;
}
public MiaoshaUser getUser() {
return user;
}
public void setUser(MiaoshaUser user) {
this.user = user;
}
}
- 改造商品列表页面(goods_list.html)里面的跳转逻辑,修改跳转请求:
使用页面静态化后不需要再去服务端请求跳转,可以直接去客户端静态资源里面请求商品详情静态页面goods_detail.htm,这里注意修改一下后缀名称
- 修改之前的商品详情页面,删除thymeleaf具有的标签,让它成为最初始的HTML静态页面(现在静态页面不需要thymeleaf),修改使其都使用id来标识各个组件:
- 一跳转到goods_detail.htm页面时,js初始化,调后端的接口请求数据渲染到页面:
goods_detail.htm静态页面完整代码:
<!DOCTYPE HTML>
<html>
<head>
<title>商品详情</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<!-- jquery -->
<script type="text/javascript" src="/js/jquery.min.js"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css"/>
<script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
<!-- jquery-validator -->
<script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
<script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
<!-- layer -->
<script type="text/javascript" src="/layer/layer.js"></script>
<!-- md5.js -->
<script type="text/javascript" src="/js/md5.min.js"></script>
<!-- common.js -->
<script type="text/javascript" src="/js/common.js"></script>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url('/img/bg2.jpg') no-repeat;
background-size: 100% 100%;
}
#goodslist td {
border-top: 1px solid #39503f61;
}
</style>
</head>
<body>
<div class="panel panel-default" style="height:100%;background-color:rgba(222,222,222,0.8)">
<div class="panel-heading">秒杀商品详情</div>
<div class="panel-body">
<span id="userTip"> 您还没有登录,请登陆后再操作<br/></span>
<span>没有收货地址的提示。。。</span>
</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="200" height="200"/></td>
</tr>
<tr>
<td>秒杀开始时间</td>
<td id="startTime"></td>
<td>
<input type="hidden" id="remainSeconds"/>
<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" />
</form>-->
<div class="row">
<div class="form-inline">
<img id="verifyCodeImg" width="80" height="32" style="display:none"
οnclick="refreshVerifyCode()"/>
<input id="verifyCode" class="form-control" style="display:none"/>
<button class="btn btn-primary" type="button" id="buyButton" οnclick="getMiaoshaPath()">立即秒杀
</button>
</div>
</div>
<input type="hidden" name="goodsId" id="goodsId"/>
</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>
function getMiaoshaPath() {
var goodsId = $("#goodsId").val();
g_showLoading();
$.ajax({
url: "/miaosha/path",
type: "GET",
data: {
goodsId: goodsId,
verifyCode: $("#verifyCode").val()
},
success: function (data) {
if (data.code == 0) {
var path = data.data;
doMiaosha(path);
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.msg("客户端请求有误");
}
});
}
function getMiaoshaResult(goodsId) {
g_showLoading();
$.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);
}, 200);
} else {
layer.confirm("恭喜你,秒杀成功!查看订单?", {btn: ["确定", "取消"]},
function () {
window.location.href = "/order_detail.htm?orderId=" + result;
},
function () {
layer.closeAll();
});
}
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.msg("客户端请求有误");
}
});
}
function doMiaosha(path) {
$.ajax({
url: "/miaosha/" + path + "/do_miaosha",
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("客户端请求有误");
}
});
}
function render(detail) {
var miaoshaStatus = detail.miaoshaStatus;
var remainSeconds = detail.remainSeconds;
var goods = detail.goods;
var user = detail.user;
if (user) {
$("#userTip").hide();
}
$("#goodsName").text(goods.goodsName);
$("#goodsImg").attr("src", goods.goodsImg);
$("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
$("#remainSeconds").val(remainSeconds);
$("#goodsId").val(goods.id);
$("#goodsPrice").text(goods.goodsPrice);
$("#miaoshaPrice").text(goods.miaoshaPrice);
$("#stockCount").text(goods.stockCount);
countDown();
}
$(function () {
getDetail();
});
function getDetail() {
var goodsId = g_getQueryString("goodsId");//获取请求路径中的参数
$.ajax({
url: "/goods/detail/" + goodsId,
type: "GET",
success: function (data) {
if (data.code == 0) {
render(data.data);//使用render方法渲染页面
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.msg("客户端请求有误");
}
});
}
function countDown() {
var remainSeconds = $("#remainSeconds").val();
var timeout;
if (remainSeconds > 0) {//秒杀还没开始,倒计时
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀倒计时:" + remainSeconds + "秒");
timeout = setTimeout(function () {
$("#countDown").text(remainSeconds - 1);
$("#remainSeconds").val(remainSeconds - 1);
countDown();
}, 1000);
} else if (remainSeconds == 0) {//秒杀进行中
$("#buyButton").attr("disabled", false);
if (timeout) {
clearTimeout(timeout);
}
$("#miaoshaTip").html("秒杀进行中");
$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId=" + $("#goodsId").val());
$("#verifyCodeImg").show();
$("#verifyCode").show();
} else {//秒杀已经结束
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀已经结束");
$("#verifyCodeImg").hide();
$("#verifyCode").hide();
}
}
function refreshVerifyCode() {
$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId=" + $("#goodsId").val() + "×tamp=" + new Date().getTime());
}
</script>
</html>
后端相关接口代码:
/**
* 在html页面上做ajax异步刷新,达到页面伪静态的效果
*/
@RequestMapping(value = "/detail/{goodsId}")
@ResponseBody
public Result<GoodsDetailVo> detail(MiaoshaUser user,
@PathVariable("goodsId") long goodsId) {
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
long startAt = goods.getStartDate().getTime();
long endAt = goods.getEndDate().getTime();
long now = System.currentTimeMillis();
int miaoshaStatus = 0;
int remainSeconds = 0;
if (now < startAt) {//秒杀还没开始,倒计时
miaoshaStatus = 0;
remainSeconds = (int) ((startAt - now) / 1000);
} else if (now > endAt) {//秒杀已经结束
miaoshaStatus = 2;
remainSeconds = -1;
} else {//秒杀进行中
miaoshaStatus = 1;
remainSeconds = 0;
}
GoodsDetailVo vo = new GoodsDetailVo();
vo.setGoods(goods);
vo.setUser(user);
vo.setRemainSeconds(remainSeconds);
vo.setMiaoshaStatus(miaoshaStatus);
return Result.success(vo);
}