小紅書自研小程序:電商體驗(yàn)與效果優(yōu)化的運(yùn)行時(shí)體系設(shè)計(jì)

一、背景介紹


【資料圖】

小程序在其誕生后的幾年內(nèi),憑借其簡單、輕量、流暢、無需安裝等特點(diǎn),引來了爆發(fā)式的增長。伴隨小紅書電商業(yè)務(wù)的發(fā)展,我們洞察到越來越多的商家和品牌大客戶有自己定制化需求場景,傳統(tǒng)的電商和薯店存在下面三大問題:

為了解決上述問題,并快速打通基于小紅書體系的支付與賬號(hào)體系。過去的一年內(nèi),我們踏上了自研小程序之路。目前,在小紅書店鋪主頁、 筆記詳情、品牌專區(qū)、開屏均可喚起小程序。

本文將主要介紹小紅書進(jìn)行小程序自研時(shí)的一些業(yè)務(wù)背景及工程化、容器能力的落地方案,以及運(yùn)行時(shí)針對(duì)雙線程架構(gòu) bridge,framework 能力的設(shè)計(jì)。

二、運(yùn)行時(shí)工程能力建設(shè)

2.1 小程序 "運(yùn)行時(shí)" 定義

運(yùn)行時(shí)在不同語言中含義有所不同,但基本可以概括為「運(yùn)行在代碼執(zhí)行階段的代碼」,類似 vue-runtime, 提供了對(duì)于頁面狀態(tài)的劫持,生命周期的解析,api 的調(diào)用能;nodejs 提供了 JS 運(yùn)行時(shí)執(zhí)行能力等。小程序 “運(yùn)行時(shí)” 則提供了在不同線程內(nèi),借助 Bridge 消息通道,進(jìn)行邏輯調(diào)度的能力。

那么可以基本概括為:運(yùn)行在小程序代碼執(zhí)行階段、用于提供在獨(dú)立線程中操作其他線程的頁面(或視圖),正確響應(yīng)用戶交互行為、并調(diào)度用戶業(yè)務(wù)邏輯能力的代碼。

2.2 小程序基礎(chǔ)架構(gòu)

小紅書小程序也是對(duì)齊業(yè)界經(jīng)典架構(gòu)進(jìn)行建設(shè):

經(jīng)典雙線程架構(gòu)

經(jīng)典架構(gòu)下,運(yùn)行時(shí)主要分為 渲染層 - Render、邏輯層 - Service。Service 用于與系統(tǒng)能力進(jìn)行交互,在安全的 JS 線程內(nèi)調(diào)度用戶業(yè)務(wù)邏輯。而 Render 則負(fù)責(zé)接受渲染指令、進(jìn)行視圖的繪制與用戶交互的響應(yīng)。邏輯層與渲染層則通過 js-bridge 進(jìn)行消息的通訊,容器則負(fù)責(zé)接受 api 指令進(jìn)行端能力的調(diào)用。

之所以需要一個(gè)獨(dú)立的線程來執(zhí)行 JS,其主要目的是為了限制 JS 靈活性。為了提供一個(gè)可用的 JS 環(huán)境,其實(shí)也有比較多的方案。比如,我們可以使用瀏覽器內(nèi)核提供的 Service Worker ,來單獨(dú)運(yùn)行 service 層 JS 代碼?;蛘呶覀兛梢允褂枚鄠€(gè) webview 實(shí)例來分別承載雙端 js 的執(zhí)行環(huán)境。

2.3 容器架構(gòu)實(shí)現(xiàn)

按照經(jīng)典架構(gòu)的設(shè)計(jì),我們需要在三端 (iOS、android、小程序開發(fā)者工具) 提供面向雙線程的容器方案。在不同的容器環(huán)境下,渲染層和邏輯層選用的方案會(huì)存在一定差異,小紅書三端選用容器的分布如下:

雖然運(yùn)行環(huán)境存在一定差異,但容器對(duì)于基礎(chǔ)庫和業(yè)務(wù)腳本的加載順序是基本一致的。我們可以將整個(gè)啟動(dòng)階段拆解為下面幾個(gè)階段:

首先,當(dāng)用戶點(diǎn)擊時(shí),會(huì)經(jīng)歷一個(gè)基本的啟動(dòng)過程。

在這個(gè)啟動(dòng)流程的背后,會(huì)對(duì)應(yīng)著上面提到渲染層 webview 容器被加載出來。于此同時(shí),在用戶看不到的地方,容器會(huì)進(jìn)行邏輯層 v8/JsCore 的初始化, 同時(shí)會(huì)加載小程序的基礎(chǔ)庫代碼。

腳本注入結(jié)束后,容器會(huì)立即通知「運(yùn)行時(shí)邏輯層框架」進(jìn)行依賴分析、并準(zhǔn)備初渲染數(shù)據(jù)。

渲染層接受到 initialData 消息后,會(huì)進(jìn)行后續(xù)渲染操作,用戶即刻看到了頁面的內(nèi)容 至此,初渲染的流程基本結(jié)束。

當(dāng)然,實(shí)際的容器的啟動(dòng)過程中的流程會(huì)更加復(fù)雜,整個(gè)啟動(dòng)流程可以用下面的這張圖來表示:

黑色、藍(lán)色、橙色分別代表了 端側(cè)、邏輯層、渲染層三個(gè)線程

實(shí)際場景中,容器還面臨更多的挑戰(zhàn),比如如何確保雙線程的是否 ready,再進(jìn)行消息的推送等。核心在于,我們通過不同線程的容器,完成了頁面渲染行為的控制。

可以看到,上述啟動(dòng)流程中容器側(cè)分別在 Render 和 Service 分別注入了 page.render.js 和 service.js 的業(yè)務(wù)代碼。那么如何進(jìn)行業(yè)務(wù)代碼構(gòu)建,來分別在雙線程下執(zhí)行呢?這就需要依靠前端工程化的能力來實(shí)現(xiàn)了。

2.4 實(shí)現(xiàn)基礎(chǔ)架構(gòu)的工程化能力

通過前端工程化能力,我們可以對(duì)資源進(jìn)行分類構(gòu)建,小紅書小程序使用 webpack 作為工程化構(gòu)建工具。通常,小程序的構(gòu)建分兩塊,一塊是針對(duì)基礎(chǔ)庫的打包,一塊是針對(duì)業(yè)務(wù)組件的構(gòu)建。

基礎(chǔ)庫的打包需要構(gòu)建基礎(chǔ)庫代碼,產(chǎn)出分別用于提供運(yùn)行時(shí)框架能力的render.base.js及service.base.js

而業(yè)務(wù)組件的構(gòu)建,則相對(duì)復(fù)雜。一個(gè)原生小程序組件或頁面通常包含下面四個(gè)文件:

通過拆分多個(gè)文件,我們可以在構(gòu)建時(shí)指定入口依賴,將對(duì)應(yīng)的依賴打入所需要的模塊內(nèi),在工程構(gòu)建時(shí),需要對(duì)文件進(jìn)行分類打包:

我們使用 loader 作為 webpack 的 entry 入口進(jìn)行構(gòu)建,每個(gè)頁面都會(huì)作為一個(gè) entry 獨(dú)立打包。這使得從行為上來說小程序更像一個(gè) MPA(多頁應(yīng)用)。入口側(cè)會(huì)進(jìn)行 app.json 的校驗(yàn),對(duì)配置以頁面維度來進(jìn)行解析,針對(duì)小程序業(yè)務(wù)代碼,會(huì)分別構(gòu)建出 page.render.js 和 service.js 分別交給不同的線程進(jìn)行加載(如上圖)。

構(gòu)建會(huì)將代碼打包成 UMD 格式文件,當(dāng)在不同線程內(nèi)執(zhí)行基礎(chǔ)庫腳本時(shí),部分腳本會(huì)自動(dòng)執(zhí)行,端側(cè)只需要關(guān)注容器加載 Js腳本的時(shí)機(jī)及消息發(fā)送的順序即可。

運(yùn)行時(shí)基礎(chǔ)能力與框架

容器和工程化能力是小程序運(yùn)行的基石,但小程序之所以可以做到高效開發(fā)、并擁有極強(qiáng)的跨平臺(tái)能力和優(yōu)秀的體驗(yàn),這也得益于在框架底層提供了完善的組件及模塊化能力,更有豐富的 api 來滿足原生場景下各種系統(tǒng)能力調(diào)用的述求。

3.1 運(yùn)行時(shí)總架構(gòu)

·這張圖主要將運(yùn)行時(shí)架構(gòu)分為了渲染層、邏輯層和jsBridge:

·渲染層面向業(yè)務(wù)提供了組件、沙箱、性能收集等框架能力,這一層業(yè)務(wù)是無法接觸到的

·邏輯層則在 JsContext 內(nèi)提供了 invoke 層來與端側(cè)進(jìn)行數(shù)據(jù)交互

·邏輯層通過適配層,完成導(dǎo)航、Render和頁面實(shí)例的管理

·邏輯層內(nèi)核主要用于向業(yè)務(wù)代碼的執(zhí)行環(huán)境,提供 Page、Component、behavior ·這類能力,并預(yù)置 JS Polyfill 來確保業(yè)務(wù)的 js 正常運(yùn)行。

·JSON schema 則用于定義 api 標(biāo)準(zhǔn)結(jié)構(gòu)和定義,并通過 js-bridge 層完成端能力調(diào)用與通訊

3.2 基礎(chǔ)能力分布

為了豐富小程序的基礎(chǔ)能力,初期我們盤點(diǎn)了業(yè)界的功能矩陣,盡可能豐富小紅書小程序的基礎(chǔ)能力,目前運(yùn)行時(shí)的基礎(chǔ)能力分布如下:

灰色部分為暫未支持的能力

其中包含了:

·App, Compnent, Page 等基礎(chǔ)能力

·網(wǎng)絡(luò)、文件系統(tǒng),設(shè)備等 API 能力

·xhs-view, xhs-button 等面向業(yè)務(wù)的組件能力

·目前,矩陣列出的功能,在小紅書小程序基礎(chǔ)庫 ≥ v3.32.x 版本上已經(jīng)得到支持。

3.3 雙線程框架能力建設(shè)

熟悉小程序語法的同學(xué)都知道,小程序可以通過 Page、Component 來進(jìn)行非常靈活的組件化開發(fā)。通過 selectComponent 、triggerEvent這類功能可以非常方便的進(jìn)行 子 → 父 或 父 → 子 實(shí)例的追溯,這就要求框架側(cè)需要維護(hù)組件之間的依賴關(guān)系。

實(shí)現(xiàn)這種架構(gòu)有多種思路,不同廠商的做法也不同。譬如微信在 Page 體系和 Component 自定義組件的實(shí)現(xiàn)上就采用了不同的設(shè)計(jì)。微信在渲染側(cè)通過Exparser模塊完成小程序內(nèi)的所有組件,包括內(nèi)置組件和自定義組件組織管理。

小紅書側(cè)在渲染層則是 Fork Vue 框架,通過定制 Vue 的一些能力來完成頁面渲染工作。借助 Vue優(yōu)秀的組件化能力的來完成 Page, Component 的渲染工作。在邏輯層,則通過消息維護(hù)一棵類 vdom 樹 , 來完成視圖 ←→ 邏輯的映射與綁定關(guān)系,整個(gè)關(guān)系大概如下圖所示:

3.4 事件系統(tǒng)

有了上述基礎(chǔ)能力和雙線程架構(gòu),運(yùn)行時(shí)還需要實(shí)現(xiàn)一套事件系統(tǒng),讓 UI 界面與用戶產(chǎn)生互動(dòng)。事件通常分為兩塊,一塊是服務(wù)于用戶的手勢交互,比如用戶的點(diǎn)擊 tap, 長按 longtap 等事件,另一塊則是渲染層交互組件的回調(diào)時(shí)間,譬如 swiper 組件的 onChange 等回調(diào)。

在小程序的事件系統(tǒng)下,我們把這些用戶的手勢操作和組件回調(diào),進(jìn)行攔截與收集,全部轉(zhuǎn)入消息隊(duì)列轉(zhuǎn)發(fā)到邏輯線程。每條消息攜帶自己的實(shí)例 ID,找到邏輯層實(shí)例進(jìn)行對(duì)應(yīng)函數(shù)的觸發(fā)。

3.5 bridge 能力設(shè)計(jì)

框架側(cè)借助 bridge 通道可以非常方便進(jìn)行消息的轉(zhuǎn)發(fā)。但實(shí)際上,一條消息需要經(jīng)過多次序列化和反序列化,才可以到達(dá)“目的地”。小紅書小程序的 bridge 側(cè)是如何實(shí)現(xiàn)的呢?

我們以渲染層事件消息舉例,當(dāng)渲染層收到一條點(diǎn)擊 消息,會(huì)經(jīng)過如下幾個(gè)階段:

不同容器下,對(duì) webview 內(nèi)核消息的攔截機(jī)制不同,ios 使用 messageHandler, android 則使用 console 通道攔截消息,但內(nèi)核底層對(duì)消息的處理流程基本一致。

這個(gè)過程可以簡單描述為以下幾個(gè)環(huán)節(jié):

·Render 側(cè)發(fā)送 postMessage 消息,此時(shí)消息需要經(jīng)過一次序列化轉(zhuǎn)成字符串

·瀏覽器攔截到消息,反序列化成 JSONObject 并發(fā)送到 Naive 容器側(cè)

·容器開始進(jìn)行跨線程事件分發(fā),并轉(zhuǎn)發(fā)消息到 service

·Service 運(yùn)行環(huán)境將消息反序列化成 string,并轉(zhuǎn)成 JS 數(shù)據(jù)類型,傳到 Service 所在的 JsContext 中

·JsContext 中 invokeCallback 函數(shù)被調(diào)用

·至此,render 消息已成功轉(zhuǎn)發(fā)至 service 層

可以看到,這個(gè)過程非常復(fù)雜,不僅要完成消息的轉(zhuǎn)發(fā),還要完成 jsonObject 和 js 數(shù)據(jù)類型的互轉(zhuǎn)。為了在兩個(gè)線程內(nèi)方便的完成這種互調(diào),并保證 bridge 的安全線,我們分別在雙端分別實(shí)現(xiàn)了 handleMessage 和 postMessage 的封裝,通過 schema 來定義 bridge 和 api 標(biāo)準(zhǔn)協(xié)議,來完成線程消息的轉(zhuǎn)發(fā)和消息類型的校驗(yàn)工作。

一個(gè)標(biāo)準(zhǔn)的 api schema 定義大概是這樣:

消息會(huì)在 JsContext 內(nèi)完成校驗(yàn),并在校驗(yàn)通過后以序列化的方式完成上述流程的傳遞。

3.6 數(shù)據(jù)編譯能力與 JS 沙箱

為什么這里要提下數(shù)據(jù)編譯能力和 js 沙箱呢。因?yàn)樾〕绦螂p線程的框架下,邏輯層通過setData 發(fā)起頁面更新請(qǐng)求,攜帶的數(shù)組字段在被渲染層對(duì)應(yīng)的組件解析時(shí),需要配合小程序的一些語法特性進(jìn)行特殊轉(zhuǎn)換。

在運(yùn)行時(shí)側(cè),我們將字段的解析能力與數(shù)據(jù)字段的處理,都收攏到沙箱 環(huán)境中進(jìn)行字段編譯。通過沙箱,我們可以攔截業(yè)務(wù)代碼對(duì)于變量的訪問,從而實(shí)現(xiàn)變量的劫持,并配合完成 sjs 這類能力的實(shí)現(xiàn)。同時(shí),沙箱可以有效防止業(yè)務(wù)動(dòng)態(tài)注入一些變量或函數(shù),帶來的變量訪問逃逸的安全問題。因此,沙箱在小程序語法和變量計(jì)算的過程中起到了至關(guān)重要的作用。

例如,下面這段代碼片段:

在編譯側(cè),我們會(huì)將 loader上面代碼通過 ast 進(jìn)行轉(zhuǎn)換:

通過沙箱,我們可以攔截到業(yè)務(wù)對(duì) sjs 模塊訪問,將訪問屬性替換為 sjs 的模塊導(dǎo)出,從而實(shí)現(xiàn)類似 sjs 這樣的腳本拓展能力。

性能優(yōu)化與監(jiān)控

雙線程在線程隔離方案上,將原本在同一線程內(nèi)執(zhí)行的腳本、渲染等工作分散到多個(gè)線程內(nèi)執(zhí)行,帶來了更好的性能。但如果單個(gè) webview 線程的渲染負(fù)擔(dān)過重或?qū)υO(shè)備內(nèi)存占用過大一樣會(huì)影響到整體的體驗(yàn)。

于此同時(shí),線程隔離也帶來了通訊的損耗,對(duì)于一次消息需要經(jīng)過多次序列化和反序列化,消息序列化的損耗與轉(zhuǎn)發(fā)也對(duì)性能有著直接的影響。

因此,小程序的性能優(yōu)化不同于傳統(tǒng)的 web,需要從框架、通道、容器三方面來考慮。

4.1 bridge 消息調(diào)度機(jī)制

bridge 消息通道的繁忙程度,會(huì)在很大程度上影響小程序的性能表現(xiàn)。通過上面對(duì)于 bridge 消息轉(zhuǎn)發(fā)機(jī)制的介紹也可以看出,頻繁的借助 bridge 進(jìn)行消息轉(zhuǎn)發(fā),意味著消息要不斷進(jìn)行序列化和反序列化的操作。

實(shí)際場景中,數(shù)據(jù)量小于64KB時(shí),時(shí)長基本在10 - 40ms內(nèi)。傳輸時(shí)間與數(shù)據(jù)量上呈現(xiàn)正相關(guān)關(guān)系,傳輸過大的數(shù)據(jù)將使這一時(shí)間顯著增加,因此減少傳輸數(shù)據(jù)量是降低數(shù)據(jù)傳輸時(shí)間的有效方式。

但是如果數(shù)據(jù)量較小,確在短時(shí)間內(nèi)多次使用 bridge ,也會(huì)導(dǎo)致通道過于繁忙。小紅書在 bridge 側(cè),通過一定消息調(diào)度能力,將特定場景下的消息進(jìn)行聚合,確保一次序列化盡可能在不影響序列化性能的情況下,多攜帶一些消息到對(duì)應(yīng)的線程內(nèi)。

4.2 渲染層任務(wù)調(diào)度與優(yōu)先級(jí)隊(duì)列

前面我們?cè)?jīng)提到,雙線程背景下,小程序的更新機(jī)制與事件系統(tǒng)全部都是通過消息進(jìn)行處理的,但消息本身的收發(fā)都存在一定的延時(shí)性,這就注定了小程序是一個(gè)異步通訊的世界。那么在一個(gè)異步多線程的場景下,線程之間“生產(chǎn)“和“消費(fèi)“的消息的速度會(huì)因性能、穩(wěn)定性等因素而不一致,這時(shí),我們便要借助消息隊(duì)列的思想來管理我們的消息:

有了消息隊(duì)列,我們可以更好的管理框架層拋出的消息體,但小程序框架內(nèi),除了更新消息和事件消息外,還有不同的消息體會(huì)與這些框架消息搶占消息通道。比如,框架收集不同的 render 線程 webview 內(nèi)的性能指標(biāo),這些性能消息會(huì)與事件消息共享同一隊(duì)列。但有些場景下,事件消息的優(yōu)先級(jí)要遠(yuǎn)高于性能指標(biāo)消息。

此外,不同的渲染層 render 實(shí)例的消息所擁有的優(yōu)先級(jí)也不同,比如 A、B 頁面在同一時(shí)間段內(nèi),因其“棧頂?shù)牡匚弧睍?huì)因用戶操作而不斷變化,此時(shí)棧頂頁面的框架消息優(yōu)先級(jí)高于 B 頁面的框架消息優(yōu)先級(jí),在底層。我們使用 二叉堆 結(jié)構(gòu)來維護(hù)優(yōu)先級(jí)隊(duì)列。

4.3 容器預(yù)加載

小程序的啟動(dòng)分為冷啟動(dòng)和熱啟動(dòng), 從用戶的角度看:

冷啟動(dòng):如果用戶首次打開,或小程序銷毀后被用戶再次打開,此時(shí)小程序需要重新加載啟動(dòng),即冷啟動(dòng)。

熱啟動(dòng):如果用戶已經(jīng)打開過某小程序,然后在一定時(shí)間內(nèi)再次打開該小程序,此時(shí)小程序并未被銷毀,只是從后臺(tái)狀態(tài)進(jìn)入前臺(tái)狀態(tài),這個(gè)過程就是熱啟動(dòng)。

通常在容器側(cè)的優(yōu)化,就是針對(duì)冷啟動(dòng)來進(jìn)行。那么容器的預(yù)載,顧名思義,就是在合適的時(shí)間提前預(yù)載小程序容器,預(yù)載的同時(shí),會(huì)提前進(jìn)行基礎(chǔ)庫的下載和渲染容器(webview)的加載。

通過前置容器的初始化時(shí)機(jī),來達(dá)到快速換起小程序,提高首屏的優(yōu)化效果。這是小程序這類容器技術(shù)方案常用的優(yōu)化策略。

4.4 性能監(jiān)控與告警

性能優(yōu)化的同時(shí),框架側(cè)需要對(duì)業(yè)務(wù)代碼的性能和行為有一定感知能力。在底層,我們通過 aop 的方式,建設(shè)了一套監(jiān)控和插件機(jī)制。在開發(fā)階段,可以感知到業(yè)務(wù)各項(xiàng)指標(biāo)的健康狀況,業(yè)務(wù)可以接收到底層框架給出的性能告警信息,并通過告警信息中的修復(fù)建議,針對(duì)性的進(jìn)行優(yōu)化。

業(yè)務(wù)側(cè),則可以通過 performance api 拿到這些性能指標(biāo),來進(jìn)行基礎(chǔ)性能數(shù)據(jù)的收集與上報(bào)。

性能告警會(huì)結(jié)合性能標(biāo)準(zhǔn)閾值來給出提示和修復(fù)建議,未來在審核階段也會(huì)結(jié)合這些指標(biāo)進(jìn)行小程序健康度的洞察。

4.5 ServiceTiming 與 RenderTiming

除了卡頓、渲染指標(biāo)外,為了滿足高級(jí)開發(fā)者洞察平臺(tái)的性能信息的需要,我們對(duì)容器和框架在啟動(dòng)階段的關(guān)鍵節(jié)點(diǎn),都預(yù)留了性能點(diǎn)位。開發(fā)者可以通過 performance.serviceTiming和performance.renderTiming 來分別獲取到各個(gè)關(guān)鍵階段的時(shí)間戳信息。

各個(gè)線程內(nèi)所預(yù)留的性能點(diǎn)位和其在啟動(dòng)階段中的位置如下圖所示:

五、總結(jié)

以上就是小紅書小程序運(yùn)行時(shí)方案的原理解析。

小程序本身是一個(gè)依托宿主流量體系衍生出的技術(shù)體系,它的價(jià)值往往緊貼應(yīng)用主體的流量,而應(yīng)用主體本身,又依賴小程序的靈活性及低成本的特點(diǎn)快速完成流量的轉(zhuǎn)換。社交、支付與搜索,這些都是互聯(lián)網(wǎng)產(chǎn)品提供的服務(wù)形態(tài),各大廠商都是結(jié)合用戶的需求和行為差異進(jìn)行更開放、安全的技術(shù)方案探索,小紅書亦是如此。小紅書依靠用戶產(chǎn)生內(nèi)容,而內(nèi)容產(chǎn)生商品,那么結(jié)合各類消費(fèi)場景,如店鋪、筆記等都可以通過小程序容器快速進(jìn)行交易鏈路閉環(huán)。

未來,我們也將在不同的品牌和賽道上,尋找更多的服務(wù)商與品牌大客戶商家與我們一起,共同豐富小紅書的商品服務(wù)供給,增加小紅書商業(yè)收入。技術(shù)上,我們則會(huì)不斷對(duì)齊業(yè)界,優(yōu)化技術(shù)架構(gòu),在提高框架性能的同時(shí),建立完善的服務(wù)市場、巡檢機(jī)制等來幫助小紅書服務(wù)商與自開發(fā)商家細(xì)致、高效的開發(fā)與管理自己的小程序。

小程序是一個(gè)比較龐大的技術(shù)體系,如果你覺得本文對(duì)你有幫助,歡迎點(diǎn)贊轉(zhuǎn)發(fā)。我們后續(xù)會(huì)根據(jù)反饋繼續(xù)展開介紹更多的技術(shù)建設(shè)細(xì)節(jié)。也歡迎訪問我們的小程序官方網(wǎng)站與我們交流:

·小紅書小程序介紹:https://miniapp.xiaohongshu.com/docs/guide/miniIntroduce

·小紅書社區(qū)專業(yè)號(hào):https://pro.xiaohongshu.com/

·你也可以通過 https://github.com/redengineer/redmini 與我們進(jìn)行交流

六、作者信息

哈笛

商業(yè)技術(shù)組 - 小程序團(tuán)隊(duì)成員(tailiang@xiaohongshu.com),目前負(fù)責(zé)小紅書運(yùn)行時(shí)相關(guān)技術(shù)開發(fā)工作。

推薦DIY文章
電腦桌面沒有了怎么恢復(fù)?或許可以嘗試重啟資源管理器-環(huán)球精選
virtualbox怎么安裝系統(tǒng) 如何以virtualbox虛擬機(jī)安裝系統(tǒng)并使用-全球微動(dòng)態(tài)
win7系統(tǒng)找不到word模板路徑怎么辦?有需要用戶可點(diǎn)擊教程參考
玩英雄聯(lián)盟fps低怎么辦 可查看電腦是否設(shè)置節(jié)能模式-全球微速訊
win7系統(tǒng)下如何運(yùn)行暗黑破壞神2:可以快捷方式小尾巴開啟窗口模式游戲-天天速看
win10訪問局域網(wǎng)出現(xiàn)“請(qǐng)檢查名稱的拼寫” 可進(jìn)入編輯組策略調(diào)整并更改
精彩新聞

超前放送