cordova/channel 从factory转变成exports
我们从cordova 的factory 中转变到exports的过程中,我们知道需要加载cordova/channel,因此我们需要知道该类在cordova加载过程中是很重要的
该类基本结构如下
var channel = {
};
module.exports = channel;
这里主要要看channel对象
var utils = require('cordova/utils');
var nextGuid = 1;
加载工具类和定义变量
var Channel = function (type, sticky) {
this.type = type;
// Map of guid -> function.
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky ? 1 : 0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;
};
定义一个对象
var channel = {
}
下面就是我们需要exports的结构体channel,我们分段实现channel
join: function (h, c) {
var len = c.length;
var i = len;
var f = function () {
if (!(--i)) h();
};
for (var j = 0; j < len; j++) {
if (c[j].state === 0) {
throw Error('Can only use join with sticky channels.');
}
c[j].subscribe(f);
}
if (!len) h();
},
第一个属性join 指向的是一个函数,有两个参数,根据实现,我们知道 h是一个函数 .类型是function(){},c是一个list
函数解释:1.要是传入的c是nil ,那么就直接执行函数h
2 要还是传入的c中含有多个数据,那么需要将c中的每个数据进行订阅f.
这里其实是绑定一个函数和多个对象之间的关系.
结构如下
create: function (type) {
return channel[type] = new Channel(type, false);
},
createSticky: function (type) {
return channel[type] = new Channel(type, true);
},
这里是给channel对象动态添加属性,但是每个属性都是Channel对象
deviceReadyChannelsArray: [],
deviceReadyChannelsMap: {},
声明两个变量
waitForInitialization: function (feature) {
if (feature) {
var c = channel[feature] || this.createSticky(feature);
this.deviceReadyChannelsMap[feature] = c;
this.deviceReadyChannelsArray.push(c);
}
},
生成feature属性对象,存放在deviceReadyChannelsArray 和 deviceReadyChannelsMap 中.保存的Channel对象的stick是1
initializationComplete: function (feature) {
var c = this.deviceReadyChannelsMap[feature];
if (c) {
c.fire();
}
}
读取feature对象,并fire
从以上我们能看出来,其实channel相对还是相对的api不多,不过这里需要注意的的,channel具有动态增加属性的能力.因此我们需要看看channel到底增加了那些属性.
function checkSubscriptionArgument (argument) {
if (typeof argument !== 'function' && typeof argument.handleEvent !== 'function') {
throw new Error(
'Must provide a function or an EventListener object ' +
'implementing the handleEvent interface.'
);
}
}
该函数来校验subscription参数的正确性的
Channel.prototype.subscribe = function (eventListenerOrFunction, eventListener) {
checkSubscriptionArgument(eventListenerOrFunction);
var handleEvent, guid;
if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {
// Received an EventListener object implementing the handleEvent interface
handleEvent = eventListenerOrFunction.handleEvent;
eventListener = eventListenerOrFunction;
} else {
// Received a function to handle event
handleEvent = eventListenerOrFunction;
}
if (this.state === 2) {
handleEvent.apply(eventListener || this, this.fireArgs);
return;
}
guid = eventListenerOrFunction.observer_guid;
if (typeof eventListener === 'object') {
handleEvent = utils.close(eventListener, handleEvent);
}
if (!guid) {
// First time any channel has seen this subscriber
guid = '' + nextGuid++;
}
handleEvent.observer_guid = guid;
eventListenerOrFunction.observer_guid = guid;
// Don't add the same handler more than once.
if (!this.handlers[guid]) {
this.handlers[guid] = handleEvent;
this.numHandlers++;
if (this.numHandlers === 1) {
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};
- 检查参数
- 声明变量
- 根据eventListenerOrFunction 类型给相关变量赋值
- 要是state == 2 那么调用handleEvent函数并返回
- 给变量guid 赋值
- 用对象utils 对handleEvent 处理
- 要是没有设置guid ,那么生成一个
- 给handleEvent赋值. (guid相当于标记)
- eventListenerOrFunction赋值
- 将guid 存入到handle中,要是第一次加入需要调用onHasSubscribersChange
Channel.prototype.unsubscribe = function (eventListenerOrFunction) {
checkSubscriptionArgument(eventListenerOrFunction);
var handleEvent, guid, handler;
if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') {
// Received an EventListener object implementing the handleEvent interface
handleEvent = eventListenerOrFunction.handleEvent;
} else {
// Received a function to handle event
handleEvent = eventListenerOrFunction;
}
guid = handleEvent.observer_guid;
handler = this.handlers[guid];
if (handler) {
delete this.handlers[guid];
this.numHandlers--;
if (this.numHandlers === 0) {
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};
订阅操作的逆向操作
Channel.prototype.fire = function (e) {
var fail = false; // eslint-disable-line no-unused-vars
var fireArgs = Array.prototype.slice.call(arguments);
// Apply stickiness.
if (this.state === 1) {
this.state = 2;
this.fireArgs = fireArgs;
}
if (this.numHandlers) {
// Copy the values first so that it is safe to modify it from within
// callbacks.
var toCall = [];
for (var item in this.handlers) {
toCall.push(this.handlers[item]);
}
for (var i = 0; i < toCall.length; ++i) {
toCall[i].apply(this, fireArgs);
}
if (this.state === 2 && this.numHandlers) {
this.numHandlers = 0;
this.handlers = {};
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};
这个函数我们在cordova中见过.但具体不知道什么用.这里需要仔细看了
- 声明变量 fail;
- 生成语法糖 fireArgs (fireArgs就是函数,fireArgs().
- 如果state 状态是1 那么变成2 ,并且记录语法糖
- 要是numhandles不是0执行5
- 对handles 处理并调用函数
- 要是state == 2 并且this.numHandlers 有数据,那么重置数据
估计好多人看到这里还是很懵逼的.不知道上面这三个函数到底是干啥用的.因此需要特别说明下.
这里大家看着混乱主要是因为里面多了好多状态的判断,我们 把代码简化
Channel.prototype.subscribe = function (eventListenerOrFunction, eventListener) {
var handleEvent;
handleEvent = eventListenerOrFunction;
if (!this.handlers[guid]) {
this.handlers[guid] = handleEvent;
this.numHandlers++;
}
};
Channel.prototype.unsubscribe = function (eventListenerOrFunction) {
var handleEvent = eventListenerOrFunction
guid = handleEvent.observer_guid;
handler = this.handlers[guid];
if (handler) {
delete this.handlers[guid];
this.numHandlers--;
}
};
Channel.prototype.fire = function (e) {
var fail = false; // eslint-disable-line no-unused-vars
var fireArgs = Array.prototype.slice.call(arguments);
if (this.numHandlers) {
var toCall = [];
for (var item in this.handlers) {
toCall.push(this.handlers[item]);
}
for (var i = 0; i < toCall.length; ++i) {
toCall[i].apply(this, fireArgs);
}
this.numHandlers = 0;
this.handlers = {};
}
};
subscribe 相当于想handles添加数据
unsubscribe 相当于从handles删除数据
fire 相当于调用handles中的数据,并清空handles
这里只不过想进行相关操作需要满足条件.具体添加什么样子的数据,后面需要往后看
channel.createSticky('onDOMContentLoaded');
// Event to indicate the Cordova native side is ready.
channel.createSticky('onNativeReady');
// Event to indicate that all Cordova JavaScript objects have been created
// and it's time to run plugin constructors.
channel.createSticky('onCordovaReady');
// Event to indicate that all automatically loaded JS plugins are loaded and ready.
// FIXME remove this
channel.createSticky('onPluginsReady');
// Event to indicate that Cordova is ready
channel.createSticky('onDeviceReady');
动态增加四个属性
channel.create('onResume');
*// Event to indicate a pause lifecycle event*
channel.create('onPause');
增加两个未知状态属性
channel.waitForInitialization('onCordovaReady');
channel.waitForInitialization('onDOMContentLoaded');
增加两个特性属性
channel的结构如下
- create->function
- createSticky->function
- deviceReadyChannelsArray->[] 包含onCordovaReady 和 onDOMContentLoaded
- deviceReadyChannelsMap->{} 其中包含onCordovaReady 和 onDOMContentLoaded
- waitForInitialization->function
- initializationComplete->function
- onDOMContentLoaded->Channel (stick=1)
- onNativeReady->Channel (stick=1)
- onCordovaReady->Channel (stick=1)
- onPluginsReady->Channel (stick=1)
- onDeviceReady->Channel (stick=1)
- onResume->Channel (stick=0)
- onPause->Channel (stick=0)
- onCordovaReady->Channel (stick=1)
- onDOMContentLoaded->Channel (stick=1)
cordova/utils 从factory转变成exports
这里我们需要看看这个工具modules.这个是在cordova/channel中require的
其实这个类还是很简单的.从命名上看就是工具类.
var utils = exports;
直接让utils指向exports .直接操作utils相当于直接操作exports
utils.defineGetterSetter = function (obj, key, getFunc, opt_setFunc) {
if (Object.defineProperty) {
var desc = {
get: getFunc,
configurable: true
};
if (opt_setFunc) {
desc.set = opt_setFunc;
}
Object.defineProperty(obj, key, desc);
} else {
obj.__defineGetter__(key, getFunc);
if (opt_setFunc) {
obj.__defineSetter__(key, opt_setFunc);
}
}
};
/**
* Defines a property getter for obj[key].
*/
utils.defineGetter = utils.defineGetterSetter;
这里定义get和set 方法,浏览器的适配. 具体怎么用可以看这里
utils.arrayIndexOf = function (a, item) {
if (a.indexOf) {
return a.indexOf(item);
}
var len = a.length;
for (var i = 0; i < len; ++i) {
if (a[i] === item) {
return I;
}
}
return -1;
};
返回对象在数组的index
utils.arrayRemove = function (a, item) {
var index = utils.arrayIndexOf(a, item);
if (index !== -1) {
a.splice(index, 1);
}
return index !== -1;
};
数组删除对象 返回是否删除成功
utils.typeName = function (val) {
return Object.prototype.toString.call(val).slice(8, -1);
};
返回属性名字
utils.isArray = Array.isArray ||
function (a) { return utils.typeName(a) === 'Array'; };
返回是否是数组
utils.isDate = function (d) {
return (d instanceof Date);
};
返回是否是日期对象
utils.clone = function (obj) {
if (!obj || typeof obj === 'function' || utils.isDate(obj) || typeof obj !== 'object') {
return obj;
}
var retVal, I;
if (utils.isArray(obj)) {
retVal = [];
for (i = 0; i < obj.length; ++i) {
retVal.push(utils.clone(obj[i]));
}
return retVal;
}
retVal = {};
for (i in obj) {
// https://issues.apache.org/jira/browse/CB-11522 'unknown' type may be returned in
// custom protocol activation case on Windows Phone 8.1 causing "No such interface supported" exception
// on cloning.
if ((!(i in retVal) || retVal[i] !== obj[i]) && typeof obj[i] !== 'undefined' && typeof obj[i] !== 'unknown') { // eslint-disable-line valid-typeof
retVal[i] = utils.clone(obj[I]);
}
}
return retVal;
};
对象深copy
utils.close = function (context, func, params) {
return function () {
var args = params || arguments;
return func.apply(context, args);
};
};
该方法就是执行函数func
了解arguments这个对象之前先来认识一下javascript的一些功能:
其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载。Javascrip中每个函数都会有一个Arguments对象实例arguments,它引用着函数的实参,可以用数组下标的方式"[]"引用arguments的元素。arguments.length为函数实参个数,arguments.callee引用函数自身。
arguments他的特性和使用方法
特性:
1.arguments对象和Function是分不开的。
2.因为arguments这个对象不能显式创建。
3.arguments对象只有函数开始时才可用。
使用方法:
虽然arguments对象并不是一个数组,但是访问单个参数的方式与访问数组元素的方式相同
例如:
arguments[0],arguments[1],。。。arguments[n];
在js中 不需要明确指出参数名,就能访问它们,
function UUIDcreatePart (length) {
var uuidpart = '';
for (var i = 0; i < length; i++) {
var uuidchar = parseInt((Math.random() * 256), 10).toString(16);
if (uuidchar.length === 1) {
uuidchar = '0' + uuidchar;
}
uuidpart += uuidchar;
}
return uuidpart;
}
utils.createUUID = function () {
return UUIDcreatePart(4) + '-' +
UUIDcreatePart(2) + '-' +
UUIDcreatePart(2) + '-' +
UUIDcreatePart(2) + '-' +
UUIDcreatePart(6);
};
uuid
utils.extend = (function () {
// proxy used to establish prototype chain
var F = function () {};
// extend Child from Parent
return function (Child, Parent) {
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.__super__ = Parent.prototype;
Child.prototype.constructor = Child;
};
}());
构建继承关系
utils.alert = function (msg) {
if (window.alert) {
window.alert(msg);
} else if (console && console.log) {
console.log(msg);
}
};
});
alert 弹框
cordova/platform 从factory转变成exports
// file: /Users/dpogue/Coding/cordova-ios/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) {
module.exports = {
id: 'ios',
bootstrap: function () {
// Attach the console polyfill that is iOS-only to window.console
// see the file under plugin/ios/console.js
require('cordova/modulemapper').clobbers('cordova/plugin/ios/console', 'window.console');
require('cordova/channel').onNativeReady.fire();
}
};
});
这部分还是很简单的.
id 代表平台
bootstrap: 平台初始化需要调用的函数
cordova/builder 从factory转变成exports
var utils = require('cordova/utils');
引用工具模块
function each (objects, func, context) {
for (var prop in objects) {
if (objects.hasOwnProperty(prop)) {
func.apply(context, [objects[prop], prop]);
}
}
}
对象的所有属性都调用func函数 .函数的第一个参数是object ,第二个参数是key
function clobber (obj, key, value) {
exports.replaceHookForTesting(obj, key);
var needsProperty = false;
try {
obj[key] = value;
} catch (e) {
needsProperty = true;
}
// Getters can only be overridden by getters.
if (needsProperty || obj[key] !== value) {
utils.defineGetter(obj, key, function () {
return value;
});
}
}
给对象obj增加属性key,值是value
function assignOrWrapInDeprecateGetter (obj, key, value, message) {
if (message) {
utils.defineGetter(obj, key, function () {
console.log(message);
delete obj[key];
clobber(obj, key, value);
return value;
});
} else {
clobber(obj, key, value);
}
}
是否打印message 也是增加属性
function include (parent, objects, clobber, merge) {
each(objects, function (obj, key) {
try {
var result = obj.path ? require(obj.path) : {};
if (clobber) {
// Clobber if it doesn't exist.
if (typeof parent[key] === 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else if (typeof obj.path !== 'undefined') {
// If merging, merge properties onto parent, otherwise, clobber.
if (merge) {
recursiveMerge(parent[key], result);
} else {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
}
}
result = parent[key];
} else {
// Overwrite if not currently defined.
if (typeof parent[key] === 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else {
// Set result to what already exists, so we can build children into it if they exist.
result = parent[key];
}
}
if (obj.children) {
include(result, obj.children, clobber, merge);
}
} catch (e) {
utils.alert('Exception building Cordova JS globals: ' + e + ' for key "' + key + '"');
}
});
}
这里有递归操作
这里each函数解析对象所有的key 和value值
判断obj 对象要是有path属性,就require 下该路径.获取路径的result
参数
- parent 在递归objects对象的时候,想当与全局变量
- objects 有可能含有path 和 children两个属性
- Cobbler 和merge 两个布尔变量,可以组合四种情况,不过 在cobbler是false的情况,merge没有意义,所以相当于三种情况
第一种情况 clobber = false
1.obj对象如果有path ,那么require下path的数据.
2.如果parent[key].没有数据,那么就设置parent[key]=result
3.判断obj是否有children属性,有,那么result作为parent,obj.children作为obj 递归操作result对象.其实就是map中需要包含children的属性和值
如图
第二种情况clobber = true merge=false
- obj对象如果有path ,那么require下path的数据.
- 如果parent[key].没有数据,那么就设置parent[key]=result
- 如果parent[key]定义了,那么就需要检查obj的path属性是否为空.要是不为空,不为空就替换parent[key]的值为result
- 判断obj是否有children属性,有,那么result作为parent,obj.children作为obj 递归操作result对象.其实就是map中需要包含children的属性和值
这里其实能看出来,这种情况下,要是obj有path,那么,我们需要替换parent[key]的值,即使parent[key]本身有值
第三种情况clobber = true merge = true
与第二种情况就是是否需要全部替换parent[key]的值,在obj.path 有值的时候.这里调用了recursiveMerge 函数.(递归)
function recursiveMerge (target, src) {
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
if (target.prototype && target.prototype.constructor === target) {
// If the target object is a constructor override off prototype.
clobber(target.prototype, prop, src[prop]);
} else {
if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
recursiveMerge(target[prop], src[prop]);
} else {
clobber(target, prop, src[prop]);
}
}
}
}
}
这里就是对属相进行赋值操作,
要是target的父类是自己,那么就直接将src相关属性赋值给自己
要是target的父类是别的,该属性和父类属性都是 object对象类型的时候,进行递归曹操别的就给target赋值
exports.buildIntoButDoNotClobber = function (objects, target) {
include(target, objects, false, false);
};
exports.buildIntoAndClobber = function (objects, target) {
include(target, objects, true, false);
};
exports.buildIntoAndMerge = function (objects, target) {
include(target, objects, true, true);
};
三种引用的操作
exports.recursiveMerge = recursiveMerge;
exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
exports.replaceHookForTesting = function () {};
主要函数的引用
cordova/modulemapper 从factory转变成exports
var builder = require('cordova/builder');
请求builder
var moduleMap = define.moduleMap;
这里获取了全局modules
var symbolList;
var deprecationMap;
exports.reset = function () {
symbolList = [];
deprecationMap = {};
};
重置reset 和deprecationMap变量
function addEntry (strategy, moduleName, symbolPath, opt_deprecationMessage) {
if (!(moduleName in moduleMap)) {
throw new Error('Module ' + moduleName + ' does not exist.');
}
symbolList.push(strategy, moduleName, symbolPath);
if (opt_deprecationMessage) {
deprecationMap[symbolPath] = opt_deprecationMessage;
}
}
这里就是给数组添加数据,每次添加三个,这样其实就是相当于一个二维数组吧.
exports.clobbers = function (moduleName, symbolPath, opt_deprecationMessage) {
addEntry('c', moduleName, symbolPath, opt_deprecationMessage);
};
exports.merges = function (moduleName, symbolPath, opt_deprecationMessage) {
addEntry('m', moduleName, symbolPath, opt_deprecationMessage);
};
exports.defaults = function (moduleName, symbolPath, opt_deprecationMessage) {
addEntry('d', moduleName, symbolPath, opt_deprecationMessage);
};
exports.runs = function (moduleName) {
addEntry('r', moduleName, null);
};
对应build的三种模式
function prepareNamespace (symbolPath, context) {
if (!symbolPath) {
return context;
}
var parts = symbolPath.split('.');
var cur = context;
for (var i = 0, part; part = parts[i]; ++i) { // eslint-disable-line no-cond-assign
cur = cur[part] = cur[part] || {};
}
return cur;
}
这函数简单,可惜不知道数据啥样子,无法判断该函数用来干啥的
exports.mapModules = function (context) {
var origSymbols = {};
context.CDV_origSymbols = origSymbols;
for (var i = 0, len = symbolList.length; i < len; i += 3) {
var strategy = symbolList[I];
var moduleName = symbolList[i + 1];
var module = require(moduleName);
// <runs/>
if (strategy === 'r') {
continue;
}
var symbolPath = symbolList[i + 2];
var lastDot = symbolPath.lastIndexOf('.');
var namespace = symbolPath.substr(0, lastDot);
var lastName = symbolPath.substr(lastDot + 1);
var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null;
var parentObj = prepareNamespace(namespace, context);
var target = parentObj[lastName];
if (strategy === 'm' && target) {
builder.recursiveMerge(target, module);
} else if ((strategy === 'd' && !target) || (strategy !== 'd')) {
if (!(symbolPath in origSymbols)) {
origSymbols[symbolPath] = target;
}
builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg);
}
}
};
- 遍历symbolList数组
- 获取strategy 和moduleName 数据,
- 调用require()函数获取exports数据
- 如果 strategy 是r 代表run ,因此继续遍历其他的
- 获取变量symbolPath
- 获取信号路径和执行名字
- 获取target 对象(这里由于不知道context对象,因此获取到的target目前不知道啥啥样子的)
- 要是有target ,并且需要merge,那么就讲module 更新到target中.
- 其他情况下,要是origSymbols对象中没有symbolPath,那么就设置symbolPath,值是target
- 把module设置进入parentObj中
这里逻辑简单,由于不知道具体对象的数据结构,因此需要后面回来再分析下这里的具体流程
exports.getOriginalSymbol = function (context, symbolPath) {
var origSymbols = context.CDV_origSymbols;
if (origSymbols && (symbolPath in origSymbols)) {
return origSymbols[symbolPath];
}
var parts = symbolPath.split('.');
var obj = context;
for (var i = 0; i < parts.length; ++i) {
obj = obj && obj[parts[I]];
}
return obj;
};
获取原始信号
cordova/pluginloader 从factory转变成exports
exports.injectScript = function (url, onload, onerror) {
var script = document.createElement('script');
// onload fires even when script fails loads with an error.
script.onload = onload;
// onerror fires for malformed URLs.
script.onerror = onerror;
script.src = url;
document.head.appendChild(script);
};
增加js注入代码
这里需要注意script的onload方法. 该方法触发时机是在加载完毕文件的时候就触发该函数
onload事件在资源被加载完成后会被触发。对于script标签,在外部js文件被加载后代码会被立即执行。那么,外部js文件中的代码和该script标签的onload回调函数,它们的执行顺序是怎样的呢?没有找到官方的说明文档,所以自己做个实验。
function injectIfNecessary (id, url, onload, onerror) {
onerror = onerror || onload;
if (id in define.moduleMap) { // eslint-disable-line no-undef
onload();
} else {
exports.injectScript(url, function () {
if (id in define.moduleMap) { // eslint-disable-line no-undef
onload();
} else {
onerror();
}
}, onerror);
}
}
如果id存在全局modules中,那么直接onload(相当于回调)
如果id不存在全局modules中,那么,我们先加载资源,加载完毕资源再检查是否在全局modules中,有就回调,没有就报错
这里外部加载的文件必须要cordova.define进行定义才行 要不加载报错.
function onScriptLoadingComplete (moduleList, finishPluginLoading) {
// Loop through all the plugins and then through their clobbers and merges.
for (var i = 0, module; module = moduleList[i]; i++) { // eslint-disable-line no-cond-assign
if (module.clobbers && module.clobbers.length) {
for (var j = 0; j < module.clobbers.length; j++) {
modulemapper.clobbers(module.id, module.clobbers[j]);
}
}
if (module.merges && module.merges.length) {
for (var k = 0; k < module.merges.length; k++) {
modulemapper.merges(module.id, module.merges[k]);
}
}
// Finally, if runs is truthy we want to simply require() the module.
if (module.runs) {
modulemapper.runs(module.id);
}
}
finishPluginLoading();
}
这里我们能看出来module 一定包含id属性,还可能有 clobbers ,merges 和 runs属性,将这些属性的值和id绑定.存放在 modulemapper的数组中.最后调用finishPluginLoading(回调) 函数
function handlePluginsObject (path, moduleList, finishPluginLoading) {
// Now inject the scripts.
var scriptCounter = moduleList.length;
if (!scriptCounter) {
finishPluginLoading();
return;
}
function scriptLoadedCallback () {
if (!--scriptCounter) {
onScriptLoadingComplete(moduleList, finishPluginLoading);
}
}
for (var i = 0; i < moduleList.length; i++) {
injectIfNecessary(moduleList[i].id, path + moduleList[i].file, scriptLoadedCallback);
}
}
处理插件
- 检查插件数组数量,空调用回调函数返回
- 声明回调函数,该函数在所有的modules都加载完毕后执行回调函数
- 注入插件
function findCordovaPath () {
var path = null;
var scripts = document.getElementsByTagName('script');
var term = '/cordova.js';
for (var n = scripts.length - 1; n > -1; n--) {
var src = scripts[n].src.replace(/\?.*$/, ''); // Strip any query param (CB-6007).
if (src.indexOf(term) === (src.length - term.length)) {
path = src.substring(0, src.length - term.length) + '/';
break;
}
}
return path;
}
读取所有的scripts标签.从中查询cordova.js 文件
获取cordova路径 .并且将src 指向指向空
exports.load = function (callback) {
var pathPrefix = findCordovaPath();
if (pathPrefix === null) {
console.log('Could not find cordova.js script tag. Plugin loading may fail.');
pathPrefix = '';
}
injectIfNecessary('cordova/plugin_list', pathPrefix + 'cordova_plugins.js', function () {
var moduleList = require('cordova/plugin_list');
handlePluginsObject(pathPrefix, moduleList, callback);
}, callback);
};
- 找cordova路径
- 要是没有该路径.不能加载
- 加载cordova_plugins.js 文件
- 调用cordova/plugin_list module
- 处理moduleList
从这里我们能看才出来,所有的路径应该都是依托cordova路径的.