今日目标:
掌握页面静态化技术
完成入门demo
熟练使用常用的demo
商品详情页面的展示
静态页面动态效果实现
一.freemarker入门Demo
1.首先为什么要使用freemarker技术?
FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。
FreeMarker 与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等。
解决高并发访问的问题,因为这样非常消耗性能,这样做肯定不对
1、商品数据量巨大,并且从三张表中获取数据。所以查询性能很差,用户体验很差
2、商品详情查看功能是频繁操作,如果每次从数据库中获取数据,给数据库造成很大的访问压力。
那我们如何解决呢?
1、将数据缓存到redis 数据量小,并且经常查询,很少发生变化的数据。
2、页面静态化。
像我们这么多的商品数据是不能存到redis数据库中的,我们只能采用一种页面静态化的技术,
我们考虑什么时候生成页面呢?
商品上架的时候。
tb_goods完成商品上架
*****注意:商品上架时,同步上架商品到索引库,并同时生成该商品对应的静态html页面。
商品下架时,同步删除索引库下架商品,并同时删除该商品对应的静态html页面。
freemarker模板的后缀是ftl
搭建一个工程实现freemarker入门demo
代码:
// 第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是
Configuration configuration = new Configuration(Configuration.getVersion());
// freemarker 的版本号。
// 第二步:设置模板文件所在的路径。
configuration.setDirectoryForTemplateLoading(new File("D:\\IdeaProjects\\projectAll\\FreemarkDemo\\src\\main\\resources"));
// 第三步:设置模板文件使用的字符集。一般就是 utf-8.
configuration.setDefaultEncoding("utf-8");
// 第四步:加载一个模板,创建一个模板对象。
Template template = configuration.getTemplate("test.ftl");
// 第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
Map map = new HashMap();
map.put("name","张三");
map.put("message","来自freemarker的世界");
map.put("success",false);
List goodList = new ArrayList();
Map goods1 = new HashMap();
goods1.put("name","苹果");
goods1.put("price",5.8);
Map goods2 = new HashMap();
goods2.put("name","香蕉");
goods2.put("price",9.9);
goodList.add(goods1);
goodList.add(goods2);
map.put("goodList",goodList);
//日期格式化
map.put("today",new Date());
//数字转化字符串
map.put("point",123456789);
map.put("aaa",null);
map.put("bbb",null);
// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
Writer out = new FileWriter(new File("F:\\test.html"));
// 第七步:调用模板对象的 process 方法输出文件。
template.process(map,out);
// 第八步:关闭流
out.close();
ftl的模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<#--我是注釋標簽-->
${name} 你好 ,我是 ${message}
</body>
</html>
结果如图:
二.freemarker的常用指令
1.FTL指令
第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
Map map = new HashMap();
map.put("name","张三");
map.put("message","来自freemarker的世界");
map.put("success",false);
List goodList = new ArrayList();
Map goods1 = new HashMap();
goods1.put("name","苹果");
goods1.put("price",5.8);
Map goods2 = new HashMap();
goods2.put("name","香蕉");
goods2.put("price",9.9);
goodList.add(goods1);
goodList.add(goods2);
map.put("goodList",goodList);
//日期格式化
map.put("today",new Date());
//数字转化字符串
map.put("point",123456789);
map.put("aaa",null);
map.put("bbb",null);
// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
模板的配置:
第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
Map map = new HashMap();
map.put("name","张三");
map.put("message","来自freemarker的世界");
map.put("success",false);
List goodList = new ArrayList();
Map goods1 = new HashMap();
goods1.put("name","苹果");
goods1.put("price",5.8);
Map goods2 = new HashMap();
goods2.put("name","香蕉");
goods2.put("price",9.9);
goodList.add(goods1);
goodList.add(goods2);
map.put("goodList",goodList);
//日期格式化
map.put("today",new Date());
//数字转化字符串
map.put("point",123456789);
map.put("aaa",null);
map.put("bbb",null);
// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
算符运算符:
1.逻辑运算符:
逻辑运算符有如下几个:
逻辑与:&&
逻辑或:||
逻辑非:!
逻辑运算符只能作用于布尔值,否则将产生错误
2.比较运算符
1 =或者==:判断两个值是否相等.
2 !=:判断两个值是否不等.
3 >或者 gt:判断左边值是否大于右边值
4 >=或者 gte:判断左边值是否大于等于右边值
5 <或者 lt:判断左边值是否小于右边值
6 <=或者 lte:判断左边值是否小于等于右边值
注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且 FreeMarker 是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker 会把>解释成 FTL 标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>
注意:一般用()把需要的逻辑判断的扩展起来
三.商品详情页面的分析以及实现
1.首先创建需要生成的freemarker的工程
page_web page_service page_interface
2.分为两部分,组装数据和组装模板,首先组装数据
分析:我们分析商品详情页可知,需要三张表的数据 tb_goods tb_goodsdesc tb_item 我们通过组合实体类获得的三张表的数据
后台代码:
@Service
@Transactional
public class PageServiceImpl implements PageService {
//一次注入三个表得数据
@Autowired
private TbGoodsMapper tbGoodsMapper;
@Autowired
private TbGoodsDescMapper tbGoodsDescMapper;
@Autowired
private TbItemMapper itemMapper;
@Override
public Goods findOne(Long goodsId) {
//获取goods表的数据
TbGoods tbGoods = tbGoodsMapper.selectByPrimaryKey(goodsId);
//获得goodsdesc表的数据
TbGoodsDesc tbGoodsDesc = tbGoodsDescMapper.selectByPrimaryKey(goodsId);
//获得item表的数据
TbItemExample example = new TbItemExample();
TbItemExample.Criteria criteria = example.createCriteria();
criteria.andGoodsIdEqualTo(goodsId);
List<TbItem> itemList = itemMapper.selectByExample(example);
Goods goods = new Goods();
goods.setTbGoods(tbGoods);
goods.setTbGoodsDesc(tbGoodsDesc);
goods.setItems(itemList);
return goods;
}
@RestController
@RequestMapping("/page")
public class PageController {
@Reference
private PageService pageService;
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@RequestMapping("/genHtml")
public String genHtml(Long goodsId){
try {
// 第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是
Configuration configuration = freeMarkerConfigurer.getConfiguration();
// freemarker 的版本号。
// 第四步:加载一个模板,创建一个模板对象。
Template template = configuration.getTemplate("item.ftl");
// 第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
Goods goods = pageService.findOne(goodsId);
List<TbItem> items = goods.getItemList();
for (TbItem item : items) {
// 在这呢我们不要将组合实体类返回,因为返回还的遍历,取值,我们直接在这封装给map就可以了
Map<String,Object> map = new HashMap<>();
map.put("goods", goods);
map.put("item",item);
// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
Writer out = new FileWriter("F:\\item\\"+item.getId()+".html");
// 第七步:调用模板对象的 process 方法输出文件。
template.process(map,out);
// 第八步:关闭流
out.close();
}
return "success.............";
} catch (Exception e) {
e.printStackTrace();
return "fail............";
}
}
}
模板的抽取:
抽取完一部分,记得及时引入刚刚抽取的代码
页面展示完后,我们添加需要的静态资源就能看到,
四.页面的展示
1.需求分析:
如下图所示:
我们知道goods表中有
category1Id category2Id category2Id
后台代码:
List<TbItem> itemList = itemMapper.selectByExample(example);
//组装分类的信息
String category1Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory1Id()).getName();
String category2Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory2Id()).getName();
String category3Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory3Id()).getName();
Goods goods = new Goods();
//获取封装的的map集合
Map<String,String> categoryMap = new HashMap<>();
//将数据封装到map集合中
categoryMap.put("category1Name",category1Name);
categoryMap.put("category2Name",category2Name);
categoryMap.put("category3Name",category3Name);
goods.setCategoryMap(categoryMap);
注意我们一定要在goods的组合实体类中添加categoryMap的Map集合,最后注意打包到仓库
2.替换照片
{"color":"金色","url":"http://192.168.25.133/group1/M00/00/01/wKgZhVwbIGmABW-LAACnGz_9YE0694.jpg"}]
-->
<#assign imageList=goods.tbGoodsDesc.itemImages?eval>
<!--放大镜效果-->
<div class="zoom">
<#if (imageList?size>0)>
<!--默认第一个预览-->
<div id="preview" class="spec-preview">
<span class="jqzoom"><img jqimg="${imageList[0].url}" src="${imageList[0].url}" width="400px" height="1000px"/></span>
</div>
3.实现规格的选项
<#--
[{"attributeValue":["移动4G","联通3G"],"attributeName":"网络"},
{"attributeValue":["32G","128G"],"attributeName":"机身内存"}]
-->
<#assign specList=goods.tbGoodsDesc.specificationItems?eval>
<#list specList as spec>
<dl>
<dt>
<div class="fl title">
<i>${spec.attributeName}</i>
</div>
</dt>
<#list spec.attributeValue as value>
<dd><a href="javascript:;" class="selected">${value}<span title="点击取消选择"> </span>
</a></dd>
</#list>
</dl>
</#list>
五.静态页面的动态效果实现
1.需求:
页面:
<div class="controls">
<input autocomplete="off" type="text" value="{{num}}" minnum="1" class="itxt" />
<a href="javascript:void(0)" class="increment plus" ng-click="addNum(num+1)">+</a>
<a href="javascript:void(0)" class="increment mins" ng-click="addNum(num-1)">-</a>
</div>
pageController.js
app.controller("pageController",function($scope,$controller){
//继承代码
$controller("baseController",{$scope:$scope});
//商品数量的加减动态数量
$scope.num = 1;
$scope.addNum = function(num){
$scope.num = num;
if($scope.num<1){
$scope.num=1;
}
}
})
注意:一定要在模板中映入资源
<#--映入资源-->
<script type="text/javascript" src="plugins/angularjs/angular.min.js"> </script>
<script type="text/javascript" src="js/base.js"> </script>
<script type="text/javascript" src="js/controller/baseController.js"> </script>
<script type="text/javascript" src="js/controller/pageController.js"> </script>
2.实现规格选项的动态切换
思路:通过获得规格列表,判断是否相等,如果都相等,获取它的id值,然后拼接成url地址,重新刷新页面通过location.href
app.controller("pageController",function($scope,$controller){
//继承代码
$controller("baseController",{$scope:$scope});
//商品数量加减动态效果
$scope.num=1;
$scope.addNum=function(num){
$scope.num=num;
if($scope.num<1){
$scope.num=1;
}
}
//当前商品记录规格对象 var spec={"网络":"移动3G","机身内存":"64G"};
//当前商品规格选项是否选择的方法
$scope.isSelected=function(specName,specOption){
if(spec[specName]==specOption){
return true;
}else{
return false;
}
}
//规格选项切换动态效果实现
$scope.updateSpecAttribute=function(specName,specOption){
//更新当前商品规格数据
spec[specName]=specOption;
//构建规格组合列表
/*var specList=[
{id:1369284,spec:{"网络":"移动3G","机身内存":"64G"}},
{id:1369285,spec:{"网络":"移动3G","机身内存":"128G"}},
{id:1369286,spec:{"网络":"移动4G","机身内存":"64G"}},
{id:1369287,spec:{"网络":"移动4G","机身内存":"128G"}},
];*/
for(var i=0;i<specList.length;i++){
if(matchObject(specList[i].spec,spec)){
location.href=specList[i].id+".html";
}
}
}
//map1={"网络":"移动3G","机身内存":"64G"}
//map2={"网络":"移动3G","机身内存":"64G","颜色":"red"}
matchObject=function(map1,map2){
for(var k in map1){
if(map1[k]!=map2[k]){
return false;
}
}
//反向遍历,可以对于第二个多的在判断
for(var k in map2){
if(map2[k]!=map1[k]){
return false;
}
}
return true;
}
});
页面代码:
<script type="text/javascript">
//当前商品记录规格对象
var spec=${item.spec};
//构建规格组合列表
var specList=[
<#list goods.itemList as item>
{id:${item.id?c},spec:${item.spec}},
</#list>
];
</script>
<#--class="selected"-->
<#list spec.attributeValue as value>
<dd><a href="javascript:;" ng-click="updateSpecAttribute('${spec.attributeName}','${value}')" class="{{isSelected('${spec.attributeName}','${value}')?'selected':''}}">${value}<span title="点击取消选择"> </span></a></dd>
</#list>
</dl>
</#list>