A計劃小程式的血與淚

我叫呂胖胖發表於2019-02-27

前言

A計劃只是一個代號,不代表任何小程式

微信小程式,簡稱小程式,英文名Mini Program,是一種不需要下載安裝即可使用的應用,它實現了應用“觸手可及”的夢想,使用者掃一掃或搜一下即可開啟應用。

最近兩週由於公司業務需求,本胖主導開發了一款功能簡單的小程式—A計劃(以下本次開發小程式的代號),可以說這兩週本胖都是在瀏覽小程式官網以及小程式論壇,適應小程式的開發模式以及填開發過程中的大坑小。這裡需要說一句,自己平時做demo和開發公司級別的正式專案永遠都是兩碼事哈,本胖這次也是深有體會,很多之前做demo時候只是模稜兩可的知識在這次開發中得到了深入認識,還有就是對小程式開放能力的深入理解,知道了小程式能做什麼,小程式不能做什麼,所以敬以此文記錄本胖對小程式的認知,如有錯誤,煩請指出,謝謝。

1 小程式登入以及授權

1.1 小程式登入

任何專案都會涉及到登入這個問題,在JSP的遠古時代登入都是直接由服務端控制,在現在react,vue單頁面時代,登入是有客戶端控制,所以現在的很多前端框架都會有全路由守護這種東西,能很方便地幫助我們控制路由許可權,那麼在小程式裡面的登入時怎麼控制的呢?

小程式裡面的登入主要分2種

A. 利用現有登入體系

直接複用現有系統的登入體系,只需要在小程式端設計使用者名稱,密碼/驗證碼輸入頁面,便可以簡便的實現登入,只需要保持良好的使用者體驗即可。
複製程式碼

B.利用OpenId

OpenId 是一個小程式對於一個使用者的標識,利用這一點我們可以輕鬆的實現一套基於小程式的使用者體系,值得一提的是這種使用者體系對使用者的打擾最低,可以實現靜默登入。具體步驟如下:

1.小程式客戶端通過 wx.login 獲取 code

2.傳遞 code 向服務端,服務端拿到 code 呼叫微信登入憑證校驗介面,微信伺服器返回 openid 和會話金鑰 session_key ,此時開發者服務端便可以利用 openid 生成使用者入庫,再向小程式客戶端返回自定義登入態

複製程式碼

這次由於需要獲取使用者手機號的,所以我們選擇了第二種(注意了,個人主體的小程式號是獲取不了使用者手機號的)。但是這裡需要強調的一點是登入和授權獲取使用者資訊(比如手機號,頭像,微信步數)是兩回事情,不要搞混淆了,登入是可以做到靜默登入的,但是很少有小程式只做一個登入的,那會是一個沒有靈魂的小程式哈。

1.2 授權

這裡不得不承認,微信是中國流量最大的app,所有小程式才有了生根萌芽的沃土,做小程式就不得不涉及到相關使用者許可權的授權。A計劃小程式這次涉及到授權的地方主要是獲取使用者手機號,還記得那年夏天微信小程式獲取手機號還是可以直接通過API方式直接彈出授權彈框的,這次發現竟然不行了,只好去官網看看改動了哪些(不得不說這也是小程式的不確定性,一家獨大,想改就改),發現現在要獲取使用者手機號只能通過引導使用者觸發相應的按鈕才行。

1.3 登入+授權解決方案

這次本胖採用的方案是專門用一個頁面(login)來進行使用者的登入以及授權,這裡需要注意的是,你在取微信使用者繫結的手機號,需先呼叫wx.login介面。

我們首先需要下面這個button元件用來引導使用者觸發登入

<button type="primary" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">微信登入</button>
複製程式碼

然後需要在login頁面的onload事件中發起wx.login(),獲取對應的code存起來。 關鍵的一步到了,在使用者點選確定授權的按鈕,我們需要監聽getPhoneNumber事件

getPhoneNumber(e) {
    if (e.detail.iv) {
      this.setData({
        iv: e.detail.iv,
        encryptedData: e.detail.encryptedData
      });
      this.login(this.getPhone);
    }
}
複製程式碼

注意到上面最後一行的程式碼,是在獲取到iv以及encryptedData這兩個解密使用者手機號必備的變數的時候才去發起登入以及獲取對應的手機號前端顯示用。

可以看到上面的微信登入按鈕其實一開始是先做了一個獲取使用者手機號許可權的操作,操作成功後才登入的,如果一開始就登入了,但是使用者沒有同意授權手機號,那麼將變得毫無意義,因為手機號才是小程式需要快捷獲取的核心使用者資訊之一。

登入完成後客戶端可以獲取到一個自定義token。類似於瀏覽器裡面的JSSSESIONID,可以存小程式本地,在需要的時候手動帶上就可以了,這一點比不了瀏覽器可以設定自動帶上cookie那麼方便。

2 生成海報

為什麼很多小程式裡面都會有生成海報呢,其實也是因為小程式不能直接轉發到朋友圈,不能直接轉發到QQ以及其他社交平臺,那麼就需要出現海報這個中間傳播者。這次A計劃小程式裡面也有這個功能,於是本胖就愉快地開始了海報的填坑。

小程式的海報一般都是通過canvas生成的,也就是填小程式canvas的各種坑哈。

2.1 實體機海報空白

A計劃小程式的血與淚
上圖中就是在生成海報的時候出現的實體機空白問題,但是在模擬器裡面卻不會有,在查閱了很多資料以後發現是圖片資源沒能及時載入出來,就會造成在drawImg的時候沒有真實的圖片可以畫,當然就是空白了。

既然是因為在畫圖的時候圖片沒有及時請求到造成的問題,那麼解決的方案當然就是在頁面初始化的時候就去非同步請求圖片

downloadFile(url, name) {
  wx.downloadFile({
    url: url,
    success: function (res) {
   
    }
  });
}
複製程式碼

2.2 背景圖片自適應

canvas是需要設定大小的,否則取他的預設大小。但是canvas的單位是px和小程式標準的單位rpx是不同的。所以我們需要動態給canvas區域設定寬高才能讓canvas滿滿填充整個理想區域。

createNewImg: function (text) {
  wx.createSelectorQuery().select('#canvas-container').boundingClientRect( (rect) => {
      this.setData({
        canvasW: rect.width,
        canvasH: rect.height
      });
      var width = this.data.canvasW,
        height = this.data.canvasH,     
        context = wx.createCanvasContext('mycanvas');
      context.drawImage(this.data.imgUrl1, 0, 0, width, height);
      context.draw();
    }).exec();
  },
複製程式碼

上面的程式碼就是用過獲取不同螢幕下canvas的包裹元素的寬高然後動態給canvas賦值,然後對應圖片的畫圖也是一個道理,就可以讓圖片只適應不同螢幕。

2.3 文字居中

好了,畫好了主圖本胖就要在主圖寫字了,需求是寫一段文字,字數不定,居中顯示。

本胖一開始想當然了用了setTextAlign設定了文字的對齊方式,可是一看竟然沒有說明用,於是本胖只好想算出所有文字的全部長度,用區域寬度減去文字的長度除以2設定文字的起始X座標就可以了。

context.fillText(`我是呂胖胖`, (width - context.measureText(`我是呂胖胖`).width) / 2, );
複製程式碼

上面程式碼裡面的measureText就是解決問題的核心API。

2.4 canvas層級太高 導致ios中海報隨著螢幕滑動

因為海報主體使用了fixed佈局,所以會被固定在螢幕的中間,在模擬器裡面測試沒有問題,但是在ios上面會出現手指滑動海報,海報會隨著頁面滑動,這是不是看起來很滑稽。這種現象是因為canvas這東西在小程式裡面地位那是相當的高,下面是小程式對canvas的介紹。

canvas元件是原生元件,他有如下的限制

原生元件的使用限制
由於原生元件脫離在 WebView 渲染流程外,因此在使用時有以下限制:

原生元件的層級是最高的,所以頁面中的其他元件無論設定 z-index 為多少,都無法蓋在原生元件上。
後插入的原生元件可以覆蓋之前的原生元件。
原生元件還無法在 scroll-view、swiper、picker-view、movable-view 中使用。
部分CSS樣式無法應用於原生元件,例如:
無法對原生元件設定 CSS 動畫
無法定義原生元件為 position: fixed
不能在父級節點使用 overflow: hidden 來裁剪原生元件的顯示區域
原生元件的事件監聽不能使用 bind:eventname 的寫法,只支援 bindeventname。原生元件也不支援 catch 和 capture 的事件繫結方式。
在iOS下,原生元件暫時不支援觸控相關事件。
原生元件會遮擋 vConsole 彈出的除錯皮膚。
在工具上,原生元件是用web元件模擬的,因此很多情況並不能很好的還原真機的表現,建議開發者在使用到原生元件時儘量在真機上進行除錯。
複製程式碼

一開始本胖查閱資源後大部分是設定如下

disable-scroll='true'
複製程式碼

但是試過後並沒有什麼軟用,最後無意間突然發現了一個好用的屬性

catchtouchmove='true'
複製程式碼

該屬性加在海報最外面的view上面。

3 生成小程式碼+自定義分析

其實微信小程式本身就自帶了資料分析,裡面有很多比如統計小程式的訪問量,訪問分析,使用者畫像等等。當然了,如果你和本胖一樣對這些統計不滿足的話,你還可以自己定義一些統計。本胖這次的需求是如下

生成N個小程式碼,然後區別不同渠道的流量

3.1 生成多個小程式二維碼

目前小程式二維碼有二種,一種是普通的正方形的(體驗版本就是這種),還有一種是小程式官方為了區別其他二維碼而專門設定的圓形的體驗碼,下面所說的體驗碼都是圓形的體驗碼,目前只能通過請求小程式官方介面獲取。本胖採用的請求A類介面才獲取二維碼

https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN
複製程式碼

這個介面需要獲取一個叫做access_token的東西

下面是獲取access_token的介面

GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
複製程式碼

好了,接下來就是愉快地獲取二維碼了。這時候又一次見識到小程式坑爹的地方了。

本胖愉快地寫了一個本地ajax請求(一定要post),程式碼如下:

$.ajax({
  method: 'post',
  url: 'https://api.weixin.qq.com/wxa/getwxacode',
  dataType: 'json',
  data: {
    access_token: 本胖的accesss_token
    path: 'pages/index/index?id=1',
  },
  success: (data) => { 
    console.log(data);
  }
});
複製程式碼

發現報錯,引數為空,再去看官方發現有下面的提示

A計劃小程式的血與淚
哦哦,原來是要json字串,而jq預設是contentType是application/x-www-form-urlencoded,於是本胖很快按照提示改成了下面的contentType

contentType: "application/json; charset=utf-8"
複製程式碼

這會總該可以了吧。

沒想到又是接受不到引數。。。。

最後在嘗試了很多次以後終於發現了正確的請求方式

$.ajax({
  method: 'post',
  url: '/wxa/getwxacode?access_token=本胖的accesss_token',
  dataType: 'json',
  contentType: 'text/plain;charset=UTF-8',
  data: JSON.stringify({
    path: 'pages/index/index?id=1',
  }),
  success: (data) => { 
    console.log(data);
  }
});
複製程式碼

有沒有很驚喜,有沒有很意外。這種設計真的很蛋疼,小程式官方最好給一個請求例項哈。

3.2 自定義分析 好了,經歷了千辛萬苦的嘗試,本胖在上一屆總算請求到了A計劃的小程式圓形碼了。接下來就是做自定義分析。

所謂自定義分析,在小程式後臺管理系統是可以找到的,顧名思義就是我們可以自己定義一些使用者行為的分析,比如使用者點選某個按鈕的次數,使用者停留在某些頁面的時間等等,可以說是小程式官方留給開發者埋點的口子

看了一下關於自定義分析的介紹(文章真的很長很長),知道了自定義分析主要步驟如下

1.新建事件
2.選擇配置方式
    A.填寫配置:不需要任何小程式程式碼
    B.API上報:需要加對應的小程式程式碼
3.儲存測試或者釋出
複製程式碼

注意到了在選擇配置方式的時候是有2種的,本胖這次需求是要通過不同的小程式碼區別不同渠道的流量,所以選擇了第二種。

對了,這裡需要說明之前在獲取小程式碼的時候path引數,本胖在路徑後面是傳了引數的,這個引數就是用來區別不同渠道。

然後配置好後,可以獲取到對應的上報程式碼

wx.reportAnalytics('statistics', {
  id: '',
});
    
複製程式碼

這裡看出本胖是選擇了一個名稱是statistics的事件名稱,然後本胖在A計劃小程式的首頁的onload事件中加入了以下程式碼

onLoad: function(options) {
  console.log(options);
  wx.reportAnalytics('statistics', {
    id: options.id
  });
}
複製程式碼

那麼不同渠道的小程式進入就會獲取到不同的id上報給小程式平臺,在後臺管理中根據不同的渠道id的上報次數來判斷不同渠道的流量情況

但是,現實有這麼美好嗎?

在體驗版的小程式的路徑中加入了不同的id,體驗了一把,去小程式的後臺檢視對應的資料(本胖的自定義事件已經發布),有下面的截圖

A計劃小程式的血與淚

看的只是不同id的去重總和資料,並沒有不同id對應的資料,也就說通過小程式的自定義分析實現不同小程式碼區別不同渠道的想法是不行的,該次嘗試以失敗告終,也讓本胖知道了小程式有哪些是還做不了的,所以說這樣的嘗試也是有意義的哈。

對於這個需求,本胖最終還是打算採用我們自己的服務端來實現不同渠道的流量分析。

4 其他坑點

哈哈,這一節的標題不知道取什麼好了,只好取了這個。

4.1 橫向滾動

做H5的時候,實現橫線滾動那是so easy的事情,大家都懂哈,不就是一個熟悉的問題嘛。 但是,這麼簡單的需求在小程式裡面並沒有那麼簡單。

<view>
  <scroll-view scroll-x>
  </scroll-view>
</view>
複製程式碼

像上面這樣,設定了scroll-x屬性就以為當scroll-view 的內容超出scroll-view 時候會出現橫向滾動條,但是實際上並不是這樣的,還需要下面的設定

scroll-view
  white-space: nowrap;
子元素
  display: inline-block;
複製程式碼

white-space:nowrap;這個屬性在css裡面的意思是

規定段落中的文字不進行換行

4.2 inupt動態繫結

嘗試過小程式的都發現他的語法和vue很像,所以上手都是很快的。但是當本胖遇到input的時候果斷寫了下面的程式碼

<input value="{{name}}">
複製程式碼

想通過這種方式的繫結來動態獲取到輸入框輸入的值。

但是現實又是殘酷的。

人家小程式根本就不支援這種寫法。

有沒有很坑,都向vue學習了90%的語法了,input繫結變數使用評率這麼高的竟然不學。

吐槽歸吐槽,活還是要繼續幹下去的。只好採用react版方式來動態獲取input的輸入值了。

this.setData({
  val: e.detail.value
});
複製程式碼

4.3 wx.request

這是小程式用來獲取服務端資料的api,使用頻率那也是很高的,但是他在本胖看來主要有2處需要注意的地方

1.預設的contentType是application/json,需要自己改寫為合適的和伺服器端通訊
2.和傳統的ajax很像,容易走入回撥地獄,可以自己封裝成promise形式哈
複製程式碼

5. 小結

這篇文章主要是總結了本胖這兩週在開發A計劃小程式遇到的問題以及對應的解決方案和對微信小程式的一些個人看法。

不過,微信小程式的確可以說是前端史上一次重大的突破,其體驗,能力(雖然大部分也都是通過使用微信app的能力)都比一般的H5更好更強。而且語法和vue很像,易於上手,開發體驗也不錯,相應的社群工具都越來越完善了,很期待小程式的未來。

這應該是本胖工作2年多來寫的最長的一篇技術博文了,二年多時間說過就過,本胖唯有努力使自己的能力與年齡成正相關才行哈。

相關文章