電商專案day12(商品詳情頁面靜態化&freemarker的入門)
今日目標:
掌握頁面靜態化技術
完成入門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>
六.總結