这是我2年前写的blog,原文在这里
来自爆栈这里
1 | // 全局作用域变量定义 |
1 | 我补充几个高级货: |
这是我2年前写的blog,原文在这里
来自爆栈这里
1 | // 全局作用域变量定义 |
1 | 我补充几个高级货: |
这是我2年前写的blog,原文在这里
来自爆栈这里
引出问题的是楼主问jquery环境下preventDefault和return false的区别。
楼下有一位总结关于return false在各个环境下的区别,以下是节选和总结:
在标准的DOM2 handler下(也就是使用addEventListener方式监听),return false不会做任何事情(也就是不会阻止事件,也不会停止冒泡)
在Microsoft DOM2 handler下(也就是使用attachEvent方式监听),return false会阻止事件,但是不会停止冒泡
在标准的DOM0 handler下(也就是在html结构里面事件中return,如:<a onclick="return ...">
),return false会阻止事件,但是不会停止冒泡
以上都是原生方式return false,都是不能默认阻止事件冒泡
但是在jquery环境下监听事件(如:$(‘a’).click(…))return false会同时阻止事件和停止事件冒泡。
这是我2年前写的blog,原文在这里
最近在用xhr2做某个项目的后台,在这过程中遇到各种疑难杂症,对比起来,flash request的跨域其实是既简单又实用,而xhr2则有各种限制。
flash request:使用配置xml “crossdomain.xml”,来进行配置,不需要修改其他比如返回http头,flash会先获取这个xml查询http请求,是否有权限进行跨域。
xhr2:使用浏览器自带的xmlhttprequest level2来进行跨域ajax请求,浏览器和服务器需要发送和返回对应的http头来进行跨域策略。
首先遇到第一个问题,因为extjs框架进行跨域访问时,会自动带上自定义头:X-Requested-With,这时候浏览器会提示出错,原因是因为当有自定义头时,服务器端需要返回头:Access-Control-Allow-Headers:X-Requested-With,表示允许可以设置请求自定义头
第二个问题是发现跨域请求没有带上cookie,原因是因为在extjs的xhr2,withCredentials默认为false,需要加上这个属性请求才能带上cookie,但是加上withCredentials属性后发现还是出错,原因是当withCredentials为true时,返回的Access-Control-Allow-Origin的值不能为*,需要为一个特定的域名,最后把这个改为测试机的域名后,以为终于可以了吧,Holy Crap!还是不行,好吧,原因是服务器端http头也要返回Access-Control-Allow-Credentials:true
当xhr2跨域ajax时,不需要带上cookie,没有自定义头,只需要返回设置Access-Control-Allow-Origin头就可以。
如果需要设置自定义头,需要服务器端返回Access-Control-Allow-Headers:自定义头。
如果需要带上cookie,则xhr2对象需要设置withCredentials为true,并且服务器返回Access-Control-Allow-Credentials:true ,并且Access-Control-Allow-Origin不能为*
这是我三年前写的blog,原文在这里
在大型的ajax项目前端开发中,模块分得很细,多个开发者负责不同的业务逻辑模块开发,会需要不同模块之间互相调用接口,在网易邮箱的前端开发中遇到过这样的情况也是家常便饭,虽然我们可以将公共调用的功能分离出来到一个“公共库”,然后开发者需要实现某个功能时,可以去“公共库”找相关的接口,然后在自己的代码中调用。
但是如果接口有变更、或者新需求需要增加调用,就导致各个模块负责人耦合维护,这种方式在小型的项目没有大问题,不过项目越做越大后,就会导致模块之间耦合越来越大,维护成本越来越高,在新的网易邮箱版本里面,使用了Observer的设计模式实现模块之间的解耦。
Observer(观察者模式):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
先举个例子:邮箱中的收件箱模块和读信模块,分别是程序员A和程序员B负责,在旧的模式里,收件箱读取一封新邮件后,B会调用A的接口.setRead(sMailId)实现去掉该邮件未读标记,然后有一天,负责左侧导航的同事C说:产品增加一个需求,读取新邮件后,左侧导航的收件箱的未读数需要更新。这时,C发给B一个更新导航的接口,然后B就要修改代码,加上C提供的接口.updateNav()。
首先定义了一个afterRead的事件,收件箱模块监听了这个事件:Listener.listen(“afterRead”, DoSetReadFunction),新邮件读取之后,会触发一个事件:Listener.fire(“afterRead”),这时,监听了这个事件的收件箱模块会调用接口,然后在新的需求里,C同事只需要监听:Listener.listen(“afterRead”, DoUpdateNavFunction),就实现了左侧导航的新邮件数更新了。
这个过程的变化相当于:
旧的方式:读取一封新邮件后 -> 调用收件箱接口 -> 调用导航接口
新的方式:读取一封新邮件后 -> 告诉所有模块:我刚读了一封新邮件 -> 其他监听了这个事件的模块被通知了 -> 调用相关接口
简单的自定义事件实现
1 | var Listener = { |
上面的简单的自定义事件实现方式,就可以很好的解决模块之间耦合的问题,当然也可以做得功能更加强大,比如事件触发可以加上参数支持、实现调用句柄获取当前的event对象等。
这是我三年前写的blog,原文在这里,因为这篇blog,我有幸被邀请到2012年的Velocity大会作为演讲嘉宾。
本文是关于comet技术的介绍,以及网易邮箱在使用comet技术中的经验介绍和各种解决方案优劣对比。
Comet是一种基于web的服务器端主动推送消息给浏览器端的技术,在传统的web应用,浏览器和服务器之间的通讯,是通过“浏览器请求-服务器端返回”这个过程服务器端被动的返回信息,Comet则是服务器端主动推送消息给浏览器端,这种方式可以及时将消息传送浏览器,所以对于一些对于消息处理需要很及时的web应用就会使用到comet技术,例如:webim
socket包括有Flash XMLSocket、activex组件等浏览器插件,Flash XMLSocket的实现是在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序,JavaScript 通过调用此 Flash 程序提供的套接口接口与服务器端的套接口进行通信,不过使用Socket协议的方式没有 HTTP 隧道功能,不能自动穿过防火墙,所以建议使用http协议的方式,本文也只讨论http协议的方式。
long-polling方式的特点有以下:
long-polling的可选择载体:
long-polling优缺点:
优点包括:实现稍微简单,可供选择的载体多,并且浏览器兼容性好
缺点包括:轮询的方式定时请求,有数据返回后断开重新请求,这种方式消息到达可能不及时,以及浏览器端不断建立请求,导致过多资源,某些载体方式不支持post
Streaming方式的特点有以下:
使用这种方式,每次数据传送不会关闭连接,连接只会在通信出现错误时,或是连接重建时关闭(一些防火墙常被设置为丢弃过长的连接, 服务器端可以设置一个超时时间, 超时后通知客户端重新建立连接,并关闭原来的连接)。
Streaming的可以选择载体:
Streaming优缺点:
优点包括:消息到达及时,浏览器端只需要一次请求,服务器端即可实现多次推送,并且get或者post方式都无压力
缺点包括:无
旧方案:
新方案:
通过各种方案对比,streaming+flash xhr是最佳的选择。
这是我三年前写的blog,原文在这里
在上一篇关于跨域获取数据中,苦逼的前端开发工程师,刚才解决了一个cross domain的问题,还没有来得及沉浸在其中喜悦之际,又迎来了另外一个cross domain的问题:邮箱出现双滚动啦。
在页面中当需要加载外域app的iframe时候,最容易出现跨frame的cross domain问题,比如刚才那前端开发工程师遇到的双滚动的问题,在页面中放入一个外域的iframe,当iframe的高度大于iframe的页面的body高度时,就会出现滚动,这时候加上本身页面已经有一条滚动,那就出现经典的双滚动问题,如果iframe里面又嵌套另一个外域iframe,那可能会出现三滚动,继续嵌套..继续滚动..继续套..继续滚…,之所以出现这个问题,是因为外域的iframe不能直接调用:
parent.document.getElementById("iframe_id").style.height = document.body.offsetHeight + "px"
这里就需要跨iframe进行cross domain,有以下提到的两个方式。
以下的例子会以这几个页面作为例子:
页面a:
http://www.a.com/a.htm
页面b:
http://www.b.com/b.htm
a的内容:
<iframe src="http://www.b.com/b.htm" id="ifrm_b"></iframe>
首先介绍一个浏览器原生的跨域调用方式,在支持html5的高级浏览器,支持这种方式:
oWin.postMessage(oMessage, sTargetDomain);
假如页面a跨域页面b,那么在页面a上调用以下进行跨域:
1 | try{ |
然后在b,需要加一个window的message事件监听
1 | fAddEvent(window, "message", function (o) { |
这样就完成了跨frame的跨域通讯
使用代理iframe的方式有两种,一种通过window.name方式跨域调用,一种是通过url参数的方式传递调用,不过两种方式的调用原理都是创建一个隐藏的iframe,iframe的url指向需要跨域的域名的一个代理页面,然后通过这个代理的iframe,和跨域的iframe通讯,因为这时代理的iframe和跨域的iframe完全同域,就可以畅通无阻进行。
在上面的例子加多一个代理页面c:
代理iframe c:http://www.b.com/c.htm
1 | function fCrossByName(sDomain, oData) { |
1 | fCrossByName("www.b.com", { |
1 | var oData = !window.name?null:(new Function('return '+window.name))(); |
这样就可以实现整个的跨域调用,通过url参数方式跨域调用,和这个类似,只是需要将数据放到代理iframe的url参数上,而不是name。
下面这个页面demo显示这三种方式的调用(因为没加JSON的转换js包,需要使用支持内置JSON对象的浏览器运行..):
http://mimg.163.com/demo/crossdomain_test.htm
总结:在邮箱实际使用过程中,当只是简单的跨域调用比如前面提到的解决双滚动问题,一般可以简单的使用第二种方式,但是如果需要复杂的双向互相调用,就需要支持html5的浏览器调用postMessage,不支持的需要第二种方法实现兼容。
这是我三年前写的blog,原文在这里
作为一个苦逼前端开发工程师,不得不面对各种cross,比如面对五花八门的浏览器我们必须cross browser,面对各种终端,我们必须cross device,在这么多年的前端开发经历中,在不同的域之间穿越中,遭受各种折磨,所以这次和大家分享的是cross domain。
这次分享的cross domain,是包括所有跨域调用,无论是跨域获取数据,还是跨域跨frame调用,所以会分为两部分,这次会先分享跨域获取数据,跨域获取数据大概有以下方式。
这是最简单,也是最实用的跨域获取数据方式,原理是在浏览器端通过生成script标签,并通过js callback的形式实现跨域访问,比如一个jsonp接口是这样:
http://mail.163.com/someapp/jsonp?somequery=xxx&callback=fSomeMethod
然后服务器端会通过以下方式返回数据:
fSomeMethod({返回的json数据对象})
这个方式会复杂一点点,原理是post表单到一个隐藏的iframe,然后iframe将数据post回来同域的一个url,这时候就可以直接调用同域的回调:
首先post表单到如:
http://other.domain.com/someapp?somequery=xxx&callback=fSomeMethod&backurl=http://mail.163.com/proxy
这个接口返回的内容:
1 | <form action="${backurl}" method="post"> |
1 | <script> |
这样就完成整个跨域获取数据的过程
优点:支持post方式,并且原生cross all browser
缺点:实现有点复杂,并且流程有点曲折,需要两次请求,而且表单post方式会引起刷新提示的问题
服务器代理方式跨域调用也是使用的比较广泛的方式,原理就是在服务器端来获取跨域数据,然后在同域里ajax方式或者其他方式返回给浏览器。
优点:客户端实现简单,没有cross browser问题
缺点:需要在服务器端实现模拟请求获取数据
终于在html5迎来了对跨域的ajax,泪牛满脸啊,这下完全可以通过浏览器的原生方式跨域ajax获取数据,这里一个对浏览器的各种跨域ajax的测试:
http://www.debugtheweb.com/test/teststreaming.aspx
优点:浏览器原生支持跨域方式ajax请求
缺点:只能在支持html5的高级浏览器中支持
这个是目前跨域请求最好的一个解决方案,建议在不能用jsonp的方式时候,都可以使用flash request方式,而且flash request可以配置一个安全策略,可以允许哪些域可以调用,然后被跨域的调用需要配置一个crossdomain.xml,允许可以被那个域的flash跨域调用,内容如下:
1 | <?xml version="1.0"?> |
这种方式的局限就是必须依赖flash,我们这边在邮箱曾经做过一个统计,99%的用户可以通过这种方式获取到数据,另外1%的用户可能是没有安装flash或者flash版本有问题。
以上是获取数据时需要cross domain的解决方案,下一篇是跨frame时候需要cross domain的分享。