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

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)如下

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

  1. 檢查參數(shù)
  2. 聲明變量
  3. 根據(jù)eventListenerOrFunction 類型給相關(guān)變量賦值
  4. 要是state == 2 那么調(diào)用handleEvent函數(shù)并返回
  5. 給變量guid 賦值
  6. 用對象utils 對handleEvent 處理
  7. 要是沒有設(shè)置guid ,那么生成一個
  8. 給handleEvent賦值. (guid相當于標記)
  9. eventListenerOrFunction賦值
  10. 將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中見過.但具體不知道什么用.這里需要仔細看了

  1. 聲明變量 fail;
  2. 生成語法糖 fireArgs (fireArgs就是函數(shù),fireArgs().
  3. 如果state 狀態(tài)是1 那么變成2 ,并且記錄語法糖
  4. 要是numhandles不是0執(zhí)行5
  5. 對handles 處理并調(diào)用函數(shù)
  6. 要是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ù)

  1. parent 在遞歸objects對象的時候,想當與全局變量
  2. objects 有可能含有path 和 children兩個屬性
  3. 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的屬性和值

如圖

clobberfalse.png
第二種情況clobber = true merge=false
  1. obj對象如果有path ,那么require下path的數(shù)據(jù).
  2. 如果parent[key].沒有數(shù)據(jù),那么就設(shè)置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 有值的時候.這里調(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);
        }
    }
};

  1. 遍歷symbolList數(shù)組
  2. 獲取strategy 和moduleName 數(shù)據(jù),
  3. 調(diào)用require()函數(shù)獲取exports數(shù)據(jù)
  4. 如果 strategy 是r 代表run ,因此繼續(xù)遍歷其他的
  5. 獲取變量symbolPath
  6. 獲取信號路徑和執(zhí)行名字
  7. 獲取target 對象(這里由于不知道context對象,因此獲取到的target目前不知道啥啥樣子的)
  8. 要是有target ,并且需要merge,那么就講module 更新到target中.
  9. 其他情況下,要是origSymbols對象中沒有symbolPath,那么就設(shè)置symbolPath,值是target
  10. 把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ù)

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. 檢查插件數(shù)組數(shù)量,空調(diào)用回調(diào)函數(shù)返回
  2. 聲明回調(diào)函數(shù),該函數(shù)在所有的modules都加載完畢后執(zhí)行回調(diào)函數(shù)
  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. 調(diào)用cordova/plugin_list module
  5. 處理moduleList

從這里我們能看才出來,所有的路徑應(yīng)該都是依托cordova路徑的.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唆鸡,一起剝皮案震驚了整個濱河市涝影,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌争占,老刑警劉巖燃逻,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異臂痕,居然都是意外死亡伯襟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門刻蟹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逗旁,“玉大人,你說我怎么就攤上這事舆瘪∑В” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵英古,是天一觀的道長淀衣。 經(jīng)常有香客問我,道長召调,這世上最難降的妖魔是什么膨桥? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任蛮浑,我火速辦了婚禮,結(jié)果婚禮上只嚣,老公的妹妹穿的比我還像新娘沮稚。我一直安慰自己,他們只是感情好册舞,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布蕴掏。 她就那樣靜靜地躺著,像睡著了一般调鲸。 火紅的嫁衣襯著肌膚如雪盛杰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天藐石,我揣著相機與錄音即供,去河邊找鬼。 笑死于微,一個胖子當著我的面吹牛逗嫡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播角雷,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼祸穷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了勺三?” 一聲冷哼從身側(cè)響起雷滚,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吗坚,沒想到半個月后祈远,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡商源,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年车份,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牡彻。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡扫沼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庄吼,到底是詐尸還是另有隱情缎除,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布总寻,位于F島的核電站器罐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渐行。R本人自食惡果不足惜轰坊,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一铸董、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肴沫,春花似錦粟害、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驻襟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芋哭,已是汗流浹背沉衣。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留减牺,地道東北人豌习。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像拔疚,于是被迫代替她去往敵國和親肥隆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355