视图渲染的大致思想
# 视图渲染的大致思想
## 数据渲染




## 动态跳转
### 1. keyword跳转
```javascript
function searchByKeyword() {
searchProducts("keyword", $("#keyword_input", false).val());
function searchProducts(name, value, forced) {
// 下面那个方法是动态添加参数的, 第一个参数是老地址, 第二个是key, 第三个是value, 第四个是是否强制添加同名key
location.href = replaceParamVal(location.href,name,value,forced)
}
}
```
### 2. 品牌的跳转
注意: 品牌可以多选
```thymeleaf
<a th:href="${'javascript:searchProducts("brandId",'+brand.brandId+', true)'}">
```
### 3. 分类的跳转
注意: 分类不能多选
```thymeleaf
<a th:href="${'javascript:searchProducts("catalog3Id",'+catalog.catalogId+', false)'}"
th:text="${catalog.catalogName}">
</a>
```
### 4. 规格参数的跳转
注意: 分类可以多选
```thymeleaf
<a th:href="${'javascript:searchProducts("attrs","'+attr.attrId+'_'+attrValue+'", true)'}"
th:text="${attrValue}">
</a>
```
调用思路大体不差, 都是基于原来的路径上拼接参数, 一种是强制添加参数, 一种是参数覆盖
### 5. 分页的跳转与渲染
如果不能正确比较并显示样式, 一般是integer类型的数据没有变成string

```JavaScript
$(".page_a").click(function () {
var pn = $(this).attr("pn");
if (pn == null) {
pn = $(this).prev().prev().val() // 完善指定跳转功能
}
var href = location.href;
if (href.indexOf("pageNum") != -1) {
//替换pageNum
location.href = replaceParamVal(href, "pageNum", pn);
} else {
location.href = location.href + "&pageNum=" + pn;
}
return false;
})
```
**调用逻辑: 判断是否有分页参数, 如果没有则添加, 有则覆盖替换, 不改变其他参数**
### 6. 排序功能
```JavaScript
$(".sort_a").click(function () {
// changeStyle(this);
$(this).toggleClass("desc");
let sort = $(this).attr("sort");
sort = $(this).hasClass("desc") ? sort + "_desc" : sort + "_asc";
location.href = replaceParamVal(location.href, "sort", sort,false);
return false;
});
```
整体的思路: 因为是价格排序, 因此key已经确定了, 但是value可能是asc或者desc, 我们想要实现点一下升序, 再点一下降序, 我们可以使用toggleClass该方法, 该方法可以动态添加class, 若有则删除, 若无则添加, 然后通过判断是否有该class来决定是asc或desc, 最后动态添加条件即可, 不可强制添加
> 注意, 把那个方法干掉, 因为这个渲染没有任何的意义, CV上述代码

### 7. 价格区间
```JavaScript
$("#skuPriceSearchBtn").click(function () {
let from = $(`#skuPriceFrom`).val();
let to = $(`#skuPriceTo`).val();
if ("" == from && "" == to) return // 如果没有值, 不能找范围
let query = from + "_" + to;
location.href = replaceParamVal(location.href, "skuPrice", query,false);
});
```

### 8. 查看是否有库存
```JavaScript
$("#showHasStock").change(function () {
if ($(this).prop("checked")) {
location.href = replaceParamVal(location.href, "hasStock", 1, false);
} else {
let re = eval('/(hasStock=)([^&]*)/gi');
location.href = (location.href + "").replace(re, "");
}
return false;
});
```

### 9. 动态展示插叙条件
```thymeleaf
<div class="JD_pre" th:each="attr : ${result.attrs}" th:if="${param.attrs != null ? !#lists.contains(param.attrs, attr.attrId+'_'+attr.attrValue) : true}">
```
> 这里我首先判断了是否有这个条件, 如果有这个条件就继续判断这个条件是否是当前规格参数, 如果是就不显示, 如果没有或不是就显示
****
```thymeleaf
<div class="JD_pre" th:if="${param.catalog3Id == null}">
```
> 参数有分类就不显示
****
```tymeleaf
<div class="JD_nav_wrap" th:if="${param.brandId == null}">
```
> 参数有品牌就不显示
### 10. 面包屑功能
```html
<a th:if="${nav.navName != '分类'}" th:href="${'list.html?'+nav.link}" th:each="nav:${result.navs}"><span th:text="${nav.navName}"></span>:<span
th:text="${nav.navValue}"></span> x</a>
<a th:if="${nav.navName == '分类'}" th:each="nav:${result.navs}"><span th:text="${nav.navName}"></span>:<span
th:text="${nav.navValue}"></span></a>
```
这里对分类来了个特殊处理, 分类的面包屑不能干掉
#### 前端页面的封装
```java
private WebVO getWebVO(EsSearchParamVO esSearchParamVO, Integer pageNum, Integer totalPage, RespAggToObject result) {
// 封装页面数据(渲染页面的方法, 若进行前后端分离, 该方法可以直接删除), 分页信息一定存在
List<String> pageNavs = new ArrayList<String>() {
{
int cur = pageNum - 2;
while (this.size() != 5 && cur <= totalPage) { // 如果还没有6个或没有遍历完
if (cur < 1) {
cur++;
}else {
this.add(cur+"");
cur++;
}
}
}
};
List<Nav> navs = new ArrayList<>(); // 初始化面包屑集合, 避免控制很
List<String> attrs = esSearchParamVO.getAttrs(); // 获取所有的规格参数查询条件
Long catalog3Id = esSearchParamVO.getCatalog3Id();
List<Long> brandIds = esSearchParamVO.getBrandId();
if (attrs != null && !attrs.isEmpty()) { // 存在规格参数查询条件时
navs.addAll(
attrs.stream().map(attr -> {
Nav nav = new Nav();
String[] navBody = attr.split("_");
String navValue = navBody[1];
Long attrId = Long.valueOf(navBody[0]);
String navName = result.attrs.stream().filter(attrVO -> attrVO.getAttrId().equals(attrId))
.collect(Collectors.toList()).get(0).getAttrName();
// 不可能查询条件只有一个规格参数, 进来一定会有keyword或catalogId
String oldStr = "&attrs=" + attrId + "_" + navValue;
// 这里需要改一下前端
String link = esSearchParamVO.getSearchParam().replace(oldStr, "");
return nav.setNavName(navName).setNavValue(navValue).setLink(link);
}).collect(Collectors.toList())
);
} // 规格参数的面包屑
if (catalog3Id != null) { // 存在分类
String navValue = result.catalogs.stream().filter(catalogVO -> catalogVO.getCatalogId().equals(catalog3Id))
.collect(Collectors.toList()).get(0).getCatalogName(); // 面包屑的值就是分类的名字
String navName = "分类"; // 面包屑的名字是分类
String oldStr = "catalog3Id=" + catalog3Id;
String link = esSearchParamVO.getSearchParam().replace(oldStr, "");
link = link.replace("&&", "&"); // 处理不是分类开头的情况
navs.add(new Nav().setNavName(navName).setNavValue(navValue).setLink(link));
} // 分类的面包屑
if (brandIds != null && !brandIds.isEmpty()) {
navs.addAll(
brandIds.stream().map(brandId -> {
String navValue = result.brands.stream().filter(brandVO -> brandVO.getBrandId().equals(brandId)).collect(Collectors.toList())
.get(0).getBrandName();
String navName = "品牌";
String oldStr = "&brandId=" + brandId;
String link = esSearchParamVO.getSearchParam().replace(oldStr, "");
return new Nav().setNavName(navName).setNavValue(navValue).setLink(link);
}).collect(Collectors.toList())
);
}
return new WebVO(pageNavs, navs);
}
private static class WebVO {
public final List<String> pageNavs;
public final List<Nav> navs;
public WebVO(List<String> pageNavs, List<Nav> navs) {
this.pageNavs = pageNavs;
this.navs = navs;
}
}
```
##### 亮点说明
1. 获取规格参数
> 老师的获取规格参数有一个非常致命的问题, 即调用微服务去获取, 微服务调用存在一个响应时间, 如果数据量足够大, 这会使得整体吞吐量下滑, 非常的糟糕, 因此, 不建议采取该方式
> 而对应的数据其实在响应结果中, 即聚合里面的数据, 因为你通过这个条件查出来的, 聚合一定有这个规格条件, 因此一定可以获取对应的名字, 这样吞吐量大大提高
2. 编码与解码
> 编码: 即将字符串或其他音频文件转换为0101的二进制, 相当于将抽象的东西编码成数字
> 解码: 即将二进制数字还原其抽象的状态, 如字符串等
```java
// 进一步封装页面请求参数字段
String queryString = servletRequest.getQueryString(); // 编码后的查询条件
// 对查询条件进行解码, 避免浏览器的URL编码和后端编码不一致问题, 直接从解码解决所有的问题
queryString = URLUtil.decode(queryString);
esSearchParamVO.setSearchParam(queryString);
```
> 这里获取参数的时候, 这个参数已被编码, 因此我们需要解码, 为什么不在后端对字符串编码呢? 原因是编码的逻辑不一样, 导致后端整体逻辑出现问题!