Cordova webapp實戰開發(十)- 開發自己的插件(cordova原理 - 1)

隨說 :項目如期進行中,利用空閑的時間,理解了一下cordova的打包原理.

你想要開發自己的插件,首先要理解cordova的調用插件原理。

1. cordova.js/cordova_plugins.js文件

——自動生成####

cordova.js :
在創建Android&iOS工程的時候,
是從cordova的lib目錄下Copy到platforms\android&iOS\assets\www\cordova.js的。
同時備份到platforms\android&iOS\platform_www\cordova.js

cordova_plugins.js :
而platforms\android&iOS\assets\www\是根據plugins文件夾的內容生成的。

——手動生成####

可以直接參考, 有詳細的安裝指令
github : https://github.com/apache/cordova-js

——cordova目錄(Android和iOS的目錄有所不同)####

;(function() {  
var CORDOVA_JS_BUILD_LABEL = '4.1.1';  
// file: src/scripts/require.js  
//...  

// file: src/cordova.js  
// cordova主要實現功能實現模塊
define("cordova", function(require, exports, module) { /*...*/ }  

//================下面這些是通用的方法iOS&Android============
// file: src/common/argscheck.js  
// 用于plugin中校驗參數
define("cordova/argscheck", function(require, exports, module) { /*...*/ }  

// file: src/common/base64.js  
// JS->Native交互時對ArrayBuffer進行uint8ToBase64
define("cordova/base64", function(require, exports, module) { /*...*/ }  

// file: src/common/builder.js  
// 對象屬性操作, 注意,它用到了utils
define("cordova/builder", function(require, exports, module) { /*...*/ }  

// file: src/common/channel.js  
// 控制事件調用
define("cordova/channel", function(require, exports, module) { /*...*/ }  

// file: src/common/exec/proxy.js 
//  用于Plugin中往已經有的模塊上添加方法
define("cordova/exec/proxy", function(require, exports, module) { /*...*/ }  

// file: src/common/init.js  
// 初始化處理
define("cordova/init", function(require, exports, module) { /*...*/ }  

// file: src/common/modulemapper.js  
// 把定義的模塊clobber到一個對象,在初始化的時候會賦給window
define("cordova/modulemapper", function(require, exports, module) { /*...*/ }  


// file: src/common/pluginloader.js  
// 加載所有cordova_plugins.js中定義的模塊,執行完成后會觸發onPluginsReady
define("cordova/pluginloader", function(require, exports, module) { /*...*/ } 
 
// file: src/common/urlutil.js  
// 獲取絕對URL,InAppBrowser中會用到
define("cordova/urlutil", function(require, exports, module) { /*...*/ }  

// file: src/common/utils.js  
// 提供了一些工具方法 , 檢測是否數組,是否數據等
define("cordova/utils", function(require, exports, module) { /*...*/ }  

//================下面iOS 與 Android 調用不同,這里是以iOS作為例子=====
// file: /Users/ednamorales/dev/apache_plugins/cordova-ios/cordova-js-src/exec.js
// 執行JS->Native交互
define("cordova/exec", function(require, exports, module) 

// file: /Users/ednamorales/dev/apache_plugins/cordova-ios/cordova-js-src/platform.js
// 模塊處理
define("cordova/platform", function(require, exports, module)

// 加載cordova模塊,賦給window.cordova
// require()第一次被調用,就開始調用factory
window.cordova = require('cordova');  

// 加載Plugin代碼等初期化處理  
require('cordova/init');  
})();  

注 :

當index.html引入<script type="text/javascript" src="cordova.js"></script>后,

1-> 定義 require, define; 開始定義 require, define; 實現函數,
2-> define() 注冊各模塊, 然后開始window.cordova = require('cordova'); 將cordova賦值給window.cordova
3-> 第一次require()被調用,就會開始調用factory,然后因為factory里面包含其他require(), 形成循環嵌套
4-> 至此所有factory,被執行完畢。

(你喜歡可以修改cordova.js。 +上alert,就能調試他們的加載順序了)

——cordova_plugins.js目錄####

cordova從3.0版本開始不再在cordova.js中包含plugin的代碼,cls引入后,plugin采用cls生成cordova_plugins.js然后動態加載需要的plugin

// 注冊cordova/plugin_list 模塊
cordova.define('cordova/plugin_list', function(require, exports, module) {
module.exports = [
    {
        "file": "plugins/cordova-plugin-console/www/console-via-logger.js",
        "id": "cordova-plugin-console.console",
        "pluginId": "cordova-plugin-console",
        "clobbers": [
            "console"
        ]
    }
];

module.exports.metadata = 
// TOP OF METADATA
{
    "cordova-plugin-console": "1.0.3"
}
// BOTTOM OF METADATA
});

這個模塊是怎樣添加進去的?在cordova.js中,找到源代碼

// 異步加載 onDeviceReady 受制于
// 插件加載完畢后,會調用 onPluginsReady.
exports.load = function(callback) {
    var pathPrefix = findCordovaPath();
    if (pathPrefix === null) {
        console.log('Could not find cordova.js script tag. Plugin loading may fail.');
        pathPrefix = '';
    }
  // 當開始加載的時候,將cordova/plugin_list的list拿出來,一個一個憑借<script>加載進去
    injectIfNecessary('cordova/plugin_list', pathPrefix + 'cordova_plugins.js', function() {
                      var moduleList = require("cordova/plugin_list");
                      handlePluginsObject(pathPrefix, moduleList, callback);
                      }, callback);
};

2. cordova自定義模塊系統,require/define

模塊化系統,異步模塊化加載。cordova-js最開始采用的是require.js作者寫的almond.js(兼容AMD和CommonJS),但之后由于特殊需求(比如模塊不存在的時候要throw異常),最終從almond.js fork過來實現了一個簡易CommonJS風格的模塊系統,同時提供了和nodejs之間很好的交互。在cordova.js中可以直接使用define()和require(),在其他文件可以通過cordova.define()和cordova.require()來調用。所以src/scripts/require.js中定義的就是一個精簡的JavaScript模塊系統。

源碼 :

// 定義2個cordova.js內部使用的全局函數require/define  
var require,  define;  
  
// 通過自調用的匿名函數來實例化全局函數require/define  
(function () {  
    // 定義一個對象,然后以ID為索引包含ID和工廠方法  ,具體看define()定義
    var modules = {},  
    // 定義一個數組,裝載正在build中的模塊ID
        requireStack = [],  
    // 定義一個對象,標示正在build中模塊ID的Map  
        inProgressModules = {},  
        SEPARATOR = ".";  
  
    // 模塊build , 當require函數執行時,才會開始build 
    function build(module) {  
        // 備份工廠方法  
        var factory = module.factory,  
        // 對require對象進行特殊處理  
            localRequire = function (id) {  
                var resultantId = id;  
                // 他是一個相對的路徑,所以砍掉./ 并添加上id
                if (id.charAt(0) === ".") {  
                    resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);  
                }  
                return require(resultantId);  
            };  
        // 給模塊定義一個空的exports對象,防止工廠類方法中的空引用  
        module.exports = {};  
        // 刪除工廠方法  
        delete module.factory;  
        // 調用備份的工廠方法(參數必須是require,exports,module,所以你在下面定義的方法基本都是這三個參數)  
        factory(localRequire, module.exports, module);  
        // 返回工廠方法中實現的module.exports對象  
        return module.exports;  
    }  
  
    // 加載模塊  
    require = function (id) {  
        // 如果不存在該模塊  ,跑出異常
        if (!modules[id]) {  
            throw "module " + id + " not found";  
        // 如果模塊正在build中拋出異常  
        } else if (id in inProgressModules) {  
            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;  
            throw "Cycle in require graph: " + cycle;  
        }  
        // 如果模塊存在工廠方法說明還未進行build(require嵌套)  
        if (modules[id].factory) {  
            try {  
                // 標示該模塊正在build  
                inProgressModules[id] = requireStack.length;  
                // 將該模塊壓入請求棧  
                requireStack.push(id);  
                // 模塊build,成功后返回module.exports  
                return build(modules[id]);  
            } finally {  
                // build完成后刪除當前請求  
                delete inProgressModules[id];  
                requireStack.pop();  
            }  
        }  
        // build完的模塊直接返回module.exports  ,在build 方法中構造成功就會刪掉原來的工廠方法
        return modules[id].exports;  
    };  
  
    // 定義模塊  
    define = function (id, factory) {  
        // 如果已經存在拋出異常  
        if (modules[id]) {  
            throw "module " + id + " already defined";  
        }  
        // 模塊以ID為索引包含ID和工廠方法  
        modules[id] = {  
            id: id,  
            factory: factory  
        };  
    };  
  
    // 移除模塊  
    define.remove = function (id) {  
        delete modules[id];  
    };  
  
    // 返回所有模塊  
    define.moduleMap = modules;  
})();  
  
// 如果處于nodejs環境的話,把require/define暴露給外部  
if (typeof module === "object" && typeof require === "function") {  
    module.exports.require = require;  
    module.exports.define = define;  
}  

define() : 將modules和factory 包裝起來, define的作用只是注冊,類似聲明import,實際不會調用factory

require() : 在window.cordova = require('cordova'); 后,第一次被調用,JS開始被調用,然后嵌套調用,然后所有module的factory都會被加載一次。

整個過程大概就是

  1. cordova.js引入,開始define()各個模塊,這個時候不斷生成以id,為索引的modules{id : id, factory : factory }對象

  2. 執行到最后window.cordova = require('cordova'); ,第一次開始調用require函數,
    傳入id為 cordova, 找到 modules[cordova] 開始build 對應的 module.factory

3.build成功后,直接返回module.exports,并刪除原來的module.facaory。

至此,整個自定義模塊加載完畢,

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,748評論 25 708
  • Cordova自定義插件實戰# 使用前提 這篇文章是之前發表在CSDN上的,拿過來充數用的,其實那個時候也寫了不少...
    one_cup閱讀 1,578評論 5 3
  • 1概念 理解自己時,充分地覺知自己。 #了解更多時,可以慶祝一下。 2常見的破壞 1評判自己。 2急于改變。 #由...
    楷歌Kale閱讀 147評論 0 0
  • 我無力的在跑道上奔跑著, 奔跑著,直到..... 沒有了路,沒有了遠方。 我孤獨的在跑道上奔跑著, 奔跑著,直到....
    奉仙兒閱讀 348評論 0 0
  • 生活中,只有你細心觀察了,才能把失眠寫成詩。 讀《荷塘月色》,就好像是在讀一首詩。 我想,朱自清先生,也不會是第一...
    大雄老師will閱讀 1,214評論 2 5