
【交流分享】Dcloud开发APP心得系列之代码性能优化篇
算起来免费使用Dcloud开发APP也两年多了,踩过的坑,蹚过的水一波又一波,在此先感谢多位大大的无私帮助。
废话不多说,先来一炮代码精简、性能优化论,个人观点,不喜勿喷。
引:h5+ APP本身就已经多了层调用,所以如果代码再不进行优化精简那就真的承受不起了

能不引入其他js框架绝不引入
虽说文件是直接存储在手机本地,但是一个webview页面载入如此多的方法,需要用到的方法却少之又少。
比如很多人都喜欢直接引入jquery之类的框架,大把的方法用起来是方便了,冗余的代码却影响了APP的性能,所以建议的方法是自己扩展封装一些常用的方法,如removeClass、hasClass、getIndex、siblings、selectValue等,这些方法再配合mui.js框架基本能满足页面交互需求,还有满足不了的那就原生js处理(当然有现成js插件肯定优先,但首选原生js插件)。
//获取兄弟元素
app.siblings=function(el,childEl) {
var r = [],n;
if(!childEl){
n = el.parentNode.children;
}else{
n = el.parentNode.querySelectorAll(childEl);
}
for(var i =0,pl= n.length;i<pl;i ) {
if(n[i] !== el){ r.push(n[i]);}
}
return r;
}
//获取索引
app.getIndex=function(el){
var child=el.parentNode.children;
for(var i=0; i<child.length; i ){
if(el==child[i])
return i;
}
}
//是否包含某个class
app.hasClass = function(el,name) {
return (el.className.indexOf(name)>=0);
}
另外模块化、MVVM基本不太适用于开发h5 APP,因为都是单页面,一个页面引入的文件就那么三两个,再加入模块化反而显得复杂了,vuejs、ng之类的同样发挥不出他们的优势,反而增加冗余,页面的渲染引入轻量级js模板引擎拼接即可,如artTemplate(template.helper过滤器处理复杂数据)。
封装常用方法便于重复使用
APP里面用得最多的可能就是openWindow方法,如果每个地方都把这个方法copy一遍那整体增加的行数就多了,所以建议进行些简单的封装,如:
//打开新窗口
app.openWV=function(id,extras,url){
mui.openWindow({
id:id,
url:url||id,
extras:extras,
show: {
autoShow:true,
aniShow: 'pop-in',
duration:300
},
waiting: {
autoShow: false
}
});
}
app.openWV('order-detial.html',parmas);
看代码应该会发现url是可以不传的,因为这里是直接将页面的url作为了webview的id来使用,这样使用的好处是直接通过页面文件名称获取webview,清晰明了,为保证url无需在文件中跳来跳去建议所有文件都放在根目录,使用一定的命名规范整理,如user相关页面均为user-name.html,order相关 order-name.html,这样url基本就用不上,也无需考虑文件夹层级关系。
其他常用的方法还有timeFormat(时间格式化)、
app.timeFormat=function(time,params){
var d=time?new Date(time):new Date(),
year=d.getFullYear(),
month=d.getMonth() 1,
day=d.getDate(),
hours=d.getHours(),
minutes=d.getMinutes(),
seconds=d.getSeconds();
if(month<10) month='0' month;
if(day<10) day='0' day;
if(hours<10) hours='0' hours;
if(minutes<10) minutes='0' minutes;
if(seconds<10) seconds='0' seconds;
if(params){
return {year:year,month:month,day:day};
}else{
return (year '-' month '-' day ' ' hours ':' minutes ':' seconds);
}
}
parseDomImg(格式化详情页图片为占位图,用于实现图片延迟加载)、
app.parseDomImg=function(str){
var objE = document.createElement("div");
objE.innerHTML = str;
var imgs=objE.querySelectorAll('img');
for(var i=0; i<imgs.length; i ){
var img=imgs[i];
img.setAttribute('data-delay',img.src);
img.src='images/blank.gif';
img.setAttribute('height','100px');
img.setAttribute('width','100%');
}
return objE.innerHTML;
};
showLogin(params)(可在params对象中传递登录后回调需要的参数)、
app.showLogin=function(params){
app.openWV('login.html',{params:params});
//此处params为json对象,可在login页面判断isEmptyObject(params),进行回调执行登录前用户操作
};
isLogin(判断是否登录)、loadShare(加载分享功能)、reloadWV(请求失败加载重新载入提示及方法)等等等。。。
遵循能少操作DOM就尽量简化、能监听不批量绑定事件的原则
当我们频繁获取、操作DOM对象时消耗的资源肯定比操作内存中的变量要多得多,所以建议将频繁操作对象缓存到内存中,如某个结构内容需要每次交互去改变,那就把他缓存起来,var List = document.getElementById('List'); 后续通过List变量去操作,而不是每次获取。
for循环往DOM中插入新获取的数据列表那就更加不可取了,1w条数据就会插入1w次,这让DOM怎么“承受”,每次发生几何变化,页面都会进行重排、重绘,合理的方式是通过template或者其他方式拼接好需要新增的列表<li>,然后一次性插入DOM中。
List.insertAdjacentHTML('beforeend', template('initData', data)); //insertAdjacentHTML方法可以实现任意位置插入,用法自行百度
关于事件绑定,如果一块区域多个元素需要绑定同一个或者不同方法,那么千万不要for循环去给每个元素绑定方法或多个方法绑定,应使用on方法监听初始已存在父级元素的点击事件,一次监听解决千万次绑定。
mui('#List').on('tap', 'li', function() {
app.openWV(this.getAttribute('data-id'));
});
mui('#parentBox').on('tap', '.child', function() {
var type=this.id;
switch(type){
case 'del':
break;
case 'save':
break;
}
});
遵循能用css解决的问题不用js、图片处理的原则
能用一句addClass active解决整个列表多个元素的特殊处理的不要用for循环去重复操作,能添加一个class执行动画的不用js去处理。
能用css轻松写出来的效果不要使用图片或js实现,活用css3。
如切换至待付款状态订单需要提供checkbox批量支付,就只需要给列表容器添加isPay class显示出来即可;
合理利用预加载
虽然整个APP同时存在的webview是有限制的,但是频繁创建webview更是要消耗资源的,所以对于常用页面使用预加载处理,如详情页,初始预加载基本结构,每次需要打开时通过fire方法去重新loadData打开即可,如筛选页面,不同的页面可能需要展示不同的筛选条件、不同的筛选规则,那么也预加载他,每次打开时传递对应的handle参数判断规则,判断是否需要reset选中。
//操作页
var filterParam={};
mui.extend({
pageno: page,
pagesize: 20,
themeid: themeid,
userkey: userkey,
type: type,
isasc:isAsc
},filterParam) //拼接筛选页返回参数json
filterBtn.addEventListener('tap', function() {
showFilter(ws.id,isEmptyObject(filterParam),'isGoods');
}); // 打开筛选页面,传递当前页面id,初始filterParam为空reset选中,handle特殊处理规则
document.addEventListener('updateFilter',function(e){
filterParam=e.detail.filterParam;
reloadPage();
});
//筛选页
var checkArr={};
if(handle=='isGoods'){.....}
if(isReset){.....}
mui.fire(plus.webview.getWebviewById(openUrl),'updateFilter',{filterParam:checkArr});
mui.back();
经测试iPhone 6plus手机,重复操作打开商品详情---关闭--打开(商品详情包含大量的图片,非预加载),循环50次左右,问题就来了,商品详情页花屏了,渲染不出完整的页面了,只能重启应用。所以,常用页面建议使用预加载避免重复创建销毁。
合理处理图片延迟加载、合理使用分页加载、避免同时发起多个请求
移动端图片加载问题是很突出的,同时请求多个图片拖缓了重要内容的加载,所以延迟加载图片是非常必要的,当然控制图片大小也是基础,这里提供一个自己写的一个原生js的图片延迟加载插件,简单粗糙,但是实用,支持背景图片及图片元素渐入延迟加载、快速滑动停留超过N毫秒加载、指定div容器滑动加载、追加新增图片等。https://github.com/xielingxiao/delayimg
delayimg.init(); //初始化 (可混用图片元素、背景图片)
delayimg.render(); // 追加图片方法
超过20条数据(具体看数据量)的列表最好进行分页加载,同时请求大量数据不仅让服务器查询耗时,客户端处理也需要时间,用户体验极度不好,所以分屏分页加载数据很有必要。
提供加载提示、渐入式展现页面,打开新页面提供菊花等待或空页面放入mui-icon mui-spinner菊花元素,待ajax请求到数据后替换。如果页面初始存在一些错乱或者不美观的结构,那么建议初始状态给mui-content添加.transparent{ opacity: 0;}透明处理,配合css3动画,数据插入DOM后移除透明渐入,给用户展现流畅协调的页面。
避免同时发起多个请求,最常见的场景就是父子页面加载问题(多子页面),如果一次性创建所有子页面并且加载子页面的数据那速度肯定慢了,所以index为入口页面,先插入多个子页面空壳,除home第一个子页面自动请求数据外,其他子页面默认不加载内容,只添加加载监听方法,用户操作点击时再加载内容。对于需要每次切入都需要更新数据的页面再监听其他的更新数据方法。
//index页面
mui('.mui-bar-tab').on('tap','a',function(){
if(href!='home.html'){
mui.fire(plus.webview.getWebviewById(this.getAttribute('data-href')),'loadData');
}
});
//子页面
var flag=false;
document.addEventListener('loadData', function() {
if(!flag){
flag=true;
mui.init();
..... // 执行页面数据加载等方法
}
});
规范化你的代码
代码的规范在开发中是重中之重,有语意有规范的代码不仅方便自己,也不会困扰与你协作的‘同志’。
精简代码,能复用扩展的绝不滥用copy,统一的缩进、统一的命名规律、统一的编写方式,代码才能一目了然,具体的规范问题就不在这里详列了,可参考网上提供的。
好了,这篇就到此吧。。。暂时没想起更多,想起再补充吧。。。
已加载完....
更多分享预告:
各种‘坑’解决方案汇总篇(持续单个问题更新)
算起来免费使用Dcloud开发APP也两年多了,踩过的坑,蹚过的水一波又一波,在此先感谢多位大大的无私帮助。
废话不多说,先来一炮代码精简、性能优化论,个人观点,不喜勿喷。
引:h5+ APP本身就已经多了层调用,所以如果代码再不进行优化精简那就真的承受不起了
能不引入其他js框架绝不引入
虽说文件是直接存储在手机本地,但是一个webview页面载入如此多的方法,需要用到的方法却少之又少。
比如很多人都喜欢直接引入jquery之类的框架,大把的方法用起来是方便了,冗余的代码却影响了APP的性能,所以建议的方法是自己扩展封装一些常用的方法,如removeClass、hasClass、getIndex、siblings、selectValue等,这些方法再配合mui.js框架基本能满足页面交互需求,还有满足不了的那就原生js处理(当然有现成js插件肯定优先,但首选原生js插件)。
//获取兄弟元素
app.siblings=function(el,childEl) {
var r = [],n;
if(!childEl){
n = el.parentNode.children;
}else{
n = el.parentNode.querySelectorAll(childEl);
}
for(var i =0,pl= n.length;i<pl;i ) {
if(n[i] !== el){ r.push(n[i]);}
}
return r;
}
//获取索引
app.getIndex=function(el){
var child=el.parentNode.children;
for(var i=0; i<child.length; i ){
if(el==child[i])
return i;
}
}
//是否包含某个class
app.hasClass = function(el,name) {
return (el.className.indexOf(name)>=0);
}
另外模块化、MVVM基本不太适用于开发h5 APP,因为都是单页面,一个页面引入的文件就那么三两个,再加入模块化反而显得复杂了,vuejs、ng之类的同样发挥不出他们的优势,反而增加冗余,页面的渲染引入轻量级js模板引擎拼接即可,如artTemplate(template.helper过滤器处理复杂数据)。
封装常用方法便于重复使用
APP里面用得最多的可能就是openWindow方法,如果每个地方都把这个方法copy一遍那整体增加的行数就多了,所以建议进行些简单的封装,如:
//打开新窗口
app.openWV=function(id,extras,url){
mui.openWindow({
id:id,
url:url||id,
extras:extras,
show: {
autoShow:true,
aniShow: 'pop-in',
duration:300
},
waiting: {
autoShow: false
}
});
}
app.openWV('order-detial.html',parmas);
看代码应该会发现url是可以不传的,因为这里是直接将页面的url作为了webview的id来使用,这样使用的好处是直接通过页面文件名称获取webview,清晰明了,为保证url无需在文件中跳来跳去建议所有文件都放在根目录,使用一定的命名规范整理,如user相关页面均为user-name.html,order相关 order-name.html,这样url基本就用不上,也无需考虑文件夹层级关系。
其他常用的方法还有timeFormat(时间格式化)、
app.timeFormat=function(time,params){
var d=time?new Date(time):new Date(),
year=d.getFullYear(),
month=d.getMonth() 1,
day=d.getDate(),
hours=d.getHours(),
minutes=d.getMinutes(),
seconds=d.getSeconds();
if(month<10) month='0' month;
if(day<10) day='0' day;
if(hours<10) hours='0' hours;
if(minutes<10) minutes='0' minutes;
if(seconds<10) seconds='0' seconds;
if(params){
return {year:year,month:month,day:day};
}else{
return (year '-' month '-' day ' ' hours ':' minutes ':' seconds);
}
}
parseDomImg(格式化详情页图片为占位图,用于实现图片延迟加载)、
app.parseDomImg=function(str){
var objE = document.createElement("div");
objE.innerHTML = str;
var imgs=objE.querySelectorAll('img');
for(var i=0; i<imgs.length; i ){
var img=imgs[i];
img.setAttribute('data-delay',img.src);
img.src='images/blank.gif';
img.setAttribute('height','100px');
img.setAttribute('width','100%');
}
return objE.innerHTML;
};
showLogin(params)(可在params对象中传递登录后回调需要的参数)、
app.showLogin=function(params){
app.openWV('login.html',{params:params});
//此处params为json对象,可在login页面判断isEmptyObject(params),进行回调执行登录前用户操作
};
isLogin(判断是否登录)、loadShare(加载分享功能)、reloadWV(请求失败加载重新载入提示及方法)等等等。。。
遵循能少操作DOM就尽量简化、能监听不批量绑定事件的原则
当我们频繁获取、操作DOM对象时消耗的资源肯定比操作内存中的变量要多得多,所以建议将频繁操作对象缓存到内存中,如某个结构内容需要每次交互去改变,那就把他缓存起来,var List = document.getElementById('List'); 后续通过List变量去操作,而不是每次获取。
for循环往DOM中插入新获取的数据列表那就更加不可取了,1w条数据就会插入1w次,这让DOM怎么“承受”,每次发生几何变化,页面都会进行重排、重绘,合理的方式是通过template或者其他方式拼接好需要新增的列表<li>,然后一次性插入DOM中。
List.insertAdjacentHTML('beforeend', template('initData', data)); //insertAdjacentHTML方法可以实现任意位置插入,用法自行百度
关于事件绑定,如果一块区域多个元素需要绑定同一个或者不同方法,那么千万不要for循环去给每个元素绑定方法或多个方法绑定,应使用on方法监听初始已存在父级元素的点击事件,一次监听解决千万次绑定。
mui('#List').on('tap', 'li', function() {
app.openWV(this.getAttribute('data-id'));
});
mui('#parentBox').on('tap', '.child', function() {
var type=this.id;
switch(type){
case 'del':
break;
case 'save':
break;
}
});
遵循能用css解决的问题不用js、图片处理的原则
能用一句addClass active解决整个列表多个元素的特殊处理的不要用for循环去重复操作,能添加一个class执行动画的不用js去处理。
能用css轻松写出来的效果不要使用图片或js实现,活用css3。
如切换至待付款状态订单需要提供checkbox批量支付,就只需要给列表容器添加isPay class显示出来即可;
合理利用预加载
虽然整个APP同时存在的webview是有限制的,但是频繁创建webview更是要消耗资源的,所以对于常用页面使用预加载处理,如详情页,初始预加载基本结构,每次需要打开时通过fire方法去重新loadData打开即可,如筛选页面,不同的页面可能需要展示不同的筛选条件、不同的筛选规则,那么也预加载他,每次打开时传递对应的handle参数判断规则,判断是否需要reset选中。
//操作页
var filterParam={};
mui.extend({
pageno: page,
pagesize: 20,
themeid: themeid,
userkey: userkey,
type: type,
isasc:isAsc
},filterParam) //拼接筛选页返回参数json
filterBtn.addEventListener('tap', function() {
showFilter(ws.id,isEmptyObject(filterParam),'isGoods');
}); // 打开筛选页面,传递当前页面id,初始filterParam为空reset选中,handle特殊处理规则
document.addEventListener('updateFilter',function(e){
filterParam=e.detail.filterParam;
reloadPage();
});
//筛选页
var checkArr={};
if(handle=='isGoods'){.....}
if(isReset){.....}
mui.fire(plus.webview.getWebviewById(openUrl),'updateFilter',{filterParam:checkArr});
mui.back();
经测试iPhone 6plus手机,重复操作打开商品详情---关闭--打开(商品详情包含大量的图片,非预加载),循环50次左右,问题就来了,商品详情页花屏了,渲染不出完整的页面了,只能重启应用。所以,常用页面建议使用预加载避免重复创建销毁。
合理处理图片延迟加载、合理使用分页加载、避免同时发起多个请求
移动端图片加载问题是很突出的,同时请求多个图片拖缓了重要内容的加载,所以延迟加载图片是非常必要的,当然控制图片大小也是基础,这里提供一个自己写的一个原生js的图片延迟加载插件,简单粗糙,但是实用,支持背景图片及图片元素渐入延迟加载、快速滑动停留超过N毫秒加载、指定div容器滑动加载、追加新增图片等。https://github.com/xielingxiao/delayimg
delayimg.init(); //初始化 (可混用图片元素、背景图片)
delayimg.render(); // 追加图片方法
超过20条数据(具体看数据量)的列表最好进行分页加载,同时请求大量数据不仅让服务器查询耗时,客户端处理也需要时间,用户体验极度不好,所以分屏分页加载数据很有必要。
提供加载提示、渐入式展现页面,打开新页面提供菊花等待或空页面放入mui-icon mui-spinner菊花元素,待ajax请求到数据后替换。如果页面初始存在一些错乱或者不美观的结构,那么建议初始状态给mui-content添加.transparent{ opacity: 0;}透明处理,配合css3动画,数据插入DOM后移除透明渐入,给用户展现流畅协调的页面。
避免同时发起多个请求,最常见的场景就是父子页面加载问题(多子页面),如果一次性创建所有子页面并且加载子页面的数据那速度肯定慢了,所以index为入口页面,先插入多个子页面空壳,除home第一个子页面自动请求数据外,其他子页面默认不加载内容,只添加加载监听方法,用户操作点击时再加载内容。对于需要每次切入都需要更新数据的页面再监听其他的更新数据方法。
//index页面
mui('.mui-bar-tab').on('tap','a',function(){
if(href!='home.html'){
mui.fire(plus.webview.getWebviewById(this.getAttribute('data-href')),'loadData');
}
});
//子页面
var flag=false;
document.addEventListener('loadData', function() {
if(!flag){
flag=true;
mui.init();
..... // 执行页面数据加载等方法
}
});
规范化你的代码
代码的规范在开发中是重中之重,有语意有规范的代码不仅方便自己,也不会困扰与你协作的‘同志’。
精简代码,能复用扩展的绝不滥用copy,统一的缩进、统一的命名规律、统一的编写方式,代码才能一目了然,具体的规范问题就不在这里详列了,可参考网上提供的。
好了,这篇就到此吧。。。暂时没想起更多,想起再补充吧。。。
已加载完....
更多分享预告:
各种‘坑’解决方案汇总篇(持续单个问题更新)
收起阅读 »
Android打包时使用自己生成的密钥出现的小坑
我开发快捎项目时,在最后进行打包测试时,使用keytool生成了一个私钥,准备进行Android打包,但却报错说私钥密码有误,我设置了一个keypass和一个storepass, 两个密码都填入去试过了,都说是私钥密码有误,顿时蒙逼。在网上搜索了好久,才发现大多数是在ios打包时出现问题,直到看到这个问题及答案(http://ask.dcloud.net.cn/question/4507),才找到了解决方案。这里有必要提示一下,在生成密钥时,一定要注意:
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
我开发快捎项目时,在最后进行打包测试时,使用keytool生成了一个私钥,准备进行Android打包,但却报错说私钥密码有误,我设置了一个keypass和一个storepass, 两个密码都填入去试过了,都说是私钥密码有误,顿时蒙逼。在网上搜索了好久,才发现大多数是在ios打包时出现问题,直到看到这个问题及答案(http://ask.dcloud.net.cn/question/4507),才找到了解决方案。这里有必要提示一下,在生成密钥时,一定要注意:
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致
》》》 keypass 和storepass要一致,要一致

header 和 mui-scroll冲突,header渐变失效[ 已解决 ]
页面中同时使用header渐变和 mui-scroll,header渐变失效的解决方法:
mui('.mui-bar-transparent').transparent({
top: 0,
offset: 150,
duration: 16,
scrollby: document.querySelector('.mui-scroll-wrapper')
})
页面中同时使用header渐变和 mui-scroll,header渐变失效的解决方法:
mui('.mui-bar-transparent').transparent({
top: 0,
offset: 150,
duration: 16,
scrollby: document.querySelector('.mui-scroll-wrapper')
})

解决刷新列表
问题:
在列表上新增转到新页面,新增成功要返回列表,刷新列表,实现方法??
当前页面代码
mui.init({
beforeback: function(){
//获得你要前往页面的webview id
var Scanner = plus.webview.getWebviewById('lists');
//触发前往页面的自定义事件(例:AddNew),从而进行数据刷新
mui.fire(Scanner,'AddNew');
//返回true,继续页面关闭逻辑
return true;
}
});
前往页面lists.html代码
//添加AddNew自定义事件监听
window.addEventListener('AddNew',function(){
plus.webview.getWebviewById('刷新页面的ID lists').reload();
});
问题:
在列表上新增转到新页面,新增成功要返回列表,刷新列表,实现方法??
当前页面代码
mui.init({
beforeback: function(){
//获得你要前往页面的webview id
var Scanner = plus.webview.getWebviewById('lists');
//触发前往页面的自定义事件(例:AddNew),从而进行数据刷新
mui.fire(Scanner,'AddNew');
//返回true,继续页面关闭逻辑
return true;
}
});
前往页面lists.html代码
//添加AddNew自定义事件监听
window.addEventListener('AddNew',function(){
plus.webview.getWebviewById('刷新页面的ID lists').reload();
});

页面之间传值
从列表页传递商品id,页面跳转到详情页:
列表页:
1) 首先预加载详情页
mui.init({
preloadPages:[{
id:'info.html',
url:'info.html',
}]
});
2) 批量点击事件
mui("#Gallery").on('tap','.mui-table-view-cell a',function(){
//获取id
var id = this.getAttribute("id");
var infoPage = null;
//传值给详情页面,通知加载新数据
if(!infoPage){
infoPage = plus.webview.getWebviewById('info.html');
}
//触发详情页面的newsId事件
mui.fire(infoPage,'newsId',{
id:id
});
//打开详情页面
mui.openWindow({
id:'info.html'
});
})
详情页监听事件:
window.addEventListener('newsId',function(event){
var id = event.detail.id;
//得到传递来的产品id,访问相应接口, 进行相应的 ajax处理
})
从列表页传递商品id,页面跳转到详情页:
列表页:
1) 首先预加载详情页
mui.init({
preloadPages:[{
id:'info.html',
url:'info.html',
}]
});
2) 批量点击事件
mui("#Gallery").on('tap','.mui-table-view-cell a',function(){
//获取id
var id = this.getAttribute("id");
var infoPage = null;
//传值给详情页面,通知加载新数据
if(!infoPage){
infoPage = plus.webview.getWebviewById('info.html');
}
//触发详情页面的newsId事件
mui.fire(infoPage,'newsId',{
id:id
});
//打开详情页面
mui.openWindow({
id:'info.html'
});
})
详情页监听事件:
window.addEventListener('newsId',function(event){
var id = event.detail.id;
//得到传递来的产品id,访问相应接口, 进行相应的 ajax处理
})
收起阅读 »

复选框实现全选全不选功能,可用于购物车
(function (m){
document.getElementById('xuan').addEventListener('change',function(e){
var list = m('.cart_select');
if(e.target.checked){
list.each(function(){
var ele = this;
ele.checked = true;
})
}else{
list.each(function(){
var ele = this;
ele.checked = false;
})
}
})
})(mui);
(function (m){
document.getElementById('xuan').addEventListener('change',function(e){
var list = m('.cart_select');
if(e.target.checked){
list.each(function(){
var ele = this;
ele.checked = true;
})
}else{
list.each(function(){
var ele = this;
ele.checked = false;
})
}
})
})(mui);
收起阅读 »

getAttribute() 用法
getAttribute() 方法返回指定属性名的属性值。
例如:
document.getElementsByTagName("a")[0].getAttribute("target");
结果: _blank
实例:
mui(".mui-table-view").on('tap','.mui-table-view-cell',function(){
//获取id
var id = this.getAttribute("id");
//传值给详情页面,通知加载新数据
mui.fire(detail,'getDetail',{id:id});
//打开新闻详情
mui.openWindow({ id:'detail', url:'detail.html' });
})
getAttribute() 方法返回指定属性名的属性值。
例如:
document.getElementsByTagName("a")[0].getAttribute("target");
结果: _blank
实例:
mui(".mui-table-view").on('tap','.mui-table-view-cell',function(){
//获取id
var id = this.getAttribute("id");
//传值给详情页面,通知加载新数据
mui.fire(detail,'getDetail',{id:id});
//打开新闻详情
mui.openWindow({ id:'detail', url:'detail.html' });
})
收起阅读 »

解决mui跨境访问问题
浅析遇到的问题~~~
入门第一坑: 跨境问题
解决方法:
1)在PHP文件(接口文档)中设置文件头:
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With");
注: 官方中提供的ajax实例模板
mui.ajax('http://server-name/login.php',{
data:{
username:'username',
password:'password'
},
dataType:'json',//服务器返回json格式数据
type:'post',//HTTP请求类型
timeout:10000,//超时时间设置为10秒;
headers:{'Content-Type':'application/json'},
success:function(data){
//服务器返回响应,根据响应结果,分析是否登录成功;
...
},
error:function(xhr,type,errorThrown){
//异常处理;
console.log(type);
}
});
在具体操作时,要去掉 headers:{'Content-Type':'application/json'},
浅析遇到的问题~~~
入门第一坑: 跨境问题
解决方法:
1)在PHP文件(接口文档)中设置文件头:
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With");
注: 官方中提供的ajax实例模板
mui.ajax('http://server-name/login.php',{
data:{
username:'username',
password:'password'
},
dataType:'json',//服务器返回json格式数据
type:'post',//HTTP请求类型
timeout:10000,//超时时间设置为10秒;
headers:{'Content-Type':'application/json'},
success:function(data){
//服务器返回响应,根据响应结果,分析是否登录成功;
...
},
error:function(xhr,type,errorThrown){
//异常处理;
console.log(type);
}
});
在具体操作时,要去掉 headers:{'Content-Type':'application/json'},

iOS打开系统设置定位、通讯录、相册权限设置
设置URL Scheme即可,如打开无线可用:
plus.runtime.launchApplication({
action: 'App-Prefs:root=WIFI'
}, function(e) {
console.log(JSON.stringify(e));
});
跳转设置本应用权限较复杂,详情请加819954692
设置URL Scheme即可,如打开无线可用:
plus.runtime.launchApplication({
action: 'App-Prefs:root=WIFI'
}, function(e) {
console.log(JSON.stringify(e));
});
跳转设置本应用权限较复杂,详情请加819954692
收起阅读 »
Mac系统svn无法使用,svn版本过低,提示javaHL not available
发现Dcloud根本不考虑mac系统下开发者的死活啊,特别像我们这种老程序员只会用svn不会git,居然就不给支持了~
今天在mac的Hbuider里面准备svn一个项目下来,结果就是卡死,一直连不上。中途还报了一个错误:
立马我就反应过来,javaHL不对了,马上查询了svn接口如下:
当时就开始安装javaHL,mac系统安装javaHL比较麻烦,还要使用homebrew,所以先用mac终端安装了homebrew。
然后再用homebrew安装javaHL。(具体自行Google去)
安装完成之后,又蒙圈了。这种方式只能安装最新版的javaHL,我安装的时候是1.12.x,版本很高了。根据官方说的,对应的svn必须用1.12,
默认的Hbuider只有svn 1.6版本,看了论坛里面,居然有官方的人说内核不支持1.6以上。真是坑爹啊~
不服气的我决定看看通过eclipse市场能不能搞定:
居然还真的安装成功了~
然后svn倒入项目,一切顺利,OK!
发现Dcloud根本不考虑mac系统下开发者的死活啊,特别像我们这种老程序员只会用svn不会git,居然就不给支持了~
今天在mac的Hbuider里面准备svn一个项目下来,结果就是卡死,一直连不上。中途还报了一个错误:
立马我就反应过来,javaHL不对了,马上查询了svn接口如下:
当时就开始安装javaHL,mac系统安装javaHL比较麻烦,还要使用homebrew,所以先用mac终端安装了homebrew。
然后再用homebrew安装javaHL。(具体自行Google去)
安装完成之后,又蒙圈了。这种方式只能安装最新版的javaHL,我安装的时候是1.12.x,版本很高了。根据官方说的,对应的svn必须用1.12,
默认的Hbuider只有svn 1.6版本,看了论坛里面,居然有官方的人说内核不支持1.6以上。真是坑爹啊~
不服气的我决定看看通过eclipse市场能不能搞定:
居然还真的安装成功了~
然后svn倒入项目,一切顺利,OK!
收起阅读 »
matchUrls配置 - wap2app教程
我们知道,在sitemap.json中,每一个页面集合配置内容如下:
{
"webviewId": "page1",
"matchUrls": [],//page1页面链接匹配规则
"webviewParameter":{
//page1原生增强实现
},
"easyConfig":{
// 简化配置
}
}
如何实现将同类型页面组合为一个页面集合,共享同样的增强方案,其实就是通过 matchUrls节点实现的,本文重点讲解matchUrls配置。
matchUrls:页面链接匹配规则集合,类型为Array,满足数组中任一项匹配条件,即可使用当前页面配置,故值域为[matchurl1,matchurl2...],对应到json文件中即为如下格式:
"matchUrls": [
{
//url匹配规则1
}, {
//url匹配规则2
}
]
简单示例
一个最简单的匹配规则如下:
"matchUrls": [
{
"href": "http://ask.dcloud.net.cn/article/12731"
}
]
可以看出一条匹配规则,由2部分构成。
"href"是第一部分,即匹配依据;
"http://ask.dcloud.net.cn/article/12731"是第二部分,即匹配值表达式。这里表达的意思其实是全字符串精确匹配且值为http://ask.dcloud.net.cn/article/12731。
但wap2app框架提供了很多强大匹配工具,除了href还有很多匹配依据选择。以及匹配值表达式也不止简单的全字符串匹配。
下面分2个章节分别介绍。
选择匹配依据
除了完整url匹配外,wap2app框架还提供了hostname、pathname、search、hash等多种匹配依据选择。这些概念的来源是js里的window.location返回的Locaton对象,该对象包含hostname等属性,分别解析了一个完整的url。
规则说明如下:
- href:页面完整url,类型为String,可选,与window.location.href进行比较
- hostname:域名信息,类型为String,可选,与window.location.hostname进行比较
- pathname:路径信息,类型为String,可选,与window.location.pathname进行比较
- search:查询字符串,类型为String,可选,与window.location.search进行比较
- hash:锚点信息,类型为String,可选,与window.location.hash进行比较
例如url:“http://ask.dcloud.net.cn/article/12731?canshu=123#maodian1”,在浏览器打开本url,并在调试控制台敲window.location,可以看到如下结果:
- href="http://ask.dcloud.net.cn/article/12731"
- hostname="ask.dcloud.net.cn"
- pathname="/article/12731"
- search="?canshu=123"
- hash="#maodian1"
(String类型,最终值并不包含引号)
也就是说如下匹配规则,也能匹配到本url
"matchUrls": [
{
"pathname": "/article/12731"
}
]
设置匹配值表达式
选择好匹配依据后,第二步就是设置匹配值表达式。
上文中看到的简单字符串全文匹配,我们称之为精确匹配,除此之外,wap2app还提供了正则匹配和通配符匹配模式。
matchUrl的每个匹配依据(href、pathname...)均支持三种匹配模式:精确匹配、正则匹配、通配符匹配:
- 默认为精确匹配,即字符串完全相等才匹配,如"/article/12731.html"
- 正则匹配:以"REGEX:"为前缀,可简写为"R:",后跟正则字符串,如"REGEX:/\/article\/\d+.html$/ "
- 通配符匹配:以"WILDCARD:"为前缀,可简写为"W:",后跟通配符,如“WILDCARD:/article/*.html”
可以看出,匹配值表达式也是由2部分组成,以冒号为分割符。冒号前为匹配模式,冒号后为具体值。
而精确匹配时,可以省略不写匹配模式。
精确匹配模式
默认匹配模式,无前缀,仅当匹配项完全等于对比字符串时,才满足条件,例如:
"pathname":"/login"
如上规则,仅当页面的pathname完全等于'/login'时才会匹配,测试结果如下:
http://www.example.com/login //匹配成功
http://www.example.com/login/ //匹配失败,pathname为'/login/',最后多了一个'/'
正则匹配模式
正则表达式是一个强大的工具,可以匹配字符串中字符的各种组合模式。如资讯站点有1000篇资讯内容,其页面链接地址分别为:
http://m.example.com/detail/1.html
http://m.example.com/detail/2.html
....
http://m.example.com/detail/1000.html
通过正则表达式可以简单匹配所有资讯内容页面链接,在JavaScript语言中,如下两种方式创建的正则表达式均可实现需求:
/\/detail\/\d+\.html$/ //正则表达值字面量,/pattern/模式
或者
new RegExp("\/detail\/\d+\.html$") //调用RegExp对象的构造函数
但sitemap.json是一个json格式的文件,在json文件中,值域只能是字符串、数字等简单类型,不支持写任何Javascript代码,故如下两种创建正则表达式的方式均无效:
"hostname":/pattern/ //正则表达式字面量
"pathname":new RegExp(pattern) //调用RegExp对象的构造函数
为了解决这个问题,sitemap.json定义"REGEX:"或"R:"开头的字符串,表示后续字符串为正则匹配表达式。而Javascript字符串中部分字符有特殊含义,需要使用"\"反斜杠进行转义,而正则中需转义的字符也是通过"\"反斜杠进行转义(例如"\d"),此时就需要变成"\\"双反斜杠。
举个例子,若要匹配"/login"这个字符串
- JavaScript中正则表达式pattern的写法应该是"\/login",第一个"\"表示正则转义,将其后的特殊字符"/"转义为字面量"/",从而匹配"/login"字符串开头的"/";
- 而在sitemap.json中,则需要写成"REGEX:\\/login",第一个"\"表示字符串的转义,第二个"\"表示正则表达式中的"\",真实进行正则匹配时再执行转义功能
简单总结,就是正则表达式中的单反斜杠(\),在sitemap.json中全部需要变成双反斜杠(\\)
正则表达式的规则参考JavaScript指南 - 正则表达式。
通配符匹配模式
正则表达式虽然强大,但学习成本略高,阅读也不直观。wap2app提供了通配符匹配模式,这种简单的模式可以解决绝大部分的url匹配需求。
它类似一种简化的正则匹配,通过有限的特殊符号来实现批量适配的目的,以"WILDCARD:"为前缀,亦可简写为"W:";wap2app目前支持如下四种通配符:
- *:星号,匹配零个或多个任意字符,例如"WILDCARD:a*c",可以匹配"ac"、"abc"、"abdc";
- ?:问号,匹配任何单个字符,类似一个占位符,例如"WILDCARD:a?c",可以匹配"abc"、"adc",但不匹配"ac"、"abdc";
- [0+]:匹配任何数字,例如"WILDCARD:detail/[0+].html",可以匹配"detail/1.html"和"detail/1024.html",但不匹配"detail/a.html"
- [a|b|c]:匹配a或b或c,是一个字符串值域选择匹配,例如"WILDCARD:[green|red]_list.html"可以匹配"green_list.html"和"red_list.html"
注意:通配符无需转义。
如果觉得WILDCARD敲起来太长,可以简写为W。
下面给一个例子,一个网站的item目录下放置详情页面,有1.html到1000.html,而1.html又分页产生了1_2.html、1_3.html。那么matchUrl规则如下:
"matchUrls": [
{
"pathname": "W:/item/[0+]"
},
{
"pathname": "W:/item/[0+]_[0+]"
}
]
多条匹配规则同时设置的关系
matchUrl的属性值均为可选,但至少要求配置一个属性,wap2app仅对配置的matchUrl属性值进行比较,未配置的属性不参与比较;若在一个matchUrl对象中配置了多个属性值,则每个属性值均需满足匹配条件,页面链接才算匹配。
如下为一个示例:
{
"pathname":"/" //仅匹配pathname,且当pathname == "/"时生效
}
如上规则可以同时匹配如下两个页面链接:
http://m.example.com/ //匹配成功
http://touch.example.com/ //匹配成功
但如下匹配规则则不同:
{
"hostname":"m.example.com" //匹配hostname
"pathname":"/" //匹配pathname
}
如上规则,同样页面链接测试结果如下:
http://m.example.com/ //匹配成功
http://touch.example.com/ //匹配失败,因为该规则同时配置了`hostname`的匹配条件
验证匹配结果
写的匹配规则对不对?可以在HBuilder的真机运行时通过控制台日志确认。
注意:若要查看HBuilder运行日志,需要先在app.js中开启调试模式,如下:
App({
options: {
debug: true //设置为true,表示开启调试模式,可以在HBuilder控制台查看wap2app的运行日志
},
//其它代码
....
});
HBuilder里插上手机,对wap2app项目点运行-选择真机,会在手机上启动你编写的项目,并在HBuilder控制台打印出wap2app日志。
当你点击手机上一个页面时,是否被匹配到,可以在控制台看结果。日志截图如下:
上图中,打开的第一个页面链接,成功匹配"page1"的webview配置,故会显示
[W2A][page1]:webview.show
第二个页面链接未匹配任何webview配置,故以"__W2A_WEBVIEWID"+随机数作为新webview的id,如下:
[W2A][__W2A_WEBVIEW_ID_0.7765246680937707]:webview.show
matchUrl并不复杂,通过wap2app的强大工具,开发者可以快速把url和Webview的映射关系建立起来。
下一步就是学习如何利用webviewParameter来增强体验。
如果开发者的实际网站页面非常多,全配好再开始优化可能会推迟成就感到来,也可以先配几个页面,就真机运行起来。边做边体验效果。
我们知道,在sitemap.json中,每一个页面集合配置内容如下:
{
"webviewId": "page1",
"matchUrls": [],//page1页面链接匹配规则
"webviewParameter":{
//page1原生增强实现
},
"easyConfig":{
// 简化配置
}
}
如何实现将同类型页面组合为一个页面集合,共享同样的增强方案,其实就是通过 matchUrls节点实现的,本文重点讲解matchUrls配置。
matchUrls:页面链接匹配规则集合,类型为Array,满足数组中任一项匹配条件,即可使用当前页面配置,故值域为[matchurl1,matchurl2...],对应到json文件中即为如下格式:
"matchUrls": [
{
//url匹配规则1
}, {
//url匹配规则2
}
]
简单示例
一个最简单的匹配规则如下:
"matchUrls": [
{
"href": "http://ask.dcloud.net.cn/article/12731"
}
]
可以看出一条匹配规则,由2部分构成。
"href"是第一部分,即匹配依据;
"http://ask.dcloud.net.cn/article/12731"是第二部分,即匹配值表达式。这里表达的意思其实是全字符串精确匹配且值为http://ask.dcloud.net.cn/article/12731。
但wap2app框架提供了很多强大匹配工具,除了href还有很多匹配依据选择。以及匹配值表达式也不止简单的全字符串匹配。
下面分2个章节分别介绍。
选择匹配依据
除了完整url匹配外,wap2app框架还提供了hostname、pathname、search、hash等多种匹配依据选择。这些概念的来源是js里的window.location返回的Locaton对象,该对象包含hostname等属性,分别解析了一个完整的url。
规则说明如下:
- href:页面完整url,类型为String,可选,与window.location.href进行比较
- hostname:域名信息,类型为String,可选,与window.location.hostname进行比较
- pathname:路径信息,类型为String,可选,与window.location.pathname进行比较
- search:查询字符串,类型为String,可选,与window.location.search进行比较
- hash:锚点信息,类型为String,可选,与window.location.hash进行比较
例如url:“http://ask.dcloud.net.cn/article/12731?canshu=123#maodian1”,在浏览器打开本url,并在调试控制台敲window.location,可以看到如下结果:
- href="http://ask.dcloud.net.cn/article/12731"
- hostname="ask.dcloud.net.cn"
- pathname="/article/12731"
- search="?canshu=123"
- hash="#maodian1"
(String类型,最终值并不包含引号)
也就是说如下匹配规则,也能匹配到本url
"matchUrls": [
{
"pathname": "/article/12731"
}
]
设置匹配值表达式
选择好匹配依据后,第二步就是设置匹配值表达式。
上文中看到的简单字符串全文匹配,我们称之为精确匹配,除此之外,wap2app还提供了正则匹配和通配符匹配模式。
matchUrl的每个匹配依据(href、pathname...)均支持三种匹配模式:精确匹配、正则匹配、通配符匹配:
- 默认为精确匹配,即字符串完全相等才匹配,如"/article/12731.html"
- 正则匹配:以"REGEX:"为前缀,可简写为"R:",后跟正则字符串,如"REGEX:/\/article\/\d+.html$/ "
- 通配符匹配:以"WILDCARD:"为前缀,可简写为"W:",后跟通配符,如“WILDCARD:/article/*.html”
可以看出,匹配值表达式也是由2部分组成,以冒号为分割符。冒号前为匹配模式,冒号后为具体值。
而精确匹配时,可以省略不写匹配模式。
精确匹配模式
默认匹配模式,无前缀,仅当匹配项完全等于对比字符串时,才满足条件,例如:
"pathname":"/login"
如上规则,仅当页面的pathname完全等于'/login'时才会匹配,测试结果如下:
http://www.example.com/login //匹配成功
http://www.example.com/login/ //匹配失败,pathname为'/login/',最后多了一个'/'
正则匹配模式
正则表达式是一个强大的工具,可以匹配字符串中字符的各种组合模式。如资讯站点有1000篇资讯内容,其页面链接地址分别为:
http://m.example.com/detail/1.html
http://m.example.com/detail/2.html
....
http://m.example.com/detail/1000.html
通过正则表达式可以简单匹配所有资讯内容页面链接,在JavaScript语言中,如下两种方式创建的正则表达式均可实现需求:
/\/detail\/\d+\.html$/ //正则表达值字面量,/pattern/模式
或者
new RegExp("\/detail\/\d+\.html$") //调用RegExp对象的构造函数
但sitemap.json是一个json格式的文件,在json文件中,值域只能是字符串、数字等简单类型,不支持写任何Javascript代码,故如下两种创建正则表达式的方式均无效:
"hostname":/pattern/ //正则表达式字面量
"pathname":new RegExp(pattern) //调用RegExp对象的构造函数
为了解决这个问题,sitemap.json定义"REGEX:"或"R:"开头的字符串,表示后续字符串为正则匹配表达式。而Javascript字符串中部分字符有特殊含义,需要使用"\"反斜杠进行转义,而正则中需转义的字符也是通过"\"反斜杠进行转义(例如"\d"),此时就需要变成"\\"双反斜杠。
举个例子,若要匹配"/login"这个字符串
- JavaScript中正则表达式pattern的写法应该是"\/login",第一个"\"表示正则转义,将其后的特殊字符"/"转义为字面量"/",从而匹配"/login"字符串开头的"/";
- 而在sitemap.json中,则需要写成"REGEX:\\/login",第一个"\"表示字符串的转义,第二个"\"表示正则表达式中的"\",真实进行正则匹配时再执行转义功能
简单总结,就是正则表达式中的单反斜杠(\),在sitemap.json中全部需要变成双反斜杠(\\)
正则表达式的规则参考JavaScript指南 - 正则表达式。
通配符匹配模式
正则表达式虽然强大,但学习成本略高,阅读也不直观。wap2app提供了通配符匹配模式,这种简单的模式可以解决绝大部分的url匹配需求。
它类似一种简化的正则匹配,通过有限的特殊符号来实现批量适配的目的,以"WILDCARD:"为前缀,亦可简写为"W:";wap2app目前支持如下四种通配符:
- *:星号,匹配零个或多个任意字符,例如"WILDCARD:a*c",可以匹配"ac"、"abc"、"abdc";
- ?:问号,匹配任何单个字符,类似一个占位符,例如"WILDCARD:a?c",可以匹配"abc"、"adc",但不匹配"ac"、"abdc";
- [0+]:匹配任何数字,例如"WILDCARD:detail/[0+].html",可以匹配"detail/1.html"和"detail/1024.html",但不匹配"detail/a.html"
- [a|b|c]:匹配a或b或c,是一个字符串值域选择匹配,例如"WILDCARD:[green|red]_list.html"可以匹配"green_list.html"和"red_list.html"
注意:通配符无需转义。
如果觉得WILDCARD敲起来太长,可以简写为W。
下面给一个例子,一个网站的item目录下放置详情页面,有1.html到1000.html,而1.html又分页产生了1_2.html、1_3.html。那么matchUrl规则如下:
"matchUrls": [
{
"pathname": "W:/item/[0+]"
},
{
"pathname": "W:/item/[0+]_[0+]"
}
]
多条匹配规则同时设置的关系
matchUrl的属性值均为可选,但至少要求配置一个属性,wap2app仅对配置的matchUrl属性值进行比较,未配置的属性不参与比较;若在一个matchUrl对象中配置了多个属性值,则每个属性值均需满足匹配条件,页面链接才算匹配。
如下为一个示例:
{
"pathname":"/" //仅匹配pathname,且当pathname == "/"时生效
}
如上规则可以同时匹配如下两个页面链接:
http://m.example.com/ //匹配成功
http://touch.example.com/ //匹配成功
但如下匹配规则则不同:
{
"hostname":"m.example.com" //匹配hostname
"pathname":"/" //匹配pathname
}
如上规则,同样页面链接测试结果如下:
http://m.example.com/ //匹配成功
http://touch.example.com/ //匹配失败,因为该规则同时配置了`hostname`的匹配条件
验证匹配结果
写的匹配规则对不对?可以在HBuilder的真机运行时通过控制台日志确认。
注意:若要查看HBuilder运行日志,需要先在app.js中开启调试模式,如下:
App({
options: {
debug: true //设置为true,表示开启调试模式,可以在HBuilder控制台查看wap2app的运行日志
},
//其它代码
....
});
HBuilder里插上手机,对wap2app项目点运行-选择真机,会在手机上启动你编写的项目,并在HBuilder控制台打印出wap2app日志。
当你点击手机上一个页面时,是否被匹配到,可以在控制台看结果。日志截图如下:
上图中,打开的第一个页面链接,成功匹配"page1"的webview配置,故会显示
[W2A][page1]:webview.show
第二个页面链接未匹配任何webview配置,故以"__W2A_WEBVIEWID"+随机数作为新webview的id,如下:
[W2A][__W2A_WEBVIEW_ID_0.7765246680937707]:webview.show
matchUrl并不复杂,通过wap2app的强大工具,开发者可以快速把url和Webview的映射关系建立起来。
下一步就是学习如何利用webviewParameter来增强体验。
如果开发者的实际网站页面非常多,全配好再开始优化可能会推迟成就感到来,也可以先配几个页面,就真机运行起来。边做边体验效果。
收起阅读 »
h5+ app 视频教程汇总,最近更新 node.js 教程、电商实战教程
知识点 : HTML 5 开发教程【免费】
知识点 : mui 全套教程、 H5+ 沉浸式状态栏详解 、h5+ 地图api【免费】
实战 : HUI APP实战教程 - 仿《京东优选》电商项目 【收费】
知识点 : JavaScript 快速提高视频教程【免费】
实战: MUI、H5 APP 实战教程 - 仿《有道词典》【收费】
知识点 : HBuilder 8.0.1 APP开发 - 新功能全接触【免费】
知识点 : APP开发教程 - 启动动画【免费】
知识点 : APP开发实例教程 - 窗口切换【免费】
知识点 : 移动端图片剪裁、上传视频教程【免费】
知识点 : app开发教程-用户注册、登录【免费】
实战 : APP开发实战 - 新闻客户端【免费】
知识点 : HTML 5 开发教程【免费】
知识点 : mui 全套教程、 H5+ 沉浸式状态栏详解 、h5+ 地图api【免费】
实战 : HUI APP实战教程 - 仿《京东优选》电商项目 【收费】
知识点 : JavaScript 快速提高视频教程【免费】
实战: MUI、H5 APP 实战教程 - 仿《有道词典》【收费】
知识点 : HBuilder 8.0.1 APP开发 - 新功能全接触【免费】
知识点 : APP开发教程 - 启动动画【免费】
知识点 : APP开发实例教程 - 窗口切换【免费】
知识点 : 移动端图片剪裁、上传视频教程【免费】
知识点 : app开发教程-用户注册、登录【免费】
实战 : APP开发实战 - 新闻客户端【免费】
教程地址
http://www.hcoder.net/course/index/cate/4