目標
解決三件事。
- 小程序嵌入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,很簡單。