双11过去了,双12还会远吗。。
对于双11web开发这一块有一些心得分享出来,希望能帮助到你
首先明确需求:
1.m站(也就是webapp,手机端网站)开发一个双11会场页(能够添加到购物车,有限时抢购功能),和一个双11预热页(纯展示的页面,没有交互)
2.ios和android的APP要镶嵌这两个页面,也要实现交互
对于公司唯一前端的我要面临的问题是:
1.需求给到我第一让我犯愁的是抢购倒计时,因为要倒计时肯定要用到定时器,android的还没什么事,但是ios在页面滑动的的过程中js是阻塞的(相当于alert),那么要解决这个首要问题,就要实现手动滑动,我选择了swiper.js,好了,这个暂且解决。
2.如果使用swiper.js就引发了另的个问题。容器和内容宽高要在实例化swiper时固定,这就要求内容的所有图片都要预先固定宽高,这个好说,跟运营、美工、ui好好商量一下就行了,不是什么大毛病。还有一个就是,M站是有头部导航和脚部菜单的,而APP有他们自己的头和脚,这就要求APP加载页面时要去掉头脚,然后再固定容器,实例化swiper,去掉就去掉,无非是判断一下设备,这个暂时解决。
3.会场与预热页本来预定是分开写的,但是对于一个有追求的前端怎么能够容忍,决定两个页面写在一块,但是预热页面虽然简单,有一些动画。但是谁能保证产品什么时候让你在加点别的啥的,对吧。如果多了js自然也不会少了,会场页和预热页的全局变量,函数,对象混杂在一起,第一容易乱,第二性能也不好,那么我们接下来就针对这个问题展开后续。
首先我们要知道一点,如果你经常阅读各类框架源码的话,对于自执行函数(IIFE)这个东西一定不会陌生,以jquery为例:
(function(w){
var obj = {
//....等等一些其他的东西
}
w.$ = w.jQuery = obj;
})(window,undefined)
源码差不多就是这么个意思。
作用:
1.函数内部的变量外部不可访问,避免全局的污染。
2.把window替换成w有利于压缩,其次是避免了多层作用域链的查找
3.这个undefined之所以这么写是因为在ES5以前的版本中,undefined是一个可写的属性,举个例子。
var undefined = true;
console.log(undefined)//true
在ES5以前版本中这么写是不会报错的,所以JQ开发人员为了避免事故发生就把undefined作为参数传入函数,就算你在全局定义了undefined = true,在函数内部undefined的值依然是undefined
你可能要说了,你说的这些我都知道啊,但这跟你的要解决的问题有毛线关系?
关系大了,我们可以模仿一下这种模块化的写法,把会场和和预热的js部分分割开来,他大概是这个样子的:
//访问的地址http://接口?system=值&style=值
//system取值为android或ios或m; style取值为0代表会场或1代表预热
function getName(name) {
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return null;
}
(function(arr,code){
arr[code]();
console.log('执行完成之后的操作')
})([function(){ console.log('会场js') },function(){console.log('预热js')}],getName('style'))
这样一来对应的js只会运行一次,且避免了全局的污染。
但是这么做产生了一个问题;那就是欠缺对于js异步操作(ajax)的考虑,我们看这一段代码:
(function(fn){
fn();
console.log('this')
})(function(){
setTimeout(function(){
console.log('time');
},1000)
});
//先打印出this,1秒后打印time
我用setTimeout模拟了ajax,问题显而易见,这种方法并不能保证代码的执行顺序(对于js异步先行百度,以后我们再聊),你可能想到了使用回调函数的方法不就ok了吗,没错,我们试一下:
(function(fn){
fn(function(){
console.log('this')
});
})(function(callback){
setTimeout(function(){
console.log('time');
callback&&callback();
},2000)
});
//先1秒后打印time,在打印出this
这个显然解决了我们的燃眉之急,但是我们又忽略了一个问题,那就是多个异步这个方法还好使吗?代码不用写,想想就知道肯定不行,但是我们又不能保证一个模块里只有一个ajax请求,那么我们就来换个写法搞他
(function(fn){
fn(function(sum){
this.sum = sum;
this.now = 0;
this.count = function(){
this.now++;
if(this.now==this.sum){
this.callback();
}
}
this.callback = function(){
console.log('this')
}
});
})(function(ObFn){
var ob = new ObFn(3);
setTimeout(function(){
console.log('time1');
ob.count();
},2000)
setTimeout(function(){
console.log('time2');
ob.count();
},5000)
setTimeout(function(){
console.log('time3');
ob.count();
},3000)
});
这里我们把回调函数换成了一个构造函数,而回调函数被添加到了构造函数的属性里。然后在模块开头new一个实例出来,每次遇到异步请求的回调里写上ob.count(),确定回调的个数后添加在new的实例的参数里,每当请求成功都会触发一次ob.count()进行检测,当全部回调完成时触发console.log('this'),这个模式模仿的观察者模式。有兴趣的可以百度一下观察者模式。
好了,style确定完成,接下来我们该说说console.log('this')这一部分该写些什么了,废话不多说,上代码:
html部分
<body>
<button onclick="o.appCart(5)">点击</button>
</body>
js回调函数部分
this.callback = function(){
var u = getName('system');
var f = {
//单独的方法写在这里
android:{
allFn:function (n) {
//安卓专用的方法
}
},
ios:{
allFn:function (n) {
//ios专用的方法
}
},
m:{
allFn:function (n){
//m站专用的方法
}
}
}
//通用的方法写在这里
f[u].appCart = function (n) {
this.allFn(n);
};
window.o = f[u];
}
仔细看一下,是不是和我们开篇说的jq的模式有类似的地方呢。哈哈,大功告成,最后附上完整代码:
html部分
<body>
<button onclick="o.appCart(5)">点击</button>
</body>
js部分
function getName(name) {
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return null;
}
(function(arr,code){
arr[code](function(sum){
this.sum = sum;
this.now = 0;
this.count = function(){
this.now++;
if(this.now==this.sum){
this.callback();
}
}
this.callback = function(){
var u = getName('system');
var f = {
//单独的方法写在这里
android:{
allFn:function (n) {
//安卓专用的方法
}
},
ios:{
allFn:function (n) {
//ios专用的方法
}
},
m:{
allFn:function (n){
//m站专用的方法
}
}
}
//通用的方法写在这里
f[u].appCart = function (n) {
this.allFn(n);
};
window.o = f[u];
}
});
})([function(ObFn){
var ob = new ObFn(2);
$.ajax({
url:'接口1',
async: true,
success:function(res){
ob.count();
}
})
$.ajax({
url:'接口2',
async: true,
success:function(res){
ob.count();
}
})
},function(ObFn){
var ob = new ObFn(2);
$.ajax({
url:'接口1',
async: true,
success:function(res){
ob.count();
}
})
$.ajax({
url:'接口2',
async: true,
success:function(res){
ob.count();
}
})
}],getName('style'));
网友评论