cordova源码文件cordova.js分析(2)

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.

这里其实是绑定一个函数和多个对象之间的关系.

结构如下

join方法含义.png
 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();
        }
    }
};

  1. 检查参数
  2. 声明变量
  3. 根据eventListenerOrFunction 类型给相关变量赋值
  4. 要是state == 2 那么调用handleEvent函数并返回
  5. 给变量guid 赋值
  6. 用对象utils 对handleEvent 处理
  7. 要是没有设置guid ,那么生成一个
  8. 给handleEvent赋值. (guid相当于标记)
  9. eventListenerOrFunction赋值
  10. 将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中见过.但具体不知道什么用.这里需要仔细看了

  1. 声明变量 fail;
  2. 生成语法糖 fireArgs (fireArgs就是函数,fireArgs().
  3. 如果state 状态是1 那么变成2 ,并且记录语法糖
  4. 要是numhandles不是0执行5
  5. 对handles 处理并调用函数
  6. 要是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

参数

  1. parent 在递归objects对象的时候,想当与全局变量
  2. objects 有可能含有path 和 children两个属性
  3. 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的属性和值

如图

clobberfalse.png
第二种情况clobber = true merge=false
  1. obj对象如果有path ,那么require下path的数据.
  2. 如果parent[key].没有数据,那么就设置parent[key]=result
  3. 如果parent[key]定义了,那么就需要检查obj的path属性是否为空.要是不为空,不为空就替换parent[key]的值为result
  4. 判断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);
        }
    }
};

  1. 遍历symbolList数组
  2. 获取strategy 和moduleName 数据,
  3. 调用require()函数获取exports数据
  4. 如果 strategy 是r 代表run ,因此继续遍历其他的
  5. 获取变量symbolPath
  6. 获取信号路径和执行名字
  7. 获取target 对象(这里由于不知道context对象,因此获取到的target目前不知道啥啥样子的)
  8. 要是有target ,并且需要merge,那么就讲module 更新到target中.
  9. 其他情况下,要是origSymbols对象中没有symbolPath,那么就设置symbolPath,值是target
  10. 把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(回调) 函数

image-20190611134953561.png


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);
    }
}

处理插件

  1. 检查插件数组数量,空调用回调函数返回
  2. 声明回调函数,该函数在所有的modules都加载完毕后执行回调函数
  3. 注入插件
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);
};
  1. 找cordova路径
  2. 要是没有该路径.不能加载
  3. 加载cordova_plugins.js 文件
  4. 调用cordova/plugin_list module
  5. 处理moduleList

从这里我们能看才出来,所有的路径应该都是依托cordova路径的.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354