技術新風向丨挖掘藏在小程式 Cookie 裡的祕密

語言: CN / TW / HK

Cookie 可不是一般的“小甜餅”

Cookie 直譯過來其實是“小甜餅”。但是在網際網路的世界裡,Cookie 是 Web 開發中一種常用的資料儲存、會話跟蹤技術。

image.png

Cookie 機制在小程式開發中也有很大的需求,然而此前多數主流小程式平臺並不支援 Cookie 機制,導致開發者們不得不通過小程式本地快取的方式來模擬 Cookie 的效果。常見使用手動管理 Cookie 或者第三方庫的形式來進行小程式端 Cookie 處理,但是這種方式不夠完美,也會存在諸多問題

| 手動管理 Cookie | 使用第三方庫 | | --- | --- | | image.png | image.png |

下面為大家詳細地介紹以下這兩種流行方式!

手動管理 Cookie

使用小程式資料快取能力模擬 Cookie,只能滿足基本需要,開發者負擔較重。

常見的操作是,開發者封裝 request 請求,從介面響應中取出需要儲存的值,儲存在本地快取 storage 中,每次介面請求時,再從 storage 中讀取相關資料新增進請求 header 或 body 中,以此模擬 Cookie 的效果。 ``` // 從介面響應中取出並儲存 cookie 值: tt.request({ url: "https://xxx.com/login", data: { / ... / }, success(res) { res.header["Set-Cookie"] !== undefined && tt.setStorageSync("cookie", res.header["Set-Cookie"]); }, fail(res) { console.log("呼叫失敗", res.errMsg); }, });

// 請求的時候讀快取資料帶上 cookie 資訊 const header = { 'content-type': 'application/json' }; const cookie = tt.getStorageSync("cookie"); if(url !== 'login' && cookie){ header['cookie'] = cookie; } tt.request({ url: "https://xxx.com/request", data: { / ... / }, header, }); ``` 1. 增加開發者手動維護負擔 - 需要前端開發者手動維護 Cookie; - 需要手動區分 domain、path; - - storage 是全域性儲存,如果需要增加對 domain、path 作用域的支援,需要前端增加維護程式碼,進一步增加開發者負擔; - 需要手動維護過期時間; - - 支援過期時間處理也需要前端增加維護程式碼,同樣增加開發者負擔。

2. 降低小程式效能 - 如果本地快取資料分多個變數儲存,讀寫資料時,將出現多次 getStorageSync、 setStorageSync 呼叫,該方法為同步方法,需要和客戶端進行資料通訊,頻繁呼叫對小程式效能有一定影響 ``` // 連續讀取 const param1 = tt.getStorageSync("param1") || ""; const param2 = tt.getStorageSync("param2") || ""; const param3 = tt.getStorageSync("param3") || ""; const param4 = tt.getStorageSync("param4") || ""; const param5 = tt.getStorageSync("param5") || "";

// 傳送請求 tt.request({ data: { param1, param2, // ... } }); ``` - 在小程式開發工具的 trace 分析面板中可以看到這樣連續呼叫的效果;

image.png - 介面請求前為了設定 Cookie,需要連續多次從 storage 中同步地讀取資料,getStorageSync 一次的耗時可能僅 1~3 ms,但累積起來可能達數十毫秒。特別當開發者將其封裝為基礎方法,在高頻介面中使用時,將帶來更多的效能損耗,且很容易被開發者忽視; - Cookie 的更新類似,需要連續呼叫 setStorageSync 寫入資料,同樣帶來效能損耗。

3. 一些非 tt.request 請求無法處理**** - 小程式中,除了 tt.request ,還有 videolive-player 等原生元件,以及 audio tt.previewImage 這類 API 都會發送網路請求。而這類請求受限於小程式能力開放程度,開發者無法修改其中請求引數,也就無法設定 Cookie; ``` // 視訊請求想要標記使用者這麼辦?

```

使用第三方庫

社群中有一些第三方庫支援小程式端的 Cookie 機制,使用雖然方便,但存在效能、安全性、相容性等問題。 以 weapp-cookie 為例,通過劫持小程式的 tt.request、tt.uploadFile、tt.downloadFile 等 API ,增加自動解析和新增 Cookie 的操作,免去了開發者自行管理 Cookie 的負擔。

使用第三方庫相比手動管理有一定的優勢: 1. 只需要引入 npm 包即可使用,減輕了開發者手動維護的成本; 2. 提供了 API 獲取、設定 Cookie; 3. 提供了 domain/path 作用域、過期等支援;

但第三方庫的 Cookie 實現,最終還是基於小程式本地快取,依然存在諸多不足:

  1. 同樣存在頻繁讀取 storage 的效能問題;
  2. 可能與小程式的自定義 Cookie 衝突;

  3. 小程式 tt.request API 支援自定義設定 Cookie,但是當使用第三方庫時,同名 Cookie 可能存在衝突,被第三方庫覆蓋掉,導致自定義 Cookie 無效。

image.png

3.無法支援 video、audio、live-player、tt.previewImage 等網路請求方式;

4.對於 uni-app、Taro 等跨平臺框架可能需要第三方庫進行相容適配,否則無法使用;

5.存在安全問題,如果有惡意的第三方框架可以獲取/修改 Cookie;

6.引入第三方庫將增大小程式包體積。

官方小程式的“Cookie” 它不香嗎?

考慮到開發者的迫切需求,以及現有使用本地快取方式的種種弊端,位元組小程式在基礎庫 2.45.0 版本開始從框架層面提供了 Cookie 的支援。

位元組小程式支援服務端在 tt.request 的響應中,使用 HTTP 首部中的 Set-Cookie 欄位設定 Cookie。框架側負責 Cookie 解析、儲存、匹配和傳送,處理邏輯遵循 RFC6265 規範。小程式之間、小程式與宿主 Cookie 相互隔離,同時支援手動設定 Cookie。

更多介紹可檢視開發者文件:小程式 Cookie 機制

小程式 Cookie 機制核心優勢凸顯

較市面上的兩種流行方式,位元組小程式官方提供的 Cookie 機制更具備以下核心優勢:

image.png

功能相對豐富完善

相比開發者使用 storage 模擬實現的 Cookie,小程式 Cookie 遵循 RFC6265 規範,提供的功能比較完善和豐富: - 支援 domain/path 作用域 - 支援 Max-Age/Expires 過期機制 - - 當 Max-Age 和 Expires 同時存在時,Max-Age 優先順序更高 - 相容小程式 tt.request 的自定義 Cookie - 相容各類第三方框架 - - 小程式 Cookie 是從平臺基礎能力提供的支援,即便使用 uni-app、Taro 等開發框架,只要能在小程式配置檔案中開啟,即可使用小程式 Cookie。

效能更優

小程式 Cookie 由框架 SDK 管理,讀寫速度要比前端 storage 模擬的方式快得多,根據資料來看,小程式 Cookie 的讀取速度 < 1ms,寫入速度在 7ms 左右。

再對比看看開發者自己讀取資料的情況,效能優勢相當明顯:

image.png

更加安全

  • 不會被前端 js 獲取,防止被惡意三方框架讀取或篡改
  • 支援 secure 屬性,保證安全傳輸 ``` // Set-Cookie: "id=1; secure;"

// http 請求將不攜帶 Cookie // Cookie: '' tt.request({ url: "http:/xxx.com" });

// https 請求才攜帶 Cookie // Cookie: 'id=1' tt.request({ url: "https:/xxx.com" }); - 隔絕跨域設定 Cookie // 跨域 Set-Cookie 不會被儲存 url: "https://a.com/" Set-Cookie: "id1=1; domain=b.com" ```

提供了一些原生元件/API中網路請求 Cookie 的支援

使用小程式 Cookie 後,原生元件/API(Video、Audio、Live-Player、tt.previewImage)也會自動攜帶cookie,可以解決資源請求難以標識使用者問題。這在框架提供支援之前,開發者是難以做到的。

Web 應用中,一些圖片、視訊等資源類的請求也會使用 Cookie 來標識使用者資訊,以此提供更加精準和個性化的服務(如許可權、個性化推薦等)。

在小程式不支援 Cookie 的時候,經常看到開發者要通過事件監聽使用者的操作,在事件回撥中再通過額外的 tt.request 傳送資訊。 ``` // ttml

// js playVideo(e) { const id = e.target.id; const userId = tt.getStorageSync('useId'); tt.request({ url: 'xxx', data: { userId, videoId: id, } }); } ``` 現在有了小程式 Cookie,完全不需要這麼麻煩,video 元件在請求視訊等資源時也能自動新增上 Cookie,服務端可以據此校驗使用者身份,前端不需要再寫過多額外程式碼,還能減少 tt.request 的次數,減少網路請求,降低服務端壓力。

減輕了小程式前端開發者負擔

完整的 Cookie 實現是比較複雜的(參見 RFC6265),開發者們使用 storage 模擬的方式,往往也只是實現一些簡單的基本功能。如果有更豐富的需求怎麼辦?domain/path 作用域要不要支援?過期機制要不要支援?這些都是開發者的負擔。

即便封裝或使用一些相關的三方庫,也只是一定程度上減輕了開發者的負擔,但隨之又帶來的效能問題、原生元件網路請求的支援問題、安全性問題、三方框架的相容適配問題等等,依然不夠令人滿意。

使用小程式 Cookie 就完全不需要考慮這麼多,前端開發者無需寫任何程式碼,服務端的開發者也可以達到類似 Web 開發中的體驗。

再不會用小程式 Cookie 你就 OUT 了!

官方小程式的 Cookie 這麼香,據說還有80%的人不會用.....別說我沒有教過你這個“小甜餅”的正確使用方法!這屆開發者已經在揹著你偷偷學習了!

image.png

如果你是前端:

只需要在 app.json 中開啟配置即可: { "cookie": { "enableStore": true // true 開啟小程式 Cookie 機制。預設 false } }

如果你需要發起請求時新增額外的 Cookie:

tt.request API 支援設定請求 header 的部分欄位,可以新增自定義的 Cookie,小程式框架側會將自定義 Cookie 與本地儲存的 Cookie 合併,當出現衝突時,自定義 Cookie 會覆蓋本地 Cookie 同名 key 部分: ``` // 本地 Cookie // key1=value1; key2=value2

// 自定義 Cookie tt.request({ url: "https://xxx.com/request", header: { "content-type": "application/json", cookie: "key1=value3", // 此處新增cookie }, success(res) { console.log("呼叫成功", res.data); }, });

// 傳送出的 Cookie // key1=value3; key2=value2 ``` 如果你是服務端:

前端配置中開啟後,開發者的服務端就可以通過 HTTP 請求響應的Set-Cookie欄位設定 Cookie,小程式框架會幫你儲存和管理,開發者前端無需再費心處理了。

更多接入細節可檢視開發者文件 看到這裡,我猜你還想了解:

Q1:小程式 Cookie 如何做隔離的?

A1:不同小程式之間隔離 Cookie;宿主賬號切換會清空 Cookie 資料。

Q2:Cookie 對數量、大小是否有限制?

A2:每個小程式每個域名下最多 50 個 Cookie總 Cookie 不超過1000個,如超過限制使用 LRU 演算法淘汰。每個 Cookie 大小不超過 4K,如超過則服務端此次 Set-Cookie 操作無效。

Q3:是否支援跨域 Set-Cookie?

A3:不支援。

只有真正在乎你的人,才會把“小程式 Cookie 機制”的祕密告訴你!還不快去做聰明人的選擇!

image.png