目标
解决三件事。
小程序嵌入h5.
获取用户信息。
将用户信息以及一个唯一ID传入web-view环境里。
一、小程序里嵌入H5网页
这个很简单。
申请注册微信小程序(必须是国内企业版)。
登录管理后台,设置–开发设置–配置业务域名。
给你的网站配置ssl证书,开启https,开发阶段可以用natapp之类的内网穿透工具做。
下载安装微信开发者工具,使用小程序模式,新建小程序,在index页面或者其他页面(跳转一下)写:<web-view src="你的URL"></web-view>,保存编译运行即可。
二、获取用户信息
在微信调试工具里打开app.js,可以看到有个方法叫wx.login,已经有写好的基本逻辑。用户信息可以console.log查看一下res这个变量。
wx.login({ success: res => { if (res.code) { console.log(res) }// 发送 res.code 到后台换取 openId, sessionKey, unionId } })
可以看到,res里包含的只是一些基本用户信息,并不包含一个唯一的ID让我们确定这个用户。所以需要利用获取openid的机制,利用ajax与自建服务器交互。其逻辑图在小程序开发文档–登录机制里写的很清楚。
在这里我直接贴出来我的代码(C#,ashx一般处理程序),里面包含了服务器端获取openid的方式(将信息传到后台的时候,我用的是明文,实际上为了安全,可以自己加密一下再传到后台。)
小程序端:
wx.login({ success: res => { if (res.code) { //console.log(res) //发起网络请求 wx.request({ url: 'https://我的URL路径/处理文件.ashx', header: { "Content-Type": "applciation/json" }, method: 'POST', data: { code: res.code }, success: function (res) { wx.setStorageSync('openid', res.data); // console.log(res.data)这里是成功后回调函数,已经拿到了服务器端获取到的openid //利用小程序的缓存能力,将获取到的openid存进缓存里,在小程序的生命周期里,都可以随意全局调用。 } }) }// 发送 res.code 到后台换取 openId, sessionKey, unionId } })
服务器端:
<%@ WebHandler Language="C#" Class="wx" %> using System.Web; using System.Net; using System.Text; using System.IO; using System.Collections.Generic; public class wx : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/plain"; HttpRequest request = context.Request; Stream stream = request.InputStream; string json = string.Empty; string responseJson = string.Empty; if (stream.Length != 0) { StreamReader streamReader = new StreamReader(stream); json = streamReader.ReadToEnd(); } Dictionary<string, object> ob =JSONSerializer.Deserialize<Dictionary<string, object>>(json); string js_code=ob["code"].ToString(); //这里就是与微信服务器交互,获取openid string data = HttpGet("https://api.weixin.qq.com/sns/jscode2session?appid=你的appid&secret=你的secret&js_code="+js_code+"&grant_type=authorization_code"); Dictionary<string, object> suggestions =JSONSerializer.Deserialize<Dictionary<string, object>>(data); string openid = suggestions["openid"].ToString(); //将openid返回 context.Response.Write(openid); } public static string HttpGet(string Url) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url); request.Method = "GET"; request.ContentType = "text/html;charset=UTF-8"; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream myResponseStream = response.GetResponseStream(); StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8")); string retString = myStreamReader.ReadToEnd(); myStreamReader.Close(); myResponseStream.Close(); return retString; } public bool IsReusable { get { return false; } } }
到此为止,就获取到了有效用户信息。
三、将用户信息传进web-view
首先我们发现,小程序里不可以像html那样随意append组件,web-view目前也仅有一个src可以和小程序环境有交互,所以,就想到可以用get方式传值,比如url配置url?openid=1234就可以把信息传进入。
以免出现没有用户信息的情况,我没有在index页面直接写web-view,我用它当前置页面,页面加载时,会判定有没有用户信息,如果有的话,会自动跳到web页面,web页面就是我方web-view的地方。如果没有用户信息的话,会强制获取用户信息,然后再跳转,其代码大概是这样的:
onLoad: function () { if (app.globalData.userInfo) { this.setData({ userInfo: app.globalData.userInfo, hasUserInfo: true }) wx.setStorageSync('url', encodeURI("你的url?nickname=" + app.globalData.userInfo.nickName + "&city=" + app.globalData.userInfo.city + "&country=" + app.globalData.userInfo.country + "&gender=" + app.globalData.userInfo.gender + "&province=" + app.globalData.userInfo.province + "&avatarUrl=" + app.globalData.userInfo.avatarUrl + "&openid=" + wx.getStorageSync('openid'))) //这里的globalData变量是存的用户基本信息,我为了方便把它们都传进去了,其实就传一个openid就够了 wx.navigateTo({ url: '../web/web' }) } else if (this.data.canIUse){ // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 // 所以此处加入 callback 以防止这种情况 app.userInfoReadyCallback = res => { this.setData({ userInfo: res.userInfo, hasUserInfo: true }) wx.setStorageSync('url', encodeURI("你的URL?nickname=" + app.globalData.userInfo.nickName + "&city=" + app.globalData.userInfo.city + "&country=" + app.globalData.userInfo.country + "&gender=" + app.globalData.userInfo.gender + "&province=" + app.globalData.userInfo.province + "&avatarUrl=" + app.globalData.userInfo.avatarUrl + "&openid=" + wx.getStorageSync('openid'))) wx.navigateTo({ url: '../web/web' }) } } else { // 在没有 open-type=getUserInfo 版本的兼容处理 wx.getUserInfo({ success: res => { //console.log(res) app.globalData.userInfo = res.userInfo this.setData({ userInfo: res.userInfo, hasUserInfo: true }) wx.setStorageSync('url', encodeURI("你的URL?nickname=" + app.globalData.userInfo.nickName + "&city=" + app.globalData.userInfo.city + "&country=" + app.globalData.userInfo.country + "&gender=" + app.globalData.userInfo.gender + "&province=" + app.globalData.userInfo.province + "&avatarUrl=" + app.globalData.userInfo.avatarUrl + "&openid=" + wx.getStorageSync('openid'))) wx.navigateTo({ url: '../web/web' }) } }) } },
然后是web页面。
web.wxml:
<web-view src="{{url}}"></web-view> web.js:
onLoad: function (options) { var dataurl = wx.getStorageSync('url');//获取缓存里的url信息 console.log(dataurl.length) this.setData({ url: dataurl }); },
上面有个重要的坑需要特别说明一下,这种传值方式在微信调试工具和安卓手机上完全没问题,但是在IOS上,你会发现传值传不进去,其原因就是在ios下,web-view的src不能出现中文或者一些特殊字符,当用户名比较特殊时,就传不进去值,所以,我在index.js里写了urlencode。当然,在这里也可以把内容加密一下再传至后台。
比如最简单的无需秘钥的base64,我写一下用法:
onLoad: function (options) {
function base64_encode(str) { // 编码,配合encodeURIComponent使用 var c1, c2, c3; var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var i = 0, len = str.length, strin = ''; while (i < len) { c1 = str.charCodeAt(i++) & 0xff; if (i == len) { strin += base64EncodeChars.charAt(c1 >> 2); strin += base64EncodeChars.charAt((c1 & 0x3) << 4); strin += "=="; break; } c2 = str.charCodeAt(i++); if (i == len) { strin += base64EncodeChars.charAt(c1 >> 2); strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); strin += base64EncodeChars.charAt((c2 & 0xF) << 2); strin += "="; break; } c3 = str.charCodeAt(i++); strin += base64EncodeChars.charAt(c1 >> 2); strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); strin += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); strin += base64EncodeChars.charAt(c3 & 0x3F) } return strin } var dataurl = wx.getStorageSync('测试base64'); var base64Url = base64_encode(dataurl) },
到此为止,用户信息就传到后台了,那么后台就可以做用户对应了,然后将openid在页面的生命周期里保持,就可以记录用户行为了。
附加:还可以做的事情
1.分享页面
首先,我们要知道,所有的内容都是在web-view里的,所以当用户分享小程序时,无论用户正在看哪个页面,分享出去之后,其他用户打开,web-view都会重新加载,直接跳到主页,那么,用户分享页面的时候,怎么样才能让其他人打开的时候直接跳到分享者正在看的页面呢?
在这里我们注意到:
微信小程序在分享的时候,可以获取到web-view里的当前url。
分享方法的回调里可以写一个打开小程序页面的方法。
那么我们可以这样设计:
对各个页面做改动,对于一些非敏感值,不要用cookie或者缓存池的方式页面传值,而是用get方式在路径上留下id:URL?id=1,这样分享出去的页面,本身就带有基本信息,直接就可以还原出来。
小程序里新建一个页面,比如叫content,也是只放一个web-view。
在分享事件里,这样写:
onShareAppMessage: function (options) {
function base64_encode(str) { // 编码,配合encodeURIComponent使用 var c1, c2, c3; var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var i = 0, len = str.length, strin = ''; while (i < len) { c1 = str.charCodeAt(i++) & 0xff; if (i == len) { strin += base64EncodeChars.charAt(c1 >> 2); strin += base64EncodeChars.charAt((c1 & 0x3) << 4); strin += "=="; break; } c2 = str.charCodeAt(i++); if (i == len) { strin += base64EncodeChars.charAt(c1 >> 2); strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); strin += base64EncodeChars.charAt((c2 & 0xF) << 2); strin += "="; break; } c3 = str.charCodeAt(i++); strin += base64EncodeChars.charAt(c1 >> 2); strin += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); strin += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); strin += base64EncodeChars.charAt(c3 & 0x3F) } return strin }
console.log(options.webViewUrl) var webViewUrl = base64_encode(options.webViewUrl) console.log(webViewUrl) return { path: 'pages/content/content?url=' + encodeURI(webViewUrl) // 路径,传递参数到指定页面。 } }
当然,content里怎么写也显而易见了。
content.wxml:
<web-view src="{{url}}"></web-view>
content.js
onLoad: function (options) { var openid = wx.getStorageSync('openid'); if (openid.length<=10) { wx.navigateTo({ url: '../index/index' }) }
function base64_decode(input) { // 解码,配合decodeURIComponent使用 var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = base64EncodeChars.indexOf(input.charAt(i++)); enc2 = base64EncodeChars.indexOf(input.charAt(i++)); enc3 = base64EncodeChars.indexOf(input.charAt(i++)); enc4 = base64EncodeChars.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } return utf8_decode(output); }
function utf8_decode(utftext) { // utf-8解码 var string = ''; let i = 0; let c = 0; let c1 = 0; let c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c1 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c1 & 63)); i += 2; } else { c1 = utftext.charCodeAt(i + 1); c2 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63)); i += 3; } } return string; }
var weburl = base64_decode(options.url)//这里就是将分享时的web-view的页面路径值还原出来 var data="" if (weburl.indexOf("?")!=-1) { data ="&openid="+openid } else { data = "?openid=" + openid }//这里是将openid加在每个页面上,可以用来开发记录用户行为的模块 this.setData({ url: weburl + data }) },
2.页面里的其他外链
如果小程序里的页面有其他链接的话,会提示“不支持打开非业务域名xxxxx,请重新配置”。因为web-view不是浏览器,不是所有的网址都可以跳的,只有你自己网址,在后台配置过业务域名的,才可以跳。所以,这里要自己在自己的网站里把跳转屏蔽一下,当用户点击一些非业务链接地址跳转的时候,弹出“此链接是外部链接,已复制到粘贴板”,然后用clipboardjs把链接内容复制到粘贴板即可,这样用户体验更好点。这里就不讲怎么用了,点击链接可以去它的GitHub主页看一下,有各种demo,很简单。
---------------------------------------------
来说点啥吧