记录一次小小的需求过程

需求背景:

在我们应用嵌入客户的native容器,客户想要通过它们的app扫一扫二维码登录pc版。类似微信扫一扫二维码登录pc版本,弹出确认登录页面,点击确认后登录。

实现思路:

1.客户使用手机app扫一扫PC端的登录二维码之后,打开一个新的webview,地址是这个登录页的地址,并且将二维码里的信息,以及token当做查询参数拼接在地址上。

2.打开后页面从url里获取对应的数据,在用户点击登录按钮的时候,发送一个POST请求给服务端,验证是否登录成功。

3.PC端轮训发送请求,询问服务端是否该用户登录成功。

要求

页面尽可能的小,需要考虑兼容性,加载速度,以及性能优化。

第一版实现

第一版的实现,没有考虑太多,只是测试一下,是否能登陆成功。没有引入库,而是直接使用XMLHttpRequest对象。另外引入了一张很小很小的图片。

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}

img {
margin: 70px auto 0 auto;
width: 127px;
height: 127px;
display: block;
background-size: 127px 127px;
}

.login {
width: 140px;
height: 36px;
margin: 160px auto 20px auto;
background: white;
color: #01A0FF;
border: 1px solid #01A0FF;
border-radius: 4px;
font-size: 8px;
display: block;
text-align: center;
line-height: 36px;
}

.cancel-login {
font-size: 14px;
color: #b2b2b2;
text-align: center;
}
.has-error{
width: 80%;
margin: 24px auto 0;
font-size:17px;
color: #ed4622;
text-align: center;
}
</style>

<body>
<img src="./images/ico_login_confirmation_PC@2x.png" />
<h3 class="has-error" id="error"></h3>
<a class="login" onclick="login()">
登录
</a>
<p class="cancel-login">取消登录</p>
</body>
<script type="text/javascript">
var xml = new XMLHttpRequest();
var errorTag = document.getElementById('error');
var qrApplyId = '0bbabf59-25c2-401e-ac0a-f0855660a534';//二维码标识
var ticket = 'GZeij22ndc3E8wDzghnCCpViwMGQ1mlMx7yP5Hm2u%2f0%3d';//token


function login() {
if (xml) {
xml.onreadystatechange = function () {
if (xml.readyState == 4) {
if ((xml.status >= 200 && xml.status <= 300) || xml.status == 304) {
successCB(JSON.parse(xml.response));
} else {
errorCB(JSON.parse(xml.response));
}
}
}
xml.open('POST', 'https://t-sso.exexm.com/qrlogin.ashx?t=auth', true);
xml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
xml.send('qrApplyId=' + qrApplyId + '&ticket=' + ticket);
}
}
var successCB = function (res) {
if (res && res.success) {

} else {
errorTag.innerHTML = res.msg;
}
}
var errorCB = function (res) {

}
</script>

</html>

宝哥的修改建议

WechatIMG234

第二版实现

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1">
<meta http-equiv="Cache-Control" content="max-age=60000" />
<title>登录</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}

img {
margin: 70px auto 0 auto;
width: 127px;
height: 127px;
display: block;
background-size: 127px 127px;
}

.login {
width: 140px;
height: 36px;
margin: 160px auto 20px auto;
background: white;
color: #01A0FF;
border: 1px solid #01A0FF;
border-radius: 4px;
font-size: 8px;
display: block;
text-align: center;
line-height: 36px;
}
h2{
text-align: center;
font-size: 16px;
font-weight: normal;
margin-top: 10px;
}
.cancel-login {
font-size: 14px;
color: #b2b2b2;
text-align: center;
}
.has-error{
width: 80%;
margin: 24px auto 0;
font-size:17px;
color: #ed4622;
text-align: center;
}
.loading{
width: 80px;
height: 20px;
margin: 0 auto;
margin-top:100px;
text-align: center;
position: fixed;
top: 150px;
left: 50%;
margin-left: -40px;
visibility: hidden;
}
.loading span{
display: inline-block;
width: 4px;
height: 50%;
border-radius: 4px;
background: lightgreen;
animation: load 1s ease infinite;
-webkit-animation: load 1s ease infinite;
}
@-webkit-keyframes load{
0%,100%{
height: 20px;
background: lightgreen;
}
50%{
height: 40px;
margin: -15px 0;
background: lightblue;
}
}
.loading span:nth-child(2){
animation-delay:0.2s;
-webkit-animation-delay:0.2s;
}
.loading span:nth-child(3){
animation-delay:0.4s;
-webkit-animation-delay:0.4s;
}
.loading span:nth-child(4){
animation-delay:0.6s;
-webkit-animation-delay:0.6s;
}
.loading span:nth-child(5){
animation-delay:0.8s;
-webkit-animation-delay:0.8s;
}
</style>

<body>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAMAAABrrFhUAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAclBMVEUAAACZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZkAAAD40WwsAAAAJHRSTlMAKo7X+NYGlP2YCbv5kTgMvZXkMCv8L4+SB/PUkCwuNAvY+vQbCt0PAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAd5JREFUeNrt2MtOwlAUhtGj1IJayh28FEHd7/+MEsIQJg66NV3fBIb/WW3CCaVIkiRJkiRJkiRJkiRpAN3dj6oYWNXD/d3l+PV4kr0mp8nj0/n8z9FM21n2W9h3s3baxLw+fVvEcpW9JqfVMsalrDfNQM9fyrbarMsuXrJ35PUSuzKKNntGXm2MShN19oy8XqMpEdkrMjudHgCA7BF/CCD7btpXtwGyH0hPjx0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgOxpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwBWAgXQTYHgBAACgiTp7RV6zeCujaLNn5NXGe9nFNHtGXtPoyn5TbbN3ZPVRbfalLGK5yl6S03YZi9NH/RxNdzhmr+m746FrYn7+AajHk+y7aU6Tx6eLxrr7/Mpe03ff790+7/27fvsa0J0MAAAAAAAAAAAAAIDBAlwvexcASZKk39Xnv9rZZwUgSZIkSZIkSZIkSZKk/9IPRdh5HZXYXfEAAAAASUVORK5CYII=" />
<h2>登录确认</h2>
<h3 class="has-error" id="error"></h3>
<a class="login" id="login">
登录
</a>
<p class="cancel-login">取消登录</p>

<div class="loading" id="loading">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</body>
<script type="text/javascript">
window.onload = function () {
document.addEventListener('gesturestart', function (event) {
event.preventDefault();
})
var xml;
if (window.XMLHttpRequest) {
xml = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xml = new ActiveXObject("Microsoft.XMLHTTP");
};

if (!xml) throw new ReferenceError('xml is not define');

var errorTag = document.getElementById('error');//错误提示标签
var loginTag = document.getElementById('login');//登录按钮
var loadingTag = document.getElementById('loading');//loading标签
var paramsStr = getParams();//获得参数
var showLoading = false;//是否显示loading

loginTag.addEventListener('click', function () {
if (!showLoading) login();
})

function getParams() {//获取参数
var paramsStr = location.href.split('?');
if (paramsStr && paramsStr.length && paramsStr.length > 1) {
return paramsStr[1];
}
throw new ReferenceError('params is not defined');
}

function successCB(res) {//成功回调
if (res && res.success) {
//关闭webview并且完成登录
} else {
errorTag.innerHTML = (res && res.msg) ? res.msg : '登录失败';
}
showLoadingTag(false);
}

function errorCB(res) {//失败回调
showLoadingTag(false);
errorTag.innerHTML = (res && res.msg) ? res.msg : '登录失败';
}

function login() {
post('https://t-sso.exexm.com/qrlogin.ashx?t=auth', paramsStr, successCB, errorCB);
showLoadingTag(true);
}

function showLoadingTag(showOrHide) {//显示或隐藏loading
showLoading = showOrHide;
loadingTag.style.visibility = showOrHide ? 'initial' : 'hidden';
showOrHide && (errorTag.innerHTML = '');
}

function post(url, paramsStr, success, error) {
xml.onreadystatechange = function () {
if (xml.readyState == 4) {
if ((xml.status >= 200 && xml.status <= 300) || xml.status == 304) {
success(JSON.parse(xml.response));
} else {
error(JSON.parse(xml.response));
}
}
}
xml.open('POST', url, true);
xml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
xml.send(paramsStr);
}
}
</script>

</html>

优化的地方(我的想法):

  1. meta标签的viewport属性,禁止页面缩放,不过在ios10以上失效。
  2. 低版本的ie浏览器使用的是ActiveXObject对象。
  3. loading样式没有使用display:none;,而是使用visibility:hidden;,减少浏览器重排。
  4. 增加了url查询参数的为空判断。
  5. a标签禁止了重复点击多次触发函数。
  6. 图片转成了Base64,直接内嵌。
  7. 登录失败如果服务端未返回msg字段,则使用默认字段。

待续。。

几个问题

1.图片转成Base64位的好处以及缺点,以及使用场景。

首先图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址,

使用场景:如果图片非常非常小(这点最重要),或者不适合做成雪碧图,以及复用性低。

优点:减少http请求,无需考虑缓存问题

缺点:Base64编码之后的文本,要比原文大约三分之一