在 Chrome 里使用 WebRTC ,因为调试的缘故,要打开日志,记录下过程。
如何给 Chrome 添加命令行参数
两种方式,一种是修改快捷方式,类似下面这样:
你可以把原来的快捷方式复制一份来修改。
另一种方式是命令行启动 Chrome ,类似下面:
js css html5 ps
在 Chrome 里使用 WebRTC ,因为调试的缘故,要打开日志,记录下过程。
两种方式,一种是修改快捷方式,类似下面这样:
你可以把原来的快捷方式复制一份来修改。
另一种方式是命令行启动 Chrome ,类似下面:
//监听提交
form.on(‘submit(demo1)’, function(data){
// layer.alert(JSON.stringify(data.field), {
// title: ‘最终的提交信息’
// })
$ = layui.jquery;
//
$.ajax({
url:’dealdata.php’,
type:’post’,
data:data.field,
dataType:”JSON”,
success:function(data){
if(data.Res){
layer.alert(“提交成功!”)
}else{
layer.alert(“提交失败!”)
}
},
});
console.log(data.field);
alert(“d”);
return false;
});
Bootstrap让前端开发变得更快速,更简单,所有设备都可以适配,所有项目都适用,是一套HTML、CSS、JS框架,用于开发响应式布局、移动设备优先的WEB项目。虽然可以直接使用 Bootstrap 提供的 CSS 样式表,不要忘记 Bootstrap 的源码是基于最流行的 CSS 预处理脚本 – Less 和 Sass 开发的。你可以采用预编译的 CSS 文件快速开发,也可以从源码定制自己需要的样式。你的网站和应用能在 Bootstrap 的帮助下通过同一份代码快速、有效适配手机、平板、PC 设备,这一切都是 CSS 媒体查询(Media Query)的功劳。Bootstrap 提供了全面、美观的文档。你能在这里找到关于 HTML 元素、HTML 和 CSS 组件、jQuery 插件方面的所有详细文档。
Node.js在其Github代码仓库(https://github.com/joyent/node)上有着一句短短的介绍:Evented I/O for V8 JavaScript。这句近似广告语的句子却道尽了Node.js自身的特色所在:基于V8引擎实现的事件驱动IO。在本文的这部分内容中,我来揭开这Evented这个关键词的一切奥秘吧。
Node.js能够在众多的后端JavaScript技术之中脱颖而出,正是因其基于事件的特点而受到欢迎。拿Rhino来做比较,可以看出Rhino引擎支持的后端JavaScript摆脱不掉其他语言同步执行的影响,导致JavaScript在后端编程与前端编程之间有着十分显著的差别,在编程模型上无法形成统一。在前端编程中,事件的应用十分广泛,DOM上的各种事件。在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的。在Rhino中,文件读取等操作,均是同步操作进行的。在这类单线程的编程模型下,如果采用同步机制,无法与PHP之类的服务端脚本语言的成熟度媲美,性能也没有值得可圈可点的部分。直到Ryan Dahl在2009年推出Node.js后,后端JavaScript才走出其迷局。Node.js的推出,我觉得该变了两个状况:
有了第二次浏览器大战中的佼佼者V8的适时助力,使得Node.js在短短的两年内达到可观的运行效率,并迅速被大家接受。这一点从Node.js项目在Github上的流行度和NPM上的库的数量可见一斑。
至于Node.js为何会选择Evented I/O for V8 JavaScript的结构和形式来实现,可以参见一下2011年初对作者Ryan Dahl的一次采访:http://bostinno.com/2011/01/31/node-js-interview-4-questions-with-creator-ryan-dahl/ 。
Node.js中大部分的模块,都继承自Event模块(http://nodejs.org/docs/latest/api/events.html )。Event模块(events.EventEmitter)是一个简单的事件监听器模式的实现。具有addListener/on,once,removeListener,removeAllListeners,emit等基本的事件监听模式的方法实现。它与前端DOM树上的事件并不相同,因为它不存在冒泡,逐层捕获等属于DOM的事件行为,也没有preventDefault()、stopPropagation()、 stopImmediatePropagation() 等处理事件传递的方法。
从另一个角度来看,事件侦听器模式也是一种事件钩子(hook)的机制,利用事件钩子导出内部数据或状态给外部调用者。Node.js中的很多对象,大多具有黑盒的特点,功能点较少,如果不通过事件钩子的形式,对象运行期间的中间值或内部状态,是我们无法获取到的。这种通过事件钩子的方式,可以使编程者不用关注组件是如何启动和执行的,只需关注在需要的事件点上即可。
var options = { host: 'www.google.com', port: 80, path: '/upload', method: 'POST' }; var req = http.request(options, function (res) { console.log('STATUS: ' + res.statusCode); console.log('HEADERS: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('BODY: ' + chunk); }); }); req.on('error', function (e) { console.log('problem with request: ' + e.message); }); // write data to request body req.write('data\n'); req.write('data\n'); req.end();
在这段HTTP request的代码中,程序员只需要将视线放在error,data这些业务事件点即可,至于内部的流程如何,无需过于关注。
值得一提的是如果对一个事件添加了超过10个侦听器,将会得到一条警告,这一处设计与Node.js自身单线程运行有关,设计者认为侦听器太多,可能导致内存泄漏,所以存在这样一个警告。调用:
emitter.setMaxListeners(0);
可以将这个限制去掉。
其次,为了提升Node.js的程序的健壮性,EventEmitter对象对error事件进行了特殊对待。如果运行期间的错误触发了error事件。EventEmitter会检查是否有对error事件添加过侦听器,如果添加了,这个错误将会交由该侦听器处理,否则,这个错误将会作为异常抛出。如果外部没有捕获这个异常,将会引起线程的退出。
实现一个继承了EventEmitter类是十分简单的,以下是Node.js中流对象继承EventEmitter的例子:
function Stream() { events.EventEmitter.call(this); } util.inherits(Stream, events.EventEmitter);
Node.js在工具模块中封装了继承的方法,所以此处可以很便利地调用。程序员可以通过这样的方式轻松继承EventEmitter对象,利用事件机制,可以帮助你解决一些问题。
在略微大一点的应用中,数据与Web服务器之间的分离是必然的,如新浪微博、Facebook、Twitter等。这样的优势在于数据源统一,并且可以为相同数据源制定各种丰富的客户端程序。以Web应用为例,在渲染一张页面的时候,通常需要从多个数据源拉取数据,并最终渲染至客户端。Node.js在这种场景中可以很自然很方便的同时并行发起对多个数据源的请求。
api.getUser("username", function (profile) { // Got the profile }); api.getTimeline("username", function (timeline) { // Got the timeline }); api.getSkin("username", function (skin) { // Got the skin });
Node.js通过异步机制使请求之间无阻塞,达到并行请求的目的,有效的调用下层资源。但是,这个场景中的问题是对于多个事件响应结果的协调并非被Node.js原生优雅地支持。为了达到三个请求都得到结果后才进行下一个步骤,程序也许会被变成以下情况:
api.getUser("username", function (profile) { api.getTimeline("username", function (timeline) { api.getSkin("username", function (skin) { // TODO }); }); });
这将导致请求变为串行进行,无法最大化利用底层的API服务器。
为解决这类问题,我曾写作一个模块(EventProxy,https://github.com/JacksonTian/eventproxy)来实现多事件协作,以下为上面代码的改进版:
var proxy = new EventProxy(); proxy.all("profile", "timeline", "skin", function (profile, timeline, skin) { // TODO }); api.getUser("username", function (profile) { proxy.emit("profile", profile); }); api.getTimeline("username", function (timeline) { proxy.emit("timeline", timeline); }); api.getSkin("username", function (skin) { proxy.emit("skin", skin); });
EventProxy也是一个简单的事件侦听者模式的实现,由于底层实现跟Node.js的EventEmitter不同,无法合并进Node.js中。但是却提供了比EventEmitter更强大的功能,且API保持与EventEmitter一致,与Node.js的思路保持契合,并可以适用在前端中。
这里的all方法是指侦听完profile、timeline、skin三个方法后,执行回调函数,并将侦听接收到的数据传入。
最后还介绍一种解决多事件协作的方案:Jscex(https://github.com/JeffreyZhao/jscex )。Jscex通过运行时编译的思路(需要时也可在运行前编译),将同步思维的代码转换为最终异步的代码来执行,可以在编写代码的时候通过同步思维来写,可以享受到同步思维的便利写作,异步执行的高效性能。如果通过Jscex编写,将会是以下形式:
var data = $await(Task.whenAll({ profile: api.getUser("username"), timeline: api.getTimeline("username"), skin: api.getSkin("username") })); // 使用data.profile, data.timeline, data.skin // TODO
此节感谢Jscex作者@老赵(http://blog.zhaojie.me/)的指正和帮助。
所谓雪崩问题,是在缓存失效的情景下,大并发高访问量同时涌入数据库中查询,数据库无法同时承受如此大的查询请求,进而往前影响到网站整体响应缓慢。那么在Node.js中如何应付这种情景呢。
var select = function (callback) { db.select("SQL", function (results) { callback(results); }); };
以上是一句数据库查询的调用,如果站点刚好启动,这时候缓存中是不存在数据的,而如果访问量巨大,同一句SQL会被发送到数据库中反复查询,影响到服务的整体性能。一个改进是添加一个状态锁。
var status = "ready"; var select = function (callback) { if (status === "ready") { status = "pending"; db.select("SQL", function (results) { callback(results); status = "ready"; }); } };
但是这种情景,连续的多次调用select发,只有第一次调用是生效的,后续的select是没有数据服务的。所以这个时候引入事件队列吧:
var proxy = new EventProxy(); var status = "ready"; var select = function (callback) { proxy.once("selected", callback); if (status === "ready") { status = "pending"; db.select("SQL", function (results) { proxy.emit("selected", results); status = "ready"; }); } };
这里利用了EventProxy对象的once方法,将所有请求的回调都压入事件队列中,并利用其执行一次就会将监视器移除的特点,保证每一个回调只会被执行一次。对于相同的SQL语句,保证在同一个查询开始到结束的时间中永远只有一次,在这查询期间到来的调用,只需在队列中等待数据就绪即可,节省了重复的数据库调用开销。由于Node.js单线程执行的原因,此处无需担心状态问题。这种方式其实也可以应用到其他远程调用的场景中,即使外部没有缓存策略,也能有效节省重复开销。此处也可以用EventEmitter替代EventProxy,不过可能存在侦听器过多,引发警告,需要调用setMaxListeners(0)移除掉警告,或者设更大的警告阀值。
田永强,新浪微博@朴灵,前端工程师,曾就职于SAP,现就职于淘宝,花名朴灵,致力于NodeJS和Mobile Web App方面的研发工作。双修前后端JavaScript,寄望将NodeJS引荐给更多的工程师。兴趣:读万卷书,行万里路。个人Github地址:http://github.com/JacksonTian。
Q:最近在琢磨javascript的自定义事件,我的理解是注册一个事件(addEvent),然后在需要的时候调用它(fireEvent),这感觉像声明好一个函数,然后在需要的时候调用,感觉不出它们之间的区别。
我觉得任何事物的存在都有它的道理,那这自定义事件到底用在哪些场景?
A:
你的感觉是对的,实际上事件机制就是从回调函数转化而来的。
实际上事件模式算是订阅/发布模式的一种,它的好处在于绑定事件和触发事件是互相隔离的,并且可以动态的添加和删除,下面我帮你一步一步的梳理出来。
现在有这么一个需求,首先,我有一个动作Action1,我想每次在Action1完成之后,都会触发一个服务Service1,那么代码我可以这么写。
// 服务1
function Service1(){}
// 动作1
function Action1(){
//other things
Service1();
}
这个就是楼主所想的,代码非常明了而且易懂,很不错。
然而现在需求来了,我的动作Action1,想在它完成以后,不仅能触发一个服务Service1,还能触发Service2,Service3……
你可能想到,应该声明另外一个叫ServiceAll的函数,然后在ServiceAll里依次调用Service1、Service2、Service3,这个主意是可行的。
function Service1(){}
function Service2(){}
function Service3(){}
function ServiceAll(){
Service1();
Service2();
Service3();
}
// 动作1
function Action1(){
//other things
ServiceAll();
}
但问题随之而来,这里的ServiceAll事先定义好了,假如我要动态的添加一个Service4怎么办?假如我突然改变心意,动作Action1以后不再执行Service2,这个时候代码正在运行,必须重新定义ServiceAll函数,这无论开销还是实现都不方便。
我们再换个思路,我们弄一个叫ServiceArray的数组,然后把所有要执行的Service都放进去,动作Action1以后,直接循环调用ServiceArray岂不是就好了?
var ServiceArray=[];
// 动作1
function Action1(){
//other things
ServiceArray.each(function(Service){
Service();
})
}
//追加Service1,Service2,Service3
ServiceArray.push(Service1);
ServiceArray.push(Service2);
ServiceArray.push(Service3);
// 实现一个数组删除函数
function deleteVal(arr,val){
var index = arr.indexOf(val);
if (index !== -1) {
arr.splice(index, 1);
}
}
// 动态增加一个Service4,并且不要Service2了
ServiceArray.push(Service4);
deleteVal(ServiceArray,Service2);
好了,我们已经用数组队列来轻松实现动态删除和动态添加Service的功能了,接下来又有一个新的需求。
我们上面都是一个动作Action1,我现在有第二个动作Action2,它也要有一堆Service要执行,这个时候最简单的实现办法是和上面一样,但如果直接复制粘贴代码的话,动作一多,重复代码就太多了,所以为了代码复用,我们写个统一的处理函数
var actionObj={};
function hanldeAction(name,serviceArr){
if(typeof actionObj[name] !=funtion){
actionObj[name]=function(){
serviceArr.forEach(function(Service){
Service();
})
}
}
return actionObj[name];
}
//调用
Action1=hanldeAction("action1",ServieArray1);
Action2=handleAction("action2",ServieArray2);
我们上面考虑的这些Action1,Action2都是相互独立的情况,下面再考虑一个问题,假设这些动作都是某个对象obj发出来的,我们必须要保证这个对象在Service1,Service2等等都能访问到,这个时候该怎么办?
你可能想到把obj带在handleAction的参数里,然后再其定义里由Servie(obj)带进去,这是可行的,不过我们换个面向对象的思路
function MyObj(){
this.actions={};
}
// 动态给Action添加Service
MyObj.prototype.addService=function(actionName,Service){
if(!this.actions[actionName])this.actions[actionName]=[];
this.actions[actionName].push(Service);
}
// 动态删除Service
MyObj.prototype.removeService=function(actionName,Service){
var ServiceArray=this.actions[actionName]?this.actions[actionName]:[];
var index = ServiceArray.indexOf(Service);
if (index !== -1) {
ServiceArray.splice(index, 1);
}
}
// Action调用Service
MyObj.prototype.emitAction=function(actionName){
var ServiceArray=this.actions[actionName]?this.actions[actionName]:[];
for(var i=0;i<ServiceArray.length;i++){
ServiceArray[i].apply(this);
}
}
看到这里,聪明的你应该就清楚了,我们是在实现一个简易的自定义事件机制,把其中的名字换一下就能更清晰的看出来了。
function EventEmitter(){
this._events={};
}
EventEmitter.prototype.addListener=function(type, listener){
if(!this._events[type])this._events[type]=[];
this._events[type].push(listener);
}
EventEmitter.prototype.removeListener=function(type, listener){
var listenerArray=this._events[type]?this._events[type]:[];
var index = listenerArray.indexOf(listener);
if (index !== -1) {
listenerArray.splice(index, 1);
}
}
EventEmitter.prototype.emit=function(type){
var listenerArray=this._events[type]?this._events[type]:[];
for(var i=0;i<listenerArray.length;i++){
listenerArray[i].apply(this);
}
}
这样我们就能通过new一个EventEmitter来使用它了。
//使用
var a = new EventEmitter();
a.addListener("type1",function(){
console.log("service1");
})
a.addListener("type1",function(){
console.log("service2");
})
a.emit("type1");
function otherService(){
console.log("other service");
}
a.addListener("type2",otherService);
a.addListener("type2",function(){console.log("service 3")})
a.emit("type2");
a.removeListener("type2",otherService);
a.emit("type2");
这个简单的事件对象已经可以满足我上面提到的种种要求了,但还缺少附加参数、移除所有事件、限定域等等功能,把这些完善以后,再优化一下事件队列的存储方式,你就完成了一个真正的EventEmitter了。
所以题主你应该明白了事件模式的好处了吧?
标签(空格分隔): 资料
有些虽已经年代久远,但仍然可以学到很多有用的东西,可以整理资料的链接和其他链接资料或许有重复…… –>>另外一个链接:分享自己长期关注的前端开发相关的优秀网站、博客、以及活跃开发者 库
搭建web app常用的样式/组件等收集列表(移动优先)