作为一名后端小伙,每次碰到页面都是感觉蜜汁痛苦的。因为对于前端不是太熟悉,好奇已久的单页应用(SPA)呢,一直没有去实践,只是猜测到怎么做的了,网上的教程也很是敷衍、模糊,所以此次抽空写一下自己前些时间实践时明白的SPA。
SPA 核心 js 源码
以下是一段js源码,是我在做课设的时候根据自己的理解写的。下面将会讲代码:
- 首先,我们知道单页应用就是将页面碎片化,一般通过 hash 改变触发函数去请求需要的碎片,用来进行动态的组装,同时通过 css 属性,隐藏不需要的部分。
- 页面定义了一个全局变量 hashSet 数组,用于存储请求的页面碎片的 id 和本身的 hash 组合值。通过符号 ‘@’ 分隔。
- 每当页面(index)发生了 hashchange事件,则触发下面的函数 hashChangeFun()。
- 函数通过 window.location.hash.toString()获取当前的 hash 值,并取最后的尾部。
- 然后遍历全局变量数组 hashSet ,如果 item 不是当前的 hash,则将当前 item 解析出来的 id片段隐藏,如果是当前的 hash,则将其显示出来(通过js操作 css 属性)。
- 如果遍历完数组 hashSet 仍没有当前 hash 对应的 item,则将当前的 hash 和基本 url 拼装,并通过 ajax 请求,将请求的页面提取的 id 和 hash拼装,并将其加入 hashSet。页面碎片添加到指定的 div中
/**
* 核心 SPA 代码
* @type {*[]} 存储请求的碎片id和hash组合值
*/
var hashSet=[];
function hashChageFun()
{
let hash=window.location.hash.toString();
hash=hash.substr(1,hash.length-1);
let baseUrl="${springMacroRequestContext.contextPath}/view/part/";
let flag=false;
for (i=0;i<hashSet.length;i++)
{
if (hashSet[i].split("@")[0]===hash)
{
$("#"+hashSet[i].split("@")[1]).show();
flag=true;
}
else
{
$("#"+hashSet[i].split("@")[1]).hide();
}
}
//排除根hash的情况
if (!flag&&hash.length!==0)
{
console.log("请求Url:"+baseUrl+hash);
$.get(baseUrl+hash,function (data) {
$("#father-div").append(data);
hashSet.push(hash+"@"+data.substr(data.indexOf("id=\"")+4,data.indexOf(" class")-10).trim());
console.log("id列表:"+JSON.stringify(hashSet));
});
}
}
后端视图控制器
用于响应前端请求基本视图 index 和页面碎片。
@Validated
@Controller
@RequestMapping(value = "/view")
@SuppressWarnings("all")
public class ViewController
{
/**
* 登陆页面
* @return login.ftl
*/
@Description(description = "登陆视图获取")
@GetMapping(value = "/user/toLogin")
public String toLogin()
{
return "login";
}
/**
* 登陆成功后进入用户主页
* SPA 核心页面,基础页面框架
* @return index.ftl
*/
@Description(description = "用户主页视图获取")
@GetMapping(value = "/user/index")
public String toIndex()
{
return "index";
}
/**
* 用于前端获取详情表格部分
* 主要是采购界面点击添加的那个药物详情表格
* 没毛病
* @return
*/
@Description(description = "药物采购详情表格获取")
@GetMapping(value = "/part/details")
public String details()
{
return "part/details";
}
/**
* 获取采购记录基础表格
* 采购基础表格界面
* SPA 碎片之一,上面 details 的父碎片
* @return
*/
@Description(description = "药物采购基础表格获取")
@GetMapping(value = "/part/record")
public String record()
{
return "part/record";
}
/**
* 获取数据表格
* 这个表格计划用来做数据报表的在线显示和查询
* SPA 大碎片之一
* @return
*/
@Description(description = "数据报表父表获取")
@GetMapping(value = "/part/table")
public String table()
{
return "part/pageTable";
}
/**
* 供SPA 获取库存扣减表格
* SPA 大碎片之一
* @return
*/
@Description(description = "销售扣减表格获取")
@GetMapping(value = "/part/sellTable")
public String sellTable()
{
return "part/sellTable";
}
/**
* 供SPA用户添加
* SPA 大碎片之一
* @return
*/
@Description(description = "用户添加表格获取")
@GetMapping(value = "/part/user")
public String getUserAdd()
{
return "part/userAdd";
}
/**
* 供前端获取各个数据表的自己的数据报表表格
* @param type 根据前端传递的代码进行条件渲染基础表格
* @param model 传递给模板引擎的参数容器
* @return
*/
@Description(description = "数据报表表格生成")
@GetMapping(value = "/part/dataTable/{type}")
public String getDataTable(@PathVariable @PositiveOrZero(message = "必须大于等于0") int type, Model model)
{
switch (type)
{
case 0:
model.addAttribute("type",0);
break;
case 1:
model.addAttribute("type",1);
break;
case 2:
model.addAttribute("type",2);
break;
case 3:
model.addAttribute("type",3);
break;
case 4:
model.addAttribute("type",4);
break;
case 5:
model.addAttribute("type",5);
break;
case 6:
model.addAttribute("type",6);
}
return "part/dataShowTable";
}
@GetMapping(value = "/test")
public String getTestTable()
{
return "test/tables";
}
}
最后
不做演示,因为没有单独去写一个小 demo,源码可以参考药品销售管理系统中的对应部分,主要就是 index.ftl 和 ViewController.java。页面碎片在 templates/part 之下。