最近在公司刚完成了一个微信的项目,我用的原生js+jsp做的一个单页应用,快完成的时候,需求添加,需要嵌入到公司app的多个入口中,我开始考虑单页应用的分模块加载问题。

刚开始直接这样重定向到index.jsp上,确定hash值为onlineOffline,正常没问题

1
2
3
4
5
6
7
8
9
10
11
12
<%
String hostStr = "http://testsaas-1000-100001.m.izhuazhua.com";
String serviceType1 = request.getParameter("serviceType");
String yua1 = request.getHeader("yua");
int platform = -1;
if(yua1.indexOf("Android") != -1){
platform = 1;
response.sendRedirect(hostStr + "/index.jsp#/onlineOffline?serviceType=" + serviceType1);
} else if(yua1.indexOf("iOS") != -1){
platform = 2;
}
%>

但是index.jsp作为单页。是这样实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<head>
<link rel="stylesheet" href="css/spinningwheel-didi.css?timestamp=34">
<link rel="stylesheet" href="css/base.css?timestamp=367fdf">
<link rel="stylesheet" href="css/main.css?timestamp=fd23f">
<link rel="stylesheet" href="css/swiper.css">
<link rel="stylesheet" href="css/zzDateRanger.css">
<script src="library/template-native-debug.js"></script>
<script src="library/fastclick.js"></script>
<script src="library/jquery.min.js"></script>
<script src="library/iscroll-lite.js"></script>
<script src="library/swiper-3.3.1.jquery.min.js"></script>
<script src="library/spinningwheel-didi.js?timestamp=436sdf5"></script>
<script src="js/services/util.js?time=3df534d3"></script>
<script src="js/services/map.js?time=3df3"></script>
<script src="js/services/zzDateRanger.js?time=3df3"></script>
<script src="js/controllers/index.js?time=0sdffds9"></script>
<script src="js/controllers/service.js?time=9s99055"></script>
<script src="js/controllers/order.js?time=sdfsdfs"></script>
<script src="js/controllers/login.js?time=sdsdfsdf"></script>
<script src="js/controllers/my.js?time=3df3"></script>
<script src="js/controllers/myInfo.js?time=34dfsd"></script>
<script src="js/controllers/myAccount.js?time=sdsdfsdfsdf"></script>
<script src="js/controllers/petInfoInput.js?time=9f32423"></script>
<script src="js/controllers/listServicePrice.js?time=3df3"></script>
<script src="js/controllers/petColor.js?time=3df3"></script>
<script src="js/controllers/petFood.js?time=3df3"></script>
<script src="js/controllers/petBreed.js?time=3df3"></script>
<script src="js/controllers/petList.js?time=3df3"></script>
<script src="js/controllers/yhj.js?time=890?time=3df3"></script>
<script src="js/controllers/orderDetail.js?time=55d4555"></script>
<script src="js/controllers/orderAccess.js?time=12sdfsd"></script>
<script src="js/controllers/accessDetail.js?time=324sdfsd"></script>
<script src="js/controllers/agreement.js?time=3df3"></script>
<script src="js/controllers/paySuccess.js?time=sd33fsd"></script>
<script src="js/controllers/onlineOffline.js?time=12sd23"></script>
<script src="js/controllers/address.js"></script>
<script src="js/controllers/addressInput.js?time=sdfsdfsdfdfsdf234"></script>
<script src="js/controllers/storeList.js?time=ssdfsddfsdf"></script>
<script src="js/controllers/dateRanger.js?time=ssf"></script>
<script src="js/controllers/hotel.js?time=3df3"></script>
<script src="js/controllers/hotelList.js?time=3df3"></script>
<script src="js/controllers/changeTime.js?time=3df3"></script>
<script type='text/javascript' src='http://res.wx.qq.com/open/js/jweixin-1.0.0.js'></script>
<%@include file="wxshare.jsp"%>
<script type="text/javascript" src="http://webapi.amap.com/maps?v=1.3&key=950f0eafe4a4b4c2ab150a2c0141d173&plugin=AMap.Geocoder,AMap.PlaceSearch"></script>
<!-- 引入模板
<%@ include file="templates/index.html" %>
<%@ include file="templates/service.html" %>
<%@ include file="templates/serviceTime.html"%>
<%@ include file="templates/login.html"%>
<%@ include file="templates/order.html" %>
<%@ include file="templates/dialog.html"%>
<%@ include file="templates/my.html" %>
<%@ include file="templates/myInfo.html"%>
<%@ include file="templates/myAccount.html"%>
<%@ include file="templates/petInfoInput.html"%>
<%@ include file="templates/listServicePrice.html"%>
<%@ include file="templates/petColor.html"%>
<%@ include file="templates/petFood.html"%>
<%@ include file="templates/petBreed.html"%>
<%@ include file="templates/yhj.html" %>
<%@ include file="templates/petList.html"%>
<%@ include file="templates/orderDetail.html"%>
<%@ include file="templates/orderAccess.html"%>
<%@ include file="templates/accessDetail.html"%>
<%@ include file="templates/agreement.html"%>
<%@ include file="templates/paySuccess.html"%>
<%@ include file="templates/onlineOffline.html"%>
<%@ include file="templates/address.html"%>
<%@ include file="templates/addressInput.html"%>
<%@ include file="templates/storeList.html"%>
<%@ include file="templates/hotel.html"%>
<%@ include file="templates/dateRanger.html"%>
<%@ include file="templates/hotelList.html"%>
<%@ include file="templates/changeTime.html"%>
<%@ include file="templates/selectStore.html"%>
</head>
<body>
<div id="content">
<div id="index-con" class="page"></div>
<div id="my-con" class="page"></div>
<div id="myInfo-con" class="page"></div>
<div id="myAccount-con" class="page"></div>
<div id="order-con" class="page"></div>
<div id="service-con" class="page"></div>
<div id="yhj-con" class="page"></div>
<div id="login-con" class="page"></div>
<div id="petInfoInput-con" class="page"></div>
<div id="listServicePrice-con" class="page"></div>
<div id="petColor-con" class="page"></div>
<div id="petFood-con" class="page " ></div>
<div id="petBreed-con" class="page"></div>
<div id="petList-con" class="page"></div>
<div id="orderDetail-con" class="page"></div>
<div id="orderAccess-con" class="page"></div>
<div id="accessDetail-con" class="page"></div>
<div id="agreement-con" class="page"></div>
<div id="paySuccess-con" class="page"></div>
<div id="onlineOffline-con" class="page"></div>
<div id="address-con" class="page"></div>
<div id="addressInput-con" class="page"></div>
<div id="storeList-con" class="page"></div>
<div id="hotel-con" class="page"></div>
<div id="dateRanger-con" class="page"></div>
<div id="hotelList-con" class="page"></div>
<div id="changeTime-con" class="page"></div>
</div>
<script type="text/javascript">
window.onhashchange = function(){
clearCovers();
//重置分享链接
if(!window.G_ISAPP) setWxData();
var hashStr, paramStr;
hashStr = util.getNowHash();
paramStr = util.getParamStr();
$('.nowpage.page').removeClass('nowpage');
$('#' + hashStr + '-con').addClass('nowpage');
util.clearStoreId(hashStr);
util.hideFooter(hashStr); //根据当前hash判断是否隐藏footer
var params = util.str2Params(paramStr);
window[hashStr].onCreate(params);
};
</script>
</body>

index.jsp结构大概如上,先加载完所有的模板文件和js,css文件,之后通过onhashchange回调,将不同的模板填充到对应的page容器中。和所有单页应用一样,弊端在于首次加载时间过长。但是考虑到业务不是很复杂,页面也不多,真实gulp压缩js后控制在330k左右。可以接受。当然这是在微信中作为一整个web app。

但嵌入app的各个入口中情况就不一样了。

点击洗澡,洁牙。。等等入口都会打开一个新的webview,如果都去加载整个index.jsp。虽然方便,但是效果可想而知。

思考1:
尝试用sea.js等按需加载库解决问题。但有三个困难点:
1.因为是单页,变量之间已经有一定耦合,项目大致已经完成。再去进行模块化和依赖关系的整理,恐怕会出问题;
2.sea.js目前只能模块化js。类似10几个模板文件这样依然很大,依然只有一次性加载,效果一般。当然还有webpack,不过暂且还没接触过;
3.页面切换的时机只在onhashchange。这样所有require js会变得很冗杂
另外按需加载也就相当于抛弃了单页应用的一个优点吧。这样渲染新模板的时候同时还是要加载js。效果也不好。

思考2:
最终解决方案,按照传递参数不同,jsp include不同的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<%
//根据入口不同,加载不同的资源文件
boolean[] mods = new boolean[24];
int i=0,ilen=mods.length;
for(i=0; i<ilen; i++){
mods[i] = false;
}
if(from==null || from.equals("web")){ //从微信中,引入所有文件
for(i=0; i<ilen; i++){
mods[i] = true;
}
} else { //app
if(from.equals("onlineOffline")){ //点击进入服务选择
mods[0]=true;
mods[1]=true;
mods[2]=true;
mods[3]=true;
mods[4]=true;
mods[5]=true;
mods[6]=true;
mods[7]=true;
mods[9]=true;
mods[10]=true;
mods[11]=true;
mods[12]=true;
mods[14]=true;
mods[15]=true;
mods[16]=true;
mods[17]=true;
mods[19]=true;
mods[20]=true;
mods[21]=true;
mods[22]=true;
}
}
%>
<% if(mods[0]){ %>
<script src="library/template-native-debug.js"></script>
<script src="library/fastclick.js"></script>
<script src="library/jquery.min.js"></script>
<script src="library/iscroll-lite.js"></script>
<% } %>
<% if(mods[1]){ %>
<link rel="stylesheet" href="css/base.css">
<% } %>
<% if(mods[2]){ %>
<link rel="stylesheet" href="css/main.css">
<% } %>
<% if(mods[3]){ %>
<link rel="stylesheet" href="css/spinningwheel-didi.css">
<script src="library/spinningwheel-didi.js"></script>
<% } %>
<% if(mods[4]){ %>
<link rel="stylesheet" href="css/swiper.css">
<script src="library/swiper-3.3.1.jquery.min.js"></script>
<% } %>
<% if(mods[5]){ %>
<%@ include file="templates/dialog.html"%>
<% } %>
<% if(mods[6]){ %>
<script src="js/services/util.js?time=3df534d3"></script>
<script type="text/javascript" src="http://webapi.amap.com/maps?v=1.3&key=950f0eafe4a4b4c2ab150a2c0141d173&plugin=AMap.Geocoder,AMap.PlaceSearch"></script>
<script src="js/services/map.js?time=3df3"></script>
<% } %>
<% if(mods[7]){ %>
<%@ include file="templates/orderAccess.html"%>
<%@ include file="templates/accessDetail.html"%>
<script src="js/controllers/orderAccess.js?time=12sdfsd"></script>
<script src="js/controllers/accessDetail.js?time=324sdfsd"></script>
<% } %>
<% if(mods[8]){ %>
<%@ include file="templates/index.html" %>
<script src="js/controllers/index.js?time=0sdffds9"></script>
<% } %>
<% if(mods[9]){ %>
<%@ include file="templates/onlineOffline.html"%>
<script src="js/controllers/onlineOffline.js"></script>
<% } %>
<% if(mods[10]){ %>
<%@ include file="templates/service.html" %>
<%@ include file="templates/serviceTime.html"%>
<%@ include file="templates/listServicePrice.html"%>
<script src="js/controllers/service.js?time=9s99055"></script>
<script src="js/controllers/listServicePrice.js?time=3df3"></script>
<% } %>

简而言之,不同的入口需要不同的模板和不同的js文件,css文件。那么就按照它所需要的提供相应的这些资源文件就可以了。实际相当于将大的单页划分成很多小单页

在其中遇到一个问题,由此引出了一部分知识:

1
2
3
4
5
//原始页面
response.sendRedirect(hostStr + "/index.jsp#/onlineOffline?entrance=onlineOffline&serviceType=" + serviceType1);
//index.jsp
String from = request.getParameter("entrance");

这是刚开始的时候我用来重定向传参的url和拿去url参数的方法。但很奇怪的是from总为空。查完资料后总结如下:


1. url中#之后的都被浏览器认为是地址符。onhashchange回调中该变的hash。 location.hash得到的就是:#/onlineOffline?entrance=onlineOffline&serviceType=1; 这也解释了为什么单页应用的参数必须在#之后,如果在#之前,任何字符的改变都会导致页面刷新,而之后hash值改变页面不会刷新,并且会记录历史。这也是单页应用的基础


2. #之后的字符用来在浏览器端做向导,不会被传递到server端。所以在服务器端url是这样的:http://testsaas-1000-100001.m.izhuazhua.com/index.jsp. 这也就是为什么request.getParameter();request.getQueryString()获得的值都为空的原因


那么怎么讲标示参数传递到index.jsp呢。最终url如下:
response.sendRedirect(hostStr + “/index.jsp?entrance=onlineOffline#/onlineOffline?serviceType=” + serviceType1);
移到#之前就可以了。