09月15, 2017

微信页面笔记

最近运营要做一个活动,活动展示在微信中。

里面涉及:网页授权、微信分享等功能。所以记录一下。

网页授权

网页授权这一块可以先使用测试账号

可以看到appId及appsecret。

alt

页面往下拉,可以找到授权这一块,点击右边的修改:

alt

alt

这里可以输IP或者具体的域名,唯一的要求是端口号必须是80。

在完成上面的功能之后,我们就可以开始拼接授权的URL了。

https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的APPID&redirect_uri=你的URL&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect

这里只要将"你的APPID"和"你的URL"(需要encode一把)替换掉即可。state这个值是可有可无的。

redirect_uri的值一般为后台的URL地址,这里我们先以某个html为跳转回调:

alt

点击完确定登录后,我们可以看到URL是长这样的:

alt

通过url中的code值,我们可以拿access_token,然后就可以拿userInfo了

因为前端会跨域,所以需要后端来做,这里我用node简单实现一下吧:

exports.queryAuthAccessToken = async function(ctx, next){
    const request = require('request-promise');
    const params = {
        appid: "你的appId",
        secret: "你的appSecret",
        code: "0212ZqtI1TrN250FsIuI1k3jtI12Zqt5",
        grant_type: "authorization_code"
    }
    const query = await request({url:"https://api.weixin.qq.com/sns/oauth2/access_token", qs: params});
    ctx.body = query;
}

alt

一般如果出错的话,有两种可能:

  • invalid code
{"errcode":40029,"errmsg":"invalid code"}
  • code been used

alt

这个告诉我们access_token不能重复获取,只要获取一次,然后放到全局上下文,当失效时,重新再取一把即可。不过我跟后台java人员沟通了解后,他们的做法是ajax返回重授权的url,让前端location.href跳转到授权页,重新授权,毕竟微信里面网页操作2个小时,也没什么意义。

注:这里没把access_token失效的情况考虑进去。

获取用户信息

exports.queryUserInfo = async function(ctx, next){
    const request = require('request-promise');

    const params = {
        "access_token": "上面接口获取的accessToken",
        "openid": "上面接口获取的openId",
        "lang": "zh_CN"
    }

    const query = await request({url:"https://api.weixin.qq.com/sns/userinfo", qs: params});

    ctx.body = query;
}

alt

前端ajax请求,即可拿到用户的头像和昵称。

分享功能

先生成access_token,这个和上面的授权access_token是不一样的,上面的需要code,则这个则不用。

exports.queryAccessToken = async function(ctx, next) {
    const request = require('request-promise');
    const params = {
        appid: "你的appId",
        secret: "你的secretId",
        grant_type: "client_credential"
    }
    const query = await request({url:"https://api.weixin.qq.com/cgi-bin/token", qs: params});
    ctx.body = query;
}

随后通过access_token得到ticket票据:

exports.queryTicket = async function(ctx, next){
    const request = require('request-promise');

    const accessToken = "上面接口得到的access_token的值";

    const query = await request({url:`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${accessToken}&type=jsapi`});

    ctx.body = query;
}

alt

紧跟着就是要得到sign了:

jsapi_ticket=HoagFKDcsGMVCIY2vOjf9iWgehCnbnyZUWHOwVswTsRlmQJQ2fSmWhJBn7JfykIl7OOG8GoEXEHiKsRH_HE_ug&noncestr=Wm3W!@sd%$wzccnW&timestamp=1505200357256&url=http://10.200.102.191/test.html?code=001OD2u70mHD0K1Wkmu705rRt70OD2u9&state=123

这里noncestr是一串随机字符串,timestamp随便搞个时间戳,url是截取#之前的路径(要分享的URL页面)。

将上面的字符串用sha1加密:

const sha1 = require('sha1');
const sign = (str) => {
  return sha1(str)
}

有了上面的这些参数,那么前端就容易了

a. 引入js:

<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

b. config配置:

wx.config({
   debug: false,
   appId: "你的AppId",
   timestamp: "1505200357256",
   nonceStr: "Wm3W!@sd%$wzccnW",
   signature: "371e8f496efe8cbe128dae7b3cbf1fb8bb7c5428",
   jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline', 'onMenuShareQQ', 'onMenuShareQZone']
})

wx.ready(function () {

     var shareInfo = {
          shareIconUrl: 'https://cdn.leoao.com/o_1bop3l9hl196n1s0f1d1oi491kji11.jpg?imageslim',
          shareUrl: window.location.href,
          shareTitle: '送你95元礼包,咱一起体验乐刻智能健身',
          shareDescription: '每月仅99元起,7x24H营业,300节操课任选!'
        };

    wx.onMenuShareAppMessage({
          title: shareInfo.shareTitle, // 分享标题
          desc: shareInfo.shareDescription, // 分享描述
          link: shareInfo.shareUrl, // 分享链接
          imgUrl: shareInfo.shareIconUrl // 分享图标
        });
});

当然正式的config里面的数据是要通过ajax获取的,我在这边纯粹是为了测试能否走通流程。(shareInfo是来自乐刻的代码)

在选择右上角“...”,选择发送给朋友,就能看到以下效果:

alt

当想通过按钮来触发分享时,这个目前微信做不到,只能通过引导的方式:

alt

alt

这里有几点需要注意的:

  • link和imgUrl必须是绝对路径,不能是相对路径或者自己去做路径拼接

  • 开发阶段可以配置一下debug为true,一般要么是签名错误,要么是无效的URL。无效的URL只要在这儿设置一下就行了:

alt

注意不能是IP

  • 分享URL必须是上面配的域名打头的。

在实际中,我和开发配了微信的授权地址,死活不成功。这个方案可以是做一个auth.html,在里面ajax后端,让后端返回授权地址,再location.href跳转。当然这种方案期间会产生空白页面,再跳入授权页面。

禁止分享

function onBridgeReady() {
    WeixinJSBridge.call('hideOptionMenu');
}

if (typeof WeixinJSBridge == "undefined") {
    if (document.addEventListener) {
        document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
    } else if (document.attachEvent) {
        document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
        document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
    }
} else {
    onBridgeReady();
}

微信cookie

我觉得略坑,一旦种了cookie,不管是设置多久过期,重新打开后,就是还在的,一脸蒙B。。所以后端只能判断redis存用户信息2个小时,一旦时间到了,就重新跳授权,重新获取数据。

动态二维码

js动态二维码

跑一下这个示例,差不多,不过可以再封装一下:

function setQRCode(dom, url, width, height) {
    if (typeof dom === "string") {
        dom = document.getElementById(dom);
    }
    var qrcode = new QRCode(dom, {
        width : width || 200,
        height : height || 200
    });
    qrcode.makeCode(url);
}

网页生成图片

比较常见的,譬如将一个“海报”的HTML变成一张图片。

我一开始选择的是dom-to-image

期间碰到一个问题,微信头像跨域。

做了很多尝试,譬如:

  • 微信头像放在项目服务端
  • 微信头像放在文件服务器
  • 微信头像在服务端转成base64
  • 微信头像在浏览器端转成base64

最后选择了微信头像在浏览器端转成base64,实现可以参考:Javascript将图片的绝对路径转换为base64编码

function getBase64Image(img) {
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, img.width, img.height);
    var ext = img.src.substring(img.src.lastIndexOf(".")+1).toLowerCase();
    var dataURL = canvas.toDataURL("image/"+ext);
    return dataURL;
}

var image = new Image();
image.crossOrigin = '';
image.src = data.headImgUrl;
image.onload = function() {
    var base64 = getBase64Image(image);
    // 可以将base64写到img的src里面,代码略
    ... 
    // 渲染成图片
    ...
}
image.onerror = function(){ alert("出错") }

发现在手机上有时候会进入onerror方法,坑爹啊!!

渲染成图片那里就是调用domtoimage的方法了。

但最终发现生成出来的图片,里面的文字略有些模糊,虽然我能接受,但是产品、运营接受不了。

无奈只能换方案,最终试了一下html2canvas

// 得到HTML最外层的容器片断
var node =$('#xx')[0];
html2canvas(node).then(function(canvas) {
var html_canvas = canvas.toDataURL();
var imgHtml = "<img width='100%' src='"+ html_canvas +"' />";
// 写到图片容器中
$("#img-container").html(imgHtml);
// 将HTML片断隐藏
$("#xx").hide();

它生成出来的图片,字体还是要比dom-to-image要清晰得多。

滚动加载更多

这个一般手机端会比较多,作法也比较简单:

监听滚动事件,判断到底,然后发起请求即可。

这里可能需要关注的是:

  • 节流
  • 节省请求

节流就是指一段时间内执行一次,而非频繁执行

// 节流
var throttle = function (func, delay) {
    var timer;
    return function () {
        var context = this;
        var args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
            func.apply(context, args);
        }, delay);
    }
};

节省请求是指后端返回接口中有一个hasNext字段,通过这个字段来显示加载更多的div容器。

那么判断条件就变成了:

// 获取“加载更多的容器“的top值
const top = wrapper.getBoundingClientRect().top;
// 获取一屏的高度
const windowHeight = window.screen.height;
// 当top<windowHeight时,即要出来时
if (top < windowHeight) {
     // 加载更多
}

真机调试

移动开发真机调试

一般问题在于不同的机型,可能css错乱,或者js不支持报错,就需要真机调试

上面贴的地址,我尝试了weinre

npm install weinre -g
weinre --boundHost 10.200.102.191

10.200.102.191为我本地的内网IP,然后手机端进行访问时:

alt

alt

然后就可以开始调试了。

这里有几个注意点:

  • 要在相关的html中加入一段script
<script src="http://10.200.102.191:8080/target/target-script-min.js#anonymous"></script>

这也意味着项目上线时,要将它移除掉

  • 无法调试js

这个weinre只能是半条腿啊。

技术选型

我觉得活动页面没有必要使用构建工具,因此我的选型如下:

  • jQuery/zepto(建议后者)
  • Vue
  • weUI

Vue可以解决模板双向绑定的问题,用jQuery/zepto(当然更简单的方式是直接用原生的fetch)来发ajax,weUI是通用的UI组件(但往往设计师做出来的页面和组件有很大的出入,这是一个无解的话题,orz)。

本文链接:www.my-fe.pub/post/weixin-page-note.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。