NEJ移动混合开发指南

概述

针对多平台的需求如android、iphone等,NEJ的移动混合应用接口为Web层提供了一致的解决方案,主要着眼于以下问题:

  • 模块重用:充分利用产品在个平台的通用模块、功能,避免重复开发
  • 灵活扩展:不同平台可以根据实际需求扩展功能
  • 快速开发:新平台的工程只需在通过在通过模块基础上做平台相关扩展即可完成开发

本文档主要介绍如何使用NEJ来进行移动平台混搭应用开发

结构

项目结构

项目结构

整套结构主要包含以下几个部分:

  • Web Common Project:
    通用WEB层工程,后续简称通用工程,主要负责产品在不同平台下的通过模块及功能的实现
  • Platform:
    各平台对应的工程,后续简称平台工程,主要负责产品在各自平台特有功能的实现,此类工程WEB层主要由两部分组成
    • common:通用模块以及功能,实际开发时切换到通用工程
    • platform:android/ios等平台特有模块以及功能,包括系统入口

单元结构

单元结构用来描述WEB层一套完整的源码结构,各工程WEB层由一个或者若干个单元结构组成,如上所示WEB下的common或者platform即为一个单元结构,Android平台的工程即由common和android两个单元结构组成。

单元结构

每个单元结构包含以下几部分:

  • style:
    定义工程相关结构的呈现样式,根据实际情况由一个或者多个css样式文件组成.
  • html:
    定义工程相关的结构,工程相关结构,分为两类:
    • 模板:此类文件仅用来实现工程相关的模板内容,模板结构规范见模板专题,模板其他相关详细信息可参阅MWF框架中模板相关的文档
    • 入口:此类文件作为系统对外的入口可供外部(Native程序或者浏览器)直接调用,入口文件规范请见模板系统专题
  • resource:工程用到的图片音频等素材资源
  • javascript:工程实现脚本源码

工程结构

工程结构即实际使用NEJ开发移动跨平台应用的目录结构,以下以Lofter移动IOS版初期开发为例

多平台公共代码common

  • base——公共基础接口目录
    • extend.js——扩展接口逻辑实现
    • global.js——全局命名空间定义
  • cache——公共缓存目录
  • ui——公共UI控件目录(以下为例子)
    • widget——公共UI控件逻辑实现
    • bar.js——顶栏/底栏控件基类
    • frame.js——模块框架基类
    • widget.js——控件基类
    • wmodule.js——模块(widget)基类
  • util——公共Util工具目录(以下为例子)
    • list——公共列表逻辑实现
      • list.js——列表基类
      • simplelist.js——简易列表基类
  • point——公共功能点逻辑实现
    • point.js——功能点基类

UI与Util部分请参考空间系统专题

IOS/Android平台相关platform

以ios为例

  • css——css文件目录
  • html——html文件目录
    • index.html——系统入口文件
    • widget.html——控件模板文件
  • images——图片资源目录
  • javascript——Js脚本目录
    • base——系统基础接口目录
      • config.js——系统相关配置
      • extend.js——系统扩展接口
  • module——模块逻辑实现目录
    • module.js ——模块基类文件
  • native——Native相关逻辑实现目录
  • ui——UI控件对象逻辑实现目录
    • bar——顶栏或底栏逻辑实现目录
    • bar.js——顶栏或底栏基类文件
    • frame.js——模块框架文件
    • wmodule.js——模块(widget)实现文件
    • widget.js——控件基类实现文件
  • util——Util工具对象逻辑实现目录
    • list——列表对象逻辑实现
    • point——功能点对象逻辑实现
    • animation.js——动画逻辑实现文件
  • system.js——系统入口文件

下面提到的开发步骤会以以上目录结构为基础

开发步骤

整个项目开发的步骤主要可以分为:1) 项目配置 ; 2) 模板文件初始化; 3) 顶栏底栏开发; 4) 模块开发; 5) 控件开发; 6) 调试。下面以案例依次进行,演示代码可以再这里下载。

项目配置

####1.命名空间配置:common/base/global.js NEJ.P('lofter.x'); // extend interface namespace NEJ.P('lofter.c'); // config namespace NEJ.P('lofter.g'); // global interface namespace NEJ.P('lofter.m'); // moudle namespace NEJ.P('lofter.d'); // cache namespace NEJ.P('lofter.t'); // util namespace NEJ.P('lofter.w'); // widget namespace

NEJ利用接口P来注册命名空间, 具体请参考NEJ的文档手册

####2.模块配置:ios/javascript/base/config.js (function(p){ // 配置模块标示名 p.CFGUMI = { 'home':'/home/', 'blog':'/blog/' }; // 设置产品编号 p.$PRODUCT = 'ios'; })(MWF.P('lofter.c'));

####3.模块注册及初始模块:ios/javascript/system.js (function(g,e,v,x,w,u){ // 模块异常事件处理 g.dispatcher._$setEvent('onerror',function(_error){ alert(error.message); }); // 模块动画全局实例 g.$animation = w.$$Animation.$allocate({parent:'ntes-lofter-module-container'}); // 解析模板 e._$parseTemplate('ntes-lofter-template'); // 注册模块 g.dispatcher.rule('root','../html/'); g.dispatcher.regist(x.umi('home'),'home.html'); g.dispatcher.regist(x.umi('blog'),'blog.html'); g.dispatcher.active(); // 初始模块 g.dispatcher.redirect('/blog/?a=1&b=2'); })(window ,MWF.P('mwf.e') ,MWF.P('mwf.v') ,MWF.P('lofter.x') ,MWF.P('lofter.w') ,MWF.P('lofter.u'));

模板文件初始化

####1.系统入口模板:ios/html/index.html

  <!-- @STYLE -->
  <link href="../css/style.css" type="text/css" rel="stylesheet"/>
  <link href="../css/widget.css" type="text/css" rel="stylesheet"/>
  <!-- /@STYLE -->
</head>
<body>
  <div class="g-body" id="ntes-lofter-module-container"></div>
  <div class="g-body" id="ntes-lofter-layer-container"></div>
  <div class="g-mask f-dn" id="ntes-lofter-mask-container"></div>
  <!-- @TEMPLATE -->
  <div id="ntes-lofter-template" style="display:none;">
    <textarea name="html" data-src="widget.html" data-version="20111220"></textarea>
  </div>
  <!-- /@TEMPLATE -->
  <!-- @SCRIPT -->
  <script src="../../../mwf/base/global.js"></script>
  <script src="../../../mwf/base/platform.js"></script>

####2.控件模板:http://ios/html/widget.html

####3.blog模块模板(其他模块相关模板类似):ios/html/blog.html

###顶栏/底栏开发 在webView混合应用中,一般顶栏/底栏是区别于其他模块独立开发

####1.顶栏:ios/javascript/ui/bar/heada.js /**

 * 顶部导航栏
 * @class   顶部导航栏
 * @extends mwf.ut._$$Event
 */
p._$$HeaderA = MWF.C();
__proHeaderA = p._$$HeaderA._$extend(p._$$IOSBar,true);
__supHeaderA = p._$$HeaderA._$supro;
/**
 * 获取控件的结构 
 * @return {String}
 */
__proHeaderA.__getXhtml = function(){
    return e._$getHtmlTemplate('jst-header-a');
};
/**
 * 初始化控件节点
 * @return {Void}
 */
__proHeaderA.__initNode = function(){
    var _list = e._$getByClassName(this.__body,
                                   x._$getHtmlTagKey());
    this.__nodes=[];
    this.__nodes.push(_list[0].children[0]);
    this.__nodes.push(_list[1].children[0]);
    this.__nodes.push(_list[2].children[0]);
};

####2.底栏:ios/javascript/ui/bar/foota.js var __proFooterA; /**

 * 底部导航栏
 * @class   底部导航栏
 * @extends mwf.ut._$$Event
 */
p._$$FooterA = MWF.C();
__proFooterA = p._$$FooterA._$extend(p._$$IOSBar,true);
__supFooterA = p._$$FooterA._$supro;
/**
 * 获取控件的结构 
 * @return {String}
 */
__proFooterA.__getXhtml = function(){
    return e._$getHtmlTemplate('jst-footer-a');
};
/**
 * 初始化控件节点
 * @return {Void}
 */
__proFooterA.__initNode = function(){
    var _list = e._$getByClassName(this.__body,x._$getHtmlTagKey()),
        l=_list.length;
    this.__nodes=[];
    for (var i=0;i<l;i++){
        this.__nodes.push(_list[i]);
    } 
};

###模块开发 模块主要是一些与业务逻辑相关的部分,这个每个产品根据自己的业务需求基本都有自己的模块产出,以案例中的blog.js为例:

/**
 * 日志模块对象
 * @class   日志模块对象
 * @extends mwf.ut._$$Module
 * @param  {Object} _options 可选配置参数,已处理参数列表如下所示
 * 
 */
p._$$WBlog = MWF.C();
__proWBlog = p._$$WBlog._$extend(w._$$IOSWModule,true);
__supWBlog = p._$$WBlog._$supro;
/**
 * 获得模块的html结构
 * @return {String}
 */
__proWBlog.__getXhtml = function(){
    return e._$getHtmlTemplate('jst-mdl-blog');
};
/**
 * 获得模块的节点
 * @return {Void}
 */
__proWBlog.__initNode = function(){
    var _ntmp = e._$getByClassName(this.__body,x._$getHtmlTagKey());
    this.__box = _ntmp[0];
    this.__btn = _ntmp[1];
};
/**
 * 日志模块重置函数
 * @param {Object} _options 重置参数
 * @return {Void}
 */
__proWBlog.__reset = function(_options){
    this.__scroller = ui._$$ScrollerY._$allocate({parent:this.__cbody})
    this.__lblog = t._$$COM_BlogList._$allocate({box:this.__box,btn:this.__btn});
};
/**
 * 日志模块的配置参数
 * @return {Object} 模块配置参数
 */
__proWBlog.__getConfig = function(){
    return {head:{clazz:w._$$HeaderA,option:{left:{value:'返回',action:dispatcher.redirect._$bind(dispatcher,'/home/?a=1&b=2')},
                                             right:{value:'发布',action:this.__onPost._$bind(this)}}},
            foot:{clazz:w._$$FooterA,option:{selIndex:1}}};
};
/**
 * 发布日志
 * @return {Void}
 */
__proWBlog.__onPost = function(){
    this.__wpost = w._$$WPost._$allocate();
};

###控件开发 控件是被模块调用的可重用小组件,以blog模块依赖的内容发布组件wcontent.js为例:

/**
 * 发布内容控件
 * @class   发布内容控件
 * @extends mb.w._$$COM_Widget
 */
p._$$WContent = MWF.C();
__proWContent = p._$$WContent._$extend(p._$$COM_Widget,true);
__supWContent = p._$$WContent._$supro;
/**
 * 获取控件的结构 
 * @return {String}
 */
__proWContent.__getXhtml = function(){
    return e._$getHtmlTemplate('jst-widget-content');
};
/**
 * 初始化控件节点
 * @return {Void}
 */
__proWContent.__initNode = function(){
    var _ntmp = e._$getByClassName(this.__body,x._$getHtmlTagKey());
    this.__title = _ntmp[0];
    this.__content = _ntmp[1];
};
/**
 * 获取数据
 * @return {Object}
 */
__proWContent._$getData = function(){
    return {title:this.__title.value,content:this.__content.value}
};

API注册接口介绍

NEJ的移动混合类的已注册接口为:

  1. navigator.capture: 多媒体捕捉相关相关接口,例如图片、视频、音频等资源的获取。
  2. navigator.notification: 通知相关接口,调用设备通知系统以听觉、视觉或触觉等形式告知用户
  3. navigator.file:文件相关接口,提供针对文件的选择、上传等操作的封装
  4. navigator.geo:地理位置相关接口,获取当前位置等信息

案例介绍

  1. LOFTER
© 1997-2013 Netease. All Rights Reserved.
comments powered by Disqus