cordova/channel 從factory轉(zhuǎn)變成exports
我們從cordova 的factory 中轉(zhuǎn)變到exports的過程中,我們知道需要加載cordova/channel,因此我們需要知道該類在cordova加載過程中是很重要的
該類基本結(jié)構(gòu)如下
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的結(jié)構(gòu)體channel,我們分段實現(xiàn)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 指向的是一個函數(shù),有兩個參數(shù),根據(jù)實現(xiàn),我們知道 h是一個函數(shù) .類型是function(){},c是一個list
函數(shù)解釋:1.要是傳入的c是nil ,那么就直接執(zhí)行函數(shù)h
? 2 要還是傳入的c中含有多個數(shù)據(jù),那么需要將c中的每個數(shù)據(jù)進行訂閱f.
這里其實是綁定一個函數(shù)和多個對象之間的關(guān)系.
結(jié)構(gòu)如下
create: function (type) {
return channel[type] = new Channel(type, false);
},
createSticky: function (type) {
return channel[type] = new Channel(type, true);
},
這里是給channel對象動態(tài)添加屬性,但是每個屬性都是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具有動態(tài)增加屬性的能力.因此我們需要看看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.'
);
}
}
該函數(shù)來校驗subscription參數(shù)的正確性的
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();
}
}
};
- 檢查參數(shù)
- 聲明變量
- 根據(jù)eventListenerOrFunction 類型給相關(guān)變量賦值
- 要是state == 2 那么調(diào)用handleEvent函數(shù)并返回
- 給變量guid 賦值
- 用對象utils 對handleEvent 處理
- 要是沒有設(shè)置guid ,那么生成一個
- 給handleEvent賦值. (guid相當于標記)
- eventListenerOrFunction賦值
- 將guid 存入到handle中,要是第一次加入需要調(diào)用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();
}
}
};
這個函數(shù)我們在cordova中見過.但具體不知道什么用.這里需要仔細看了
- 聲明變量 fail;
- 生成語法糖 fireArgs (fireArgs就是函數(shù),fireArgs().
- 如果state 狀態(tài)是1 那么變成2 ,并且記錄語法糖
- 要是numhandles不是0執(zhí)行5
- 對handles 處理并調(diào)用函數(shù)
- 要是state == 2 并且this.numHandlers 有數(shù)據(jù),那么重置數(shù)據(jù)
估計好多人看到這里還是很懵逼的.不知道上面這三個函數(shù)到底是干啥用的.因此需要特別說明下.
這里大家看著混亂主要是因為里面多了好多狀態(tài)的判斷,我們 把代碼簡化
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添加數(shù)據(jù)
unsubscribe 相當于從handles刪除數(shù)據(jù)
fire 相當于調(diào)用handles中的數(shù)據(jù),并清空handles
這里只不過想進行相關(guān)操作需要滿足條件.具體添加什么樣子的數(shù)據(jù),后面需要往后看
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');
動態(tài)增加四個屬性
channel.create('onResume');
*// Event to indicate a pause lifecycle event*
channel.create('onPause');
增加兩個未知狀態(tài)屬性
channel.waitForInitialization('onCordovaReady');
channel.waitForInitialization('onDOMContentLoaded');
增加兩個特性屬性
channel的結(jié)構(gòu)如下
- 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轉(zhuǎn)變成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;
};
返回對象在數(shù)組的index
utils.arrayRemove = function (a, item) {
var index = utils.arrayIndexOf(a, item);
if (index !== -1) {
a.splice(index, 1);
}
return index !== -1;
};
數(shù)組刪除對象 返回是否刪除成功
utils.typeName = function (val) {
return Object.prototype.toString.call(val).slice(8, -1);
};
返回屬性名字
utils.isArray = Array.isArray ||
function (a) { return utils.typeName(a) === 'Array'; };
返回是否是數(shù)組
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);
};
};
該方法就是執(zhí)行函數(shù)func
了解arguments這個對象之前先來認識一下javascript的一些功能:
其實Javascript并沒有重載函數(shù)的功能,但是Arguments對象能夠模擬重載讶舰。Javascrip中每個函數(shù)都會有一個Arguments對象實例arguments鞍盗,它引用著函數(shù)的實參,可以用數(shù)組下標的方式"[]"引用arguments的元素跳昼。arguments.length為函數(shù)實參個數(shù)般甲,arguments.callee引用函數(shù)自身。
arguments他的特性和使用方法
特性:
1.arguments對象和Function是分不開的敷存。
2.因為arguments這個對象不能顯式創(chuàng)建。
3.arguments對象只有函數(shù)開始時才可用。
使用方法:
雖然arguments對象并不是一個數(shù)組茧痕,但是訪問單個參數(shù)的方式與訪問數(shù)組元素的方式相同
例如:
arguments[0],arguments[1],舀患。旷痕。。arguments[n]拱燃;
在js中 不需要明確指出參數(shù)名弄跌,就能訪問它們非竿,
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;
};
}());
構(gòu)建繼承關(guān)系
utils.alert = function (msg) {
if (window.alert) {
window.alert(msg);
} else if (console && console.log) {
console.log(msg);
}
};
});
alert 彈框
cordova/platform 從factory轉(zhuǎn)變成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: 平臺初始化需要調(diào)用的函數(shù)
cordova/builder 從factory轉(zhuǎn)變成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]);
}
}
}
對象的所有屬性都調(diào)用func函數(shù) .函數(shù)的第一個參數(shù)是object ,第二個參數(shù)是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函數(shù)解析對象所有的key 和value值
判斷obj 對象要是有path屬性,就require 下該路徑.獲取路徑的result
參數(shù)
- parent 在遞歸objects對象的時候,想當與全局變量
- objects 有可能含有path 和 children兩個屬性
- Cobbler 和merge 兩個布爾變量,可以組合四種情況,不過 在cobbler是false的情況,merge沒有意義,所以相當于三種情況
第一種情況 clobber = false
1.obj對象如果有path ,那么require下path的數(shù)據(jù).
2.如果parent[key].沒有數(shù)據(jù),那么就設(shè)置parent[key]=result
3.判斷obj是否有children屬性,有,那么result作為parent,obj.children作為obj 遞歸操作result對象.其實就是map中需要包含children的屬性和值
如圖
第二種情況clobber = true merge=false
- obj對象如果有path ,那么require下path的數(shù)據(jù).
- 如果parent[key].沒有數(shù)據(jù),那么就設(shè)置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 有值的時候.這里調(diào)用了recursiveMerge 函數(shù).(遞歸)
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相關(guān)屬性賦值給自己
要是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 () {};
主要函數(shù)的引用
cordova/modulemapper 從factory轉(zhuǎn)變成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;
}
}
這里就是給數(shù)組添加數(shù)據(jù),每次添加三個,這樣其實就是相當于一個二維數(shù)組吧.
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);
};
對應(yīng)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;
}
這函數(shù)簡單,可惜不知道數(shù)據(jù)啥樣子,無法判斷該函數(shù)用來干啥的
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數(shù)組
- 獲取strategy 和moduleName 數(shù)據(jù),
- 調(diào)用require()函數(shù)獲取exports數(shù)據(jù)
- 如果 strategy 是r 代表run ,因此繼續(xù)遍歷其他的
- 獲取變量symbolPath
- 獲取信號路徑和執(zhí)行名字
- 獲取target 對象(這里由于不知道context對象,因此獲取到的target目前不知道啥啥樣子的)
- 要是有target ,并且需要merge,那么就講module 更新到target中.
- 其他情況下,要是origSymbols對象中沒有symbolPath,那么就設(shè)置symbolPath,值是target
- 把module設(shè)置進入parentObj中
這里邏輯簡單,由于不知道具體對象的數(shù)據(jù)結(jié)構(gòu),因此需要后面回來再分析下這里的具體流程
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轉(zhuǎn)變成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方法. 該方法觸發(fā)時機是在加載完畢文件的時候就觸發(fā)該函數(shù)
onload事件在資源被加載完成后會被觸發(fā)滔驶。對于script標簽著角,在外部js文件被加載后代碼會被立即執(zhí)行昂勒。那么血柳,外部js文件中的代碼和該script標簽的onload回調(diào)函數(shù),它們的執(zhí)行順序是怎樣的呢?沒有找到官方的說明文檔衡瓶,所以自己做個實驗哮针。
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(相當于回調(diào))
如果id不存在全局modules中,那么,我們先加載資源,加載完畢資源再檢查是否在全局modules中,有就回調(diào),沒有就報錯
這里外部加載的文件必須要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的數(shù)組中.最后調(diào)用finishPluginLoading(回調(diào)) 函數(shù)
?
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);
}
}
處理插件
- 檢查插件數(shù)組數(shù)量,空調(diào)用回調(diào)函數(shù)返回
- 聲明回調(diào)函數(shù),該函數(shù)在所有的modules都加載完畢后執(zhí)行回調(diào)函數(shù)
- 注入插件
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 文件
- 調(diào)用cordova/plugin_list module
- 處理moduleList
從這里我們能看才出來,所有的路徑應(yīng)該都是依托cordova路徑的.