(转载)微信内置浏览器的JsAPI

之前有写过几篇关于微信内置浏览器(WebView)中特有的Javascript API(Javascript Interface)的文章,不过随着微信官方的调整,部分API已经不能直接使用,比如类似直接分享到朋友圈 WeixinJSBridge.invoke(‘shareTimeline’,data,callback) 这样的功能,直接调用,会得到一个访问拒绝的response。后来重新调研了下,整理出来了一个WeixinAPI的Javascript类库,分享出来,如果你对微信公众平台开发感兴趣,应该对你有用。
/**!

  • 微信内置浏览器的Javascript API,功能包括:
    *
  • 1、分享到微信朋友圈
  • 2、分享给微信好友
  • 3、分享到腾讯微博
  • 4、隐藏/显示右上角的菜单入口
  • 5、隐藏/显示底部浏览器工具栏
  • 6、获取当前的网络状态
  • 7、调起微信客户端的图片播放组件
    *
  • @author zhaoxianlie(http://www.baidufe.com)
    */
    var WeixinApi = (function () {

    / 这里省略了一堆代码……下面直接看调用接口 /
    return {

    ready           :wxJsBridgeReady,
    shareToTimeline :weixinShareTimeline,
    shareToWeibo    :weixinShareWeibo,
    shareToFriend   :weixinSendAppMessage,
    showOptionMenu  :showOptionMenu,
    hideOptionMenu  :hideOptionMenu,
    showToolbar     :showToolbar,
    hideToolbar     :hideToolbar,
    getNetworkType  :getNetworkType,
    imagePreview    :imagePreview
    

    };

});
下面,我们先来看一下这些API都应该怎么使用,先从最简单的入手。

1、假如我希望一打开网页后,就隐藏掉右上角的PopUp菜单入口,并且隐藏掉浏览器下方的工具栏,同时还要获得当前的网络状态,那么,我们的代码可以这样来写:
// 所有功能必须包含在 WeixinApi.ready 中进行
WeixinApi.ready(function(Api){
// 隐藏右上角popup菜单入口
Api.hideOptionMenu();

// 隐藏浏览器下方的工具栏
Api.hideToolbar();

// 获取网络状态
Api.getNetworkType(function(network){
    // 拿到 network 以后,做任何你想做的事
});

});
如示例代码中的注释所示,所有的功能执行必须放在 WeixinApi.ready 方法中执行,就好比你用jQuery的时候,通常都需要使用 jQuery(document).ready(function(){ }) 一样。为什么要这样做?相信不用我解释大家都能明白,因为我们必须要保证在执行这些方法的时候,WeixinJsBridge API已经被加入到WebView上了!

2、再来看一个有关分享的例子,假如用户在阅读我的文章(或在使用我的产品)的过程中,发现它很有意思或有价值,一般都会将其收藏或分享(给好友、朋友圈、微博等)出去,那现在我就希望能监测到用户的分享行为,比如:自定义用户可分享的内容、甚至是在用户分享之、分享被取消、分享失败、分享成功、以及整个分享操作过程结束,我们都去做点儿什么。那么,这个代码我们可以这样来写:
// 所有功能必须包含在 WeixinApi.ready 中进行
WeixinApi.ready(function(Api){

// 微信分享的数据
var wxData = {
    "imgUrl":'http://www.baidufe.com/fe/blog/static/img/weixin-qrcode-2.jpg',
    "link":'http://www.baidufe.com',
    "desc":'大家好,我是Alien,Web前端&Android客户端码农,喜欢技术上的瞎倒腾!欢迎多交流',
    "title":"大家好,我是赵先烈"
};

// 分享的回调
var wxCallbacks = {
    // 分享操作开始之前
    ready:function () {
        // 你可以在这里对分享的数据进行重组
    },
    // 分享被用户自动取消
    cancel:function (resp) {
        // 你可以在你的页面上给用户一个小Tip,为什么要取消呢?
    },
    // 分享失败了
    fail:function (resp) {
        // 分享失败了,是不是可以告诉用户:不要紧,可能是网络问题,一会儿再试试?
    },
    // 分享成功
    confirm:function (resp) {
        // 分享成功了,我们是不是可以做一些分享统计呢?
    },
    // 整个分享过程结束
    all:function (resp) {
        // 如果你做的是一个鼓励用户进行分享的产品,在这里是不是可以给用户一些反馈了?
    }
};

// 用户点开右上角popup菜单后,点击分享给好友,会执行下面这个代码
Api.shareToFriend(wxData, wxCallbacks);

// 点击分享到朋友圈,会执行下面这个代码
Api.shareToTimeline(wxData, wxCallbacks);

// 点击分享到腾讯微博,会执行下面这个代码
Api.shareToWeibo(wxData, wxCallbacks);

});

3、当然,如果你的业务需求相当复杂,比如,你的产品就是一个微信网页游戏(类似“2048数字游戏微信版”),你希望用户分享出去的数据是一个网页截屏、或者需要将用户当前的游戏状态回传到服务器动态生成可分享的内容;那么这种情况我们又该怎么做呢?来看下面这个示例代码吧:
// 所有功能必须包含在 WeixinApi.ready 中进行
WeixinApi.ready(function(Api){

// 分享的回调
var wxCallbacks = {
    // 分享过程需要异步执行
    async : true,
    // 分享操作开始之前
    ready:function () {
        var self = this;
        // 假设你需要在这里发一个 ajax 请求去获取分享数据
        $.post(yourServerUrl,yourPostData,function(responseData){
            // 可以解析reponseData得到wxData
            var wxData = responseData;
            // 调用dataLoaded方法,会自动触发分享操作
            // 注意,当且仅当 async为true时,wxCallbacks.dataLoaded才会被初始化,并调用
            self.dataLoaded(wxData);
        });
    }
    /* cancel、fail、confirm、all 方法同示例2,此处略掉 */
};

// 用户点开右上角popup菜单后,点击分享给好友,会执行下面这个代码
Api.shareToFriend({}, wxCallbacks);

});
唯一的区别就是在wxCallbacks中,增加了配置项async为true,表示这个分享过程是异步调用的,其实就是指的ready方法异步执行,在这种情况下,我们需要在ready方法中显式地调用wxCallbacks的dataLoaded方法,以保证分享过程能继续往下执行。也许你会发现,这个wxCallbacks中,根本就没有配置dataLoaded方法啊!是的,当async为true时,WeixinApi中我会自动对其进行初始化,dataLoaded方法需要一个参数,表示需要分享出去的数据!

4、当然,如果你非要去配置dataLoaded方法,也是没有问题的,你的配置也会被执行,不会被覆盖,执行顺序是:用户配置优先。

上面是直接给出使用方法,也许你现在开始关心每个方法的参数列表是什么样的了?我们以分享到朋友圈的方法为例,来看看参数都有哪些配置项:
/**

  • 分享到微信朋友圈
  • @param {Object} data 待分享的信息
  • @p-config {String} appId 公众平台的appId(服务号可用)
  • @p-config {String} imageUrl 图片地址
  • @p-config {String} link 链接地址
  • @p-config {String} desc 描述
  • @p-config {String} title 分享的标题
    *
  • @param {Object} callbacks 相关回调方法
  • @p-config {Boolean} async ready方法是否需要异步执行,默认false
  • @p-config {Function} ready(argv) 就绪状态
  • @p-config {Function} dataLoaded(data) 数据加载完成后调用,async为true时有用
  • @p-config {Function} cancel(resp) 取消
  • @p-config {Function} fail(resp) 失败
  • @p-config {Function} confirm(resp) 成功
  • @p-config {Function} all(resp) 无论成功失败都会执行的回调
    */
    WeixinApi.shareToTimeline(data,callbacks);
    分享给微信好友以及分享到腾讯微博的参数列表都一样,这里就不罗列了。

5、如果你的文章中有很多图片,那么,点击图片直接调起微信客户端自带的图片播放组件,那必然是一件好事;对此,你可以这样来做:
// 调起微信客户端的图片播放组件进行播放
WeixinApi.ready(function(Api){
var srcList = [];
$.each($(‘img’),function(i,item){
if(item.src) {
srcList.push(item.src);
$(item).click(function(e){
// 通过这个API就能直接调起微信客户端的图片播放组件了
Api.imagePreview(this.src,srcList);
});
}
});
});
就这么一段儿简单的代码,一切都搞定了!不过,需要指出的是,Api.imagePreview的参数是会进行强检测的:
/**

  • 调起微信Native的图片播放组件。
  • 这里必须对参数进行强检测,如果参数不合法,直接会导致微信客户端crash
    *
  • @param {String} curSrc 当前播放的图片地址
  • @param {Array} srcList 图片地址列表
    */
    function imagePreview(curSrc,srcList) ;

需要指出的是,微信公众平台对Android、iOS平台支持力度不统一,比较费劲,具体有:
iOS平台下,分享出去的数据wxData中,imageUrl可以是DataURI格式的;但在Android平台下,必须是全路径的图片地址
iOS平台下,分享的回调callback基本全都可以得到执行;但在Android平台下,分享到微信朋友圈的callback无法得到执行(ready方法除外)
iOS平台下,无法在非mp.weixin.qq.com域下的页面中通过WeixinJSBridge.invoke(‘profile’)的方式打开某微信号的资料页面;Android平台下则可以通过的方式打开资料页;WinPhone下,则是通过的方式打开。
期待官方能早日实现各平台API的统一吧!!!

至于API内部是怎么实现的,如果感兴趣,那就看源码吧,使用过程中如遇到什么Bug,请来这里反馈。

为了便于Api的维护与共享,已将其放到Github上了,大家这里走起:https://github.com/zxlie/WeixinApi

/**!

  • 微信内置浏览器的Javascript API,功能包括:
    *
  • 1、分享到微信朋友圈
  • 2、分享给微信好友
  • 3、分享到腾讯微博
  • 4、隐藏/显示右上角的菜单入口
  • 5、隐藏/显示底部浏览器工具栏
  • 6、获取当前的网络状态
    *
  • @author zhaoxianlie(http://www.baidufe.com)
    */
    var WeixinApi = (function () {
/**
 * 分享到微信朋友圈
 * @param       {Object}    data       待分享的信息
 * @p-config    {String}    appId      公众平台的appId(服务号可用)
 * @p-config    {String}    imageUrl   图片地址
 * @p-config    {String}    link       链接地址
 * @p-config    {String}    desc       描述
 * @p-config    {String}    title      分享的标题
 *
 * @param       {Object}    callbacks  相关回调方法
 * @p-config    {Boolean}   async                   ready方法是否需要异步执行,默认false
 * @p-config    {Function}  ready(argv)             就绪状态
 * @p-config    {Function}  dataLoaded(data)        数据加载完成后调用,async为true时有用,也可以为空
 * @p-config    {Function}  cancel(resp)    取消
 * @p-config    {Function}  fail(resp)      失败
 * @p-config    {Function}  confirm(resp)   成功
 * @p-config    {Function}  all(resp)       无论成功失败都会执行的回调
 */
function weixinShareTimeline(data, callbacks) {
    callbacks = callbacks || {};
    var shareTimeline = function (theData) {
        WeixinJSBridge.invoke('shareTimeline', {
            "appid":theData.appId ? theData.appId : '',
            "img_url":theData.imgUrl,
            "link":theData.link,
            "desc":theData.title,
            "title":theData.desc, // 注意这里要分享出去的内容是desc
            "img_width":"120",
            "img_height":"120"
        }, function (resp) {
            switch (resp.err_msg) {
                // share_timeline:cancel 用户取消
                case 'share_timeline:cancel':
                    callbacks.cancel && callbacks.cancel(resp);
                    break;
                // share_timeline:fail 发送失败
                case 'share_timeline:fail':
                    callbacks.fail && callbacks.fail(resp);
                    break;
                // share_timeline:confirm 发送成功
                case 'share_timeline:confirm':
                    callbacks.confirm && callbacks.confirm(resp);
                    break;
            }
            // 无论成功失败都会执行的回调
            callbacks.all && callbacks.all(resp);
        });
    };
    WeixinJSBridge.on('menu:share:timeline', function (argv) {
        if (callbacks.async && callbacks.ready) {
            if(!callbacks.__dataLoadedFuncInited) {
                var loadedCb = callbacks.dataLoaded || new Function();
                callbacks.dataLoaded = function (newData) {
                    loadedCb(newData);
                    shareTimeline(newData);
                };
                callbacks.__dataLoadedFuncInited = true;
            }
            // 然后就绪
            callbacks.ready && callbacks.ready(argv);
        } else {
            // 就绪状态
            callbacks.ready && callbacks.ready(argv);
            shareTimeline(data);
        }
    });
}


/**
 * 发送给微信上的好友
 * @param       {Object}    data       待分享的信息
 * @p-config    {String}    appId      公众平台的appId(服务号可用)
 * @p-config    {String}    imageUrl   图片地址
 * @p-config    {String}    link       链接地址
 * @p-config    {String}    desc       描述
 * @p-config    {String}    title      分享的标题
 *
 * @param       {Object}    callbacks  相关回调方法
 * @p-config    {Boolean}   async                   ready方法是否需要异步执行,默认false
 * @p-config    {Function}  ready(argv)             就绪状态
 * @p-config    {Function}  dataLoaded(data)        数据加载完成后调用,async为true时有用,也可以为空
 * @p-config    {Function}  cancel(resp)    取消
 * @p-config    {Function}  fail(resp)      失败
 * @p-config    {Function}  confirm(resp)   成功
 * @p-config    {Function}  all(resp)       无论成功失败都会执行的回调
 */
function weixinSendAppMessage(data, callbacks) {
    callbacks = callbacks || {};
    var sendAppMessage = function (theData) {
        WeixinJSBridge.invoke('sendAppMessage', {
            "appid":theData.appId ? theData.appId : '',
            "img_url":theData.imgUrl,
            "link":theData.link,
            "desc":theData.desc,
            "title":theData.title,
            "img_width":"120",
            "img_height":"120"
        }, function (resp) {
            switch (resp.err_msg) {
                // send_app_msg:cancel 用户取消
                case 'send_app_msg:cancel':
                    callbacks.cancel && callbacks.cancel(resp);
                    break;
                // send_app_msg:fail 发送失败
                case 'send_app_msg:fail':
                    callbacks.fail && callbacks.fail(resp);
                    break;
                // send_app_msg:confirm 发送成功
                case 'send_app_msg:confirm':
                    callbacks.confirm && callbacks.confirm(resp);
                    break;
            }
            // 无论成功失败都会执行的回调
            callbacks.all && callbacks.all(resp);
        });
    };
    WeixinJSBridge.on('menu:share:appmessage', function (argv) {
        if (callbacks.async && callbacks.ready) {
            if(!callbacks.__dataLoadedFuncInited) {
                var loadedCb = callbacks.dataLoaded || new Function();
                callbacks.dataLoaded = function (newData) {
                    loadedCb(newData);
                    sendAppMessage(newData);
                };
                callbacks.__dataLoadedFuncInited = true;
            }
            // 然后就绪
            callbacks.ready && callbacks.ready(argv);
        } else {
            // 就绪状态
            callbacks.ready && callbacks.ready(argv);
            sendAppMessage(data);
        }
    });
}


/**
 * 分享到腾讯微博
 * @param       {Object}    data       待分享的信息
 * @p-config    {String}    imageUrl   图片地址
 * @p-config    {String}    link       链接地址
 * @p-config    {String}    desc       描述
 * @p-config    {String}    title      分享的标题
 *
 * @param       {Object}    callbacks  相关回调方法
 * @p-config    {Boolean}   async                   ready方法是否需要异步执行,默认false
 * @p-config    {Function}  ready(argv)             就绪状态
 * @p-config    {Function}  dataLoaded(data)        数据加载完成后调用,async为true时有用,也可以为空
 * @p-config    {Function}  cancel(resp)    取消
 * @p-config    {Function}  fail(resp)      失败
 * @p-config    {Function}  confirm(resp)   成功
 * @p-config    {Function}  all(resp)       无论成功失败都会执行的回调
 */
function weixinShareWeibo(data, callbacks) {
    callbacks = callbacks || {};
    var shareWeibo = function (theData) {
        WeixinJSBridge.invoke('shareWeibo', {
            "content":theData.desc,
            "link":theData.link,
            "img_url":theData.imgUrl,
            "title":theData.title,
            "img_width":"120",
            "img_height":"120"
        }, function (resp) {
            switch (resp.err_msg) {
                // share_weibo:cancel 用户取消
                case 'share_weibo:cancel':
                    callbacks.cancel && callbacks.cancel(resp);
                    break;
                // share_weibo:fail 发送失败
                case 'share_weibo:fail':
                    callbacks.fail && callbacks.fail(resp);
                    break;
                // share_weibo:confirm 发送成功
                case 'share_weibo:confirm':
                    callbacks.confirm && callbacks.confirm(resp);
                    break;
            }
            // 无论成功失败都会执行的回调
            callbacks.all && callbacks.all(resp);
        });
    };
    WeixinJSBridge.on('menu:share:weibo', function (argv) {
        if (callbacks.async && callbacks.ready) {
            if(!callbacks.__dataLoadedFuncInited) {
                var loadedCb = callbacks.dataLoaded || new Function();
                callbacks.dataLoaded = function (newData) {
                    loadedCb(newData);
                    shareWeibo(newData);
                };
                callbacks.__dataLoadedFuncInited = true;
            }
            // 然后就绪
            callbacks.ready && callbacks.ready(argv);
        } else {
            // 就绪状态
            callbacks.ready && callbacks.ready(argv);
            shareWeibo(data);
        }
    });
}


/**
 * 显示网页右上角的按钮
 */
function showOptionMenu() {
    WeixinJSBridge.call('showOptionMenu');
}




/**
 * 隐藏网页右上角的按钮
 */
function hideOptionMenu() {
    WeixinJSBridge.call('hideOptionMenu');
}


/**
 * 显示底部工具栏
 */
function showToolbar() {
    WeixinJSBridge.call('showToolbar');
}


/**
 * 隐藏底部工具栏
 */
function hideToolbar() {
    WeixinJSBridge.call('hideToolbar');
}


/**
 * 返回如下几种类型:
 *
 * network_type:wifi     wifi网络
 * network_type:edge     非wifi,包含3G/2G
 * network_type:fail     网络断开连接
 * network_type:wwan     2g或者3g
 *
 * 使用方法:
 * WeixinApi.getNetworkType(function(networkType){
 *
 * });
 *
 * @param callback
 */
function getNetworkType(callback) {
    if (callback && typeof callback == 'function') {
        WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
            // 在这里拿到e.err_msg,这里面就包含了所有的网络类型
            callback(e.err_msg);
        });
    }
}


/**
 * 当页面加载完毕后执行,使用方法:
 * WeixinApi.ready(function(Api){
 *     // 从这里只用Api即是WeixinApi
 * });
 * @param readyCallback
 */
function wxJsBridgeReady(readyCallback) {
    if (readyCallback && typeof readyCallback == 'function') {
        var Api = this;
        var wxReadyFunc = function () {
            readyCallback(Api);
        };
        if (typeof window.WeixinJSBridge == "undefined"){
            if (document.addEventListener) {
                document.addEventListener('WeixinJSBridgeReady', wxReadyFunc, false);
            } else if (document.attachEvent) {
                document.attachEvent('WeixinJSBridgeReady', wxReadyFunc);
                document.attachEvent('onWeixinJSBridgeReady', wxReadyFunc);
            }
        }else{
            wxReadyFunc();
        }
    }
}


return {
    version         :"1.2",
    ready           :wxJsBridgeReady,
    shareToTimeline :weixinShareTimeline,
    shareToWeibo    :weixinShareWeibo,
    shareToFriend   :weixinSendAppMessage,
    showOptionMenu  :showOptionMenu,
    hideOptionMenu  :hideOptionMenu,
    showToolbar     :showToolbar,
    hideToolbar     :hideToolbar,
    getNetworkType  :getNetworkType
};

})();