PhoneGap源码分析8——cordova
转了一圈,再回到cordova这个模块。
在cordova中,首先是导入cordova/channel模块,这就是前一篇分析的,之后就触发在channel创建的onDOMContectLoaded事件,接着为了侦听deviceready、resume、pause等事件而重新定义了DOM规范中window和document的addEventListener和removeEventListener,然后再创建cordova这个对象,并作为结果“返回”。
1 function (require, exports, module) { 2 3 var channel = require('cordova/channel');//导入通道模块 4 5 /** 6 * 将通道里面创建的onDOMContectLoaded事件添加至文档的DOMContentLoaded 7 * DOMContentLoaded是一个在HTML5中定义的事件,在形成完整的DOM树之后触发,不理会图像、JS文件、CSS文件等是否已下载完毕,类似于jQuery中的ready 8 */ 9 //在兼容DOM浏览器 10 document.addEventListener('DOMContentLoaded', function() { 11 channel.onDOMContentLoaded.fire(); 12 }, false);//第三个参数false表示在事件冒泡阶段触发 13 //在IE浏览器 14 if (document.readyState == 'complete' || document.readyState == 'interactive') { 15 channel.onDOMContentLoaded.fire(); 16 } 17 18 //将addEventListener和removeEventListener原函数保留 19 var m_document_addEventListener = document.addEventListener; 20 var m_document_removeEventListener = document.removeEventListener; 21 var m_window_addEventListener = window.addEventListener; 22 var m_window_removeEventListener = window.removeEventListener; 23 24 var documentEventHandlers = {},//缓存所有的事件处理函数 25 windowEventHandlers = {}; 26 27 //重定义addEventListener和removeEventListener,以方便后面注册添加pause、resume、deviceReady等事件 28 document.addEventListener = function(evt, handler, capture) { 29 }; 30 31 window.addEventListener = function(evt, handler, capture) { 32 }; 33 34 document.removeEventListener = function(evt, handler, capture) { 35 }; 36 37 window.removeEventListener = function(evt, handler, capture) { 38 }; 39 40 function createEvent(type, data) { 41 } 42 43 if(typeof window.console === "undefined") {//兼容控制台,如果未定义,就是用简单对象代替 44 } 45 46 var cordova = {//创建cordova对象字面量,并作为结果返回 47 }; 48 49 // 注册pause、resume、deviceReady事件 50 channel.onPause = cordova.addDocumentEventHandler('pause'); 51 channel.onResume = cordova.addDocumentEventHandler('resume'); 52 channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); 53 54 module.exports = cordova;//返回结果 55 56 }
从代码结构上来说,还是比较清晰的,这里补充一点关于IE中的readyState属性:
(1)uninitialized:未初始化,对象存在但尚未初始化
(2)loading:正在加载,对象正在加载数据
(3)loaded:加载完毕,对象加载数据完成
(4)interactive:交互,可以操作对象了,但还没有完全加载
(5)complete:完成,对象已经加载完毕。
在第40行有一个createEvent方法:
function createEvent(type, data) { var event = document.createEvent('Events');//创建新的Event对象 event.initEvent(type, false, false);//初始化事件对象,参数:事件类型,事件是否冒泡,是否可以使用preventDefault()方法取消事件 if (data) { for (var i in data) { if (data.hasOwnProperty(i)) {//剔除原型中的属性 event[i] = data[i];//将传入的属性copy至事件对象
} } } return event; }
这里的逻辑就是先创建事件,再使用相应方法初始化,然后复制数据属性。关于createEvent,我查阅了《Javascript高级程序设计(第3版)》和其它是一些资料,发现描述的有些出入,各位有兴趣的朋友可以自己实践一探究竟,总体来说,这里就是创建一个事件,不影响后续分析。
下面看一下重定义的addEventListener:
//添加事件侦听 document.addEventListener = function(evt, handler, capture) { var e = evt.toLowerCase();//表示事件的名称 if (typeof documentEventHandlers[e] != 'undefined') { if (evt === 'deviceready') {//设备就绪事件,则只调用一次 documentEventHandlers[e].subscribeOnce(handler); } else {//其它事件,将事件处理函数注入到事件通道中 documentEventHandlers[e].subscribe(handler); } } else {//事件的第一个处理程序,调用DOM中原有的添加事件侦听函数来添加事件侦听 m_document_addEventListener.call(document, evt, handler, capture); } }; window.addEventListener = function(evt, handler, capture) { var e = evt.toLowerCase(); if (typeof windowEventHandlers[e] != 'undefined') { windowEventHandlers[e].subscribe(handler); } else { m_window_addEventListener.call(window, evt, handler, capture); } };
移除事件处理程序是其反过程,对于channel.subscribe注入的使用unsubscribe反注入,而通过DOM中addEventListener的添加的使用removeEventListener移除。
再来看看cordova的定义:
1 var cordova = { 2 define:define, // 将内部的define作为cordova中一个属性开放给调用者 3 require:require, // 将内部的require作为cordova中一个属性开放给调用者 4 5 addWindowEventHandler:function(event, opts) {//添加window事件侦听,使用内部数组缓存 6 return (windowEventHandlers[event] = channel.create(event, opts)); 7 }, 8 addDocumentEventHandler:function(event, opts) {//添加document事件侦听 9 return (documentEventHandlers[event] = channel.create(event, opts)); 10 }, 11 removeWindowEventHandler:function(event) {//移除window事件侦听 12 delete windowEventHandlers[event]; 13 }, 14 removeDocumentEventHandler:function(event) {//移除document事件侦听 15 delete documentEventHandlers[event]; 16 }, 17 18 //以对象形式返回DOM中原来定义的事件侦听函数 19 getOriginalHandlers: function() { 20 return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, 21 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; 22 }, 23 24 //触发document事件 25 fireDocumentEvent: function(type, data) { 26 var evt = createEvent(type, data); 27 if (typeof documentEventHandlers[type] != 'undefined') {//已经缓存事件类型
28 setTimeout(function() { 29 documentEventHandlers[type].fire(evt); 30 }, 0);//一个超时0s的调用,也即是当前代码结束后立即调用
31 } else { 32 document.dispatchEvent(evt);//触发事件
33 } 34 }, 35 fireWindowEvent: function(type, data) { 36 var evt = createEvent(type,data); 37 if (typeof windowEventHandlers[type] != 'undefined') { 38 setTimeout(function() { 39 windowEventHandlers[type].fire(evt); 40 }, 0); 41 } else { 42 window.dispatchEvent(evt); 43 } 44 }, 45 shuttingDown:false, 46 UsePolling:false, 47 commandQueue:[], 48 commandQueueFlushing:false, 49 50 callbackId: 0, 51 callbacks: {}, 52 callbackStatus: { 53 NO_RESULT: 0, 54 OK: 1, 55 CLASS_NOT_FOUND_EXCEPTION: 2, 56 ILLEGAL_ACCESS_EXCEPTION: 3, 57 INSTANTIATION_EXCEPTION: 4, 58 MALFORMED_URL_EXCEPTION: 5, 59 IO_EXCEPTION: 6, 60 INVALID_ACTION: 7, 61 JSON_EXCEPTION: 8, 62 ERROR: 9 63 }, 64 65 callbackSuccess: function(callbackId, args) {//回调函数 66 }, 67 callbackError: function(callbackId, args) {//发生异常时的回调 68 }, 69 addConstructor: function(func) {//在cordova初始的时候添加处理程序 70 } 71 };
cordova是作为整个结果返回的,主要的方法有用于模块化的require、define方法、添加和移除window/document上事件侦听方法、触发window/document事件方法以及回调等。
至此,源码中的5672行的window.cordova = require('cordova');才算执行完。
郴江幸自绕郴山,为谁流下潇湘去?
欲将心事付瑶琴,知音少,弦断有谁听?
倩何人,唤取红巾翠袖,揾英雄泪!
零落成泥碾作尘,只有香如故!