移动端SeeApp开发总结-2

哟

上一次记录了Fiddler和js结构组织,这次写一下在js里具体一个模块是怎么写的。

通用函数

encodeURIComponent()和decodeURIComponent()

encodeURIComponent

  • 把字符串作为URI 组件进行编码
  • 不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII标点符号进行编码
  • 其他字符(比如:;/?:@&=+$,# 这些用于分隔URI组件的标点符号),都是由一个或多个十六进制的转义序列替换的。
  • encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。
    栗子:encodeURIComponent("你好,世界!")输出为%E4%BD%A0%E5%A5%BD%EF%BC%8C%E4%B8%96%E7%95%8C%EF%BC%81

decodeURIComponent

  • decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
  • encodeURIComponent()相反。

什么时候用到这两个函数:登录成功后或者任何操作后对于跳转回原页面。

1
t.login_a.attr('href','http://m.seeapp.com/see/static/detail/login.html?ref='+ encodeURIComponent(window.location.href));

在链接后面加上一个参数ref,参数值为当前页面的URI。
为什么需要进行编码:为什么要对URI进行编码

  Url中有些字符会引起歧义:例如Url参数字符串中使用key=value键值对这样的形式来传参,键值对之间以&符号分隔,如/s?q=abc&ie=utf- 8。如果你的value字符串中包含了=或者&,那么势必会造成接收Url的服务器解析错误,因此必须将引起歧义的&和=符号进行转义, 也就是对其进行编码
  Url的编码格式采用的是ASCII码,而不是Unicode,这也就是说你不能在Url中包含任何非ASCII字符,例如中文。否则如果客户端浏览器和服务端浏览器支持的字符集不同的情况下,中文可能会造成问题。

在跳转到login.html之后点击登陆,在js里面获取ref参数,链接直接跳转到参数值,就能回到原来的页面了:

1
window.location.href = see.get_param('ref');

获取URI中的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
t.get_param = function(key){
if(window.location.href.indexOf('?')==-1){
return null;
}
var uri = window.location.href.replace(/^[^\?]*\?/,'');
var data = {};
var pairs = uri.split('&');
for(var k in pairs){
if(typeof(pairs[k])=='string') {
var k_v_arr = pairs[k].split('=');
if (k_v_arr.length) {
data[k_v_arr[0]] = k_v_arr[1] || '';
}
}
}
if(key in data){
return data[key];
}
return null;
};

window.location.href.replace(/^[^\?]*\?/,'');
这个正则把URL中?之前的字符串都替换成了空格,只剩下?后面的参数
然后通过split()函数,把参数字符串通过&分隔开来成为数组。
不断地对每一个数组项进行判断。对数组项比如"tid=10222"通过=来分隔,就得到参数名和参数值了。

1
2
3
4
5
6
var a = "http://m.seeapp.com/see/static/detail/index.html?tid=10222&h=121&oo=0000".replace(/^[^\?]*\?/,'');
/* a = "tid=10222&h=121&oo=0000" */
a.split('&');
/* a = ["tid=10222", "h=121", "oo=0000"] */
var barr = a[0].split('=');
/* barr = ['tid','10222'];

当时阿里的网招的笔试题也有这道题目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function parseQueryString(input) {
var output;
var arr = input.replace(/^[^\?]*\?/,'');
var data = [];
data = arr.split('&');
var result = {};
for(var key in data) {
if(typeof data[key] == 'string') {
var barr = data[key].split('=');
result[barr[0]] = barr[1];
}
}
console.log(result);
}

注意result[barr[0]] = barr[1];这里如果直接用result.barr[0] = barr[1];会报错。

一般流程

其实回过了头来看,大部分的流程其实很类似,一般的流程都是这样:

  • 创建一个匿名函数,代表一个功能
  • 在函数内定义将要使用到的各种DOM节点,请求的url
  • 在用jQuery找DOM节点的时候,多用find()方法,避免全局寻找节点。对应的标签用name属性来分辨
  • 创建一个初始化方法:包括一系列的判断,事件绑定
  • 在初始化方法里会用到一些请求,把请求包装成另外一个函数
  • 在请求函数里,判断对应返回的数据进行DOM节点的更新
  • 注意不要在异步请求里返回数据,要么直接在请求成功后的回调函数里处理,要么传入一个回调函数
  • 某些需要被其他类使用的状态可以保存在类的公共变量里,在执行时都能使用
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
/**获取页面求同款的详情*/
var theme = new (function(){
var t = this;
//在函数内定义将要使用到的各种DOM节点,请求的url
t.url = 'http://m.seeapp.com/index.php/theme/getTheme?theme_id=';
t.theme_box = $('#theme_box');
t.banner_box = t.theme_box.find('[name=banner_box]');
t.banner = t.theme_box.find('[name=banner]');
t.img = t.theme_box.find('[name=img]');
t.nick = t.theme_box.find('[name=nick]');
t.time = t.theme_box.find('[name=time]');
t.type = t.theme_box.find('[name=type]');
t.content = t.theme_box.find('[name=content]');
t.wish_list = t.theme_box.find('[name=wish_list]');
t.price_range = t.theme_box.find('[name=price_range]');
t.like_count = t.theme_box.find('[name=like_count]');
t.reply_count = t.theme_box.find('[name=reply_count]');
t.loading_box = $('#loading_box');
t.like_btn = $("#like_btn");
t.reply_count_num = 0;
t.adminBox = t.theme_box.find('[name=adminBox]');
t.isbyowner = t.theme_box.find('[name=isbyowner]');
t.replyBtnBox = t.theme_box.find('[name=replyBtnBox]');
//创建一个初始化方法:包括一系列的判断,事件绑定
//在初始化方法里会用到一些请求,把请求包装成另外一个函数
t.get_theme = function(){
$.getJSON(t.get_url(),function(ret){
if(ret.result == 1){
t.render_theme(ret.data);
t.loading_box.hide();
t.theme_box.show();
}else{
see.tips('OOPS!该内容获取不到...');
}
});
};
t.get_theme_id = function(){
var id = see.get_param('tid');
if($.isNumeric(id)){
return parseInt(id);
}
return 0;
};
t.get_url = function(){
return t.url + t.get_theme_id();
};
//对在请求函数里返回的数据进行DOM节点的更新
t.render_theme = function(dt){
if(dt.theme){
t.banner.attr('src', dt.theme.t_imgurl);
t.nick.text(dt.theme.u_username);
t.img.attr('src',dt.theme.u_headimg);
t.time.text(see.format_time(dt.theme.t_time));
t.content.text(dt.theme.t_title);
t.price_range.text(dt.theme.t_price);
t.like_count.text(dt.theme.t_followcount);
t.reply_count.text(dt.theme.t_findcount);
t.reply_count_num = parseInt(dt.theme.t_findcount);
t.type.text(parseInt(dt.theme.t_hunttype)==0?'求同款':'求搭配');
t.wish_list.text(dt.wanted[0].o_remark || dt.wanted[0].o_name);
if(dt.theme.isbyowner) {
t.replyBtnBox.hide();
t.adminBox.show();
t.isbyowner.text(dt.theme.t_followcount);
}
for(var k in dt.wanted[0].clue){
var c = dt.wanted[0].clue[k];
var param = JSON.parse(c.cl_parameter);
var span = $('<span class="tag tag-'+param.direction+'" style="left:'+parseFloat(param.x)*100+'%; top:'+parseFloat(param.y)*100+'%;"><i></i><font>'+param.title+'</font></span>');
t.banner_box.append(span);
}
};
if (dt.isfollow) {
t.like_btn.addClass('liked');
} else {
t.like_btn.removeClass('liked');
}
};
});

微信JSSDK

这次分享和图片预览用到了微信的SDK。

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.getJSON('http://m.seeapp.com/index.php/activity/getConfig?u='+encodeURIComponent(location.href), function(json){
wx.config(json);
wx.ready(function(){
var url = '';
var img = '';
var global_share_title = '你负责看,See负责找';
var desc = '你负责看,See负责找';
wx.onMenuShareTimeline({
title: global_share_title, // 分享标题
link: url, // 分享链接
imgUrl: img, // 分享图标
success: function () {},
cancel: function () {}
});
});
});

公司之前是写了一个接口,传入当前的url获取JSSDK的参数对象

1
2
3
4
5
6
7
8
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

配置成功后就可以使用各种api了,最常用的是自定义分享的标题,图片。
有时候需要在分享后做一些处理,就在回调函数里写一些东西,比如发送请求来统计等。

1
2
3
4
5
6
7
8
9
10
t.image_preview = function (current_img) {
if (typeof WeixinJSBridge != 'undefined') {
WeixinJSBridge.invoke('imagePreview', {
'current' : current_img,
'urls' : t.img_view_list
});
} else {
//留个空给后面补齐非微信浏览器
}
}

这个在旧版本的微信貌似可以使用,是图片预览插件。有时候可以用有时候不可以=。=

分享到