加载中…
正文 字体大小:

结合项目讲解Mui--最快速的多端(Andriod、Ios、Web)发布利器

(2016-11-26 15:26:06)
分类: javascript/css
       使用Mui有一段时间了,今天和大家一起分享一下,欢迎大家指点。我从以下几点介绍Mui的使用:
  1. 效果视频使用andriod,ios,chrome演示
  2. 学习Mui:简单介绍学习Mui过程
  3. 功能介绍:介绍会员消费、消费查询、会员报表三个功能
  4. 技术介绍一(前端):我重新封装了打开界面的方式(现载、预载、预载父子模板页),兼容web
  5. 技术介绍二(前端):Mui结合 Vue.js、Require.js 使用Mvvm, Amd规范模块化开发, 提高开发效率
  6. 技术介绍三(后端):Asp.net WebApi 提供数据接口

 

1.效果视频使用andriod,ios,chrome演示

    安卓手机演示:

    


    苹果(mac模拟器演示):

 



    Web(Chrome浏览器演示):

 

 

2.学习Mui:Mui学习过程

          我的学习是从 mui初级入门教程(一)— 小白入手mui的学习路线 开始的,推荐初学者读一读,会很有帮助的。

          DCloud 可以给我们提供构建快速三个平台并且使用一套源码的平台,给前端开发人员提供了开发安卓、苹果app的便捷途径,如果你还不了解Css,Javascript,建议首先学习下Css,Javascript,对于JQuery和Zepto,官方不推荐使用,因为会降低体验效果,个人认为看个人喜好,如果能提高开发效率,可以使用,现在手机的性能越来越好,使用基本没有问题,学习之后有几个概念需要先了解下:Html5、Html5+、Mui和Webview
         Html5:相比Html4增加了canvas, video, audio这三个标签,使得浏览器可以进一步摆脱插件,如:flash;更多的JavaScript API使得浏览器可以完成以前无法完成或者必须要通过插件才能完成的工作,比如控制摄像头之类的,所以HTML5使得HTML 应用(就是单纯使用JavaScript/HTML/CSS技术制作的应用程序)成为可能。
         Html5+:Html5+是DCloud提供的html5强化引擎,可以把HTML5制作的页面打包为原生App,并且达到原生的功能和体验(基于原生Webview)。说白了就是原本只能原生APP才能实现的功能,现在可以通过html5+这个强化引擎作为桥梁,你通过调用plus.*方法实现,也就是你可以通过书写js代码实现android和ios两套的原生功能。html5+封装了一些最常用的功能,如:扫描二维码等功能,具体的可以参考Html5+ 规范
        Mui: Mui是基于html5+构建的框架,所以对于mui中提供的原生组件,其适用环境是app中的原生组件webview,并不能在浏览器中运行,同时封装了调用原生组件的方法,如:原生日期组件、基于原生webview的下拉更新、上拉加载组件、侧滑菜单等等,但是如果想用mui构建浏览器上的手机站点,只能使用mui中基于h5的组件。对于原生实现的组件,mui都有对应的h5实现(如果你不喜欢,可以找到很多类似的框架,如:jm,amaze等等,大同小异吧),所以开发者可以做合理的处理,在书写较少代码的情况下,实现多端

发布。有兴趣的朋友可参考多端发布开发指南

        WebView:Mui核心使用组件,我们知道每个页面都是基于浏览器的显示的,所以Mui对5+环境里的WebView进行了封装,使我们方便的操纵WebView,举个简单的例子,Webview可以想象为浏览器的一个标签页;我们知道打开网页加载需要时间,而Mui通过预加载技术,在使用该页面之前就用一个Webview把该页面加载好,但不显示,在需要使用时,直接打开这个Webview即可,而这个过程是在调用手机原生系统的资源,这样就达到了接近原生体验的目的,官网也提供了提升HTML5的性能体验系列教程,有兴趣的朋友可以参考提升HTML5的性能体验系列教程    

 

    了解了上面的概念,就可以通过下面的学习过程进行学习了
    
          CSS+Javascript基础-->

        学习Hbuilder开发工具(工欲善其事,必先利其器,很不错的开发工具)-->

        通过Hbuilder建立Hello World例子,边学边看文档,了解Mui-->

       了解Html5+ Api-->

        使用jquery,Mvvm框架、模块化等开发工具(不使用也是完全可以的)-->

       多端发布

 

3.功能介绍:介绍会员消费、消费查询、会员报表三个功能

       这里只介绍三个功能吧,都用到了Vue,Require

        会员消费:app扫描会员二维码,显示会员卡号,添加消费项目,填写费用信息,结算。       

        会员消费记录:上拉加载查询消费记录。

        会员报表:显示当日营业情况。

 

4.技术介绍一(前端):我重新封装了三种打开界面的方式(现载、预载、预载父子模板页),兼容web

        App最常用的功能当然是页面切换了,我参考文档总结了三种常用打开界面的方式(现载、预载、父子模板页),分享给大家,为了兼容非5+环境,打开窗口前用mui.os.plus进行环境判断,选择商品或者选择会员界面可以用弹层实现

        现载:就是通过mui.openWindow直接打开页面,但是,为了用户体验效果,当用户点击后,出现原生loading,页面先不出现,当页面加载完成再载入页面,代码如下:

OpenWindow: function(id, url, extras) {
   mui.openWindow({
    url: url,
    id: id,
    styles: {
     top: '0px', //新页面顶部位置
     bottom: '0px', //新页面底部位置
     width: '100%', //新页面宽度,默认为100%
     height: '100%', //新页面高度,默认为100%
    },
    extras: extras,
    createNew: false, //是否重复创建同样id的webview,默认为false:不重复创建,直接显示
    show: {
     autoShow: false, //页面loaded事件发生后自动显示,默认为true
     aniShow: 'pop-in', //页面显示动画,默认为”slide-in-right“;//当用slide-in-right这个的时候,miui8 openwindow后返回,主页面样式丢失
     duration: 200 //页面动画持续时间,Android平台默认100毫秒,iOS平台默认200毫秒;
    },
    waiting: {
     autoShow: true, //自动显示等待框,默认为true
     title: '', //等待对话框上显示的提示内容
     //   options: {
     //    width: '100%', //等待框背景区域宽度,默认根据内容自动计算合适宽度
     //    height: '100%', //等待框背景区域高度,默认根据内容自动计算合适高度
     //    round: "0px"
     //   }
    }
   });

   if(mui.os.plus) {
    var wv = plus.webview.getWebviewById(id);
    wv.addEventListener("loaded", function() {
     setTimeout(function() {
      plus.nativeUI.closeWaiting();
      wv.show("pop-in", 200);
     }, 20);

    });
   }
  }

 

      预载:当有的页面被频繁使用,使用预载的方式是最好的选择,因为可以在登录时预载好页面,在需要时显示即可,如会员消费界面。代码如下:

     预载窗口创建:appCommon.PreloadWindow("fastconsume.html", "../card/fastconsume.html");

     预载窗口打开:appCommon.OpenPreload(this.getAttribute("id"), this.getAttribute("href"))

    //预载普通窗口
  PreloadWindow: function(id, url) {
   mui.preload({
    url: url,
    id: id,
    styles: {
     render: 'always'
    }
   }); //加render也是为了,显示时使用pop-in,否则点第2次白屏,部分安卓6.0手机
  }

//预载单页面打开
  OpenPreload: function(id, url, options) {
   if(mui.os.plus) {
    if(!window.plus) return;
    var wv = plus.webview.getWebviewById(id);
    wv.show("pop-in"); //pop-in也有问题,先用fade-in ,如果用pop-in ,那么创建的参数为render: 'always',也可以
      } else {
    appCommon.OpenWindow(id, url);
   }
  }

     父子模板页:对于公共头部的页面,如:消费查询,积分查询等,只需要改变父页面的头部标题,子页面加载新的页面即可,父页面和子页面提前预载,这样还可以较少创建WebView的数量,代码如下:

     预载父页面和子页面:

  PreloadTemplate: function(head_id, head_url, options) {
   // 预加载模板父页面
   var parentWv = mui.preload({
    url: head_url,
    id: head_id,
    styles: {
     popGesture: "hide"
    }
   });
   // 预加载公用子页面
   var subWv = mui.preload({
    url: '',
    id: head_id + "_sub",
    styles: {
     top: appConfig.topoffset.Get() + 'px',
     bottom: '0px'
    }
   });
   // 将子页面填充到父页面
   if(parentWv) parentWv.append(subWv);
  }

打开模板页面:

OpenTemplate: function(head_id, title, sub_url, options) {
   if(mui.os.plus) {
    var parentWv = plus.webview.getWebviewById(head_id);
    mui.fire(parentWv, 'updateHeader', {
     title: title,
     href: sub_url,
     aniShow: 'pop-in',
     options: options
    });
    // 加载子页面地址

    var subWv = plus.webview.getWebviewById(head_id + "_sub");
    if(plus.io.convertLocalFileSystemURL(subWv.getURL()) == plus.io.convertLocalFileSystemURL(sub_url)) {

     subWv.show();
     if(options && options.sub_event) {
      mui.fire(subWv, options.sub_event, options.sub_data);
     }
    } else {
     subWv.loadURL(sub_url);
     // 子页面加载完成显示.,没有下面的也能显示android
     subWv.removeEventListener('loaded');
     subWv.addEventListener('loaded', function() {
      setTimeout(function() {
       subWv.show();
       if(options && options.sub_event) {
        mui.fire(subWv, options.sub_event, options.sub_data);
       }
      }, 50);
     });
    }
    // 显示模板父页面
    parentWv.show('pop-in', 200);
    //}

   } else {
    appCommon.OpenWindow(sub_url, sub_url); //非5+环境打开子页面
   }
  }

在父页面里监听更新标题事件

var titleElem = document.getElementByIdx_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x("title");
 var subWv = null,
  self = null;
 mui.plusReady(function() {
  self = plus.webview.currentWebview();
 });
 document.getElementByIdx_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x("menuSearch").addEventListener('tap', function() {
  if(subWv == null) {
   subWv = self.children()[0];
  }
  mui.fire(subWv, 'search', {});
 });
 // 自定义事件接收参数修改模板父页面头部
 window.addEventListener("updateHeader", function(e) {
  var title = e.detail.title;
  var href = e.detail.href;
  var aniShow = e.detail.aniShow;
  var options = e.detail.options;
  titleElem.innerHTML = title;
  titleElem.className = "mui-title mui-fadein";
  if(options) {
   document.getElementByIdx_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x("menuSearch").style.display = (options.showsearch ? "block" : "none");
  }
 });

 // 返回事件(隐藏模板父页面,并在窗体动画结束后,隐藏共用子页面)
 //var oldback = mui.back;
 mui.back = function() {
  self.hide('auto');
  setTimeout(function() {
   titleElem.className = 'mui-title mui-fadeout';
   titleElem.innerText = '';
   if(subWv == null) {
    subWv = self.children()[0];
   }
   subWv.hide("none");
  }, 350);
  //oldback();
 }

     对于非5+环境,有2种打开页面的方式,一种是直接跳转打开,mui.openWindow就可以了,一种是弹出层加载新页面,我们可以使用弹出层插件,也可以自己写,这里我自己写了一个,用于会员消费时,添加商品

  OpenIframe: function(url, options) {
   return myQLayer.Open(url, options);
  },
  CloseIframe: function(data, obj) {
   if(!obj) obj = top;
   obj.myQLayer.Close(data);
  }

其中myQLayer是我自己封装的弹层插件,大家可以换成自己的弹层插件

5.技术介绍二(前端):Mui结合 Vue.js、Require.js 使用Mvvm, Amd规范模块化开发, 提高开发效率

   Vue:  Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定组合的视图组件,我这里使用的是数据绑定,数据驱动,有兴趣的朋友可以访问Vue学习

   我这里,在会员消费界面,使用Vue,进行数据绑定,在数据修改时只需要修改Vue绑定的对象,而不是直接操作Dom对象,举个例子:

   var vm = new Vue({
  el: "#body",
  data: crud.listjson,
  methods: {
   DelItem: crud.DelItem,
   EditItem: crud.EditItem,
   ChangeItem: crud.ChangeItem
  }
 })

其中

  curd.listjson={

        cash:0,

        card:0,

        money:0 //应付金额=现金+会员刷卡

   }

 页面绑定:《input type="text" v-model="money" debounce="300" type="number" class="mui-input-clear mui-input" placeholder="请输入现金" number>

    当我们需要计算money时,只需要 curd.listjson.money =  curd.listjson.cash+curd.listjson.card,这时计算结果会自动响应到input ,而不需要 document.getElementByIdx_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x()访问三次dom,计算赋值了。大家还可以了解Mvvm框架,Vue就是其中一个不错的框架。

 

        再举个列表绑定的例子:

       我们经常通过ajax从服务器获取数据然后绑定到列表,一般这样写:

appCommon.AjaxJson(appConfig.api_url.Get() + appConfig.api.GetJiFenReport, {


page: ++crud.currPageIndex,

rows: appConfig.pager.pageSize,

filter: JSON.stringify(crud.GetSqlJson())

}, true, function(rst) {


if(rst.errcode && rst.errcode != "0") {

appCommon.;

appCommon.HideLoadingH5();

return;

}


var frag = document_createDocumentFragment();

mui.each(rst.rows, function(i, item) {

var li = document_createElement_x_x_x_x_x_x('li');

li.className = 'mui-table-view-cell';

var arr = [];

arr.push('《a class="mui-navigate-right"》');

arr.push(' 《div class="mui-row"》');

arr.push(' 《div class="mui-col-xs-6 " 》');

arr.push(item.integraldate);

arr.push(' 《/div》');

arr.push(' 《div class=" mui-col-xs-6 " 》');

arr.push(item.billno);

arr.push(' 《/div》');

arr.push(' 《div class="mui-col-xs-6 "》');

arr.push(item.card_no);

arr.push(' 《/div》');

arr.push(' 《div class="mui-col-xs-6 "》');

arr.push(item.name);

arr.push(' 《/div》');

arr.push(' 《/div》');

arr.push(' 《div class="mui-row"》');

arr.push('                   《div class="mui-col-xs-6 " 》');

arr.push('类型:' + item.type);

arr.push(' 《/div》');

arr.push(' 《div class="mui-col-xs-6 " 》');

arr.push(' 金额:' + item.integral);

arr.push(' 《/div》');

arr.push(' 《div class="mui-col-xs-6 "》');

arr.push('');

arr.push(' 《/div》');

arr.push(' 《div class="mui-col-xs-6 "》');

arr.push('');

arr.push(' 《/div》');

arr.push(' 《/div》');

arr.push(' 《/a》');

li.innerHTML = arr.join('');

frag.a(li);

});

if(frag.hasChildNodes()) document.getElementByIdx_x_x_x_x_x_x("list").a(frag);

appCommon.HideLoadingH5();

mui('#pullrefresh').pullRefresh().endPullupToRefresh(crud.currPageIndex * appConfig.pager.pageSize 》 rst.total);


}, function() {

appCommon.AjaxJsonError();

});

    

      而使用Vue后,代码变成这样,你会很高兴这样使用

     appCommon.AjaxJson(appConfig.api_url.Get() + appConfig.api.GetProductList, {

    page: ++crud.currPageIndex,
    rows: appConfig.pager.pageSize,
    filter: JSON.stringify(crud.GetSqlJson())
   }, true, function(rst) {

    if(rst.errcode && rst.errcode != "0") {
     appCommon.;
     appCommon.HideLoadingH5();
     return;
    }
    crud.listjson.arrItemInfo = crud.listjson.arrItemInfo.concat(rst.rows);//仅此一句即可
    appCommon.HideLoadingH5();
    mui('#pullrefresh').pullRefresh().endPullupToRefresh(crud.currPageIndex * appConfig.pager.pageSize > rst.total);

   }, function() {
    appCommon.AjaxJsonError();
   });

    当然页面需要设置绑定,也很简单

   《template v-for="item in listmx">//自动绑定的
      《li class="mui-table-view-cell">
       《div class="mui-slider-right mui-disabled">
        《a class="mui-btn mui-btn-yellow mui-icon mui-icon-compose" v-on:tap="EditItem(item,$event)">《/a>
        《a class="mui-btn mui-btn-red mui-icon mui-icon-close" v-on:tap="DelItem(item,$event)">《/a>

       《/div>
       《div class="mui-slider-handle ">
        《div class="mui-row">
         《span>{{item.productname}}({{item.product_id}})《/span>
        《/div>
        《div class="mui-row listmx-num">

         《div class="mui-col-xs-4">
          《span>{{item.amount}}《/span>
         《/div>
         《div class="mui-col-xs-4">
          《span>¥{{item.price}}《/span>
         《/div>
         《div class="mui-col-xs-4">
          《span class="listmx-je">¥{{item.money}}《/span>
         《/div>
        《/div>
       《/div>
      《/li>
     《/template>

     细心的朋友会发现 DelItem,EditItem事件,也很简单而且还能传递对象,非常方便,我就不贴代码了

 

    下面再来看看require.js

      我们开始写javascript ,都是随便定义函数,时常出现函数名冲突的情况,后来使用json格式的函数形式,情况好多了,但是当我们模块很多的时候,难免对象名又冲突,而且js文件越写越多,页面需要引用很多js,看着很乱,怎么办呢,require给我们带了福利,它是模块化开发的利器,遵循Amd规范,异步加载和预加载,相比Cmd规范的Sea.js(按需加载),我更偏爱require.js,至于两者的区别有兴趣的朋友可以进一步学习。

      这里,我列出简单的require的使用,有兴趣的朋友可以学习 require.js

     会员消费页面举例:mui也可以不使用require引入,因为它不是amd规范的格式,只要自定义的模块使用require定义即可

                《script src="../../js/mui.min.js"》《/script》

《script src="../../js/mui.picker.min.js"》《/script》

《script src="../../js/require.js" data-main="../../js/requireConfig"》《/script》

《script》

require(["view/card/fastconsume"]);

《/script》

   仅此而已,是不是很简洁,然后再看看requireConfig.js和fastconsume.js都是怎么定义的

   requireConfig.js

require.config({
     baseUrl: '../../js',
     paths:{
         'zepto' : 'lib/zepto.min'
        },
     shim:{
       }
});

fastconsume.js

define(["lib/vue.min","common/app", "common/myconvert", "common/myimg", "common/mylayer"], function(Vue) {

  //这里是业务处理

  ...

}

);

这里消费模块依赖于刚才提到的Vue,通用app函数,转换类,弹出层等等,依赖模块很清晰,代码也很简洁易懂。通用app函数还可以再依赖其他模块,如:

define(["common/common", "common/myloading", "common/mydate"], function() {

          //通用app函数实现

});

这里还依赖于loading模块,日期处理函数,最后在看一下require.js的函数加载顺序结合项目讲解Mui--最快速的多端(Andriod、Ios、Web)发布利器

最后,为了减少请求js的次数,也可以通过require提供的方法将多个js合并为1个js文件

 

6.技术介绍三(后端):Asp.net WebApi 提供数据接口,SwaggerUI提供测试接口

   后台我采用asp.net webapi 开发,项目如图:

结合项目讲解Mui--最快速的多端(Andriod、Ios、Web)发布利器
这里,重点提及SwaggerUI,因为给前段和后端开发人员提供了便利,经常遇到后端提供给前端的接口文档不完善,更新不及时,导致开发效率低下,所以通过这个工具建立了便利的桥梁,前端人员可以很方便的调试接口,如图:

结合项目讲解Mui--最快速的多端(Andriod、Ios、Web)发布利器

结合项目讲解Mui--最快速的多端(Andriod、Ios、Web)发布利器

   以上就是我做的一个Mui项目涉及的相关内容,不是很复杂,但是使用的知识点,还是挺多的,希望对初学者有所帮助。

 更多精彩内容请访问DCloud官网(http://www.dcloud.io/)

0

阅读 评论 收藏 转载 喜欢 打印举报
已投稿到:
  • 评论加载中,请稍候...
发评论

       

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 不良信息反馈 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有