您的位置:首页 > 产品设计 > UI/UE

5 (phonegap源码分析)通用配置/构建模块(common builder)

2013-08-12 17:14 465 查看
phonegap中模块数目有几十个,并且按模块功能分成了不同的层次,如果完全手动请求构建会出现大量的类似window.myphonegap=require(“myphonegap”)的语句。

phonegap的设计者采用了配置构建的方式,即用一个common对象对所有通用的模块进行配置,用一个platform对象对所有与平台相关的模块进行配置,builder对象提供的函数则根据这两个配置对象作为参数构建对应的对象模块。

先来看common对象,因为它是理解builder对象的基础。

define("myphonegap/common",function(require,exports,module){

module.exports = {
defaults: {
myphonegap: {
path: 'myphonegap',
children: {
exec: {
path: 'myphonegap/exec'
},
logger: {
path: 'myphonegap/plugin/logger'
}
}
},
Myphonegap: {
children: {
exec: {
path: 'myphonegap/exec'
}
}
},
open : {
path: 'myphonegap/plugin/InAppBrowser'
},
Acceleration: {
path: 'myphonegap/plugin/Acceleration'
},
Camera:{
path: 'myphonegap/plugin/CameraConstants'
}
},
//省略若干属性
clobbers: {
navigator: {
children: {
connection: {
path: 'myphonegap/plugin/network'
}
}
}
}
};

});


上面的代码看出,这个导出模块较为简单,没有任何的函数对象;整个导出对象就是一个对象字面量定义的。这个字面量就是所有通用模块的配置信息。

下面分析builder模块,builder主要代码结构如下:

define("myphonegap/builder", function(require, exports, module) {
一
var utils = require('myphonegap/utils');

function each(objects, func, context) {
}

function clobber(obj, key, value) {
}

function assignOrWrapInDeprecateGetter(obj, key, value, message) {
}

function include(parent, objects, clobber, merge) {
}
function recursiveMerge(target, src) {
}

module.exports = {
buildIntoButDoNotClobber: function(objects, target) {
include(target, objects, false, false);
},
buildIntoAndClobber: function(objects, target) {
include(target, objects, true, false);
},
buildIntoAndMerge: function(objects, target) {
include(target, objects, true, true);
},
recursiveMerge: recursiveMerge,
assignOrWrapInDeprecateGetter: assignOrWrapInDeprecateGetter
};

});

先分析each函数:

function each(objects, func, context) {
for (var prop in objects) {
if (objects.hasOwnProperty(prop)) {
func.apply(context, [objects[prop], prop]);
}
}
}


for循环遍历objects中的属性,if判断条件选出了非原型对象中的属性,func函数处理属性,context作为执行环境,属性和属性名作为参数。这应该属于一个可复用性较强的函数,对于新手来说理解的难点在Object原型对象提供的两个函数hasOwnProperty和apply,弄懂了这两个函数的用法就容易了。

function clobber(obj, key, value) {
obj[key] = value;
// Getters can only be overridden by getters.
if (obj[key] !== value) {
utils.defineGetter(obj, key, function() {
return value;
});
}
}

上面这个函数是给obj对象插入一个属性,属性名为key,值为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);
}
}

下面看看include函数,each函数将调用作为参数的匿名函数并以objects的属性和属性名作为参数,全局环境作为执行环境。

assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated)函数的本质其实就是插入属性值,之前的版本有更简单的实现parent[key] = result;如果觉得下面的代码难理解就将所有的assignOrWrapInDeprecateGetter函数和clobber函数用parent[key]=result替换。

clobber是指定合并规则,是以parent为标准还是以objects为标准,clobber为true表示以objects为准,如果parent中不存在的属性就添加该属性,存在的属性则覆盖或者递归添加。Clobber为false时表示以parent为准,如果该属性在parent中不存在,则直接添加,如果存在则将该属性作为下一次递归的目标属性。

function include(parent, objects, clobber, merge) {
each(objects, function (obj, key) {
try {
var result = obj.path ? require(obj.path) : {};

if (clobber) {
////如果目标对象不存在该属性,直接赋值为导入的模块
if (typeof parent[key] === 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else if (typeof obj.path !== 'undefined') {
// 如果存在该属性,合并则递归合并,否则覆盖
if (merge) {
recursiveMerge(parent[key], result);
} else {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
}
}
result = parent[key];
} else {

if (typeof parent[key] == 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else {
// 目标对象已有该属性,使用该属性作为下一次递归处理的参数
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 + '"');
}
});
}

上面使用了函数recursizveMerge递归合并对象,src对象中属性合并到target属性中。这个函数的可复用性较强。

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

现在来测试下,先让myphonegap模块中导出一个Hello函数,exec模块直接导出函数对象。然后我们通过builder和common模块来构建myphonegap和exec模块。不明白的话看代码。

先是myphonegap模块,前面说过define函数,define函数的第二个参数是一个工厂函数,用来创建最终的模块对象。这里module是形参,module.exports就是最终的模块对象。如果对此有不理解的回头看第三节。

//注册myphonegap模块
  define("myphonegap", function(require, exports, module){
console.info("create myphonegap module");

module.exports = {
Hello:function(name){
console.info("hello, "+name +" !");
}
};
  });

exec本来是执行本地API的函数借口,这里我们先用一个简单的函数替代。

define("myphonegap/exec", function(require, exports, module){
module.exports= function(name){
console.info("Exec native function " + name+"  !");
};

  });


接下来上测试代码,先将之前的测试代码注释,当然也可以不注释掉,之后的window对象用一个任意的自定义对象替换。

//window.myphonegap = require('myphonegap');
var builder = require("myphonegap/builder");
var common  = require("myphonegap/common");

builder.buildIntoButDoNotClobber( common.defaults,window);

window.myphonegap.Hello("Jack");
window.myphonegap.exec("myfunc");

执行代码,console输出结果如下:



注:本节中用到utils模块的内容,uitls只是一个简单的工具模块,从源码拷贝过来改名即可。

myphonegap.js 本例完整源码:

;(function(){
  var  require,//myphonegap内部的工具函数,用来导入相关的模块
  define;//在myphonegap注册相关的模块

  //通过一个立即调用的匿名函数,来给require和define赋上实际的函数
  (function(){
var modules={}; // 模块数组,添加模块类似给这个对象添加了属性,模块名为属性名,模块对象为属性值,或者说是键值对

build = function(module){ //根据模块对象构造模块导出对象,模块导出对象存储在modules这个对象数组内
var factory = module.factory;
module.exports = {}; //给当前模块加入了一个exports属性
delete module.factory; //删除了module的属性
factory(require,module.exports,module); //构建导出模块,module.exports是传出参数(实参,引用传递)
return module.exports;
}

require = function(id){ //根据模块名称/id请求模块对象,如果是第一次请求,就构建对象
if(!modules[id]){
throw "module " + id + " not found!";
}
return modules[id].factory?build(modules[id]):modules[id].exports;
}

define = function(id,factory){ //定义模块,模块名称、构建模块对象的工厂方法。
if(modules[id]){
throw "module " + id + " is exist!";
}
modules[id] = { //定义模块对象,左边的值为属性名,右边的值为传入的参数
id:id,
factory:factory
};
}
  })();

  //注册myphonegap模块
  define("myphonegap", function(require, exports, module){
console.info("create myphonegap module");
var myphonegap = {
Hello:function(name){
console.info("hello, "+name +" !");
}
};

module.exports = myphonegap;
  });

  //注册myphonegap/builder模块
define("myphonegap/builder", function(require, exports, module) {

var utils = require('myphonegap/utils');

function each(objects, func, context) {
for (var prop in objects) {
if (objects.hasOwnProperty(prop)) {
//console.info(prop);
func.apply(context, [objects[prop], prop]);
}
}
}

function clobber(obj, key, value) { obj[key] = value; // Getters can only be overridden by getters. if (obj[key] !== value) { utils.defineGetter(obj, key, function() { return 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); } }

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 myphonegap JS globals: ' + e + ' for key "' + key + '"');
}
});
}

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

module.exports = {
buildIntoButDoNotClobber: function(objects, target) {
include(target, objects, false, false);
},
buildIntoAndClobber: function(objects, target) {
include(target, objects, true, false);
},
buildIntoAndMerge: function(objects, target) {
include(target, objects, true, true);
},
recursiveMerge: recursiveMerge,
assignOrWrapInDeprecateGetter: assignOrWrapInDeprecateGetter
};

});

  define("myphonegap/channel",function(require,exports,module){
var utils = require("myphonegap/utils"),
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;

}, channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceReadyChannelsArray: [],
deviceReadyChannelsMap: {},
waitForInitialization: function(feature) {
if (feature) {
var c = channel[feature] || this.createSticky(feature);
this.deviceReadyChannelsMap[feature] = c;
this.deviceReadyChannelsArray.push(c);
}
},
initializationComplete: function(feature) {
var c = this.deviceReadyChannelsMap[feature];
if (c) {
c.fire();
}
},
join: function (h, c) {//join也就是将一组Channel连接起来,并注入一个在所有Channel上只执行一次的公共处理函数
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribe(f):i--;
}
if (!i) h();
}
};

function forceFunction(f){
if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
}
//给对象Channel的原型对象添加订阅函数,参数:函数对象、上下文、全局唯一ID
Channel.prototype.subscribe = function(f,c,g){
forceFunction(f);//确保f为函数

if(this.state==2){ //apply方法能劫持另外一个对象的方法,继承另外一个对象的属性;f里面的指针为c(Channel的实例)
f.apply(c||this,this.fireArgs);
}

var func = f,
guid = f.observer_guid;
if(typeof f == "object"){ func = utils.close(c,f);}

if(!guid){
guid = ''+ nextGuid++;
}
f.observer_guid = guid;
func.observer_guid = guid;

//防止重复添加
if(!this.handlers[guid]){
this.handlers[guid] = func;
this.numHandlers++;
if(this.numHandlers===1){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}

};
Channel.prototype.unsubscribe = function(f){
forceFunction(f);
var guid = f.observer_guid;
if(this.handlers[guid]){
delete this.handlers[guid];
this.numHandlers--;
if(numHandlers===0){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
//订阅一次
Channel.prototype.subscribeOnce = function(f,c,g){

};
//调用了在通道订阅的所有函数
Channel.prototype.fire = function(e){
//console.info('fire start:type/'+this.type + ' numHandlers/' +this.numHandlers);
var fail = false,
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);

// console.info(this.type+' enter func fire ');
}
if (this.state == 2 && this.numHandlers) {
this.numHandlers = 0;
this.handlers = {};
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};

channel.create('onDOMContentLoaded');
channel.create('onNativeReady');
channel.create('onDeviceReady');

channel.waitForInitialization('onMyphonegapReady');
channel.waitForInitialization('onMyphoneConnectionReady');

module.exports = channel;

//console.info('define myphonegap/channel completed');
});

  //注册myphonegap/common模块
  //配置对象,将公共模块组织起来
define("myphonegap/common",function(require,exports,module){

module.exports = {
defaults: {
myphonegap: {
path: 'myphonegap',
children: {
exec: {
path: 'myphonegap/exec'
}
}
},
Myphonegap: {
children: {
exec: {
path: 'myphonegap/exec'
}
}
}
},
clobbers: {
navigator: {
children: {
connection: {
path: 'myphonegap/plugin/network'
}
}
}
}
};

});

 
define("myphonegap/exec", function(require, exports, module) {
module.exports= function(name){
console.info("Exec native function " + name+" !");
};
});

  //注册myphonegap/platform模块
  define("myphonegap/platform", function(require, exports, module){
  });

  // 这里省略了其它插件的注册

   //注册myphonegap/utils模块
  define("myphonegap/utils", function(require, exports, module){
var 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);
}
}
};

utils.defineGetter = utils.defineGetterSetter;

utils.alert = function(msg) {
if (window.alert) {
window.alert(msg);
} else if (console && console.log) {
console.log(msg);
}
};

});;

(function (context) {
}(window));
  //所有模块注册完之后,再导入myphonegap至全局环境中
  //window.myphonegap = require('myphonegap');
//window.myphonegap.Hello("wen");

//测试channel模块
/*
var channel = require("myphonegap/channel");
channel.onNativeReady.subscribe(function(){
console.info("onNativeReady");
}
);
console.info("before native ready");
channel.onNativeReady.fire();
console.info("after native ready");
*/

//测试common模块与builder模块
var builder = require("myphonegap/builder");
var common = require("myphonegap/common");

builder.buildIntoButDoNotClobber( common.defaults,window);

window.myphonegap.Hello("Jack");
window.myphonegap.exec("myfunc");
})();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: