HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

初学一个多月,终于制作完成了一个简陋的app并上架了!

上架 App

首先,感谢社区里的大神们和QQ群里的大神们的帮助,自己摸爬滚打制作的简易APP终于上线了。

苹果的app store也顺利上线!

感觉自己又有信心了!

首先,感谢社区里的大神们和QQ群里的大神们的帮助,自己摸爬滚打制作的简易APP终于上线了。

苹果的app store也顺利上线!

感觉自己又有信心了!

【公告】关于HBuilder 8.1.2不能正常启动,Android平台地图滑动卡顿、地图POI搜索后不显示数据等问题的解决办法

公告 HTML5+ Runtime HBuilder

IDE

HBuilder不能正常启动

HBuilder启动时长时间停留在启动界面或闪退。

Android

Android平台地图滑动卡顿

Android平台下,创建plus.maps.Map控件时,不设置position参数默认使用static,会使得地图控件滑动非常卡顿,影响使用

Android平台修复地图POI搜索后不显示数据的问题

Android平台下,plus.maps.Map控件中搜索地点后数据不能正确标识在地图

解决办法

8.1.3及以上版本的HBuilder已修复以上问题,请更新或下载最新版HBuilder。

继续阅读 »

IDE

HBuilder不能正常启动

HBuilder启动时长时间停留在启动界面或闪退。

Android

Android平台地图滑动卡顿

Android平台下,创建plus.maps.Map控件时,不设置position参数默认使用static,会使得地图控件滑动非常卡顿,影响使用

Android平台修复地图POI搜索后不显示数据的问题

Android平台下,plus.maps.Map控件中搜索地点后数据不能正确标识在地图

解决办法

8.1.3及以上版本的HBuilder已修复以上问题,请更新或下载最新版HBuilder。

收起阅读 »

HBuilder 工具条插件 新手必备插件 【终身免费的】

HBuilder

HBuilder 工具条插件 新手必备插件

管理大大 觉得可以给加个精哦~!

功能:

1、集成常用网页开发手册 php js css jq 等
PHP超级手册【核心功能: 直接将常用近千个常用PHP命令汉化中文 解决很多新手小白 知道某些函数功能 但是苦于不知道是哪个命令 去百度搜索又不能很快的找到说明 那么此功能就帮助很大了 你只要知道你需要写的功能的大概汉语意思 就可以直接定位搜索出 英文函数命令哦】

2、工具 集成了常用的一些计算机工具 省却了在开发途中需要打开某些东西影响效率

3、编码 集成常用的一些文本编码换算 适合快速操作。

安装:
直接解压覆盖到HBuilder 根目录覆盖即可
请将此文件直接复制到 HBuilder安装根目录下。 直接替换。

启动办法:F10 或者HOME都可以呼出 呼出区别 F10 不吸附窗体 HOME键 吸附内嵌到HBuilder软件工具条

拖放移动:本软件上述两种模式均支持随意移动 鼠标移动到绿色工具条顶部边缘 鼠标按着不放 可以随意移动

作者:九六重生
QQ:14434001
作者官网:96wk.com

祝您使用愉悦!

下面贴上插件部分截图 喜欢的就下载吧

插件下载见附件 最底部哦

继续阅读 »

HBuilder 工具条插件 新手必备插件

管理大大 觉得可以给加个精哦~!

功能:

1、集成常用网页开发手册 php js css jq 等
PHP超级手册【核心功能: 直接将常用近千个常用PHP命令汉化中文 解决很多新手小白 知道某些函数功能 但是苦于不知道是哪个命令 去百度搜索又不能很快的找到说明 那么此功能就帮助很大了 你只要知道你需要写的功能的大概汉语意思 就可以直接定位搜索出 英文函数命令哦】

2、工具 集成了常用的一些计算机工具 省却了在开发途中需要打开某些东西影响效率

3、编码 集成常用的一些文本编码换算 适合快速操作。

安装:
直接解压覆盖到HBuilder 根目录覆盖即可
请将此文件直接复制到 HBuilder安装根目录下。 直接替换。

启动办法:F10 或者HOME都可以呼出 呼出区别 F10 不吸附窗体 HOME键 吸附内嵌到HBuilder软件工具条

拖放移动:本软件上述两种模式均支持随意移动 鼠标移动到绿色工具条顶部边缘 鼠标按着不放 可以随意移动

作者:九六重生
QQ:14434001
作者官网:96wk.com

祝您使用愉悦!

下面贴上插件部分截图 喜欢的就下载吧

插件下载见附件 最底部哦

收起阅读 »

【5+】跨webview多页面 触发事件

HTML5+ 技术分享

本文源地址:https://segmentfault.com/a/1190000008844889

在日常撸功能中,很多情况都需要用到通知页面,mui呢给我们已经内置写好啦,当当当,就是 mui.fire
我们来看看之前所写的用法

mui.fire

耶?这时候有童鞋就会问了,咋了之前不是写了mui.fire的文章了吗?为啥又有了这一篇捏?
哈哈,这篇文章我们主要来讲解5+的实现方案,不用mui.fire,就相当于自己写一个通知的js功能,让大家更明白其原理,以及更好地不止是拘束于非得用到mui.js


原理介绍

关键词
  • plus.webview.evalJS
  • dispatchEvent

在B页面通知A页面,我们暂时不管通知A页面的内容,只是希望B页面调用一段代码,能让A页面弹出一个alert
这时候我们就要用到webview的evalJS方法了

A.html

        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我是A页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">打开B页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    mui.openWindow('B.html')  
                })  
            })  
        </script>

B.html

        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我是B页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">通知A页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    // 通知A页面的方法  
                })  
            })  
        </script>

那么,怎么才能通知A弹出框呢?我们需要用到关键词所提到的 plus.webview.evalJS
嘿嘿, 我们现在B获取到A的Webview对象,然后通过evalJS来向A页面发送一段代码让其执行

// 通知A页面的方法  
var A = plus.webview.getLaunchWebview()  
A.evalJS('alert("我是被B的")')

点击一下按钮,果不其然,我们的弹出框就显示出来,当然我们也还可以定义其它的函数来接收响应

A

function faqme(){  
    alert('啊,乖乖站好!')  
}

B

// 通知A页面的方法  
var A = plus.webview.getLaunchWebview()  
A.evalJS('faqme()')

当然,A页面执行了faqme函数,弹出了乖乖站好

其实,mui.fire的内部实现就是其原理
我们可以看一下其代码

clipboard.png

clipboard.png

clipboard.png

这个 dispatchEvent 是什么呢?
我们可以将这个方法理解为用来触发dom事件
相关详细文档:
事件触发器-----dispatchEvent

这下条理就很清楚拉!
A页面自定义事件 => B页面触发A页面事件回调并传参

嘿嘿,就是这么简单,但是本文章还没有结束,既然都到这了,干脆我们来自己封装一下这个通知功能吧!

造轮子

我们新建一个文件,美其名曰:Broadcast.js
在这里我采用ES6 Class的方式编写


//页面通知  

class Broadcast{  
    /**  
     * 构造器函数  
     */  
    constructor(){  

    }  

}  

我们先来实现最基础的两个功能

  • 监听事件(订阅)
  • 触发事件(发布)

//页面通知  

class Broadcast{  
    /**  
     * 构造器函数  
     */  
    constructor(){  

    }  

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     * @return {Broadcast} this  
     */  
    on(eventName, callback){  
        document.addEventListener(eventName, e => {  
            callback.call(e, e.detail)  
        })  
        return this  
    }  

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 参数  
     * @return {Broadcast} this  
     */  
    emit(eventName, data){  
        // 获取所有的webview  
        var all = plus.webview.all()  
        // 遍历全部页面  
        for(var w in all){  
            // 挨个来evalJS  
            all[w].evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {  
                detail:JSON.parse('${JSON.stringify(data)}'),  
                bubbles: true,  
                cancelable: true  
            }));`)  
        }  
        return this  
    }  

}  

ok, 我们在页面中引用并尝试用一下

A

        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我是A页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">打开B页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript" src="js/Broadcast.js" ></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    mui.openWindow('B.html')  
                })  
            })  
            new Broadcast().on('say', function(data){  
                alert(JSON.stringify(data))  
            })  
        </script>

B

        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我是B页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">通知A页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript" src="js/Broadcast.js" ></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    // 通知A页面的方法  
                    //var A = plus.webview.getLaunchWebview()  
                    //A.evalJS('alert("我是被B的")')  
                    new Broadcast().emit('say', {  
                        from: '我是B啊',  
                        id: 666  
                    })  
                })  
            })  
        </script>

点击B页面的按钮

clipboard.png

哇哈哈,基础功能已经实现了怎么样,
当然,这只是最基础的实现了监听,触发而已,后续还需要更多的优化,以及管理,辣么,下章见

上一章我们了解到通过webview evalJS的方法来跨页面通知事件,但是在其中还是有需要优化的地方,接下来我们慢慢的来分析。

上节回顾:【5+】跨webview多页面 触发事件(一)
代码:

//页面通知  

class Broadcast{  
    /**  
     * 构造器函数  
     */  
    constructor(){  

    }  

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     * @return {Broadcast} this  
     */  
    on(eventName, callback){  
        document.addEventListener(eventName, e => {  
            callback.call(e, e.detail)  
        })  
        return this  
    }  

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 参数  
     * @return {Broadcast} this  
     */  
    emit(eventName, data){  
        // 获取所有的webview  
        var all = plus.webview.all()  
        // 遍历全部页面  
        for(var w in all){  
            // 挨个来evalJS  
            all[w].evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {  
                detail:JSON.parse('${JSON.stringify(data)}'),  
                bubbles: true,  
                cancelable: true  
            }));`)  
        }  
        return this  
    }  

}

自定义需要通知页面

可以看到,之前我们emit发送通知时,是对所有的webview进行获取通知,但是有时候我们并不想通知所有的页面,而且通知别人的时候也不想通知自己啊,怎么办,在这里我们在emit方法参数多加一个配置项

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        //code...  
    }

然后我们针对传进来的拓展参数,进行逻辑判断,得到最终我们需要通知的webview list

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let all = []  
        // 获取 特定 webview 数组  
        if(views.length > 0) {  
            // 如果是string 类型,则统一处理获取为 webview对象  
            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)  
        } else {  
            // 不特定通知的webview数组时,直接获取全部已存在的webview  
            all = plus.webview.all()  
        }  
        // 如果不需要通知到当前webview 则过滤  
        if(!self) {  
            let v =  plus.webview.currentWebview()  
            all = all.filter(item => item.id !== v.id)  
        }  
        // 遍历所有需要通知的页面  
        for(let v of all) {  
            v.evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {  
                detail:JSON.parse('${JSON.stringify(data)}'),  
                bubbles: true,  
                cancelable: true  
            }));`)  
        }  
    }  

如何调用

new Broadcast().emit('say',{  
    name: 'newsning',  
    age: 26  
},{  
    self: true, // 通知当前页面 默认不通知  
    views: ['A.html','C.html'] // 默认通知所有页面,但不包括当前页面  
})  
// 如上代码就只通知到了3个页面, 当前页面, A页面, C页面

事件 - [ 订阅 | 发布 | 取消 ]

如果你遇到那种还需要移除监听事件,亦或者Once只监听一次的事件,再或是你看个代码不爽
clipboard.png

ok!我们来撸一套简单的 守望先锋模式,哦不,是观察者模式

事件订阅

瞧瞧我们之前的代码,on方法是直接把传进来的函数作为调用,这样子在外部调用时移除事件就没路子了,包括Once也很是蛋疼

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     * @return {Broadcast} this  
     */  
    on(eventName, callback){  
        document.addEventListener(eventName, e => {  
            callback.call(e, e.detail)  
        })  
        return this  
    }

我们先来定义好2个专门放置事件的存储对象,碧如 :

    // 事件列表  
    const events = {  
        // 事件名称 : 事件方法数组      
    },  
    // 单次事件列表  
    events_one = {  

    }

之后我们修改一下on方法,并新增一个once方法

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    on(eventName, callback) {  
        // 获取已存在的事件列表  
        if(!events[eventName]) {  
            events[eventName] = []  
        }  
        // 添加至数组  
        events[eventName].push(callback)  
    }  

    /**  
     * 事件监听 (单次)  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    once(eventName, callback) {  
        // 获取已存在的单次事件列表  
        if(!events_one[eventName]) {  
            events_one[eventName] = []  
        }  
        // 添加至数组  
        events_one[eventName].push(callback)  
    }

酱紫,每次添加事件时,都会放入我们的事件列表中,但是!我们并没有给任何dom添加事件,而仅仅是放入所对应的事件列表中,奇怪了,看看我们之前的添加事件方法

clipboard.png

给document监听一个事件

clipboard.png

触发document事件

nonono , 我们不这么借助document亦或者其它dom的事件监听,还记得上一章的 evalJS('faqme()')么?我们就用亲切的函数来触发事件

事件发布

在事件订阅当中,我们仅仅只是把事件放入了事件列表中,我们该如何触发?

编写一个静态方法,用来触发当前页面的事件, 然后通过

    static _emitSelf(eventName, data) {  
        if(typeof data === 'string') {  
            data = JSON.parse(data)  
        }  
        // 获取全部事件列表 和 单次事件列表,并且合并  
        let es = [...(events[eventName] || []), ...(events_one[eventName] || [])]  
        // 遍历触发  
        for(let f of es) {  
            f && f.call(f, data)  
        }  
        // 单次事件清空  
        events_one[eventName] = []  
    }

再配合修改一下 emit 里面的 evalJS

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let all = []  
        // 获取 特定 webview 数组  
        if(views.length > 0) {  
            // 如果是string 类型,则统一处理获取为 webview对象  
            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)  
        } else {  
            // 不特定通知的webview数组时,直接获取全部已存在的webview  
            all = plus.webview.all()  
        }  
        // 如果不需要通知到当前webview 则过滤  
        if(!self) {  
            let v =  plus.webview.currentWebview()  
            all = all.filter(item => item.id !== v.id)  
        }  
        // 遍历所有需要通知的页面  
        for(let v of all) {  
            /////////////////////////  
            ////////////////这里是重点, 调用Broadcast的静态方法  
            /////////////////////////  
            v.evalJS(`Broadcast && Broadcast._emitSelf && Broadcast._emitSelf('${eventName}', '${JSON.stringify(data)}')`)  
        }  
    }  

这样子,就巧妙的触发了每个webview页面 相对应的事件,并且单次事件也得到了清除

事件移除

我们知道前面的事件订阅只是将事件存起来了,事件移除相应的就是把事件列表清空

    static _offSelf(eventName) {  
        //清空事件列表  
        events[eventName] = []  
        events_one[eventName] = []  
    }

最后收尾

所定义的2个静态方法,触发 和 移除 事件,我们在内部代理2个相应的方法

    /**  
     * 当前页面事件触发   
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     */  
    emitSelf(eventName) {  
        Broadcast._emitSelf(eventName, data)  
    }  

    /**  
     * 清空当前页面事件   
     * @param {String} eventName 事件名称  
     */  
    offSelf(eventName) {  
        Broadcast._offSelf(eventName)  
    }

最后,成果已经出现

A.html

            var b = new Broadcast()  

            b.on('say', function(data){  
                alert(JSON.stringify(data))  

                // 删除本页面say事件  
                //b.offSelf('say')  
            })  

            b.once('say', function(data){  
                //单次  
                alert('单次:'+JSON.stringify(data))  
            })

B.html

            new Broadcast().emit('say', {  
                from: '我是B啊',  
                id: 666  
            })

最后附上源码:

/**  
 * 5+ Broadcast.js by NewsNing 宁大大   
 */  

// 获取当前webview  
const getIndexView = (() => {  
        // 缓存  
        let indexView = null  
        return(update = false) => {  
            if(update || indexView === null) {  
                indexView = plus.webview.currentWebview()  
            }  
            return indexView  
        }  
    })(),  
    // 获取全部webview   
    getAllWebview = (() => {  
        // 缓存  
        let allView = null  
        return(update = false) => {  
            if(update || allView === null) {  
                allView = plus.webview.all()  
            }  
            return allView  
        }  
    })()  

// 事件列表  
const events = {  

    },  
    // 单次事件列表  
    events_one = {  

    }  

//页面通知类  
class Broadcast {  
    /**  
     * 构造器函数  
     */  
    constructor() {  

    }  

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    on(eventName, callback) {  
        // 获取已存在的事件列表  
        if(!events[eventName]) {  
            events[eventName] = []  
        }  
        // 添加至数组  
        events[eventName].push(callback)  
    }  

    /**  
     * 事件监听 (单次)  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    once(eventName, callback) {  
        // 获取已存在的单次事件列表  
        if(!events_one[eventName]) {  
            events_one[eventName] = []  
        }  
        // 添加至数组  
        events_one[eventName].push(callback)  
    }  

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let jsstr = `Broadcast && Broadcast._emitSelf && Broadcast._emitSelf('${eventName}', '${JSON.stringify(data)}')`  
        this._sendMessage(jsstr, self, views)  
    }  

    /**  
     * 当前页面事件触发   
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     */  
    emitSelf(eventName) {  
        Broadcast._emitSelf(eventName, data)  
    }  

    /**  
     * 事件关闭移除  
     * @param {String} eventName 事件名称  
     * @param {Object} options 其它配置参数  
     */  
    off(eventName, {  
        self = false, // 是否通知自己,默认不通知  
        views = [] // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let jsstr = `Broadcast && Broadcast._offSelf && Broadcast._offSelf('${eventName}')`  
        this._sendMessage(jsstr, self, views)  
    }  

    /**  
     * 清空当前页面事件    
     * @param {String} eventName 事件名称  
     */  
    offSelf(eventName) {  
        Broadcast._offSelf(eventName)  
    }  

    /**  
     * 页面通知  
     * @param {String} jsstr 需要运行的js代码  
     * @param {Boolean} self 是否通知自己,默认不通知  
     * @param {Array} views 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
     */  
    _sendMessage(  
        jsstr = '',  
        self = false,  
        views = []  
    ) {  
        let all = []  
        // 获取 特定 webview 数组  
        if(views.length > 0) {  
            // 如果是string 类型,则统一处理获取为 webview对象  
            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)  
        } else {  
            // 不特定通知的webview数组时,直接获取全部已存在的webview  
            all = getAllWebview(true)  
        }  
        // 如果不需要通知到当前webview 则过滤  
        if(!self) {  
            let v = getIndexView()  
            all = all.filter(item => item.id !== v.id)  
        }  
        // 遍历全部页面  
        for(let v of all) {  
            v.evalJS(jsstr)  
        }  
    }  

    static _emitSelf(eventName, data) {  
        if(typeof data === 'string') {  
            data = JSON.parse(data)  
        }  
        // 获取全部事件列表 和 单次事件列表,并且合并  
        let es = [...(events[eventName] || []), ...(events_one[eventName] || [])]  
        // 遍历触发  
        for(let f of es) {  
            f && f.call(f, data)  
        }  
        // 单次事件清空  
        events_one[eventName] = []  
    }  

    static _offSelf(eventName) {  
        //清空事件列表  
        events[eventName] = []  
        events_one[eventName] = []  
    }  

}

您也可以通过babel在线转化成es5 在线转换地址

clipboard.png

最后您还可以在github上看到一些其它5+ Api封装的源码 [5+ api整合]

https://github.com/NewsNIng/ni

class Man{  
    constructor(){  
        this.name = 'newsning'  
    }  
    say(){  
        console.log('天行健, 君子以自强不息. ')  
    }  
}
继续阅读 »

本文源地址:https://segmentfault.com/a/1190000008844889

在日常撸功能中,很多情况都需要用到通知页面,mui呢给我们已经内置写好啦,当当当,就是 mui.fire
我们来看看之前所写的用法

mui.fire

耶?这时候有童鞋就会问了,咋了之前不是写了mui.fire的文章了吗?为啥又有了这一篇捏?
哈哈,这篇文章我们主要来讲解5+的实现方案,不用mui.fire,就相当于自己写一个通知的js功能,让大家更明白其原理,以及更好地不止是拘束于非得用到mui.js


原理介绍

关键词
  • plus.webview.evalJS
  • dispatchEvent

在B页面通知A页面,我们暂时不管通知A页面的内容,只是希望B页面调用一段代码,能让A页面弹出一个alert
这时候我们就要用到webview的evalJS方法了

A.html

        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我是A页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">打开B页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    mui.openWindow('B.html')  
                })  
            })  
        </script>

B.html

        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我是B页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">通知A页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    // 通知A页面的方法  
                })  
            })  
        </script>

那么,怎么才能通知A弹出框呢?我们需要用到关键词所提到的 plus.webview.evalJS
嘿嘿, 我们现在B获取到A的Webview对象,然后通过evalJS来向A页面发送一段代码让其执行

// 通知A页面的方法  
var A = plus.webview.getLaunchWebview()  
A.evalJS('alert("我是被B的")')

点击一下按钮,果不其然,我们的弹出框就显示出来,当然我们也还可以定义其它的函数来接收响应

A

function faqme(){  
    alert('啊,乖乖站好!')  
}

B

// 通知A页面的方法  
var A = plus.webview.getLaunchWebview()  
A.evalJS('faqme()')

当然,A页面执行了faqme函数,弹出了乖乖站好

其实,mui.fire的内部实现就是其原理
我们可以看一下其代码

clipboard.png

clipboard.png

clipboard.png

这个 dispatchEvent 是什么呢?
我们可以将这个方法理解为用来触发dom事件
相关详细文档:
事件触发器-----dispatchEvent

这下条理就很清楚拉!
A页面自定义事件 => B页面触发A页面事件回调并传参

嘿嘿,就是这么简单,但是本文章还没有结束,既然都到这了,干脆我们来自己封装一下这个通知功能吧!

造轮子

我们新建一个文件,美其名曰:Broadcast.js
在这里我采用ES6 Class的方式编写


//页面通知  

class Broadcast{  
    /**  
     * 构造器函数  
     */  
    constructor(){  

    }  

}  

我们先来实现最基础的两个功能

  • 监听事件(订阅)
  • 触发事件(发布)

//页面通知  

class Broadcast{  
    /**  
     * 构造器函数  
     */  
    constructor(){  

    }  

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     * @return {Broadcast} this  
     */  
    on(eventName, callback){  
        document.addEventListener(eventName, e => {  
            callback.call(e, e.detail)  
        })  
        return this  
    }  

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 参数  
     * @return {Broadcast} this  
     */  
    emit(eventName, data){  
        // 获取所有的webview  
        var all = plus.webview.all()  
        // 遍历全部页面  
        for(var w in all){  
            // 挨个来evalJS  
            all[w].evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {  
                detail:JSON.parse('${JSON.stringify(data)}'),  
                bubbles: true,  
                cancelable: true  
            }));`)  
        }  
        return this  
    }  

}  

ok, 我们在页面中引用并尝试用一下

A

        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我是A页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">打开B页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript" src="js/Broadcast.js" ></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    mui.openWindow('B.html')  
                })  
            })  
            new Broadcast().on('say', function(data){  
                alert(JSON.stringify(data))  
            })  
        </script>

B

        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我是B页面</h1>  
        </header>  
        <div class="mui-content">  
            <button type="button" class="mui-btn mui-btn-blue">通知A页面</button>  
        </div>  
        <script src="js/mui.min.js"></script>  
        <script type="text/javascript" src="js/Broadcast.js" ></script>  
        <script type="text/javascript">  
            mui.init()  
            mui.plusReady(function(){  
                document.querySelector("button").addEventListener('tap',function(){  
                    // 通知A页面的方法  
                    //var A = plus.webview.getLaunchWebview()  
                    //A.evalJS('alert("我是被B的")')  
                    new Broadcast().emit('say', {  
                        from: '我是B啊',  
                        id: 666  
                    })  
                })  
            })  
        </script>

点击B页面的按钮

clipboard.png

哇哈哈,基础功能已经实现了怎么样,
当然,这只是最基础的实现了监听,触发而已,后续还需要更多的优化,以及管理,辣么,下章见

上一章我们了解到通过webview evalJS的方法来跨页面通知事件,但是在其中还是有需要优化的地方,接下来我们慢慢的来分析。

上节回顾:【5+】跨webview多页面 触发事件(一)
代码:

//页面通知  

class Broadcast{  
    /**  
     * 构造器函数  
     */  
    constructor(){  

    }  

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     * @return {Broadcast} this  
     */  
    on(eventName, callback){  
        document.addEventListener(eventName, e => {  
            callback.call(e, e.detail)  
        })  
        return this  
    }  

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 参数  
     * @return {Broadcast} this  
     */  
    emit(eventName, data){  
        // 获取所有的webview  
        var all = plus.webview.all()  
        // 遍历全部页面  
        for(var w in all){  
            // 挨个来evalJS  
            all[w].evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {  
                detail:JSON.parse('${JSON.stringify(data)}'),  
                bubbles: true,  
                cancelable: true  
            }));`)  
        }  
        return this  
    }  

}

自定义需要通知页面

可以看到,之前我们emit发送通知时,是对所有的webview进行获取通知,但是有时候我们并不想通知所有的页面,而且通知别人的时候也不想通知自己啊,怎么办,在这里我们在emit方法参数多加一个配置项

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        //code...  
    }

然后我们针对传进来的拓展参数,进行逻辑判断,得到最终我们需要通知的webview list

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let all = []  
        // 获取 特定 webview 数组  
        if(views.length > 0) {  
            // 如果是string 类型,则统一处理获取为 webview对象  
            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)  
        } else {  
            // 不特定通知的webview数组时,直接获取全部已存在的webview  
            all = plus.webview.all()  
        }  
        // 如果不需要通知到当前webview 则过滤  
        if(!self) {  
            let v =  plus.webview.currentWebview()  
            all = all.filter(item => item.id !== v.id)  
        }  
        // 遍历所有需要通知的页面  
        for(let v of all) {  
            v.evalJS(`document.dispatchEvent(new CustomEvent('${eventName}', {  
                detail:JSON.parse('${JSON.stringify(data)}'),  
                bubbles: true,  
                cancelable: true  
            }));`)  
        }  
    }  

如何调用

new Broadcast().emit('say',{  
    name: 'newsning',  
    age: 26  
},{  
    self: true, // 通知当前页面 默认不通知  
    views: ['A.html','C.html'] // 默认通知所有页面,但不包括当前页面  
})  
// 如上代码就只通知到了3个页面, 当前页面, A页面, C页面

事件 - [ 订阅 | 发布 | 取消 ]

如果你遇到那种还需要移除监听事件,亦或者Once只监听一次的事件,再或是你看个代码不爽
clipboard.png

ok!我们来撸一套简单的 守望先锋模式,哦不,是观察者模式

事件订阅

瞧瞧我们之前的代码,on方法是直接把传进来的函数作为调用,这样子在外部调用时移除事件就没路子了,包括Once也很是蛋疼

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     * @return {Broadcast} this  
     */  
    on(eventName, callback){  
        document.addEventListener(eventName, e => {  
            callback.call(e, e.detail)  
        })  
        return this  
    }

我们先来定义好2个专门放置事件的存储对象,碧如 :

    // 事件列表  
    const events = {  
        // 事件名称 : 事件方法数组      
    },  
    // 单次事件列表  
    events_one = {  

    }

之后我们修改一下on方法,并新增一个once方法

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    on(eventName, callback) {  
        // 获取已存在的事件列表  
        if(!events[eventName]) {  
            events[eventName] = []  
        }  
        // 添加至数组  
        events[eventName].push(callback)  
    }  

    /**  
     * 事件监听 (单次)  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    once(eventName, callback) {  
        // 获取已存在的单次事件列表  
        if(!events_one[eventName]) {  
            events_one[eventName] = []  
        }  
        // 添加至数组  
        events_one[eventName].push(callback)  
    }

酱紫,每次添加事件时,都会放入我们的事件列表中,但是!我们并没有给任何dom添加事件,而仅仅是放入所对应的事件列表中,奇怪了,看看我们之前的添加事件方法

clipboard.png

给document监听一个事件

clipboard.png

触发document事件

nonono , 我们不这么借助document亦或者其它dom的事件监听,还记得上一章的 evalJS('faqme()')么?我们就用亲切的函数来触发事件

事件发布

在事件订阅当中,我们仅仅只是把事件放入了事件列表中,我们该如何触发?

编写一个静态方法,用来触发当前页面的事件, 然后通过

    static _emitSelf(eventName, data) {  
        if(typeof data === 'string') {  
            data = JSON.parse(data)  
        }  
        // 获取全部事件列表 和 单次事件列表,并且合并  
        let es = [...(events[eventName] || []), ...(events_one[eventName] || [])]  
        // 遍历触发  
        for(let f of es) {  
            f && f.call(f, data)  
        }  
        // 单次事件清空  
        events_one[eventName] = []  
    }

再配合修改一下 emit 里面的 evalJS

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let all = []  
        // 获取 特定 webview 数组  
        if(views.length > 0) {  
            // 如果是string 类型,则统一处理获取为 webview对象  
            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)  
        } else {  
            // 不特定通知的webview数组时,直接获取全部已存在的webview  
            all = plus.webview.all()  
        }  
        // 如果不需要通知到当前webview 则过滤  
        if(!self) {  
            let v =  plus.webview.currentWebview()  
            all = all.filter(item => item.id !== v.id)  
        }  
        // 遍历所有需要通知的页面  
        for(let v of all) {  
            /////////////////////////  
            ////////////////这里是重点, 调用Broadcast的静态方法  
            /////////////////////////  
            v.evalJS(`Broadcast && Broadcast._emitSelf && Broadcast._emitSelf('${eventName}', '${JSON.stringify(data)}')`)  
        }  
    }  

这样子,就巧妙的触发了每个webview页面 相对应的事件,并且单次事件也得到了清除

事件移除

我们知道前面的事件订阅只是将事件存起来了,事件移除相应的就是把事件列表清空

    static _offSelf(eventName) {  
        //清空事件列表  
        events[eventName] = []  
        events_one[eventName] = []  
    }

最后收尾

所定义的2个静态方法,触发 和 移除 事件,我们在内部代理2个相应的方法

    /**  
     * 当前页面事件触发   
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     */  
    emitSelf(eventName) {  
        Broadcast._emitSelf(eventName, data)  
    }  

    /**  
     * 清空当前页面事件   
     * @param {String} eventName 事件名称  
     */  
    offSelf(eventName) {  
        Broadcast._offSelf(eventName)  
    }

最后,成果已经出现

A.html

            var b = new Broadcast()  

            b.on('say', function(data){  
                alert(JSON.stringify(data))  

                // 删除本页面say事件  
                //b.offSelf('say')  
            })  

            b.once('say', function(data){  
                //单次  
                alert('单次:'+JSON.stringify(data))  
            })

B.html

            new Broadcast().emit('say', {  
                from: '我是B啊',  
                id: 666  
            })

最后附上源码:

/**  
 * 5+ Broadcast.js by NewsNing 宁大大   
 */  

// 获取当前webview  
const getIndexView = (() => {  
        // 缓存  
        let indexView = null  
        return(update = false) => {  
            if(update || indexView === null) {  
                indexView = plus.webview.currentWebview()  
            }  
            return indexView  
        }  
    })(),  
    // 获取全部webview   
    getAllWebview = (() => {  
        // 缓存  
        let allView = null  
        return(update = false) => {  
            if(update || allView === null) {  
                allView = plus.webview.all()  
            }  
            return allView  
        }  
    })()  

// 事件列表  
const events = {  

    },  
    // 单次事件列表  
    events_one = {  

    }  

//页面通知类  
class Broadcast {  
    /**  
     * 构造器函数  
     */  
    constructor() {  

    }  

    /**  
     * 事件监听  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    on(eventName, callback) {  
        // 获取已存在的事件列表  
        if(!events[eventName]) {  
            events[eventName] = []  
        }  
        // 添加至数组  
        events[eventName].push(callback)  
    }  

    /**  
     * 事件监听 (单次)  
     * @param {String} eventName 事件名称  
     * @param {Function} callback 事件触发后执行的回调函数  
     */  
    once(eventName, callback) {  
        // 获取已存在的单次事件列表  
        if(!events_one[eventName]) {  
            events_one[eventName] = []  
        }  
        // 添加至数组  
        events_one[eventName].push(callback)  
    }  

    /**  
     * 事件触发  
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     * @param {Object} options 其它配置参数  
     */  
    emit(eventName, data, {  
        self = false, // 是否通知自己,默认不通知  
        views = [], // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let jsstr = `Broadcast && Broadcast._emitSelf && Broadcast._emitSelf('${eventName}', '${JSON.stringify(data)}')`  
        this._sendMessage(jsstr, self, views)  
    }  

    /**  
     * 当前页面事件触发   
     * @param {String} eventName 事件名称  
     * @param {Object} data 传参参数值  
     */  
    emitSelf(eventName) {  
        Broadcast._emitSelf(eventName, data)  
    }  

    /**  
     * 事件关闭移除  
     * @param {String} eventName 事件名称  
     * @param {Object} options 其它配置参数  
     */  
    off(eventName, {  
        self = false, // 是否通知自己,默认不通知  
        views = [] // 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
    } = {}) {  
        let jsstr = `Broadcast && Broadcast._offSelf && Broadcast._offSelf('${eventName}')`  
        this._sendMessage(jsstr, self, views)  
    }  

    /**  
     * 清空当前页面事件    
     * @param {String} eventName 事件名称  
     */  
    offSelf(eventName) {  
        Broadcast._offSelf(eventName)  
    }  

    /**  
     * 页面通知  
     * @param {String} jsstr 需要运行的js代码  
     * @param {Boolean} self 是否通知自己,默认不通知  
     * @param {Array} views 为空数组时,默认通知全部,为string数组时,认为是id,为object时,认为是webview对象  
     */  
    _sendMessage(  
        jsstr = '',  
        self = false,  
        views = []  
    ) {  
        let all = []  
        // 获取 特定 webview 数组  
        if(views.length > 0) {  
            // 如果是string 类型,则统一处理获取为 webview对象  
            all.map(item => typeof item === 'string' ? plus.webview.getWebviewById(item) : item)  
        } else {  
            // 不特定通知的webview数组时,直接获取全部已存在的webview  
            all = getAllWebview(true)  
        }  
        // 如果不需要通知到当前webview 则过滤  
        if(!self) {  
            let v = getIndexView()  
            all = all.filter(item => item.id !== v.id)  
        }  
        // 遍历全部页面  
        for(let v of all) {  
            v.evalJS(jsstr)  
        }  
    }  

    static _emitSelf(eventName, data) {  
        if(typeof data === 'string') {  
            data = JSON.parse(data)  
        }  
        // 获取全部事件列表 和 单次事件列表,并且合并  
        let es = [...(events[eventName] || []), ...(events_one[eventName] || [])]  
        // 遍历触发  
        for(let f of es) {  
            f && f.call(f, data)  
        }  
        // 单次事件清空  
        events_one[eventName] = []  
    }  

    static _offSelf(eventName) {  
        //清空事件列表  
        events[eventName] = []  
        events_one[eventName] = []  
    }  

}

您也可以通过babel在线转化成es5 在线转换地址

clipboard.png

最后您还可以在github上看到一些其它5+ Api封装的源码 [5+ api整合]

https://github.com/NewsNIng/ni

class Man{  
    constructor(){  
        this.name = 'newsning'  
    }  
    say(){  
        console.log('天行健, 君子以自强不息. ')  
    }  
}
收起阅读 »

如何获取证书md5值和sha1

如何获取证书的md5和sha1?

如何获取证书的md5和sha1?

咨询一下wap2app,能否支持打包成独立的app安装版本

如题,测试了一下,我们线下有很多客户,他们委托我们给他们开发了很多html5移动页面应用,可以在微信里面使用,现在他们希望能够转换成独立的app,我试用了你们的wap2app,发现只能打包成流应用,能够直接打包成独立app?

如题,测试了一下,我们线下有很多客户,他们委托我们给他们开发了很多html5移动页面应用,可以在微信里面使用,现在他们希望能够转换成独立的app,我试用了你们的wap2app,发现只能打包成流应用,能够直接打包成独立app?

MUI开发 

外包

我司在做互联网教育APP开发,基于H5跨平台APP开发,包括微信及其他各种主流平台。
有兴趣合作者,加QQ1153522373或微信 waynxh  王先生

我司在做互联网教育APP开发,基于H5跨平台APP开发,包括微信及其他各种主流平台。
有兴趣合作者,加QQ1153522373或微信 waynxh  王先生

Hbuilder集成微信支付教程(简单流程)

微信支付

整个系统运作的流程大致是这样:

(1)APP服务器 发出需要付款的请求
(2)服务器 请求 微信服务端 下单
(3)服务器 将下单数据回复给 APP
(4)APP微信服务端 发起请求并输入密码进行支付

关键字用加粗表示,APP指用户正在使用的APP,服务器指自己的业务服务器,微信服务端指微信端的服务器。

那整个流程具体怎么实现?请看下文,将会从申请接口开始讲述。

第一步

在浏览器新标签页中打开https://open.weixin.qq.com/,注册一个账号,并且审核开发者资质(个人或者是企业)。

创建一个APP移动应用,应用的开发信息需要填写完整,如下载页面地址、Bundle ID(IOS)和应用签名(Android)等。应用签名不要填错,怎么获取它可以网上搜索方法。

接下来为该应用申请微信支付,填写好相应的资料,等待审核成功。

第二步

申请成功后,邮箱会收到这么一封邮件。

用这封邮件上显示的账号密码,登录微信支付,地址是:https://pay.weixin.qq.com/index.php/core/home/login

把网页上提示需要设置的都设置好,需要的控件也都给它装上。最好使用IE浏览器登录微信支付,谷歌浏览器会出现问题,原因是它们的插件不是很兼容非IE浏览器。

API密钥可以先设置好,证书也可以先下载下来,后面会用到。

第三步

之前做支付宝接口的时候,这一步基本都是开始进行小额测试了,但是微信为了广大的开发者能够在真枪实弹之前先熟悉这一切,所以就增加了一个“验收”的过程(个人表示这一步略坑,文档写的不清楚,走了些弯路)。

验收的指引文档在这里:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1

这个“验收”就是在实际支付交易之前,先进行一些虚拟的交易。交易过程同真实交易一样,也是先在APP上买东西,调用微信支付,然后付款,但是不用真正的付款,商户账号也不会收到钱。

千万别忘记,要先打开微信,关注“微信支付商户接入验收助手”,然后绑定自己的微信支付账号。

依次点击“验收case”->“app支付”->“APP支付验收用例”,就可看到完成“验收”过程需要哪些步骤了。

标注了“必选”的用例都要去做掉,“可选”的可以不用管它。

每个用例都规定了交易的金额,一定要按照这个金额来支付,否则不会成功。

第四步

“验收”其实就是把整个支付流程全部跑一遍,所以APP中要记得集成微信支付插件。

Hbuilder在云打包时会自动集成微信支付,只要在manifest.json的“SDK配置”中填写好相关信息就可以了。

原生APP需要自己去集成,具体方法就要自己看文档实现了。

服务端程序需要按照https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1文档中的API去实现,“验收”过程中,所有的URL地址都需要加上sandboxnew,而且API密钥不能用上文提到自己去填写的(这是验收结束后真枪实弹时用的),而是从https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey获取的,这些在验收指引文档中都有说明。

坑1提示:“验收”过程中,在调用微信客户端支付时,微信会提示APP ID没有绑定PAY SIGN KEY。其实这个时候已经算是支付成功了,别管它,直接点确定!

坑2提示:服务器从微信服务端获取到下单数据,要给APP回复时,如果用Hbuilder,其实回复的是一串JSON数据,别把XML数据回复过去了(会提示支付失败-1)。格式如下:

{“appid”:”应用APPID”,”noncestr”:”随机数”,”package”:”Sign=WXPay”,”partnerid”:”微信支付商户号”,”prepayid”:”XXXXXXXXXXXX”,”timestamp”:时间戳,”sign”:”签名”}

将XML数据转成以上的JSON数据回给APP就可以了,记得重新签名要重新生成!

第五步

每完成一个验收流程,都要去微信公众号的“我的验收”->“查询验收结果”->“查询用例接入结果”->“APP支付”中查看一下,确定是否成功。

等所有“必选”用例全部完成,那么恭喜你,验收完成。这时别再用验收时获取到的API密钥了,用自己填写的API密钥开始真枪实弹地小额测试一笔吧。

涉及到退款等资金回滚的API业务,微信支付要求使用证书(上文说要下载的)来验证安全,如果服务器用的PHP就在CURL中设置好证书地址,方法在微信支付文档中有。

微信支付相比支付宝支付有一点好处,就是退款后,微信支付会退回扣的千分之六手续费,而支付宝不会退。

原文地址:http://blog.luotiankeji.com/252.html

继续阅读 »

整个系统运作的流程大致是这样:

(1)APP服务器 发出需要付款的请求
(2)服务器 请求 微信服务端 下单
(3)服务器 将下单数据回复给 APP
(4)APP微信服务端 发起请求并输入密码进行支付

关键字用加粗表示,APP指用户正在使用的APP,服务器指自己的业务服务器,微信服务端指微信端的服务器。

那整个流程具体怎么实现?请看下文,将会从申请接口开始讲述。

第一步

在浏览器新标签页中打开https://open.weixin.qq.com/,注册一个账号,并且审核开发者资质(个人或者是企业)。

创建一个APP移动应用,应用的开发信息需要填写完整,如下载页面地址、Bundle ID(IOS)和应用签名(Android)等。应用签名不要填错,怎么获取它可以网上搜索方法。

接下来为该应用申请微信支付,填写好相应的资料,等待审核成功。

第二步

申请成功后,邮箱会收到这么一封邮件。

用这封邮件上显示的账号密码,登录微信支付,地址是:https://pay.weixin.qq.com/index.php/core/home/login

把网页上提示需要设置的都设置好,需要的控件也都给它装上。最好使用IE浏览器登录微信支付,谷歌浏览器会出现问题,原因是它们的插件不是很兼容非IE浏览器。

API密钥可以先设置好,证书也可以先下载下来,后面会用到。

第三步

之前做支付宝接口的时候,这一步基本都是开始进行小额测试了,但是微信为了广大的开发者能够在真枪实弹之前先熟悉这一切,所以就增加了一个“验收”的过程(个人表示这一步略坑,文档写的不清楚,走了些弯路)。

验收的指引文档在这里:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1

这个“验收”就是在实际支付交易之前,先进行一些虚拟的交易。交易过程同真实交易一样,也是先在APP上买东西,调用微信支付,然后付款,但是不用真正的付款,商户账号也不会收到钱。

千万别忘记,要先打开微信,关注“微信支付商户接入验收助手”,然后绑定自己的微信支付账号。

依次点击“验收case”->“app支付”->“APP支付验收用例”,就可看到完成“验收”过程需要哪些步骤了。

标注了“必选”的用例都要去做掉,“可选”的可以不用管它。

每个用例都规定了交易的金额,一定要按照这个金额来支付,否则不会成功。

第四步

“验收”其实就是把整个支付流程全部跑一遍,所以APP中要记得集成微信支付插件。

Hbuilder在云打包时会自动集成微信支付,只要在manifest.json的“SDK配置”中填写好相关信息就可以了。

原生APP需要自己去集成,具体方法就要自己看文档实现了。

服务端程序需要按照https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1文档中的API去实现,“验收”过程中,所有的URL地址都需要加上sandboxnew,而且API密钥不能用上文提到自己去填写的(这是验收结束后真枪实弹时用的),而是从https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey获取的,这些在验收指引文档中都有说明。

坑1提示:“验收”过程中,在调用微信客户端支付时,微信会提示APP ID没有绑定PAY SIGN KEY。其实这个时候已经算是支付成功了,别管它,直接点确定!

坑2提示:服务器从微信服务端获取到下单数据,要给APP回复时,如果用Hbuilder,其实回复的是一串JSON数据,别把XML数据回复过去了(会提示支付失败-1)。格式如下:

{“appid”:”应用APPID”,”noncestr”:”随机数”,”package”:”Sign=WXPay”,”partnerid”:”微信支付商户号”,”prepayid”:”XXXXXXXXXXXX”,”timestamp”:时间戳,”sign”:”签名”}

将XML数据转成以上的JSON数据回给APP就可以了,记得重新签名要重新生成!

第五步

每完成一个验收流程,都要去微信公众号的“我的验收”->“查询验收结果”->“查询用例接入结果”->“APP支付”中查看一下,确定是否成功。

等所有“必选”用例全部完成,那么恭喜你,验收完成。这时别再用验收时获取到的API密钥了,用自己填写的API密钥开始真枪实弹地小额测试一笔吧。

涉及到退款等资金回滚的API业务,微信支付要求使用证书(上文说要下载的)来验证安全,如果服务器用的PHP就在CURL中设置好证书地址,方法在微信支付文档中有。

微信支付相比支付宝支付有一点好处,就是退款后,微信支付会退回扣的千分之六手续费,而支付宝不会退。

原文地址:http://blog.luotiankeji.com/252.html

收起阅读 »

IOS指纹识别插件开发方法

插件开发 5+sdk

更新:HTML5+已经自带指纹识别。http://www.html5plus.org/doc/zh_cn/fingerprint.html。以下文档已过期。

之前一直考虑使用Native.js调用原生方法去实现指纹识别,但是,发现不行,因为Native.js如果调用原生方法中含有闭包的情况时,JS的回调函数无法正确识别参数的内容。
下面来说一下插件的开发,发现还是插件的开发灵活方便。
开发插件需要具备以下准备:
1、MAC系统并安装XCode。
2、按照官方文档配置好5+SDK的配置,并且调试成功。

以上准备工作完成后,就可以开发了:
1、在项目工程中,新建一个FingerprintIdent类,内容如下:
FingerprintIdent.h文件

#import <UIKit/UIKit.h>  
#import <PGPlugin.h>  
#import <PGMethod.h>  

@interface FingerprintIdent : PGPlugin  

// 是否支持指纹识别  
- (void)canEvaluatePolicy:(PGMethod*)command;  

// 弹出指纹识别窗口的方法  
- (void)evaluatePolicy:(PGMethod*)command;  

@end

FingerprintIdent.m文件

#import "FingerprintIdent.h"  
#import <LocalAuthentication/LocalAuthentication.h>  

@implementation FingerprintIdent  

- (void)canEvaluatePolicy:(PGMethod*)command{  
    if(command) {  
        NSString* cbId = [command.arguments objectAtIndex:0];  
        LAContext *lacontent = [[LAContext alloc] init];  
        NSError *error = nil;  
        if([lacontent canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]){  
            [self toSucessCallback:cbId withInt:1];  
        } else {  
            [self toSucessCallback:cbId withInt:0];  
        }  

    }  
}  

- (void)evaluatePolicy:(PGMethod*)command{  
    if(command) {  
        NSString* cbId = [command.arguments objectAtIndex:0];  
        NSString* pArgument1 = [command.arguments objectAtIndex:1];  
        LAContext *lacontent = [[LAContext alloc] init];  
        NSError *error = nil;  
        if([lacontent canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]){  
            [lacontent evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:pArgument1 reply:^(BOOL success, NSError * _Nullable error) {  
                NSMutableDictionary *result = [NSMutableDictionary dictionary];  
                if(success){  
                    [result setValue:@"" forKey:@"error"];  
                } else {  
                    NSString *errorMsg;  
                    switch (error.code) {  
                        case LAErrorSystemCancel:  
                        {  
                            errorMsg = @"切换到其他APP,系统取消验证Touch ID";  
                            break;  
                        }  
                        case LAErrorUserCancel:  
                        {  
                            errorMsg = @"用户取消验证Touch ID";  
                            break;  
                        }  
                        case LAErrorUserFallback:  
                        {  
                            errorMsg = @"用户选择输入密码,切换主线程处理";  
                            break;  
                        }  
                        default:  
                        {  
                            errorMsg = @"其他情况,切换主线程处理";  
                            break;  
                        }  
                    }  
                    [result setValue:errorMsg forKey:@"error"];  
                }  
                NSData *data=[NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:nil];  
                NSString *jsonStr=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  
                [self toSucessCallback:cbId withString:jsonStr];  
            }];  
        } else {  
            [self toSucessCallback:cbId withInt:0];  
        }  
    }  
}  
@end

2、接下来,设置几项内容。
在Xcode项目中找到PandoraApi.bundle >> feature.plist
在里面添加一项 key为FingerprintIdent, type为Dictionary。在里面新建一子项 key为class ,type为String, value为FingerprintIdent。在新建一个子项 key为global ,type为Boolean, value为NO。

3、编辑你的测试页面test.html
内容如下:

<!doctype html>  
<html>  
    <head>  
        <meta charset="UTF-8">  
        <title>登录</title>  
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">  
        <meta name="apple-mobile-web-app-capable" content="yes">  
        <meta name="apple-mobile-web-app-status-bar-style" content="black">  
    </head>  
    <body>  
        <div class="content">  
                    <div class="button" onclick="iscanEvaluatePolicy()">是否支持指纹识别</div>  
                    <div class="button" onclick="toEvaluatePolicy()">指纹识别</div>  
        </div>  
    <script src="js/mui.min.js"></script>  
    <script type="text/javascript">  
      document.addEventListener( "plusready",  function() {  
        // 声明的JS“扩展插件别名”  
        var _BARCODE = 'FingerprintIdent',  
        B = window.plus.bridge;  
        var fingerprintIdent = {  
            canEvaluatePolicy : function (successCallback, errorCallback ) {  
              var success = typeof successCallback !== 'function' ? null : function(args) {  
                successCallback(args);  
              },  
              fail = typeof errorCallback !== 'function' ? null : function(code) {  
                errorCallback(code);  
              };  
              callbackID = B.callbackId(success, fail);  
              // 通知Native层plugintest扩展插件运行”PluginTestFunction”方法  
              return B.exec(_BARCODE, "canEvaluatePolicy", [callbackID]);  
            },  
            evaluatePolicy : function (Argus1, successCallback, errorCallback ) {  
              var success = typeof successCallback !== 'function' ? null : function(args) {  
                successCallback(args);  
              },  
              fail = typeof errorCallback !== 'function' ? null : function(code) {  
                errorCallback(code);  
              };  
              callbackID = B.callbackId(success, fail);  
              // 通知Native层plugintest扩展插件运行”PluginTestFunction”方法  
              return B.exec(_BARCODE, "evaluatePolicy", [callbackID, Argus1]);  
            }  
        };  
        window.plus.fingerprintIdent = fingerprintIdent;  
      }, true );  

    // 是否支持指纹识别  
      function iscanEvaluatePolicy() {  
        plus.fingerprintIdent.canEvaluatePolicy(function( result ) {  
            alert( result);  
          },  
          function(result){  
            //alert(result)  
          });  
      }  
    // 校验指纹识别  
      function toEvaluatePolicy() {  
        plus.fingerprintIdent.evaluatePolicy('请校验指纹', function( result ) {  
            var data = mui.parseJSON(result);  
            if(!data.error) {  
                alert('校验成功');  
            } else {  
               alert( '错误原因:'+data.error);  
            }  
          },  
          function(result){  
            //alert(result)  
          });  
      }  
    </script>  
    </body>  
</html>

OK,至此,IOS指纹识别插件已经开发完成。但是记住,一定要在本地使用XCode调试并打包才会生效。看到官方文档里面的IOS插件实例还是空的,我这提供一个实例。哈哈。

继续阅读 »

更新:HTML5+已经自带指纹识别。http://www.html5plus.org/doc/zh_cn/fingerprint.html。以下文档已过期。

之前一直考虑使用Native.js调用原生方法去实现指纹识别,但是,发现不行,因为Native.js如果调用原生方法中含有闭包的情况时,JS的回调函数无法正确识别参数的内容。
下面来说一下插件的开发,发现还是插件的开发灵活方便。
开发插件需要具备以下准备:
1、MAC系统并安装XCode。
2、按照官方文档配置好5+SDK的配置,并且调试成功。

以上准备工作完成后,就可以开发了:
1、在项目工程中,新建一个FingerprintIdent类,内容如下:
FingerprintIdent.h文件

#import <UIKit/UIKit.h>  
#import <PGPlugin.h>  
#import <PGMethod.h>  

@interface FingerprintIdent : PGPlugin  

// 是否支持指纹识别  
- (void)canEvaluatePolicy:(PGMethod*)command;  

// 弹出指纹识别窗口的方法  
- (void)evaluatePolicy:(PGMethod*)command;  

@end

FingerprintIdent.m文件

#import "FingerprintIdent.h"  
#import <LocalAuthentication/LocalAuthentication.h>  

@implementation FingerprintIdent  

- (void)canEvaluatePolicy:(PGMethod*)command{  
    if(command) {  
        NSString* cbId = [command.arguments objectAtIndex:0];  
        LAContext *lacontent = [[LAContext alloc] init];  
        NSError *error = nil;  
        if([lacontent canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]){  
            [self toSucessCallback:cbId withInt:1];  
        } else {  
            [self toSucessCallback:cbId withInt:0];  
        }  

    }  
}  

- (void)evaluatePolicy:(PGMethod*)command{  
    if(command) {  
        NSString* cbId = [command.arguments objectAtIndex:0];  
        NSString* pArgument1 = [command.arguments objectAtIndex:1];  
        LAContext *lacontent = [[LAContext alloc] init];  
        NSError *error = nil;  
        if([lacontent canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]){  
            [lacontent evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:pArgument1 reply:^(BOOL success, NSError * _Nullable error) {  
                NSMutableDictionary *result = [NSMutableDictionary dictionary];  
                if(success){  
                    [result setValue:@"" forKey:@"error"];  
                } else {  
                    NSString *errorMsg;  
                    switch (error.code) {  
                        case LAErrorSystemCancel:  
                        {  
                            errorMsg = @"切换到其他APP,系统取消验证Touch ID";  
                            break;  
                        }  
                        case LAErrorUserCancel:  
                        {  
                            errorMsg = @"用户取消验证Touch ID";  
                            break;  
                        }  
                        case LAErrorUserFallback:  
                        {  
                            errorMsg = @"用户选择输入密码,切换主线程处理";  
                            break;  
                        }  
                        default:  
                        {  
                            errorMsg = @"其他情况,切换主线程处理";  
                            break;  
                        }  
                    }  
                    [result setValue:errorMsg forKey:@"error"];  
                }  
                NSData *data=[NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:nil];  
                NSString *jsonStr=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  
                [self toSucessCallback:cbId withString:jsonStr];  
            }];  
        } else {  
            [self toSucessCallback:cbId withInt:0];  
        }  
    }  
}  
@end

2、接下来,设置几项内容。
在Xcode项目中找到PandoraApi.bundle >> feature.plist
在里面添加一项 key为FingerprintIdent, type为Dictionary。在里面新建一子项 key为class ,type为String, value为FingerprintIdent。在新建一个子项 key为global ,type为Boolean, value为NO。

3、编辑你的测试页面test.html
内容如下:

<!doctype html>  
<html>  
    <head>  
        <meta charset="UTF-8">  
        <title>登录</title>  
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">  
        <meta name="apple-mobile-web-app-capable" content="yes">  
        <meta name="apple-mobile-web-app-status-bar-style" content="black">  
    </head>  
    <body>  
        <div class="content">  
                    <div class="button" onclick="iscanEvaluatePolicy()">是否支持指纹识别</div>  
                    <div class="button" onclick="toEvaluatePolicy()">指纹识别</div>  
        </div>  
    <script src="js/mui.min.js"></script>  
    <script type="text/javascript">  
      document.addEventListener( "plusready",  function() {  
        // 声明的JS“扩展插件别名”  
        var _BARCODE = 'FingerprintIdent',  
        B = window.plus.bridge;  
        var fingerprintIdent = {  
            canEvaluatePolicy : function (successCallback, errorCallback ) {  
              var success = typeof successCallback !== 'function' ? null : function(args) {  
                successCallback(args);  
              },  
              fail = typeof errorCallback !== 'function' ? null : function(code) {  
                errorCallback(code);  
              };  
              callbackID = B.callbackId(success, fail);  
              // 通知Native层plugintest扩展插件运行”PluginTestFunction”方法  
              return B.exec(_BARCODE, "canEvaluatePolicy", [callbackID]);  
            },  
            evaluatePolicy : function (Argus1, successCallback, errorCallback ) {  
              var success = typeof successCallback !== 'function' ? null : function(args) {  
                successCallback(args);  
              },  
              fail = typeof errorCallback !== 'function' ? null : function(code) {  
                errorCallback(code);  
              };  
              callbackID = B.callbackId(success, fail);  
              // 通知Native层plugintest扩展插件运行”PluginTestFunction”方法  
              return B.exec(_BARCODE, "evaluatePolicy", [callbackID, Argus1]);  
            }  
        };  
        window.plus.fingerprintIdent = fingerprintIdent;  
      }, true );  

    // 是否支持指纹识别  
      function iscanEvaluatePolicy() {  
        plus.fingerprintIdent.canEvaluatePolicy(function( result ) {  
            alert( result);  
          },  
          function(result){  
            //alert(result)  
          });  
      }  
    // 校验指纹识别  
      function toEvaluatePolicy() {  
        plus.fingerprintIdent.evaluatePolicy('请校验指纹', function( result ) {  
            var data = mui.parseJSON(result);  
            if(!data.error) {  
                alert('校验成功');  
            } else {  
               alert( '错误原因:'+data.error);  
            }  
          },  
          function(result){  
            //alert(result)  
          });  
      }  
    </script>  
    </body>  
</html>

OK,至此,IOS指纹识别插件已经开发完成。但是记住,一定要在本地使用XCode调试并打包才会生效。看到官方文档里面的IOS插件实例还是空的,我这提供一个实例。哈哈。

收起阅读 »

滚动条不回归开始位置

滚动 区域滚动 scroll

官方教程,滚动区域需要加入如下代码
<div class="mui-scroll-wrapper">
<div class="mui-scroll">
<!--这里放置真实显示的DOM内容-->
</div>
</div>

options = {
scrollY: true, //是否竖向滚动
scrollX: false, //是否横向滚动
startX: 0, //初始化时滚动至x
startY: 0, //初始化时滚动至y
indicators: true, //是否显示滚动条
deceleration:0.0006, //阻尼系数,系数越小滑动越灵敏
bounce: true //是否启用回弹
}

mui('.mui-scroll-wrapper').scroll(options);
这样添加好代码,当内容滚动一段距离,如果有需求需要更换滚动区域的内容,特别是后加载的内容比之前少,会发现看不到数据,需要下拉一下滚动区域。
查看了一下 滚动区域是通过 transform 实现的,可以动态的去修改 transform 就可以实现回归位置

var scroll= mui(".mui-scroll-wrapper .mui-scroll")[0];
scroll.style.transform ="translate3d(0px, 0px, 0px) translateZ(0px)";

继续阅读 »

官方教程,滚动区域需要加入如下代码
<div class="mui-scroll-wrapper">
<div class="mui-scroll">
<!--这里放置真实显示的DOM内容-->
</div>
</div>

options = {
scrollY: true, //是否竖向滚动
scrollX: false, //是否横向滚动
startX: 0, //初始化时滚动至x
startY: 0, //初始化时滚动至y
indicators: true, //是否显示滚动条
deceleration:0.0006, //阻尼系数,系数越小滑动越灵敏
bounce: true //是否启用回弹
}

mui('.mui-scroll-wrapper').scroll(options);
这样添加好代码,当内容滚动一段距离,如果有需求需要更换滚动区域的内容,特别是后加载的内容比之前少,会发现看不到数据,需要下拉一下滚动区域。
查看了一下 滚动区域是通过 transform 实现的,可以动态的去修改 transform 就可以实现回归位置

var scroll= mui(".mui-scroll-wrapper .mui-scroll")[0];
scroll.style.transform ="translate3d(0px, 0px, 0px) translateZ(0px)";

收起阅读 »

DCloud赞助W3C,2017年W3C顾问委员会在京顺利召开;HBuilder 8.1.2版启动异常及Android平台地图问题的解决办法

W3C HTML5

【公告】关于HBuilder 8.1.2不能正常启动,Android平台地图卡顿、POI搜索后不显示数据问题的解决办法,请参考http://ask.dcloud.net.cn/article/1356

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

近日,2017 W3C顾问委员会会议(W3C AC 2017)在北京的国家会议中心顺利举行。本届会议共安排了8个会议环节,覆盖W3C的愿景与战略发展、加强Web核心、下一代用户体验与工业需求、延伸Web的覆盖度内容、W3C流程与AB优先级等话题。包括DCloud在内,共有近100名W3C的会员代表及全球团队成员参加了本次AC会议。

W3C顾问委员会是W3C全球团队与W3C会员交流的重要平台,每年在全球举行两次,仅面向会员开放。

W3C全球CEO Jeff在开场致谢大会赞助商阿里巴巴和DCloud:
W3C全球CEO Jeff在开场致谢大会赞助商阿里巴巴和DCloud

在本次会议中,DCloud CEO、HTML5中国产业联盟秘书长王安,向包括W3C CEO在内的众多组织成员和领导汇报了HTML5在中国的发展情况,分析了在移动领域,为什么web没有成为主流计算平台。加载速度、渲染流畅度、功能不足、可安装性不足、弱网适应能力差是当前HTML5的瓶颈问题。用户更喜欢用原生版本,而开发商也发现原生版本的留存率和付费转化率超过HTML5版本数倍。HTML5中国产业联盟通过HTML5+扩展标准做出了积极的尝试,目前已经在商用环境中基于HTML5+标准已经做出了大量功能体验、留存、付费转换等业务数据与原生一致的产品。

在中国,因为腾讯小程序的强势和谷歌的缺位,HTML5强化扩展这个产业发展非常快,行业竞争激烈程度是国外所没有的。在讨论中W3C CEO Jeff回复称在中国这么大的市场出现了这样的新的突破他并不惊讶,希望中国的标准组织能够与W3C有更紧密的合作,将这些商用经验输出到全球。

DCloud CEO、HTML5中国产业联盟秘书长王安 在工作会议中讲话:
DCloud CEO、HTML5中国产业联盟秘书长王安 在工作会议中讲话

作为W3C的会员,DCloud一直秉承标准化路线,推进繁荣开放的增强型web生态。以后也将继续与W3C全球保持紧密沟通,增强中国技术标准在全球的影响力!

继续阅读 »

【公告】关于HBuilder 8.1.2不能正常启动,Android平台地图卡顿、POI搜索后不显示数据问题的解决办法,请参考http://ask.dcloud.net.cn/article/1356

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

近日,2017 W3C顾问委员会会议(W3C AC 2017)在北京的国家会议中心顺利举行。本届会议共安排了8个会议环节,覆盖W3C的愿景与战略发展、加强Web核心、下一代用户体验与工业需求、延伸Web的覆盖度内容、W3C流程与AB优先级等话题。包括DCloud在内,共有近100名W3C的会员代表及全球团队成员参加了本次AC会议。

W3C顾问委员会是W3C全球团队与W3C会员交流的重要平台,每年在全球举行两次,仅面向会员开放。

W3C全球CEO Jeff在开场致谢大会赞助商阿里巴巴和DCloud:
W3C全球CEO Jeff在开场致谢大会赞助商阿里巴巴和DCloud

在本次会议中,DCloud CEO、HTML5中国产业联盟秘书长王安,向包括W3C CEO在内的众多组织成员和领导汇报了HTML5在中国的发展情况,分析了在移动领域,为什么web没有成为主流计算平台。加载速度、渲染流畅度、功能不足、可安装性不足、弱网适应能力差是当前HTML5的瓶颈问题。用户更喜欢用原生版本,而开发商也发现原生版本的留存率和付费转化率超过HTML5版本数倍。HTML5中国产业联盟通过HTML5+扩展标准做出了积极的尝试,目前已经在商用环境中基于HTML5+标准已经做出了大量功能体验、留存、付费转换等业务数据与原生一致的产品。

在中国,因为腾讯小程序的强势和谷歌的缺位,HTML5强化扩展这个产业发展非常快,行业竞争激烈程度是国外所没有的。在讨论中W3C CEO Jeff回复称在中国这么大的市场出现了这样的新的突破他并不惊讶,希望中国的标准组织能够与W3C有更紧密的合作,将这些商用经验输出到全球。

DCloud CEO、HTML5中国产业联盟秘书长王安 在工作会议中讲话:
DCloud CEO、HTML5中国产业联盟秘书长王安 在工作会议中讲话

作为W3C的会员,DCloud一直秉承标准化路线,推进繁荣开放的增强型web生态。以后也将继续与W3C全球保持紧密沟通,增强中国技术标准在全球的影响力!

收起阅读 »

ThinkPHP5.0入门到精通(含项目)

ThinkPHP5.0入门到精通
http://study.163.com/course/courseMain.htm?courseId=1003838084
课程介绍:
第一部分:
Thinkphp5.0入门教程,使开发者快速从thinkphp3.2版本过度到thinkphp5.0,最高效的降低学习成本,不研究概念性的东西,只讲究实际开发中用到的东西。
第二部分:
属于高级教程,有一定的难度,开发项目中常用的到类库或则组件,都会陆续更新上去,比如phpmailer邮件服务,阿里云oss存储服务,
支付宝支付,微信支付,七牛云存错,图片上传等等,整理成插件的形式,便于学习和使用。
由于课程标题数量限制,只能突出一部分知识点。

有php的基础知识,有了解过thinkphp方面的知识。

第一阶段(学前私信:835173372)
001:站点部署环境和框架解释
002:代码执行流程和命名空间
003:数据库配置和引入db类库

004:数据库CURD-查询数据,插入数据
单条查询,批量查询,单条插入,批量插入。

005:数据库CURD-更新数据,删除数据
单条更新,批量更新,单条删除,批量删除,字段加1/n,字段健1/n

006:数据库CURD-查询方法,查询语法
getTableInfo()方法,where()F方法,whereOr()方法,混合查询,LIKE查询, BETWEEN查询等等

007:数据库CURD-多表联查,缓存查询
alias(),join(),limit(),group(),order(),cache(),select()使用技巧。

008:数据库CURD-分页查询,聚合查询
page(),limit(),count(),max(),min(),avg()使用技巧

009:数据请求-请求信息,请求接受变量
Request类的引入和使用,is_Get();is_Post();is_Ajax();is_Mobile();input()助手函数的使用技巧

010:数据请求-伪静态,公共方法创建等
数据请求-伪静态,HTTP头信息,公共方法引入等

011:控制器初始化-空操作-跳转重定向等
控制器定义,初始化,空操作,跳转重定向详解

012:视图,模板赋值渲染,循环遍历等等
模板引擎,模板赋值,模板渲染,模板定位,模板标签,变量输出,循环输出标签,比较标签,原生php,定义标签。

013:Tp5.0-session功能的封装和执行原理
Session的创建,获取,删除,助手函数的使用。

014:分页实现-内置Paginator对象详解等
分页实现(内置Paginator对象来实现)

015:Workerman-PHP socket服务器框架
修改站点入口地址和Workerman-PHP socket服务器框架

插件开发
016:搭建PHPMailer类库开发邮件发送
git地址:https://github.com/PHPMailer/PHPMailer
php.ini注意开启ssl服务,命名空间的注意事项,smtp授权码的概念,网易邮箱客户端端口465

017:Tp5.0验证码的创建和验证使用技巧
验证码类库(think-captcha)引入使用,一个页面创建个多个验证吗,多个验证码的验证等等

018:验证码的ajax动态切换效果开发案例
cdn模式引入jquery类库,标准的ajax请求api接口数据案例,http请求和响应分析。

019:命令行自动生成模块目录和控制器
命令行自动生成模块目录和控制器
ThinkPHP5.0 具备自动创建功能,可以用来自动生成需要的模块及目录结构和文件等,自动生成主要调用\think\Build类库

020:Composer工具安装和使用-安装Tp5和插件
1:Composer工具安装和使用。2:使用中国镜像设置。3:安装thinkphp最新包和扩展类库。

项目实战-PHP订单系统开发

021:项目需求分析和库表结构分析
商品主表和附表分析,订单主表和附表分析,页面开发所用到js库-vue.js,layer-mobile.js,jqurey.js

022:下单页面模板引入,vue对象赋值技巧
以json字符串的格式赋值到模板的vue对象里

023:vue实现页面的数据绑定(需要vue的知识)
vue的高级用法,包括click事件,watch,v-model用法等等。

024:vue配合后台接口ajax实现省市区三级联动插件
后台提供ajax接口数据,用户不同的操作响应不同的返回数据。

025:vue配合Layer-mobil组合表单数据,下单操作
vue配合Layer-mobil组合表单数据,下单操作,layer-mobile做一些提示效果

026:订单入库操作,库表的回滚操作
订单入库操作,库表的回滚操作-多表更新操作容易产生垃圾数据。

027:代码优化和项目终结
代码优化和项目终结

学习地址:
http://study.163.com/course/courseMain.htm?courseId=1003838084

继续阅读 »

ThinkPHP5.0入门到精通
http://study.163.com/course/courseMain.htm?courseId=1003838084
课程介绍:
第一部分:
Thinkphp5.0入门教程,使开发者快速从thinkphp3.2版本过度到thinkphp5.0,最高效的降低学习成本,不研究概念性的东西,只讲究实际开发中用到的东西。
第二部分:
属于高级教程,有一定的难度,开发项目中常用的到类库或则组件,都会陆续更新上去,比如phpmailer邮件服务,阿里云oss存储服务,
支付宝支付,微信支付,七牛云存错,图片上传等等,整理成插件的形式,便于学习和使用。
由于课程标题数量限制,只能突出一部分知识点。

有php的基础知识,有了解过thinkphp方面的知识。

第一阶段(学前私信:835173372)
001:站点部署环境和框架解释
002:代码执行流程和命名空间
003:数据库配置和引入db类库

004:数据库CURD-查询数据,插入数据
单条查询,批量查询,单条插入,批量插入。

005:数据库CURD-更新数据,删除数据
单条更新,批量更新,单条删除,批量删除,字段加1/n,字段健1/n

006:数据库CURD-查询方法,查询语法
getTableInfo()方法,where()F方法,whereOr()方法,混合查询,LIKE查询, BETWEEN查询等等

007:数据库CURD-多表联查,缓存查询
alias(),join(),limit(),group(),order(),cache(),select()使用技巧。

008:数据库CURD-分页查询,聚合查询
page(),limit(),count(),max(),min(),avg()使用技巧

009:数据请求-请求信息,请求接受变量
Request类的引入和使用,is_Get();is_Post();is_Ajax();is_Mobile();input()助手函数的使用技巧

010:数据请求-伪静态,公共方法创建等
数据请求-伪静态,HTTP头信息,公共方法引入等

011:控制器初始化-空操作-跳转重定向等
控制器定义,初始化,空操作,跳转重定向详解

012:视图,模板赋值渲染,循环遍历等等
模板引擎,模板赋值,模板渲染,模板定位,模板标签,变量输出,循环输出标签,比较标签,原生php,定义标签。

013:Tp5.0-session功能的封装和执行原理
Session的创建,获取,删除,助手函数的使用。

014:分页实现-内置Paginator对象详解等
分页实现(内置Paginator对象来实现)

015:Workerman-PHP socket服务器框架
修改站点入口地址和Workerman-PHP socket服务器框架

插件开发
016:搭建PHPMailer类库开发邮件发送
git地址:https://github.com/PHPMailer/PHPMailer
php.ini注意开启ssl服务,命名空间的注意事项,smtp授权码的概念,网易邮箱客户端端口465

017:Tp5.0验证码的创建和验证使用技巧
验证码类库(think-captcha)引入使用,一个页面创建个多个验证吗,多个验证码的验证等等

018:验证码的ajax动态切换效果开发案例
cdn模式引入jquery类库,标准的ajax请求api接口数据案例,http请求和响应分析。

019:命令行自动生成模块目录和控制器
命令行自动生成模块目录和控制器
ThinkPHP5.0 具备自动创建功能,可以用来自动生成需要的模块及目录结构和文件等,自动生成主要调用\think\Build类库

020:Composer工具安装和使用-安装Tp5和插件
1:Composer工具安装和使用。2:使用中国镜像设置。3:安装thinkphp最新包和扩展类库。

项目实战-PHP订单系统开发

021:项目需求分析和库表结构分析
商品主表和附表分析,订单主表和附表分析,页面开发所用到js库-vue.js,layer-mobile.js,jqurey.js

022:下单页面模板引入,vue对象赋值技巧
以json字符串的格式赋值到模板的vue对象里

023:vue实现页面的数据绑定(需要vue的知识)
vue的高级用法,包括click事件,watch,v-model用法等等。

024:vue配合后台接口ajax实现省市区三级联动插件
后台提供ajax接口数据,用户不同的操作响应不同的返回数据。

025:vue配合Layer-mobil组合表单数据,下单操作
vue配合Layer-mobil组合表单数据,下单操作,layer-mobile做一些提示效果

026:订单入库操作,库表的回滚操作
订单入库操作,库表的回滚操作-多表更新操作容易产生垃圾数据。

027:代码优化和项目终结
代码优化和项目终结

学习地址:
http://study.163.com/course/courseMain.htm?courseId=1003838084

收起阅读 »