1. 程式人生 > >電商專案day12(商品詳情頁面靜態化&freemarker的入門)

電商專案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="點選取消選擇">&nbsp;</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="點選取消選擇">&nbsp;</span></a></dd>
                                    </#list>
									</dl>
                                </#list>

 

六.總結