利用web-view組件將已有h5網站移植到微信小程序,且可以用微信身份直接登錄

目標

解決三件事。

  1. 小程序嵌入h5.
  2. 獲取用戶信息。
  3. 將用戶信息以及一個唯一ID傳入web-view環境裏。

一、小程序裏嵌入H5網頁

這個很簡單。

  1. 申請註冊微信小程序(必須是國內企業版)。
  2. 登錄管理後臺,設置–開發設置–配置業務域名。
  3. 給你的網站配置ssl證書,開啓https,開發階段可以用natapp之類的內網穿透工具做。
  4. 下載安裝微信開發者工具,使用小程序模式,新建小程序,在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都會重新加載,直接跳到主頁,那麼,用戶分享頁面的時候,怎麼樣才能讓其他人打開的時候直接跳到分享者正在看的頁面呢?
在這裏我們注意到:

  1. 微信小程序在分享的時候,可以獲取到web-view裏的當前url。
  2. 分享方法的回調裏可以寫一個打開小程序頁面的方法。

那麼我們可以這樣設計:

  1. 對各個頁面做改動,對於一些非敏感值,不要用cookie或者緩存池的方式頁面傳值,而是用get方式在路徑上留下id:URL?id=1,這樣分享出去的頁面,本身就帶有基本信息,直接就可以還原出來。
  2. 小程序裏新建一個頁面,比如叫content,也是隻放一個web-view。
  3. 在分享事件裏,這樣寫:
 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,很簡單。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章