HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

app内区域截图利用html2Canvals保存到手机

截屏

app内区域截图利用html2Canvals保存到手机

app内有时候需要区域内的截图保存dom为图像,我们可以使用html2Canvas将dom转换成base64图像字符串,然后再利用5+api保存至app
,通用代码如下:

function saveDomImage (html2Canvas, dom, fileName) {  //使用之前要引入 html2Canvas.js  
        if(mui.os.plus){  
            if (typeof html2Canvas == null)  
                throw Error("html2Canvas is not defined");  
            if (dom == null)  
                throw Error("saveDomImage param : dom is null");  
            if (fileName == null || fileName == "")  
                fileName = "untitled.png";  

            var getPixelRatio = function(context) {  
                var backingStore = context.backingStorePixelRatio ||  
                    context.webkitBackingStorePixelRatio || 1;  
                return (window.devicePixelRatio || 1) / backingStore;  
            };  

            var _canvas = document.createElement('canvas');  

            var ctx = _canvas.getContext('2d');  
            var ratio = getPixelRatio(ctx);  
            ctx.scale(ratio,ratio);  

            var w = dom.offsetWidth;  
            var h = dom.offsetHeight;  

            _canvas.width = w;  
            _canvas.height = h;  
            _canvas.style.width = w * ratio + 'px';  
            _canvas.style.height = h * ratio + "px";  

            html2Canvas(dom, {  
                allowTaint:true,  
                logging: false,  
                profile: true,  
                useCROS: true,  
                canvas : _canvas,  
                onrendered: function (canvas) {  
                    var dataUrl = canvas.toDataURL();  
                    var b = new plus.nativeObj.Bitmap('bitblmap');  

                    b.loadBase64Data(dataUrl, function () {  
                        /*这里一定要是_doc目录*/  
                        b.save("_doc/" + fileName, {overwrite: true}, function (object) {  
                            //保存到相册  
                            plus.gallery.save("_doc/" + fileName, function () {  
                                mui.toast("图片已保存到相册");  
                            }, function () {  
                                mui.toast("图片保存失败");  
                            });  
                        }, function () {  
                            mui.toast("图片保存失败");  
                        });  
                    }, function () {  
                        mui.toast("图片保存失败");  
                    });  
                }  
            });  
        }  
    }

至于为什么要是_doc目录,本人未查实原因,hbuilder测试包可以用 _www ,但是打出来的正式包只能用 _doc。
有好心人知晓请告诉本人。

这里html2Canvas使用版本是:0.5.0-beta3。

说明:
1 ,dom元素最好是文档流定位的,用absolute 和 fixed 截取出来的有偏移, 如果想解决这个问题,可以把这个dom复制到新的无偏移(top:0 ;left:0)的dom里面,再对新dom执行保存操作。
2,保存的图片肯定是没有原图清晰的,勉强也能接受,如果图上有二维码识别, 尽量把二维码做的识别度高点。
3,本功能适合保存dom合成的图片。屏幕全截屏,保存远端img图片,mui提供更简单的方式, 请参考其他文档。

html2Canvas: http://html2canvas.hertzen.com

继续阅读 »

app内区域截图利用html2Canvals保存到手机

app内有时候需要区域内的截图保存dom为图像,我们可以使用html2Canvas将dom转换成base64图像字符串,然后再利用5+api保存至app
,通用代码如下:

function saveDomImage (html2Canvas, dom, fileName) {  //使用之前要引入 html2Canvas.js  
        if(mui.os.plus){  
            if (typeof html2Canvas == null)  
                throw Error("html2Canvas is not defined");  
            if (dom == null)  
                throw Error("saveDomImage param : dom is null");  
            if (fileName == null || fileName == "")  
                fileName = "untitled.png";  

            var getPixelRatio = function(context) {  
                var backingStore = context.backingStorePixelRatio ||  
                    context.webkitBackingStorePixelRatio || 1;  
                return (window.devicePixelRatio || 1) / backingStore;  
            };  

            var _canvas = document.createElement('canvas');  

            var ctx = _canvas.getContext('2d');  
            var ratio = getPixelRatio(ctx);  
            ctx.scale(ratio,ratio);  

            var w = dom.offsetWidth;  
            var h = dom.offsetHeight;  

            _canvas.width = w;  
            _canvas.height = h;  
            _canvas.style.width = w * ratio + 'px';  
            _canvas.style.height = h * ratio + "px";  

            html2Canvas(dom, {  
                allowTaint:true,  
                logging: false,  
                profile: true,  
                useCROS: true,  
                canvas : _canvas,  
                onrendered: function (canvas) {  
                    var dataUrl = canvas.toDataURL();  
                    var b = new plus.nativeObj.Bitmap('bitblmap');  

                    b.loadBase64Data(dataUrl, function () {  
                        /*这里一定要是_doc目录*/  
                        b.save("_doc/" + fileName, {overwrite: true}, function (object) {  
                            //保存到相册  
                            plus.gallery.save("_doc/" + fileName, function () {  
                                mui.toast("图片已保存到相册");  
                            }, function () {  
                                mui.toast("图片保存失败");  
                            });  
                        }, function () {  
                            mui.toast("图片保存失败");  
                        });  
                    }, function () {  
                        mui.toast("图片保存失败");  
                    });  
                }  
            });  
        }  
    }

至于为什么要是_doc目录,本人未查实原因,hbuilder测试包可以用 _www ,但是打出来的正式包只能用 _doc。
有好心人知晓请告诉本人。

这里html2Canvas使用版本是:0.5.0-beta3。

说明:
1 ,dom元素最好是文档流定位的,用absolute 和 fixed 截取出来的有偏移, 如果想解决这个问题,可以把这个dom复制到新的无偏移(top:0 ;left:0)的dom里面,再对新dom执行保存操作。
2,保存的图片肯定是没有原图清晰的,勉强也能接受,如果图上有二维码识别, 尽量把二维码做的识别度高点。
3,本功能适合保存dom合成的图片。屏幕全截屏,保存远端img图片,mui提供更简单的方式, 请参考其他文档。

html2Canvas: http://html2canvas.hertzen.com

收起阅读 »

app打开应用市场

Appstore Native.JS

有时候应用内需要让用户跳转到app应用市场来评分, ios跳转到appstore , 安卓跳转到对应的应用市场
分享一段代码 ios和 安卓通用

  function  jumpToAppMarket (code) {  
        if (plus.os.name == "iOS") {  
            plus.runtime.openURL("itms-apps://" + 'itunes.apple.com/cn/app/wechat/id414478124?mt=8');  
        } else if (plus.os.name == "Android") {  
            var Uri = plus.android.importClass("android.net.Uri");  
            var uri = Uri.parse("market://details?id=" + 'com.tencent.mm' );  
            var Intent = plus.android.importClass('android.content.Intent');  
            var intent = new Intent(Intent.ACTION_VIEW, uri);  
            var main = plus.android.runtimeMainActivity();  
            main.startActivity(intent);  
        }  
    }

(这里都用微信示例,实际项目需要替换自己的iOS包地址和Android包名)
安卓 如果又多个应用市场,会弹出列表提供选择
ios 则直接进入appstore

继续阅读 »

有时候应用内需要让用户跳转到app应用市场来评分, ios跳转到appstore , 安卓跳转到对应的应用市场
分享一段代码 ios和 安卓通用

  function  jumpToAppMarket (code) {  
        if (plus.os.name == "iOS") {  
            plus.runtime.openURL("itms-apps://" + 'itunes.apple.com/cn/app/wechat/id414478124?mt=8');  
        } else if (plus.os.name == "Android") {  
            var Uri = plus.android.importClass("android.net.Uri");  
            var uri = Uri.parse("market://details?id=" + 'com.tencent.mm' );  
            var Intent = plus.android.importClass('android.content.Intent');  
            var intent = new Intent(Intent.ACTION_VIEW, uri);  
            var main = plus.android.runtimeMainActivity();  
            main.startActivity(intent);  
        }  
    }

(这里都用微信示例,实际项目需要替换自己的iOS包地址和Android包名)
安卓 如果又多个应用市场,会弹出列表提供选择
ios 则直接进入appstore

收起阅读 »

自己有个网站,使用mui做了移动端h5界面,供各位参考

mui

具体截图如下:











网站地址:http://www.pinhuba.com,移动端可以扫码体验:

继续阅读 »

具体截图如下:











网站地址:http://www.pinhuba.com,移动端可以扫码体验:

收起阅读 »

plus.zip.compressImage在IOS下“文件不存在”的解决方法

plus.zip.compressImage在IOS下“文件不存在“主要是由于plus.zip.compressImage的src、 dst文件目录错误引起的,将src、dst 参数前面加上:"file://"即可解决。

                     plus.io.resolveLocalFileSystemURL(p,function(entry){  
                        plus.zip.compressImage({  
                            src:"file://"+entry.fullPath,  
                            dst:"file://"+entry.fullPath,  
                            width:"800px",  //缩小到原来的一半  
                            quality:90,  
                            overwrite:true //覆盖文件  
                        },function(){  
                            plus.nativeUI.toast("照片压缩成功");  
                        },function(error) {  
                            plus.nativeUI.toast("照片压缩失败");  
                        });       
                    });
继续阅读 »

plus.zip.compressImage在IOS下“文件不存在“主要是由于plus.zip.compressImage的src、 dst文件目录错误引起的,将src、dst 参数前面加上:"file://"即可解决。

                     plus.io.resolveLocalFileSystemURL(p,function(entry){  
                        plus.zip.compressImage({  
                            src:"file://"+entry.fullPath,  
                            dst:"file://"+entry.fullPath,  
                            width:"800px",  //缩小到原来的一半  
                            quality:90,  
                            overwrite:true //覆盖文件  
                        },function(){  
                            plus.nativeUI.toast("照片压缩成功");  
                        },function(error) {  
                            plus.nativeUI.toast("照片压缩失败");  
                        });       
                    });
收起阅读 »

easyConfig配置 - wap2app教程

easyConfig wap2app

综述

sitemap中的webviewParameter配置其实是HTML5Plus规范中的Webview的参数设置。
除了配置Webview的参数外,开发者还会有其他业务需求。
在wap2app的设计中,复杂的扩展需求是通过app.js自行编程实现的。
但实际开发中,有很多较为通用的处理工作,为了减少开发者的重复工作量,官方在wap2app的sitemap下面增加了easyConfig节点,封装了一些常用操作,通过简单的配置,就可以完成原来需要很多编码才能完成的工作。

下面是目前支持的easyConfig配置。

back

用户按下back按键或点击顶部标题栏的返回箭头时,将触发wap2app的后退逻辑。
常规情况下,back按钮是关闭当前Webview并显示上一个Webview。
但我们有时需定制back的逻辑。
关于后退的定制,在back节点下进行配置。
目前back节点下有before和history这2个配置。

before

若页面有弹出层时,此时用户按下back按键,其实不应该把这个Webview关闭。
理想的效果是按下back时关闭弹出的浮层,当浮层关闭后再按back键,才是关闭Webview。
wap2app会智能处理弹出层的关闭,当用户点击back按键时,先探测当前页面是否存在弹出层,若存在则关闭弹出层,否则关闭当前页面。
但前端写法过于灵活,wap2app目前无法处理所有的弹出层情况。在实际业务中,弹出层的显示通常有两种模式:

  • 全屏弹出层(比如modal组件),通常会提供按钮关闭弹出层,比如取消按钮或关闭图标
  • 非全屏弹出层,通常也会有取消按钮,另外大多还会有半透明遮罩层,用户点击遮罩层,也可以关闭弹出层;

鉴于这些常见的弹出层关闭逻辑,wap2app提供了一种简化配置,可以配置弹出层选择器、取消按钮(或遮罩层)选择器、取消按钮(或遮罩层)点击事件,示例如下:

    "easyConfig":{  
        "back":{  
            "before":[  
                {  
                    "popupSelector":".popup", //弹出层选择器  
                    "closeSelector":".mask", //取消按钮选择器或遮罩层选择器  
                    "eventType":"click" //事件类型,默认为"click"  
                }  
            ]  
        }  
    }

如上示例,在用户点击back按键时,wap2app执行如下逻辑:

  • 若document.querySelector(".popup")元素存在且处于屏幕可视区域,则触发document.querySelector(".mask")元素的click事件,之后不再执行webview的后退逻辑
  • 若document.querySelector(".popup")元素不存在或在屏幕可视区域不可见,则继续执行webview的后退逻辑

before节点为Array类型,若当前页面有多个弹出层,则配置多个节点即可,例如:

    "easyConfig":{  
        "back":{  
            "before":[  
                {//第一个弹出层  
                    "popupSelector":".popup", //弹出层选择器  
                    "closeSelector":".mask", //取消按钮选择器或遮罩层选择器  
                    "eventType":"click" //事件类型,默认为"click"  
                },  
                {//第二个弹出层  
                    "popupSelector":".modal", //弹出层选择器  
                    "closeSelector":"#cancel_btn", //取消按钮选择器  
                    "eventType":"touchstart" //事件类型  
                }  
            ]  
        }  
    }

history

wap2app默认窗体逻辑是点击打开新Webview,点back关闭该Webview并漏出上一个Webview。但如果是同一组页面在一个Webview里跳转,此时不开新窗体,点back会走history.back。
但有时我们需要面对其他情况。比如
用户从首页点击链接,打开a.html,然后在a.html链接中通过相似推荐再次点击打开b.html

a和b都是一组页面,Webview配置也相同,matchUrls规则匹配的页面,会在同一个webview中打开,如下:

    {  
        "webviewId":"detail",  
        "matchUrls":[  
            {  
                "pathname":"a.html"  
            },  
            {  
                "pathname":"b.html"  
            }  
        ]  
    }

在wap2app中,相应执行逻辑如下:

  • 打开a.html时,匹配到webviewId为detail的页面配置,则创建id为detail的webview(简称为detailWebview),并加载a.html
  • 打开b.html时,再次匹配到webviewId为detail的页面配置,此时发现id为detail的webview已存在,即detailWebview,则不再创建新webview,而在detailWebview内执行location.href = b.html的逻辑,webview内部跳转到b.html

若此时用户按下back按键,则会在detailWebview内执行history.back(),返回到a.html,并不会直接关闭当前webview,返回到首页。

如果此时,希望用户按下back时,不执行history.back(),而是直接关闭webview,返回到上一层webview,则可以通过easyConfig的back下的设置history配置实现。

    {  
        "webviewId":"detail",  
        "matchUrls":[  
            {  
                "pathname":"a.html"  
            },  
            {  
                "pathname":"b.html"  
            }  
        ],  
        "easyConfig":{  
            "back":{  
                "history":false //不允许执行history.back  
            }  
        }  
    }

按以上代码配置,点back时,会关闭detail Webview,回到首页,即便是页面跳转到了b.html。

open

为了更好的窗体切换体验,wap2app实现了大量的封装;其中,webview自身相关的属性在webviewParameter节点中配置,打开新窗口的其它优化则在easyConfig->open节点下配置。

animation

wap2app在打开新窗口时,新窗口默认使用从右向左的移动动画;开发者也可以通过open->animation属性配置窗口动画类型及动画执行时间,代码示例如下:

{  
    "webviewId": "page1",  
    "matchUrls": [  
        //url匹配规则  
    ],  
    "easyConfig":{  
        "open":{  
            "animation":{//窗口切换动画配置  
                "type":"slide-in-right",//窗口动画类型  
                "duration":300//窗口动画执行时间  
            }  
        }  
    }  
}

目前支持的动画类型包括:

  • "none": 无动画效果,立即显示页面,无任何动画效果,此效果忽略动画时间参数。
  • "slide-in-right": 页面从屏幕右侧外向内横向滑动显示
  • "slide-in-left": 页面从屏幕左侧向右横向滑动显示
  • "slide-in-top": 页面从屏幕上侧向下竖向滑动显示
  • "slide-in-bottom": 页面从屏幕下侧向上竖向滑动显示
  • "fade-in": 页面从完全透明到不透明逐渐显示
  • "zoom-out": 页面在屏幕中间从小到大逐渐放大显示
  • "zoom-fade-out": 页面在屏幕中间从小到大逐渐放大并且从透明到不透明逐渐显示
  • "pop-in": 页面从屏幕右侧滑入显示,同时上一个页面带阴影效果从屏幕左侧滑出隐藏

waiting

wap2app在打开新窗口时,默认会显示waiting等待框,新webview的titleUpdate事件触发后,自动关闭waiting等待框。(titleUpdate是页面title节点解析触发的事件,详见HTML5Plus Webview的event)

等待框默认使用5+引擎的默认waiting框效果,开发者可以通过参数配置自定义效果,可配置参数包括:

  • title:可选,类型为String,等待对话框上显示的提示标题内容
  • options:可选,类型为Object,等待对话框的显示参数,设置等待框的宽、高、边距、背景等样式等;完整参数参考5+原生等待框参数

如下是一个示例,配置等待框为粉色背景、白色文字,等待提示语为“加载中”:

{  
    "webviewId": "page1",  
    "matchUrls": [  
        //url匹配规则  
    ],  
    "easyConfig":{  
        "open":{  
            "waiting":{  
                "title":"加载中...",  
                "options":{  
                    "background":"#EF5FA7",//粉色背景  
                    "color":"#ffffff"//白色文字  
                }  
            }  
        }  

    }  
}

运行结果如下:

如果M站页面响应较快,不需要显示waiting框,则可以通过配置waiting:false来禁用waiting等待框,例如:

{  
    "webviewId": "page1",  
    "matchUrls": [  
        //url匹配规则  
    ],  
    "easyConfig":{  
        "open":{  
            "waiting":false  // 禁用waiting,打开page1页面时,不显示waiting等待框  
        }  
    }  
}

Tips:
waiting常见于内容渲染慢的场景。如果内容渲染快,那么waiting圈反而碍事。
如果使用了NView模板,建议禁用waiting等待框。

quit

用户在应用首页按下back按键,会触发wap2app的退出逻辑,应用退出的相关配置在global->easyConfig->quit节点下配置。

Tips:quit节点仅支持在global下配置,不支持页面继承。

wap2app退出时,默认会弹出toast消息框,提示用户“再按一次返回键退出”,同时在toast信息中给出“反馈意见”的链接,效果图如下:

在toast消息显示时,用户可以点击“反馈意见”链接,反馈App的使用问题;开发者可以登录DCloud开发者中心查看用户反馈的问题,解决并持续改进。

wap2app可以通过配置,在应用退出的toast消息中不显示“反馈意见”的链接,方式如下:

"global": {  
    "webviewParameter": {  
        //webview相关通用配置  
    },  
    "easyConfig": {  
        "quit": {  
            "toast": {  
                "showFeedback": false //不显示“反馈意见”链接,默认为true  
            }  
        }  
    }  
}

FAQ

Q:退出时候的反馈如何去掉
A:参考上面文档中的说明,调整首页 easyConfig -> quit 的配置即可。

继续阅读 »

综述

sitemap中的webviewParameter配置其实是HTML5Plus规范中的Webview的参数设置。
除了配置Webview的参数外,开发者还会有其他业务需求。
在wap2app的设计中,复杂的扩展需求是通过app.js自行编程实现的。
但实际开发中,有很多较为通用的处理工作,为了减少开发者的重复工作量,官方在wap2app的sitemap下面增加了easyConfig节点,封装了一些常用操作,通过简单的配置,就可以完成原来需要很多编码才能完成的工作。

下面是目前支持的easyConfig配置。

back

用户按下back按键或点击顶部标题栏的返回箭头时,将触发wap2app的后退逻辑。
常规情况下,back按钮是关闭当前Webview并显示上一个Webview。
但我们有时需定制back的逻辑。
关于后退的定制,在back节点下进行配置。
目前back节点下有before和history这2个配置。

before

若页面有弹出层时,此时用户按下back按键,其实不应该把这个Webview关闭。
理想的效果是按下back时关闭弹出的浮层,当浮层关闭后再按back键,才是关闭Webview。
wap2app会智能处理弹出层的关闭,当用户点击back按键时,先探测当前页面是否存在弹出层,若存在则关闭弹出层,否则关闭当前页面。
但前端写法过于灵活,wap2app目前无法处理所有的弹出层情况。在实际业务中,弹出层的显示通常有两种模式:

  • 全屏弹出层(比如modal组件),通常会提供按钮关闭弹出层,比如取消按钮或关闭图标
  • 非全屏弹出层,通常也会有取消按钮,另外大多还会有半透明遮罩层,用户点击遮罩层,也可以关闭弹出层;

鉴于这些常见的弹出层关闭逻辑,wap2app提供了一种简化配置,可以配置弹出层选择器、取消按钮(或遮罩层)选择器、取消按钮(或遮罩层)点击事件,示例如下:

    "easyConfig":{  
        "back":{  
            "before":[  
                {  
                    "popupSelector":".popup", //弹出层选择器  
                    "closeSelector":".mask", //取消按钮选择器或遮罩层选择器  
                    "eventType":"click" //事件类型,默认为"click"  
                }  
            ]  
        }  
    }

如上示例,在用户点击back按键时,wap2app执行如下逻辑:

  • 若document.querySelector(".popup")元素存在且处于屏幕可视区域,则触发document.querySelector(".mask")元素的click事件,之后不再执行webview的后退逻辑
  • 若document.querySelector(".popup")元素不存在或在屏幕可视区域不可见,则继续执行webview的后退逻辑

before节点为Array类型,若当前页面有多个弹出层,则配置多个节点即可,例如:

    "easyConfig":{  
        "back":{  
            "before":[  
                {//第一个弹出层  
                    "popupSelector":".popup", //弹出层选择器  
                    "closeSelector":".mask", //取消按钮选择器或遮罩层选择器  
                    "eventType":"click" //事件类型,默认为"click"  
                },  
                {//第二个弹出层  
                    "popupSelector":".modal", //弹出层选择器  
                    "closeSelector":"#cancel_btn", //取消按钮选择器  
                    "eventType":"touchstart" //事件类型  
                }  
            ]  
        }  
    }

history

wap2app默认窗体逻辑是点击打开新Webview,点back关闭该Webview并漏出上一个Webview。但如果是同一组页面在一个Webview里跳转,此时不开新窗体,点back会走history.back。
但有时我们需要面对其他情况。比如
用户从首页点击链接,打开a.html,然后在a.html链接中通过相似推荐再次点击打开b.html

a和b都是一组页面,Webview配置也相同,matchUrls规则匹配的页面,会在同一个webview中打开,如下:

    {  
        "webviewId":"detail",  
        "matchUrls":[  
            {  
                "pathname":"a.html"  
            },  
            {  
                "pathname":"b.html"  
            }  
        ]  
    }

在wap2app中,相应执行逻辑如下:

  • 打开a.html时,匹配到webviewId为detail的页面配置,则创建id为detail的webview(简称为detailWebview),并加载a.html
  • 打开b.html时,再次匹配到webviewId为detail的页面配置,此时发现id为detail的webview已存在,即detailWebview,则不再创建新webview,而在detailWebview内执行location.href = b.html的逻辑,webview内部跳转到b.html

若此时用户按下back按键,则会在detailWebview内执行history.back(),返回到a.html,并不会直接关闭当前webview,返回到首页。

如果此时,希望用户按下back时,不执行history.back(),而是直接关闭webview,返回到上一层webview,则可以通过easyConfig的back下的设置history配置实现。

    {  
        "webviewId":"detail",  
        "matchUrls":[  
            {  
                "pathname":"a.html"  
            },  
            {  
                "pathname":"b.html"  
            }  
        ],  
        "easyConfig":{  
            "back":{  
                "history":false //不允许执行history.back  
            }  
        }  
    }

按以上代码配置,点back时,会关闭detail Webview,回到首页,即便是页面跳转到了b.html。

open

为了更好的窗体切换体验,wap2app实现了大量的封装;其中,webview自身相关的属性在webviewParameter节点中配置,打开新窗口的其它优化则在easyConfig->open节点下配置。

animation

wap2app在打开新窗口时,新窗口默认使用从右向左的移动动画;开发者也可以通过open->animation属性配置窗口动画类型及动画执行时间,代码示例如下:

{  
    "webviewId": "page1",  
    "matchUrls": [  
        //url匹配规则  
    ],  
    "easyConfig":{  
        "open":{  
            "animation":{//窗口切换动画配置  
                "type":"slide-in-right",//窗口动画类型  
                "duration":300//窗口动画执行时间  
            }  
        }  
    }  
}

目前支持的动画类型包括:

  • "none": 无动画效果,立即显示页面,无任何动画效果,此效果忽略动画时间参数。
  • "slide-in-right": 页面从屏幕右侧外向内横向滑动显示
  • "slide-in-left": 页面从屏幕左侧向右横向滑动显示
  • "slide-in-top": 页面从屏幕上侧向下竖向滑动显示
  • "slide-in-bottom": 页面从屏幕下侧向上竖向滑动显示
  • "fade-in": 页面从完全透明到不透明逐渐显示
  • "zoom-out": 页面在屏幕中间从小到大逐渐放大显示
  • "zoom-fade-out": 页面在屏幕中间从小到大逐渐放大并且从透明到不透明逐渐显示
  • "pop-in": 页面从屏幕右侧滑入显示,同时上一个页面带阴影效果从屏幕左侧滑出隐藏

waiting

wap2app在打开新窗口时,默认会显示waiting等待框,新webview的titleUpdate事件触发后,自动关闭waiting等待框。(titleUpdate是页面title节点解析触发的事件,详见HTML5Plus Webview的event)

等待框默认使用5+引擎的默认waiting框效果,开发者可以通过参数配置自定义效果,可配置参数包括:

  • title:可选,类型为String,等待对话框上显示的提示标题内容
  • options:可选,类型为Object,等待对话框的显示参数,设置等待框的宽、高、边距、背景等样式等;完整参数参考5+原生等待框参数

如下是一个示例,配置等待框为粉色背景、白色文字,等待提示语为“加载中”:

{  
    "webviewId": "page1",  
    "matchUrls": [  
        //url匹配规则  
    ],  
    "easyConfig":{  
        "open":{  
            "waiting":{  
                "title":"加载中...",  
                "options":{  
                    "background":"#EF5FA7",//粉色背景  
                    "color":"#ffffff"//白色文字  
                }  
            }  
        }  

    }  
}

运行结果如下:

如果M站页面响应较快,不需要显示waiting框,则可以通过配置waiting:false来禁用waiting等待框,例如:

{  
    "webviewId": "page1",  
    "matchUrls": [  
        //url匹配规则  
    ],  
    "easyConfig":{  
        "open":{  
            "waiting":false  // 禁用waiting,打开page1页面时,不显示waiting等待框  
        }  
    }  
}

Tips:
waiting常见于内容渲染慢的场景。如果内容渲染快,那么waiting圈反而碍事。
如果使用了NView模板,建议禁用waiting等待框。

quit

用户在应用首页按下back按键,会触发wap2app的退出逻辑,应用退出的相关配置在global->easyConfig->quit节点下配置。

Tips:quit节点仅支持在global下配置,不支持页面继承。

wap2app退出时,默认会弹出toast消息框,提示用户“再按一次返回键退出”,同时在toast信息中给出“反馈意见”的链接,效果图如下:

在toast消息显示时,用户可以点击“反馈意见”链接,反馈App的使用问题;开发者可以登录DCloud开发者中心查看用户反馈的问题,解决并持续改进。

wap2app可以通过配置,在应用退出的toast消息中不显示“反馈意见”的链接,方式如下:

"global": {  
    "webviewParameter": {  
        //webview相关通用配置  
    },  
    "easyConfig": {  
        "quit": {  
            "toast": {  
                "showFeedback": false //不显示“反馈意见”链接,默认为true  
            }  
        }  
    }  
}

FAQ

Q:退出时候的反馈如何去掉
A:参考上面文档中的说明,调整首页 easyConfig -> quit 的配置即可。

收起阅读 »

webviewParameter配置 - wap2app教程

教程 webviewParameter wap2app

综述

wap2app使用webview打开每个页面链接,而每个webview相关的属性设置,在对应页面的webviewParameter节点下配置,如下TODO位置:

    {  
        "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            //TODO webview相关属性在这里配置  
        },  
        "easyConfig":{}  
    }

webviewParameter节点下可配置如下子节点:

  • titleNView:原生标题栏样式配置
  • statusbar:系统状态栏样式配置
  • subNViews:NView模板配置
  • pullToRefresh:下拉刷新配置
  • appendCss:向服务端页面插入的css代码
  • appendJs:向服务端页面插入的JavaScript代码
  • tabBar:选项卡切换效果优化,目前仅支持首页底部选项卡

接下来对每个节点进行详细介绍。

titleNView

titleNView用于设置原生标题栏样式, 顾名思义,它是一个原生的NView,并且用途是顶部title。
titleNView是由原生渲染引擎渲染,而不是Webview渲染的。在窗体动画切换时可以瞬间出现,不会让页面整屏白屏。

titleNView的具体教程参考titleNView配置
如果你是第一次学习,阅读到此处时,直接点击上述链接继续学习。完毕后回到本文继续。

statusbar

系统状态栏位于手机屏幕顶端,如下图所示:

通过statusbar可以配置系统状态栏的样式,可配置的参数包括:

  • style:状态栏前景色(文字颜色)
  • background:状态栏背景色,默认应该和原生导航条背景色一致;

style

状态栏的前景色,仅支持浅色、深色两种选项,可取值如下:

  • dark:深色前景色样式(即状态栏前景文字为黑色)
  • light:浅色前景色样式(即状态栏前景文字为白色)

关于两种前景色的效果对比,参考下图所示:

注意:系统状态栏的前景色目前是App全局配置,仅可在global节点中配置,不支持在不同webview中进行覆盖。

background

若webview使用了原生导航条,则系统状态栏默认使用和原生导航条一样的背景色,此时无需再单独配置background参数;换言之,仅需在未使用原生导航条的webview中,配置background参数。

在wap2app项目中,除首页外,其它页面默认均启用了原生导航条;因此,首页需要配置background,其它页面,均无需配置。

对于首页而言,也分两种情况:

  • 首页未启用原生导航(默认):必需配置background参数,同时建议将background配置为global->webviewParameter->titleNView->backgroundColor颜色值,保持App整体风格一致;
  • 首页启用原生导航:无需配置,同时建议将statusbar节点配置为false

首页未启用原生导航条的配置示例:

{  
    "webviewId": "__W2A__m.example.com",//首页  
    "matchUrls": [  
        //url配置规则  
    ],  
    "webviewParameter": {  
        "titleNView": false,//首页默认不使用原生导航  
        "statusbar": {  
            //状态条背景色,  
            //首页不使用原生导航条,颜色值建议和global->webviewParameter->titleNView->backgroundColor颜色值保持一致  
            "background": "#f7f7f7"  
        }  
    }  
}

首页启用原生导航条配置示例:

{  
    "webviewId": "__W2A__m.example.com",//首页  
    "matchUrls": [  
        //url配置规则  
    ],  
    "webviewParameter": {  
        "titleNView": {//首页启用原生导航条  
            "backgroundColor": "#FF00FF",//导航栏背景色  
            "titleColor": "#00ffff",//标题颜色为白色  
            "titleText": "wap2app首页"  
        },  
        //若首页启用了原生导航条,则建议将首页的statusbar配置为false(或直接删除statusbar节点),这样状态条可以和原生导航条背景色保持一致;  
        "statusbar": false   

    }  
}

注意:非首页的其它页面(默认使用原生导航条),如果禁用了原生导航条,则也需要在对应页面的webviewParameter节点下配置statusbar->background,配置方式同首页逻辑。

subNViews

subNView也是原生渲染区域,它位于titleNView下方,从属于webview。
类似的技术有点像flash,内嵌在网页中,但实际是由原生引擎渲染的。
但subNView的加载速度快多了,在窗体切换动画时,subNView可以瞬间出现,而Webview则仍在慢慢经历:联网下载页面代码、构建Dom、渲染Dom等过程。因此subNView是加快web页面渲染的一大利器。如下为一个使用subNView增强后的商品详情页示例:

这样页面在一个Webview里创建了2个subNView,在页面加载动画时,subNView可以直接渲染出来,而后wap页面才慢慢渲染完毕。

DCloud为NView提供了一种前端式模板写法,可在sitemap.json中配置NView模板文件路径,编写一个模板。如下为一个示例:

    {  
        "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "subNViews": "page1.nview" //NView模板路径  
        }  
    }

关于subNView及NView模板更详细的介绍,参考:NView模板教程
NView略复杂,它不像sitemap仅仅是配置,它涉及真正编程。
不使用subNView也可以完成一个wap2app项目的基础版。
你可以一口气学完NView,也可以先做一个wap2app的基础版,然后再引入NView进一步强化体验。

appendCss

wap2app需要M站进行少量改造,比如因为使用了原生标题栏titleNView,就不再需要DIV标题栏,M站服务端应根据navigator.userAgent判断是流应用环境下,不生成(或隐藏)原来的DIV标题栏,教程参考去除M站DOM元素

这部分改造工作,建议由服务端改造,这样可以保证css尽早生效;如果服务端不方便修改,wap2app还支持通过appendCss节点向M站页面插入css代码,从而实现类似的效果。
也就是通过appendCss,可以在wap2app中直接影响wap站的渲染样式。这些css由wap2app引擎在运行时动态插入到原wap站的页面dom中,和在wap页面里写同样的css效果类似。

wap2app支持两种方式配置appendCss节点:

  • 直接编写css代码
  • 配置css文件路径

wap2app框架会智能识别配置的是代码还是文件路径,然后将对应的css代码,插入到对应的M站页面中。

直接配置css代码

如果要插入的css代码比较简单,则可以直接在sitemap.json文件中appendCss节点后编写css代码,如下:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendCss":"#header{display:none}" //插入css代码  
        },  
        "easyConfig":{}  
    }

配置css文件路径

如果要插入的css代码比较复杂,则在sitemap.json的字符串中编写就不太方便(比如:缺少css语法提示),此时建议新建一个css文件编写需插入的css代码,然后将css文件相对路径配置到appendCss节点下即可,例如:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendCss":"page1.append.css" //需插入的css文件路径  
        },  
        "easyConfig":{}  
    }

建议将需插入的css文件放在项目根目录下,然后遵循%webviewId%.append.css这样的命名格式;如此命名的话,无需在sitemap.json中配置appendCss节点,wap2app框架会自动扫描该文件;如上示例中css文件名为“page1.append.css”,遵循我们建议的命名格式,因此可简化配置为:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            //无需配置appendCss节点,wap2app会自动扫描"page1.append.css"文件并插入  
        },  
        "easyConfig":{}  
    }

当然,开发者也可以按照自己的习惯,自定义css文件名,此时记得配置appendCss节点即可,例如:

     {  

       "webviewId":"page2",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendCss":"pages/page2/append.css" //自定义的css文件路径  
        },  
        "easyConfig":{}  
    }

首页css插入

因wap2app启动机制限制,目前仅支持通过css文件的方式向M站首页插入css代码;又因M站首页的webviewId就是当前应用的appid,故在HBuilder中新建wap2app项目时,会自动新建一个%appid%.append.css的文件,且名字不可修改。
开发者在%appid%.append.css里编写的样式,会直接插入到wap站首页里运行,从而影响首页的渲染效果。

appendJs

wap2app项目中,M站也会涉及部分JavaScript逻辑的改造,比如在wap2app环境下,将wap分享替换为原生分享,可以分享给微信好友、朋友圈,或者调起系统分享组件,分享到短信、邮件等。这部分增强逻辑改造,尽量由M站改造完成。如果不方便修改M站,wap2app还支持通过appendJs节点向M站页面插入JavaScript代码,从而实现类似的效果,如下为一个示例:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendJs":"page1.append.js" //需插入的JavaScript文件路径  
        },  
        "easyConfig":{}  
    }

类似appendCss插入文件的命名机制,插入的js文件建议遵循%webviewId%.append.js规范,wap2app默认会扫描项目根目录下名字为%webviewId%.append.js的文件;若覆盖该命名规范,则无需配置appendJs节点;如上示例中js文件名为“page1.append.js”,遵循我们建议的命名格式,因此可简化配置为:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            //无需配置appendJs节点,wap2app会自动扫描"page1.append.js"文件并插入  
        },  
        "easyConfig":{}  
    }

当然,开发者也可以按照自己的习惯,自定义js文件名,此时记得配置appendJs节点即可,例如:

     {  

       "webviewId":"page2",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendJs":"pages/page2/append.js" //自定义的js文件路径  
        },  
        "easyConfig":{}  
    }

注意:通过appendJs节点配置的JavaScript代码,虽然是在HBuilder客户端编写,但最终会运行在M站的页面中,可以操作M站的DOM结构、执行M站的业务逻辑。

appendJs插入的js,运行时机是晚于原页面的其他js的,所以开发者应尽量在原wap页面中直接修改js,减少appendJs的使用。

pullToRefresh

我们知道页面里基于div方式的下拉刷新是不流畅的,wap2app框架提供了原生的下拉刷新。
配置pullToRefresh -> support 节点就可以启用原生下拉刷新,如下:

    "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "pullToRefresh":{  
                "support":true //启用下拉刷新  
            }  
        },  
        "easyConfig":{}  
    }

当然这种下拉刷新本质是刷新一个网页,效果是一个圆圈从titleNView位置被拉下来,拉到一定程度会自动刷新页面。
一般用于list页面的配置。
如果开发者想要下拉刷新用于更新list的dom而不是刷新网页,需要在app.js中进行强化编程。

tabBar

tabBar可以对首页底部选项卡进行优化,实现选项卡切换时,仅变化内容区,选项卡区域不变。详细配置参考选项卡切换优化教程

继续阅读 »

综述

wap2app使用webview打开每个页面链接,而每个webview相关的属性设置,在对应页面的webviewParameter节点下配置,如下TODO位置:

    {  
        "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            //TODO webview相关属性在这里配置  
        },  
        "easyConfig":{}  
    }

webviewParameter节点下可配置如下子节点:

  • titleNView:原生标题栏样式配置
  • statusbar:系统状态栏样式配置
  • subNViews:NView模板配置
  • pullToRefresh:下拉刷新配置
  • appendCss:向服务端页面插入的css代码
  • appendJs:向服务端页面插入的JavaScript代码
  • tabBar:选项卡切换效果优化,目前仅支持首页底部选项卡

接下来对每个节点进行详细介绍。

titleNView

titleNView用于设置原生标题栏样式, 顾名思义,它是一个原生的NView,并且用途是顶部title。
titleNView是由原生渲染引擎渲染,而不是Webview渲染的。在窗体动画切换时可以瞬间出现,不会让页面整屏白屏。

titleNView的具体教程参考titleNView配置
如果你是第一次学习,阅读到此处时,直接点击上述链接继续学习。完毕后回到本文继续。

statusbar

系统状态栏位于手机屏幕顶端,如下图所示:

通过statusbar可以配置系统状态栏的样式,可配置的参数包括:

  • style:状态栏前景色(文字颜色)
  • background:状态栏背景色,默认应该和原生导航条背景色一致;

style

状态栏的前景色,仅支持浅色、深色两种选项,可取值如下:

  • dark:深色前景色样式(即状态栏前景文字为黑色)
  • light:浅色前景色样式(即状态栏前景文字为白色)

关于两种前景色的效果对比,参考下图所示:

注意:系统状态栏的前景色目前是App全局配置,仅可在global节点中配置,不支持在不同webview中进行覆盖。

background

若webview使用了原生导航条,则系统状态栏默认使用和原生导航条一样的背景色,此时无需再单独配置background参数;换言之,仅需在未使用原生导航条的webview中,配置background参数。

在wap2app项目中,除首页外,其它页面默认均启用了原生导航条;因此,首页需要配置background,其它页面,均无需配置。

对于首页而言,也分两种情况:

  • 首页未启用原生导航(默认):必需配置background参数,同时建议将background配置为global->webviewParameter->titleNView->backgroundColor颜色值,保持App整体风格一致;
  • 首页启用原生导航:无需配置,同时建议将statusbar节点配置为false

首页未启用原生导航条的配置示例:

{  
    "webviewId": "__W2A__m.example.com",//首页  
    "matchUrls": [  
        //url配置规则  
    ],  
    "webviewParameter": {  
        "titleNView": false,//首页默认不使用原生导航  
        "statusbar": {  
            //状态条背景色,  
            //首页不使用原生导航条,颜色值建议和global->webviewParameter->titleNView->backgroundColor颜色值保持一致  
            "background": "#f7f7f7"  
        }  
    }  
}

首页启用原生导航条配置示例:

{  
    "webviewId": "__W2A__m.example.com",//首页  
    "matchUrls": [  
        //url配置规则  
    ],  
    "webviewParameter": {  
        "titleNView": {//首页启用原生导航条  
            "backgroundColor": "#FF00FF",//导航栏背景色  
            "titleColor": "#00ffff",//标题颜色为白色  
            "titleText": "wap2app首页"  
        },  
        //若首页启用了原生导航条,则建议将首页的statusbar配置为false(或直接删除statusbar节点),这样状态条可以和原生导航条背景色保持一致;  
        "statusbar": false   

    }  
}

注意:非首页的其它页面(默认使用原生导航条),如果禁用了原生导航条,则也需要在对应页面的webviewParameter节点下配置statusbar->background,配置方式同首页逻辑。

subNViews

subNView也是原生渲染区域,它位于titleNView下方,从属于webview。
类似的技术有点像flash,内嵌在网页中,但实际是由原生引擎渲染的。
但subNView的加载速度快多了,在窗体切换动画时,subNView可以瞬间出现,而Webview则仍在慢慢经历:联网下载页面代码、构建Dom、渲染Dom等过程。因此subNView是加快web页面渲染的一大利器。如下为一个使用subNView增强后的商品详情页示例:

这样页面在一个Webview里创建了2个subNView,在页面加载动画时,subNView可以直接渲染出来,而后wap页面才慢慢渲染完毕。

DCloud为NView提供了一种前端式模板写法,可在sitemap.json中配置NView模板文件路径,编写一个模板。如下为一个示例:

    {  
        "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "subNViews": "page1.nview" //NView模板路径  
        }  
    }

关于subNView及NView模板更详细的介绍,参考:NView模板教程
NView略复杂,它不像sitemap仅仅是配置,它涉及真正编程。
不使用subNView也可以完成一个wap2app项目的基础版。
你可以一口气学完NView,也可以先做一个wap2app的基础版,然后再引入NView进一步强化体验。

appendCss

wap2app需要M站进行少量改造,比如因为使用了原生标题栏titleNView,就不再需要DIV标题栏,M站服务端应根据navigator.userAgent判断是流应用环境下,不生成(或隐藏)原来的DIV标题栏,教程参考去除M站DOM元素

这部分改造工作,建议由服务端改造,这样可以保证css尽早生效;如果服务端不方便修改,wap2app还支持通过appendCss节点向M站页面插入css代码,从而实现类似的效果。
也就是通过appendCss,可以在wap2app中直接影响wap站的渲染样式。这些css由wap2app引擎在运行时动态插入到原wap站的页面dom中,和在wap页面里写同样的css效果类似。

wap2app支持两种方式配置appendCss节点:

  • 直接编写css代码
  • 配置css文件路径

wap2app框架会智能识别配置的是代码还是文件路径,然后将对应的css代码,插入到对应的M站页面中。

直接配置css代码

如果要插入的css代码比较简单,则可以直接在sitemap.json文件中appendCss节点后编写css代码,如下:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendCss":"#header{display:none}" //插入css代码  
        },  
        "easyConfig":{}  
    }

配置css文件路径

如果要插入的css代码比较复杂,则在sitemap.json的字符串中编写就不太方便(比如:缺少css语法提示),此时建议新建一个css文件编写需插入的css代码,然后将css文件相对路径配置到appendCss节点下即可,例如:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendCss":"page1.append.css" //需插入的css文件路径  
        },  
        "easyConfig":{}  
    }

建议将需插入的css文件放在项目根目录下,然后遵循%webviewId%.append.css这样的命名格式;如此命名的话,无需在sitemap.json中配置appendCss节点,wap2app框架会自动扫描该文件;如上示例中css文件名为“page1.append.css”,遵循我们建议的命名格式,因此可简化配置为:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            //无需配置appendCss节点,wap2app会自动扫描"page1.append.css"文件并插入  
        },  
        "easyConfig":{}  
    }

当然,开发者也可以按照自己的习惯,自定义css文件名,此时记得配置appendCss节点即可,例如:

     {  

       "webviewId":"page2",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendCss":"pages/page2/append.css" //自定义的css文件路径  
        },  
        "easyConfig":{}  
    }

首页css插入

因wap2app启动机制限制,目前仅支持通过css文件的方式向M站首页插入css代码;又因M站首页的webviewId就是当前应用的appid,故在HBuilder中新建wap2app项目时,会自动新建一个%appid%.append.css的文件,且名字不可修改。
开发者在%appid%.append.css里编写的样式,会直接插入到wap站首页里运行,从而影响首页的渲染效果。

appendJs

wap2app项目中,M站也会涉及部分JavaScript逻辑的改造,比如在wap2app环境下,将wap分享替换为原生分享,可以分享给微信好友、朋友圈,或者调起系统分享组件,分享到短信、邮件等。这部分增强逻辑改造,尽量由M站改造完成。如果不方便修改M站,wap2app还支持通过appendJs节点向M站页面插入JavaScript代码,从而实现类似的效果,如下为一个示例:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendJs":"page1.append.js" //需插入的JavaScript文件路径  
        },  
        "easyConfig":{}  
    }

类似appendCss插入文件的命名机制,插入的js文件建议遵循%webviewId%.append.js规范,wap2app默认会扫描项目根目录下名字为%webviewId%.append.js的文件;若覆盖该命名规范,则无需配置appendJs节点;如上示例中js文件名为“page1.append.js”,遵循我们建议的命名格式,因此可简化配置为:

     {  

       "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            //无需配置appendJs节点,wap2app会自动扫描"page1.append.js"文件并插入  
        },  
        "easyConfig":{}  
    }

当然,开发者也可以按照自己的习惯,自定义js文件名,此时记得配置appendJs节点即可,例如:

     {  

       "webviewId":"page2",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "appendJs":"pages/page2/append.js" //自定义的js文件路径  
        },  
        "easyConfig":{}  
    }

注意:通过appendJs节点配置的JavaScript代码,虽然是在HBuilder客户端编写,但最终会运行在M站的页面中,可以操作M站的DOM结构、执行M站的业务逻辑。

appendJs插入的js,运行时机是晚于原页面的其他js的,所以开发者应尽量在原wap页面中直接修改js,减少appendJs的使用。

pullToRefresh

我们知道页面里基于div方式的下拉刷新是不流畅的,wap2app框架提供了原生的下拉刷新。
配置pullToRefresh -> support 节点就可以启用原生下拉刷新,如下:

    "webviewId":"page1",  
        "matchUrls": [  
            //url 匹配规则  
        ],  
        "webviewParameter": {  
            "pullToRefresh":{  
                "support":true //启用下拉刷新  
            }  
        },  
        "easyConfig":{}  
    }

当然这种下拉刷新本质是刷新一个网页,效果是一个圆圈从titleNView位置被拉下来,拉到一定程度会自动刷新页面。
一般用于list页面的配置。
如果开发者想要下拉刷新用于更新list的dom而不是刷新网页,需要在app.js中进行强化编程。

tabBar

tabBar可以对首页底部选项卡进行优化,实现选项卡切换时,仅变化内容区,选项卡区域不变。详细配置参考选项卡切换优化教程

收起阅读 »

【交流分享】局域网ipad应用解决方案-APP部分

技术分享

最近在做一个现场订货的项目,分享交流下,需求是这样的:
秀台现场预订明年新款,为防止资料泄漏,会场无法访问外网,均使用ipad连接自建服务器预订商品,超过600台客户端同时连接,实时推送消息、自动切换至台上model展示商品,实时查看各种订货数据报表图表,包含4-5种不同权限角色操作。

硬件设备这里暂时不说,有专业的局域网搭建解决方案,来说APP如何保证所有客户端能保证流畅即时的使用。

由于是局域网,所以推送肯定就不能使用个推之类的外网推送,那就只有websocket(目前使用ASP .NET SignalR),登录后创建连接,实时推送通告。
自动切换也是如此,websocket连接所有客户端(包括主讲人),主讲人切换商品传给服务器,其他ipad获取到当前商品id及主题自动打开指定商品详情。

同时连接了600+ ipad那数据的响应就成了大问题,大量的商品图片已经是相当大的数据量,所以图片采用本地化机制,APP中接口返回的所有图片地址初始均为相对路径,而修改过的图片则由服务端记录统一返回绝对路径访问线上图片。(第一次访问线上图片下载至本地,告知服务端,后续继续访问本地图片)
copy服务器图片文件夹至APP,这样APP的大小就达到了500M以上。然后使用企业证书离线打包APP,自建本地服务器应用托管平台,这样APP的下载安装就变成了局域网传输,只受限于服务器的吞吐量,经测试单ipad下载安装APP不到一分钟可以安装完成(中间包括了苹果检测安装)。

做了上面这些,基本就腾出了足够的带宽来处理其他接口数据。接下来就是些常规的大数据分页分段处理,后台服务器缓存等等,目前仍在继续优化性能中。。。

各位大神如有类似的优化方案,欢迎交流分享。另附上成品小test视频...

继续阅读 »

最近在做一个现场订货的项目,分享交流下,需求是这样的:
秀台现场预订明年新款,为防止资料泄漏,会场无法访问外网,均使用ipad连接自建服务器预订商品,超过600台客户端同时连接,实时推送消息、自动切换至台上model展示商品,实时查看各种订货数据报表图表,包含4-5种不同权限角色操作。

硬件设备这里暂时不说,有专业的局域网搭建解决方案,来说APP如何保证所有客户端能保证流畅即时的使用。

由于是局域网,所以推送肯定就不能使用个推之类的外网推送,那就只有websocket(目前使用ASP .NET SignalR),登录后创建连接,实时推送通告。
自动切换也是如此,websocket连接所有客户端(包括主讲人),主讲人切换商品传给服务器,其他ipad获取到当前商品id及主题自动打开指定商品详情。

同时连接了600+ ipad那数据的响应就成了大问题,大量的商品图片已经是相当大的数据量,所以图片采用本地化机制,APP中接口返回的所有图片地址初始均为相对路径,而修改过的图片则由服务端记录统一返回绝对路径访问线上图片。(第一次访问线上图片下载至本地,告知服务端,后续继续访问本地图片)
copy服务器图片文件夹至APP,这样APP的大小就达到了500M以上。然后使用企业证书离线打包APP,自建本地服务器应用托管平台,这样APP的下载安装就变成了局域网传输,只受限于服务器的吞吐量,经测试单ipad下载安装APP不到一分钟可以安装完成(中间包括了苹果检测安装)。

做了上面这些,基本就腾出了足够的带宽来处理其他接口数据。接下来就是些常规的大数据分页分段处理,后台服务器缓存等等,目前仍在继续优化性能中。。。

各位大神如有类似的优化方案,欢迎交流分享。另附上成品小test视频...

收起阅读 »

怎么实现一站式跨平台开发

![

](http://qiniu.betweenfriends.cn/blog/20170824/Hdl-NLsa68f5Q_QBsqlrQC5u.png)### 理想状态下的跨平台
我们之所以选择WEBAPP,最大的原因应该就是可以实现一套代码在多个平台上的复用。

理想状态下,一套代码就可以同时打包成android和ios的app,并且可以作为手机站和PC站直接使用。

当然,之所以说是理想状态,是因为由于PC浏览器和移动浏览器支持的事件和操作差异都比较大,如tap和click事件,以及移动设备上的拖动操作等。而且从mui的定位上来说,主要也是针对移动设备进行的封装。另外,从应用场景的发展趋势来说,移动设备使用率越来越高,PC端则是下降趋势,因此除非必要,可以忽略PC端的兼容性,只考虑移动设备上的兼容性。

移动设备的一站式开发

因此,我们这里讨论的一站式开发,主要是指一套代码同时可以部署成移动网站,并打包成Android和iosAPP

所需环境

  1. MUI
  2. HBuilder

原理

在开发时,利用js和H5在各个平台上的共性,实现在各个平台上的公用部分A,并针对不同的环境开发必要的补充部分B C D...

运行时,首先加载公共部分A,通过技术手段检测当前的运行环境,并根据当前环境加载补充部分B C D...,这样就实现了一套代码在多个平台上的自适应,也就实现了所谓的跨平台一站式开发。

实现

实现跨平台的关键就是剥离公共部分并根据需要定制不同平台上的个性部分。一般来说,公共部分就是手机站的代码,然后在手机站的基础上,进行必要的代码覆盖,比如在APP中把mui的弹窗替换成系统原生的弹窗。

公共部分代码

根据自己的业务需要,定义一个页面初始化的架子,包含页面的初始化,组件的加载,事件的监听等。同时包含各个方法的基本实现。一般来说,这个架子本身就是一个完整可用的页面框架,在不需要其他环境适配的代码补充的情况下,就可以独立完成手机站的初始化。

alt

定制代码适配不同的环境

具体需要补充几个版本的代码需要根据自己的需求,比如需要针对微信,或者针对IOS,Android等进行定制。
定制的方法是在公共部分代码的基础上,只针对性的实现在当前要适配的环境下需要定制的部分。然后在页面初始化的时候,利用JS的继承用定制版的代码替换掉公共部分的代码,从而实现对当前定制环境的定制开发。
以下示例即通过对公共部分的代码的一部分进行覆盖从而在APP环境中可以体现出和手机站不一样的效果。

alt

完整的代码框架

其中PcPage是基础的框架,WapPage包含手机站的适配代码。WxPage包含微信环境内的手机站适配代码,AppPage则是针对App打包环境下的环境适配代码。

当然,根据需要,适配代码的补充可以有更多或者更少的版本,比如如果不需要考虑微信环境,那么就可以跳过WxPage的开发,如果需要针对App环境区分IOS和Android并实现不同的效果,那么甚至可以在AppPage之后分别实现IOSPage和AndroidPage。一切随你心意。

alt

环境检测代码

即 initPage方法的实现,在对一个页面进行初始化时,通过调用initPage方法,并传入必要的页面初始化参数,initPage会根据当前运行的环境,选择和环境适配的代码,然后根据传入的页面初始化参数来完成页面的初始化操作。

initPage的调用完成,标志着一个页面的初始化完成。
水平有限,代码有点乱,能看得懂实现原理就好,具体实现可以自己去做。initPage的核心功能,就是完成环境检测,然后根据环境检测结果,加载和环境适配的代码。

完整内容(原文链接):https://blog.betweenfriends.cn/post/crossdomaindev.html

继续阅读 »

![

](http://qiniu.betweenfriends.cn/blog/20170824/Hdl-NLsa68f5Q_QBsqlrQC5u.png)### 理想状态下的跨平台
我们之所以选择WEBAPP,最大的原因应该就是可以实现一套代码在多个平台上的复用。

理想状态下,一套代码就可以同时打包成android和ios的app,并且可以作为手机站和PC站直接使用。

当然,之所以说是理想状态,是因为由于PC浏览器和移动浏览器支持的事件和操作差异都比较大,如tap和click事件,以及移动设备上的拖动操作等。而且从mui的定位上来说,主要也是针对移动设备进行的封装。另外,从应用场景的发展趋势来说,移动设备使用率越来越高,PC端则是下降趋势,因此除非必要,可以忽略PC端的兼容性,只考虑移动设备上的兼容性。

移动设备的一站式开发

因此,我们这里讨论的一站式开发,主要是指一套代码同时可以部署成移动网站,并打包成Android和iosAPP

所需环境

  1. MUI
  2. HBuilder

原理

在开发时,利用js和H5在各个平台上的共性,实现在各个平台上的公用部分A,并针对不同的环境开发必要的补充部分B C D...

运行时,首先加载公共部分A,通过技术手段检测当前的运行环境,并根据当前环境加载补充部分B C D...,这样就实现了一套代码在多个平台上的自适应,也就实现了所谓的跨平台一站式开发。

实现

实现跨平台的关键就是剥离公共部分并根据需要定制不同平台上的个性部分。一般来说,公共部分就是手机站的代码,然后在手机站的基础上,进行必要的代码覆盖,比如在APP中把mui的弹窗替换成系统原生的弹窗。

公共部分代码

根据自己的业务需要,定义一个页面初始化的架子,包含页面的初始化,组件的加载,事件的监听等。同时包含各个方法的基本实现。一般来说,这个架子本身就是一个完整可用的页面框架,在不需要其他环境适配的代码补充的情况下,就可以独立完成手机站的初始化。

alt

定制代码适配不同的环境

具体需要补充几个版本的代码需要根据自己的需求,比如需要针对微信,或者针对IOS,Android等进行定制。
定制的方法是在公共部分代码的基础上,只针对性的实现在当前要适配的环境下需要定制的部分。然后在页面初始化的时候,利用JS的继承用定制版的代码替换掉公共部分的代码,从而实现对当前定制环境的定制开发。
以下示例即通过对公共部分的代码的一部分进行覆盖从而在APP环境中可以体现出和手机站不一样的效果。

alt

完整的代码框架

其中PcPage是基础的框架,WapPage包含手机站的适配代码。WxPage包含微信环境内的手机站适配代码,AppPage则是针对App打包环境下的环境适配代码。

当然,根据需要,适配代码的补充可以有更多或者更少的版本,比如如果不需要考虑微信环境,那么就可以跳过WxPage的开发,如果需要针对App环境区分IOS和Android并实现不同的效果,那么甚至可以在AppPage之后分别实现IOSPage和AndroidPage。一切随你心意。

alt

环境检测代码

即 initPage方法的实现,在对一个页面进行初始化时,通过调用initPage方法,并传入必要的页面初始化参数,initPage会根据当前运行的环境,选择和环境适配的代码,然后根据传入的页面初始化参数来完成页面的初始化操作。

initPage的调用完成,标志着一个页面的初始化完成。
水平有限,代码有点乱,能看得懂实现原理就好,具体实现可以自己去做。initPage的核心功能,就是完成环境检测,然后根据环境检测结果,加载和环境适配的代码。

完整内容(原文链接):https://blog.betweenfriends.cn/post/crossdomaindev.html

收起阅读 »

索引列表动态添加数据并排序

mui 索引列表

最近做项目用到索引列表,这里跟大家分享下我的经验,效率不是最好,大家一起讨论
接口返回数据格式:
{"result":{"code":"200","data":[{"phone":"未知","userId":"D63E526F8BF64FB6AF82E662B997F8FE","userName":"ceshi01","userRealName":"测试账号"},{"phone":"13256441111","userId":"8773107092004B40BB9FD8C159888D96","userName":"dongsheng_ys","userRealName":"dongsheng_ys"},{"phone":"17773069705","qq":"252231478","userId":"C542D82CE9AE4E0CA67E097008E5EC9C","userName":"fangjing6066","userRealName":"方静"},{"phone":"未知","userId":"5EB7CE0783774147A54E69A7E77A8B8A","userName":"fyw1","userRealName":"fyw1"},{"phone":"17773069331","qq":"645708253","userId":"F59788ADA12D42F38955749B10183FD2","userName":"gaomingzhu","userRealName":"高明珠"},{"phone":"17773069730","userId":"42D715F3BCEF47598A874340836A161C","userName":"geshenghua","userRealName":"葛生华"},{"phone":"17773069332","qq":"317268372","userId":"7788347362204F2690274CBA94891771","userName":"lishuang","userRealName":"李双"},{"phone":"17773069330","qq":"1161097955","userId":"2642A854E83140DC9E9C18B2F579FFF4","userName":"liuya","userRealName":"刘涯"},{"phone":"未知","userId":"87AE4788E1E74C5AA7BF214194DAAFEE","userName":"majiao","userRealName":"马姣"},{"phone":"13575009113","userId":"D85D37778F5744C7823D9DE75C2C77F9","userName":"xuzhixia","userRealName":"许志霞"},{"phone":"17773069329","qq":"124912358","userId":"721DB6536501485198F033D371D12A5E","userName":"yangtao","userRealName":"杨涛"},{"phone":"17773069328","qq":"42426382","userId":"851D39149D294A598BE778C30D33CD06","userName":"youxianyi","userRealName":"游险夷"}]}};
数据是已经排过序的,这样做起来会方便很多。
封装成函数

function wrapContactList(data){  
    var html = "",oldChar = "";  
    mui.each(data,function(index,result){  
        var c = result.userName.substr(0,1);  
        if(c != oldChar){  
            //当前userName与上一个不同则另加一个索引  
            html += '<li data-group="'+c.toUpperCase()+'" class="mui-table-view-divider mui-indexed-list-group">'+c.toUpperCase()+'</li>';  
            oldChar = c;  
        }  
        html += '<li data-userid="'+result.userId+'" data-username="'+result.userName+'" data-phone="'+result.phone+'" class="mui-table-view-cell mui-indexed-list-item">'+result.userRealName+ (result.phone.length>7?"("+result.phone+")":"") +'</li>';  
    });  
    //遍历结束插入数据  
    document.getElementById("contactList").innerHTML = html;  
    //重新实例化索引列表,不然快速查找不生效  
    mui("#list").indexedList();  
}  
继续阅读 »

最近做项目用到索引列表,这里跟大家分享下我的经验,效率不是最好,大家一起讨论
接口返回数据格式:
{"result":{"code":"200","data":[{"phone":"未知","userId":"D63E526F8BF64FB6AF82E662B997F8FE","userName":"ceshi01","userRealName":"测试账号"},{"phone":"13256441111","userId":"8773107092004B40BB9FD8C159888D96","userName":"dongsheng_ys","userRealName":"dongsheng_ys"},{"phone":"17773069705","qq":"252231478","userId":"C542D82CE9AE4E0CA67E097008E5EC9C","userName":"fangjing6066","userRealName":"方静"},{"phone":"未知","userId":"5EB7CE0783774147A54E69A7E77A8B8A","userName":"fyw1","userRealName":"fyw1"},{"phone":"17773069331","qq":"645708253","userId":"F59788ADA12D42F38955749B10183FD2","userName":"gaomingzhu","userRealName":"高明珠"},{"phone":"17773069730","userId":"42D715F3BCEF47598A874340836A161C","userName":"geshenghua","userRealName":"葛生华"},{"phone":"17773069332","qq":"317268372","userId":"7788347362204F2690274CBA94891771","userName":"lishuang","userRealName":"李双"},{"phone":"17773069330","qq":"1161097955","userId":"2642A854E83140DC9E9C18B2F579FFF4","userName":"liuya","userRealName":"刘涯"},{"phone":"未知","userId":"87AE4788E1E74C5AA7BF214194DAAFEE","userName":"majiao","userRealName":"马姣"},{"phone":"13575009113","userId":"D85D37778F5744C7823D9DE75C2C77F9","userName":"xuzhixia","userRealName":"许志霞"},{"phone":"17773069329","qq":"124912358","userId":"721DB6536501485198F033D371D12A5E","userName":"yangtao","userRealName":"杨涛"},{"phone":"17773069328","qq":"42426382","userId":"851D39149D294A598BE778C30D33CD06","userName":"youxianyi","userRealName":"游险夷"}]}};
数据是已经排过序的,这样做起来会方便很多。
封装成函数

function wrapContactList(data){  
    var html = "",oldChar = "";  
    mui.each(data,function(index,result){  
        var c = result.userName.substr(0,1);  
        if(c != oldChar){  
            //当前userName与上一个不同则另加一个索引  
            html += '<li data-group="'+c.toUpperCase()+'" class="mui-table-view-divider mui-indexed-list-group">'+c.toUpperCase()+'</li>';  
            oldChar = c;  
        }  
        html += '<li data-userid="'+result.userId+'" data-username="'+result.userName+'" data-phone="'+result.phone+'" class="mui-table-view-cell mui-indexed-list-item">'+result.userRealName+ (result.phone.length>7?"("+result.phone+")":"") +'</li>';  
    });  
    //遍历结束插入数据  
    document.getElementById("contactList").innerHTML = html;  
    //重新实例化索引列表,不然快速查找不生效  
    mui("#list").indexedList();  
}  
收起阅读 »

【交流分享】Dcloud开发APP心得系列之轻松调试解决问题篇

调试 技术分享

经常遇到群里或社区有人问些无从答起的问题:为什么我这块的数据没有显示出来?为什么我这个页面滚动不了?为什么控制台报错了,然后各种截图丢出来。。。
一脸懵逼,完全不知道从何答起。

对于这些摸不着头脑的问题真的不如自己调试下来的快。

1、当然是Hbuilder编辑器自带的控制台输出调试,console.log输出字符串,json对象JSON.stringify()后输出,object对象使用Object.keys()循环输出属性、键值,配合输出就能轻松判断问题在哪里,哪里出错。

2、作为一名前端首选的调试工具当然是chrome谷歌浏览自带的调试工具,APP如何使用谷歌调试?
真机或模拟器(限安卓系统)运行APP,谷歌浏览器访问chrome://inspect,就会列出当前设备运行的页面,inspect就可以打开谷歌控制台,就跟web一样打断点,审查元素、css等,点击左上角手机图标开启手机预览,部分手机可能左侧不会显示当前页面的视图,就无法用点击查看元素,得手动展开元素结构。
注:有时可能需要翻墙inspect才能加载出控制台,不过Hbuilder最新alpha版本已支持完全不需要翻墙。
补充:ios系统 请求使用Safari浏览器调试,用法类似,参考:http://ask.dcloud.net.cn/article/143

3、FE助手,一款谷歌插件,自动格式化访问接口返回的json对象,以及前端开发常用工具

有了这些不愁问题找不到根源,轻松解决各种莫名其妙的问题。当然所有的前提是你的代码整洁规范,不然谁都救不了你。

飞☞☞☞ 初始篇

继续阅读 »

经常遇到群里或社区有人问些无从答起的问题:为什么我这块的数据没有显示出来?为什么我这个页面滚动不了?为什么控制台报错了,然后各种截图丢出来。。。
一脸懵逼,完全不知道从何答起。

对于这些摸不着头脑的问题真的不如自己调试下来的快。

1、当然是Hbuilder编辑器自带的控制台输出调试,console.log输出字符串,json对象JSON.stringify()后输出,object对象使用Object.keys()循环输出属性、键值,配合输出就能轻松判断问题在哪里,哪里出错。

2、作为一名前端首选的调试工具当然是chrome谷歌浏览自带的调试工具,APP如何使用谷歌调试?
真机或模拟器(限安卓系统)运行APP,谷歌浏览器访问chrome://inspect,就会列出当前设备运行的页面,inspect就可以打开谷歌控制台,就跟web一样打断点,审查元素、css等,点击左上角手机图标开启手机预览,部分手机可能左侧不会显示当前页面的视图,就无法用点击查看元素,得手动展开元素结构。
注:有时可能需要翻墙inspect才能加载出控制台,不过Hbuilder最新alpha版本已支持完全不需要翻墙。
补充:ios系统 请求使用Safari浏览器调试,用法类似,参考:http://ask.dcloud.net.cn/article/143

3、FE助手,一款谷歌插件,自动格式化访问接口返回的json对象,以及前端开发常用工具

有了这些不愁问题找不到根源,轻松解决各种莫名其妙的问题。当然所有的前提是你的代码整洁规范,不然谁都救不了你。

飞☞☞☞ 初始篇

收起阅读 »

【交流分享】Dcloud开发APP心得系列之接口安全加密篇

技术分享 安全 加密 性能优化

================ 补充 ====================

此方法已被 @wenju 大神验证无效,仅作参考,还是开启混淆 js吧

==============================================================================

继代码性能优化篇再来一波。。。

使用Dcloud开发APP以来大家讨论关注比较多的问题估计就是APP代码安全性问题,很多人都在考虑加密、代码混淆啊,因为打包好的apk、ipa可以直接解压从中找到完整的页面文件,包括HTML、css、js代码等,怕怕。。。这样另有目的的人会不会直接copy你的代码打包新的APP?

其实我觉得那些静态页面根本就不值钱,就像web网页一样,照样能扒下所有的静态文件。打包时使用代码混淆功能感觉也得不偿失,消耗资源,影响APP性能。整个APP里面其实唯一值得保护的就只有manifest.json文件里面的sdk配置以及你的后台API数据接口,manifest.json文件配置Dcloud已经处理过了,所有是相对安全的。

能看到你的代码就能使用你的接口,那怎么保证你的接口数据是安全的?提供一个解决方案:

1、用户信息的储存

用户登录后存储用户唯一标识userkey加密串至localStorage,后续所有的用户相关数据获取传userkey参数获取。

2、接口签名认证机制

接口除传正常参数外再补加几个加密用参数:
timestamp:时间戳,获取客户端当前时间,如果时间差与服务器超过五分钟,则请求失败
appkey:签名appkey
sign:接口签名参数,使用md5摘要算法处理一定规律的字符串,所有接口需要参数 + timestamp + appkey + 按参数名排序后拼接成字符串(参数名+值)+ appsecret;

封装成一个统一处理接口data参数的方法

app.md5Data=function(oldData){  
    var data=JSON.parse(JSON.stringify(oldData||{})) //保存原始数据  
    data.appkey=appkey;  
    data.timestamp =new Date().toUTCString();    //**由于国外访问存在时差,会导致时间戳验证失败,故此处改为UTC标准时间,服务端处理转换**  
    var keys=[];  
    for(var k in data){  
        if(data[k]===0||data[k]===false||data[k]){  
            keys.push(k);  
        }else{  
            delete data[k]; //剔除空参数  
        }  
    }  

    keys=keys.sort();  
    var signStr='';  
    for(var i=0; i<keys.length; i  ){  
        signStr =keys[i].toLowerCase()+data[keys[i]];  
    }  

    data.sign=md5(signStr+appsecret);  
    return data;  
}

处理后的data为:

{  
    pageno: page,  
    pagesize: 20,  
    userkey: userkey,  
    type: type,  
    timestamp:'2017-08-23 16:39:50',  
    appkey:'.....',  
    sign:'A765E24767546E1109B5576538C87FF6'  
}  

mui.ajax({  
    data:app.md5Data({ .... })  
})

后台进行同样的方式生成sign,如果配对不一致则请求失败。

那么问题又来了,appkey、appsecret存储在哪里?肯定不能直接写死在js中,那样就没意义了,目前采用的方法是获取manifest.json配置的某个第三方sdk的appid,appkey,最后发现只有个推的配置信息是提供了方法获取的,果断使用plus.push.getClientInfo().appkey 、appsecret获取配置文件中的值(解压是获取不到sdk配置的)。

当然也可以直接写了上述方法的js文件打包的时候启用js原生混淆,这样明文写死也没有关系,解压打开你的app.js看到的全是乱码,只是不支持安卓4.0以下手机。

补充:没有提供获取方法的sdk配置也是可以通过native.js获取到(真不知道安不安全),方法链接: https://ask.dcloud.net.cn/article/488

这样就算别人拿到你的源码,也无法请求到你API接口的任何数据。

注:目前只想到这个方法存储appkey、appsecret,或许大家有更靠谱的方法存储,欢迎交流。

代码精简、性能优化篇

继续阅读 »

================ 补充 ====================

此方法已被 @wenju 大神验证无效,仅作参考,还是开启混淆 js吧

==============================================================================

继代码性能优化篇再来一波。。。

使用Dcloud开发APP以来大家讨论关注比较多的问题估计就是APP代码安全性问题,很多人都在考虑加密、代码混淆啊,因为打包好的apk、ipa可以直接解压从中找到完整的页面文件,包括HTML、css、js代码等,怕怕。。。这样另有目的的人会不会直接copy你的代码打包新的APP?

其实我觉得那些静态页面根本就不值钱,就像web网页一样,照样能扒下所有的静态文件。打包时使用代码混淆功能感觉也得不偿失,消耗资源,影响APP性能。整个APP里面其实唯一值得保护的就只有manifest.json文件里面的sdk配置以及你的后台API数据接口,manifest.json文件配置Dcloud已经处理过了,所有是相对安全的。

能看到你的代码就能使用你的接口,那怎么保证你的接口数据是安全的?提供一个解决方案:

1、用户信息的储存

用户登录后存储用户唯一标识userkey加密串至localStorage,后续所有的用户相关数据获取传userkey参数获取。

2、接口签名认证机制

接口除传正常参数外再补加几个加密用参数:
timestamp:时间戳,获取客户端当前时间,如果时间差与服务器超过五分钟,则请求失败
appkey:签名appkey
sign:接口签名参数,使用md5摘要算法处理一定规律的字符串,所有接口需要参数 + timestamp + appkey + 按参数名排序后拼接成字符串(参数名+值)+ appsecret;

封装成一个统一处理接口data参数的方法

app.md5Data=function(oldData){  
    var data=JSON.parse(JSON.stringify(oldData||{})) //保存原始数据  
    data.appkey=appkey;  
    data.timestamp =new Date().toUTCString();    //**由于国外访问存在时差,会导致时间戳验证失败,故此处改为UTC标准时间,服务端处理转换**  
    var keys=[];  
    for(var k in data){  
        if(data[k]===0||data[k]===false||data[k]){  
            keys.push(k);  
        }else{  
            delete data[k]; //剔除空参数  
        }  
    }  

    keys=keys.sort();  
    var signStr='';  
    for(var i=0; i<keys.length; i  ){  
        signStr =keys[i].toLowerCase()+data[keys[i]];  
    }  

    data.sign=md5(signStr+appsecret);  
    return data;  
}

处理后的data为:

{  
    pageno: page,  
    pagesize: 20,  
    userkey: userkey,  
    type: type,  
    timestamp:'2017-08-23 16:39:50',  
    appkey:'.....',  
    sign:'A765E24767546E1109B5576538C87FF6'  
}  

mui.ajax({  
    data:app.md5Data({ .... })  
})

后台进行同样的方式生成sign,如果配对不一致则请求失败。

那么问题又来了,appkey、appsecret存储在哪里?肯定不能直接写死在js中,那样就没意义了,目前采用的方法是获取manifest.json配置的某个第三方sdk的appid,appkey,最后发现只有个推的配置信息是提供了方法获取的,果断使用plus.push.getClientInfo().appkey 、appsecret获取配置文件中的值(解压是获取不到sdk配置的)。

当然也可以直接写了上述方法的js文件打包的时候启用js原生混淆,这样明文写死也没有关系,解压打开你的app.js看到的全是乱码,只是不支持安卓4.0以下手机。

补充:没有提供获取方法的sdk配置也是可以通过native.js获取到(真不知道安不安全),方法链接: https://ask.dcloud.net.cn/article/488

这样就算别人拿到你的源码,也无法请求到你API接口的任何数据。

注:目前只想到这个方法存储appkey、appsecret,或许大家有更靠谱的方法存储,欢迎交流。

代码精简、性能优化篇

收起阅读 »

ios端view制件app底部bar,使用的字体图标不显示问题。

tabbar view

android端使用正常

ios中字体图标不显示。

问题原因:在使用iconfont的时候,font family和dcloud内部的字体文件冲突(大概是这个样子)

解决方案:在icontfont网站,字体项目里-》更多操作-》编辑项目里修改font family的值


到这里问题解决,重新下载字体文件试试。

继续阅读 »

android端使用正常

ios中字体图标不显示。

问题原因:在使用iconfont的时候,font family和dcloud内部的字体文件冲突(大概是这个样子)

解决方案:在icontfont网站,字体项目里-》更多操作-》编辑项目里修改font family的值


到这里问题解决,重新下载字体文件试试。

收起阅读 »