亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

淺談鴻蒙 JavaScript GUI 技術(shù)棧

瀏覽:44日期:2023-10-12 15:50:01

作者:doodlewind鏈接:https://juejin.im/post/6872154561574862855

眾所周知,剛剛開(kāi)源的「鴻蒙 2.0」以 JavaScript 作為 IoT 應(yīng)用開(kāi)發(fā)的框架語(yǔ)言。這標(biāo)志著繼 SpaceX 上天之后,JavaScript 再一次蹭到了新聞聯(lián)播級(jí)的熱點(diǎn)。這么好的機(jī)會(huì),只拿來(lái)陰陽(yáng)怪氣實(shí)在太可惜了。作為科普,這篇文章不會(huì)拿著放大鏡找出代碼中的槽點(diǎn)來(lái)吹毛求疵,而是希望通俗地講清楚它所支持的 GUI 到底是怎么一回事。只要對(duì)計(jì)算機(jī)基礎(chǔ)有個(gè)大概的了解,應(yīng)該就不會(huì)對(duì)本文有閱讀上的障礙。

我們已經(jīng)知道在「鴻蒙 2.0」上,開(kāi)發(fā)者只需編寫(xiě)形如 Vue 組件式的 JavaScript 業(yè)務(wù)邏輯,即可將其渲染為智能手表等嵌入式硬件上的 UI 界面。這個(gè)過(guò)程中需要涉及哪些核心的模塊呢?這些模塊中又有哪些屬于自研,哪些使用了現(xiàn)成的開(kāi)源項(xiàng)目呢?這里將其分為自上而下的三個(gè)抽象層來(lái)介紹:

JS 框架層,可理解為一個(gè)大幅簡(jiǎn)化的 Vue 式 JavaScript 框架 JS 引擎與運(yùn)行時(shí)層,可理解為一個(gè)大幅簡(jiǎn)化的 WebKit 式運(yùn)行時(shí) 圖形渲染層,可理解為一個(gè)大幅簡(jiǎn)化的 Skia 式圖形繪制庫(kù)

這三個(gè)抽象層,整體構(gòu)成了一套面向嵌入式硬件的 GUI 技術(shù)棧。不同于許多高呼「不明覺(jué)厲 / 深不可測(cè)」的輿論,個(gè)人認(rèn)為至少對(duì)于 GUI 部分,國(guó)內(nèi)凡是接觸過(guò)目前主流 Hybrid 式跨端方案或 JS 運(yùn)行時(shí)研發(fā)的一線開(kāi)發(fā)者,都很容易從源碼出發(fā)來(lái)理解它。下面逐層對(duì)其做一些解讀和分析。

JS 框架層

從最頂層的視角出發(fā),要想用「鴻蒙 2.0」渲染出一段動(dòng)態(tài)的文本,你只需要編寫(xiě)如下的 HML(類 XML)格式代碼:

<!-- hello.hml --><text onclick='boil'>{{hello}}</text>

然后在同級(jí)目錄編寫(xiě)這樣的 JavaScript:

// hello.jsexport default { data: { hello: ’PPT’ }, boil() { this.hello = ’核武器’; }}

這樣只要點(diǎn)擊文本,就會(huì)調(diào)用 boil 方法,讓 PPT 變成 核武器。

這背后發(fā)生了什么呢?熟悉 Vue 2.0 的同學(xué)應(yīng)該會(huì)立刻聯(lián)想到下面這幾件事:

需要對(duì) XML 的預(yù)處理機(jī)制,將其轉(zhuǎn)換為 JS 中的嵌套函數(shù)結(jié)構(gòu)。這樣只需在運(yùn)行時(shí)做一次簡(jiǎn)單 eval ,即可用 JS 生成符合 XML 結(jié)構(gòu)的 UI。 需要事件機(jī)制,使得觸發(fā) onclick 事件時(shí)能執(zhí)行相應(yīng)回調(diào)。 需要數(shù)據(jù)劫持機(jī)制,使得對(duì) this.hello 賦值時(shí)能執(zhí)行相應(yīng)回調(diào)。 需要能在回調(diào)中更新 UI 對(duì)象控件。

這幾件事分別是怎么實(shí)現(xiàn)的呢?簡(jiǎn)單說(shuō)來(lái)是這樣的:

XML 預(yù)處理依賴現(xiàn)成的 NPM 開(kāi)源包,從而把 XML 中的 onclick 屬性轉(zhuǎn)換為 JS 對(duì)象的屬性字段。 事件的注冊(cè)和觸發(fā)都直接由 C++ 實(shí)現(xiàn)。如上一步所獲得的 JS 對(duì)象 onclick 屬性會(huì)在 C++ 中被檢查和注冊(cè),相當(dāng)于全部組件均為原生。 數(shù)據(jù)劫持機(jī)制用 JS 實(shí)現(xiàn),是個(gè)基于 Object.defineProperty 的(幾百行量級(jí)的)ViewModel。 UI 控件的更新,會(huì)在 ViewModel 自動(dòng)執(zhí)行的 JS 回調(diào)中,調(diào)用 C++ 的原生方法實(shí)現(xiàn)。這部分完全隱式完成,并未開(kāi)放 document.createElement 式的標(biāo)準(zhǔn)化 API。

由于大量常見(jiàn) JS 框架中的能力都直接做進(jìn)了 C++,所以整套 GUI 技術(shù)棧里用純 JavaScript 所實(shí)現(xiàn)的東西(主要見(jiàn) ace_lite_jsfwk 倉(cāng)庫(kù)下的 core/index.js、observer.js 和 subject.js),相當(dāng)于有且只有這么一個(gè)功能:

一個(gè)可以 watch 的 ViewModel。

至于純 JS 框架部分的實(shí)現(xiàn)復(fù)雜度和質(zhì)量,客觀地說(shuō)如果是個(gè)人業(yè)余作品,可以當(dāng)作校招面試中不錯(cuò)的加分項(xiàng)。

JS 引擎與運(yùn)行時(shí)層

理解了 JS 框架層之后,我們既可以認(rèn)為「鴻蒙 2.0」選擇把高度簡(jiǎn)化后的 Vue 深度定制進(jìn)了 C++ 里,也可以認(rèn)為它緊密圍繞著高度簡(jiǎn)化(且私有)的 DOM 實(shí)現(xiàn)了配套的前端框架。因此要想繼續(xù)探索這套 GUI 的原理,我們就必須進(jìn)入其 C++ 部分,了解其 JS 引擎與運(yùn)行時(shí)層的實(shí)現(xiàn)。

JS 引擎和運(yùn)行時(shí)之間,有什么區(qū)別與聯(lián)系呢?JS 引擎一般只需符合 ECMA-262 規(guī)范,其中沒(méi)有對(duì)任何帶「副作用」的平臺(tái) API 的定義。從 setTimeout 到 document.getElementById 到 console.log 再到 fs.readFile,這些能執(zhí)行實(shí)際 IO 操作的功能,都需要由「將引擎 API 和平臺(tái) API 膠合到一起」的運(yùn)行時(shí)提供。運(yùn)行時(shí)本身的原理并不復(fù)雜,譬如在個(gè)人的文章《從 JS 引擎到 JS 運(yùn)行時(shí)》中,你就可以看到如何借助現(xiàn)成的QuickJS 引擎,自己搭建一個(gè)運(yùn)行時(shí)。

那么在「鴻蒙 2.0」中,JS 運(yùn)行時(shí)是如何搭建出來(lái)的呢?有這么幾條重點(diǎn):

JS 引擎選擇了 JerryScript,這是一款由三星開(kāi)發(fā)的嵌入式 JS 引擎。 每種形如 <text> 和 <div> 的 XML 標(biāo)簽組件,都對(duì)應(yīng)一個(gè)綁定到 JerryScript 上的 C++ Component 類,如 TextComponent 和 DivComponent 等。 除 UI 原生對(duì)象外,還有一系列在 JS 中以 @system 為前綴的 built-in 模塊,它們提供了 JS 中可用的 Router / Audio / File 等平臺(tái)能力(參見(jiàn) ohos_module_config.h)。

這里特別值得一提的是 Router。它和 vue-router 等常見(jiàn) Web 平臺(tái)路由的實(shí)現(xiàn)原理有很大區(qū)別,是專門在運(yùn)行時(shí)內(nèi)深度定制的(參見(jiàn) router_module.cpp、js_router.cpp 和 js_page_state_machine.cpp)。簡(jiǎn)單說(shuō)來(lái)這個(gè)「路由」是這樣實(shí)現(xiàn)的:

在 JS 中調(diào)用切換頁(yè)面的 router.replace 原生方法,走進(jìn) C++。 C++ 中根據(jù)新頁(yè)面 URI 路徑(如 pages/detail)加載新頁(yè)面 JS,新建頁(yè)面狀態(tài)機(jī)實(shí)例,將其切換至 Init 狀態(tài)。 在新?tīng)顟B(tài)機(jī)的 Init 過(guò)程中,調(diào)用 JS 引擎去 eval 新頁(yè)面的 JS 代碼,獲得新頁(yè)面的 ViewModel。 將路由參數(shù)附加到 ViewModel 上,銷毀舊狀態(tài)機(jī)及其上的 JS 對(duì)象。

所以我們可以發(fā)現(xiàn),這里所謂的「切換路由」,其實(shí)更接近 Web 瀏覽器的「刷新頁(yè)面」。那么我們可以認(rèn)為這個(gè) JS 運(yùn)行時(shí)的能力,已經(jīng)可以對(duì)標(biāo) WebKit 級(jí)的瀏覽器內(nèi)核了嗎?

當(dāng)然還差得很遠(yuǎn)。與 WebKit 相比,它并未支持對(duì) HTML 和 CSS 的解析(二者都會(huì)在開(kāi)發(fā)階段被解析轉(zhuǎn)換成同等執(zhí)行效果的 JS),也沒(méi)有瀏覽器中持續(xù)動(dòng)態(tài)加載、解析與執(zhí)行資源的挑戰(zhàn)(小程序不外乎是幾個(gè)本地的靜態(tài) JS 文件)。至于排版布局和渲染方面自然也有很大差距,這點(diǎn)會(huì)在最后一節(jié)提及。

另外,相信很多同學(xué)都會(huì)對(duì) JerryScript 引擎感到好奇。本部分最后分享一些個(gè)人對(duì)此所掌握的消息。

JerryScript 引擎是一款專為嵌入式硬件實(shí)現(xiàn)的 JS 解釋器,只支持到 ES5.1 標(biāo)準(zhǔn)。在 QuickJS Benchmark 中,可以查看到它們的性能對(duì)比結(jié)果:

淺談鴻蒙 JavaScript GUI 技術(shù)棧

可以看到論性能,JerryScript 在無(wú) JIT 的引擎中大幅弱于 QuickJS 和 Hermes。如果和開(kāi)啟了 JIT 的 V8 相比,甚至?xí)鰞蓚€(gè)數(shù)量級(jí)。因此這是非常特定于低端設(shè)備的引擎,如果需要支持 React 和 Vue 這類中大型前端項(xiàng)目中標(biāo)配的基礎(chǔ)庫(kù)(甚至其相應(yīng)全家桶),仍然可能需要使用更強(qiáng)大的引擎。

對(duì)于 JerryScript 的使用,有同場(chǎng)景重度應(yīng)用經(jīng)驗(yàn)的當(dāng)屬 RT-Thread 創(chuàng)始人 @午夜熊,他們和某國(guó)內(nèi)一線廠商合作研發(fā)的智能手表就用 JerryScript 實(shí)現(xiàn)了 UI,目前產(chǎn)品馬上就要上市了。他們團(tuán)隊(duì)對(duì) JerryScript 的一些使用反饋也吻合上述評(píng)價(jià),概括說(shuō)來(lái)是這樣的:

JerryScript 在體積和內(nèi)存占用上,相比 QuickJS 有更好的表現(xiàn)。 JerryScript 的穩(wěn)定性弱于 QuickJS,有一些難以繞過(guò)的問(wèn)題。 JerryScript 面對(duì)稍大(1M 以上)的 JS 代碼庫(kù),就有些力不從心了。

那么師出名門的 QuickJS 和 Facebook 的 Hermes,是否就是無(wú) JIT 式 JS 引擎的下一代標(biāo)桿了嗎?倒也未必如此。這方面可以參考個(gè)人的知乎回答:隨著 TypeScript 繼續(xù)普及,會(huì)不會(huì)出現(xiàn)直接跑 TypeScript 的運(yùn)行時(shí)?這里提到的微軟為教育項(xiàng)目 MakeCode 研發(fā)的 Static TypeScript,就相當(dāng)有潛力成為下一代的高性能 JS 系語(yǔ)言環(huán)境。通過(guò)限定 TypeScript 的靜態(tài)強(qiáng)類型子集并為其搭建工具鏈,STS 可以做到無(wú)需 JIT 也能接近 V8 的性能水平,同時(shí)內(nèi)存占用比 V8 少兩個(gè)數(shù)量級(jí)。這使得 STS 不光能用于開(kāi)發(fā)普通 app 這類 IO 密集的應(yīng)用,還能順利在嵌入式硬件上開(kāi)發(fā)小游戲這類更偏計(jì)算密集(需逐幀更新渲染)的應(yīng)用,在工程能力上是一項(xiàng)很大的突破。

所以說(shuō),當(dāng)「鴻蒙 2.0」還需要熟練開(kāi)發(fā)者勉強(qiáng)搭建出環(huán)境跑通 Hello World 時(shí),微軟已經(jīng)讓上百萬(wàn)小朋友都能用 TypeScript 在網(wǎng)頁(yè)里給教學(xué)用的掌上游戲機(jī)寫(xiě)小游戲入門編程了。這里沒(méi)什么唱反調(diào)的意思,只希望提醒一下我們?cè)跒閲?guó)產(chǎn)「里程碑」歡呼時(shí),也要清醒地看到業(yè)界前沿的動(dòng)向,僅此而已。

圖形繪制層

理解 JS 運(yùn)行時(shí)之后,還剩最后一個(gè)問(wèn)題,即 JS 運(yùn)行時(shí)中的各種 Component 對(duì)象,是如何被繪制為手表等設(shè)備上的像素的呢?

這就涉及「鴻蒙 2.0」中的另一個(gè) graphic_lite 倉(cāng)庫(kù)了。可以認(rèn)為,這里才是真正執(zhí)行實(shí)際繪制的 GUI。像之前的 TextComponent 等原生組件,都會(huì)對(duì)應(yīng)到這里的某種圖形庫(kù) View。它以一種相當(dāng)經(jīng)典的方式,在 C++ 層實(shí)現(xiàn)并提供了「Canvas 風(fēng)格的立即模式 GUI」和「DOM 風(fēng)格的保留模式 GUI」兩套 API 體系(對(duì)于立即模式和保留模式 GUI 的區(qū)別與聯(lián)系,可參見(jiàn)個(gè)人這篇IMGUI 科普回答)。概括說(shuō)來(lái),這個(gè)圖形子系統(tǒng)的要點(diǎn)大致如下:

圖形庫(kù)提供了 UIView 這個(gè) C++ 控件基類,其中有一系列形如 OnClick / OnLongPress / OnDrag 的虛函數(shù)。基本每種 JS 中可用的原生 Component 類,都對(duì)應(yīng)于一種 UIView 的子類。 除了各種定制化 View 之外,它還開(kāi)放了一系列形如 DrawLine / DrawCurve / DrawText 等命令式的繪制方法。 這個(gè)圖形庫(kù)具備名為 GFX 的 GPU 加速模塊,但它目前似乎只有象征性的 FillArea 矩形單色填充能力。

在基礎(chǔ) UI 控件方面,不難找到一些值得一提的自研模塊特性:

支持了簡(jiǎn)易的 RecycleView 長(zhǎng)列表。 支持了簡(jiǎn)易的 Flex 布局。 支持了內(nèi)部的 Invalidate 臟標(biāo)記更新機(jī)制。

至于 2D UI 渲染中的幾項(xiàng)關(guān)鍵能力,則基本可分為路徑、位圖和文字三類。這個(gè)圖形庫(kù)在這幾個(gè)方面都有涉及,最后簡(jiǎn)單介紹一下。

首先對(duì)于位圖,這個(gè)圖形庫(kù)依賴了 libpng 和 libjpeg 做圖像解碼,然后即可使用內(nèi)存中的 bitmap 圖像做繪制。

然后對(duì)于路徑,這個(gè)圖形庫(kù)自己實(shí)現(xiàn)了各種 CPU 中的像素繪制方法,典型的例子就是這個(gè)貝塞爾曲線的繪制源碼:

void DrawCurve::DrawCubicBezier(const Point& start, const Point& control1, const Point& control2, const Point& end, const Rect& mask, int16_t width, const ColorType& color, OpacityType opacity){ if (width == 0 || opacity == OPA_TRANSPARENT) { return; } Point prePoint = start; for (int16_t t = 1; t <= INTERPOLATION_RANGE; t++) { Point point; point.x = Interpolation::GetBezierInterpolation(t, start.x, control1.x, control2.x, end.x); point.y = Interpolation::GetBezierInterpolation(t, start.y, control1.y, control2.y, end.y); if (prePoint.x == point.x && prePoint.y == point.y) { continue; } DrawLine::Draw(prePoint, point, mask, width, color, opacity); prePoint = point; }}

基于高中的數(shù)學(xué)知識(shí),我們不難明白這種曲線是如何繪制出來(lái)的:取足夠多的點(diǎn)(也就是那個(gè)默認(rèn) 1000 的 INTERPOLATION_RANGE)作為插值輸入,逐點(diǎn)計(jì)算出曲線表達(dá)式的 XY 坐標(biāo),然后直接修改像素位置所在的 framebuffer 內(nèi)存即可。這種教科書(shū)式的實(shí)現(xiàn)是最經(jīng)典的,不過(guò)如果要拿它對(duì)標(biāo) Skia 里的黑魔法,還是不要勉為其難了吧。

最后對(duì)于文字的繪制,會(huì)涉及一些字體解析、定位、RTL和折行等方面的處理。這部分實(shí)際上也是組合使用了一些業(yè)界通用的開(kāi)源基礎(chǔ)庫(kù)來(lái)實(shí)現(xiàn)的。比如對(duì)于「牢」這個(gè)字,就可以找到圖形庫(kù)的這么幾個(gè)開(kāi)源依賴,它們各自扮演不同的角色:

harfbuzz - 用來(lái)告訴調(diào)用者,應(yīng)該把「牢」的 glyph 字形放在哪里。 freetype - 從宋體、黑體等字體文件中解碼出「牢」的 glyph 字形,將其光柵化為像素。 icu - 處理 Unicode 中許多奇葩的特殊情況,這塊個(gè)人不了解,略過(guò)。

到這里,我們就可以理出一個(gè)非常概括性的渲染流程了:

JS 中執(zhí)行 this.hello = ’PPT’ 之類的代碼,觸發(fā)依賴追蹤。 JS 依賴追蹤回調(diào)觸發(fā)原生函數(shù),更新 C++ 的 Component 組件狀態(tài)。 Component 更新其綁定的 UIView 子類狀態(tài),觸發(fā)圖形庫(kù)更新。 圖形庫(kù)更新內(nèi)存中的像素狀態(tài),完成繪制。

這就是個(gè)人對(duì)「鴻蒙 2.0」這套 GUI 技術(shù)棧的解讀了。時(shí)間有限并未進(jìn)一步深挖,歡迎(文明的)批評(píng)指正。

總結(jié)

特別聲明:本部分主觀評(píng)論僅針對(duì)「鴻蒙 2.0」當(dāng)前的 GUI 框架部分,請(qǐng)勿隨意曲解。

對(duì)于「鴻蒙 2.0」在 GUI 部分的亮點(diǎn),個(gè)人能想到這些:

確實(shí)有務(wù)實(shí)(但和當(dāng)年 PPT 介紹完全兩碼事)的代碼。 不是 WebView 套殼,布局和繪制是自己做的。 無(wú)需超過(guò)大學(xué)本科水平的計(jì)算機(jī)知識(shí),也能順利閱讀理解。

而至于明顯(不只是某幾行代碼寫(xiě)得丑)的缺失或問(wèn)題,目前看來(lái)則有這么一些:

JS 框架層

沒(méi)有基本的組件間通信(如 props / emit 等)能力 沒(méi)有基本的自定義組件能力 沒(méi)有除基礎(chǔ)依賴追蹤以外的狀態(tài)管理能力

JS 引擎與運(yùn)行時(shí)層

標(biāo)準(zhǔn)支持過(guò)低,無(wú)法運(yùn)行 Vue 3.0 這類需 Proxy 的下一代前端框架 性能水平弱,難以支持中大型 JS 應(yīng)用 沒(méi)有開(kāi)放 DOM 式的對(duì)象模型 API,不利于上層抹平差異

圖形渲染層

沒(méi)有實(shí)質(zhì)可用的 GPU 加速 沒(méi)有 SVG 和富文本等高級(jí)渲染能力 Canvas 完成度低,缺狀態(tài)棧和很多 API

看起來(lái)槽點(diǎn)很多,但是你會(huì)指責(zé)汽車沒(méi)有噴氣式發(fā)動(dòng)機(jī)嗎?對(duì)于不同復(fù)雜度的場(chǎng)景,自然存在著不同的最優(yōu)架構(gòu)設(shè)計(jì)。目前看來(lái),這套設(shè)計(jì)確實(shí)很適合嵌入式硬件和簡(jiǎn)易「小程序」的場(chǎng)景。但如果按照所謂「分布式全場(chǎng)景跨平臺(tái)」的要求來(lái)審視,那么不管比起現(xiàn)代的 Web 瀏覽器還是 iOS 和安卓的 GUI,這套架構(gòu)的復(fù)雜度都是完全無(wú)法相提并論的。如果想在手機(jī)上實(shí)裝,幾乎必定還需要追加大量復(fù)雜模塊,進(jìn)行大幅的架構(gòu)演化與重新設(shè)計(jì)。

當(dāng)然,汽車廠商也不會(huì)說(shuō)自己造的是飛機(jī),對(duì)吧?

總之這確實(shí)是一盤自己做的麻婆豆腐,但不是某些人口中的滿漢全席。最后是個(gè)人的主觀評(píng)論:首先,這套 GUI 技術(shù)棧達(dá)到了組裝和借鑒開(kāi)源產(chǎn)品時(shí)所能獲得的主流水平。但論性能和表現(xiàn)力上限,其核心模塊距離微軟MakeCode 這類業(yè)界 cutting-edge 級(jí)的產(chǎn)學(xué)研結(jié)合前沿方案,仍然有數(shù)量級(jí)的代際差距。

其次,不必把它當(dāng)作需要海量專家精密計(jì)算的 Rocket Science——不是貶低自主研發(fā),而是真心地希望大家能明白,「這件事我也可以實(shí)際參與進(jìn)來(lái)!」操作系統(tǒng)和 GUI 沒(méi)有那么神秘,已有很多國(guó)產(chǎn)的成熟開(kāi)源產(chǎn)品可供學(xué)習(xí)、使用與貢獻(xiàn)(這里順便推薦極易體驗(yàn)且同為國(guó)產(chǎn)的 RT-Thread 作為嘗鮮入門之用)。畢竟只有真正搞懂了某個(gè)產(chǎn)品在技術(shù)上到底是怎么一回事,才不容易被別有用心的人帶節(jié)奏,對(duì)吧?

最后,對(duì)于所有熟悉 JavaScript 的前端開(kāi)發(fā)者們,你們?yōu)槭裁催€要陰陽(yáng)怪氣地嘲笑鴻蒙呢?鴻蒙就是 JavaScript 在中國(guó)的財(cái)富密碼啊!JavaScript 被鴻蒙這樣的「國(guó)之重器」采用,可以大大增強(qiáng)前端的道路自信、理論自信、文化自信和技術(shù)棧自信。只要以這種形式結(jié)合拼接與自研,就可以一舉在全國(guó)上下獲得崇高的聲望,這條路真是太讓人心馳神往了呀(小聲)

我們要團(tuán)結(jié)起來(lái),大力弘揚(yáng)和宣傳 JavaScript 在大國(guó)競(jìng)爭(zhēng)中的核威懾級(jí)地位,爭(zhēng)取上升到只要說(shuō)自己會(huì)寫(xiě)JavaScript,大家就會(huì)對(duì)你肅然起敬的高度——只要你是前端程序員,買票可以插隊(duì),搭車可以讓座,開(kāi)房可以白嫖……好時(shí)代,來(lái)臨了!

想成為國(guó)之棟梁?jiǎn)幔縼?lái)寫(xiě) JavaScript 吧!

不多說(shuō)了,我要去實(shí)干興邦啦!

以上就是淺談鴻蒙 JavaScript GUI 技術(shù)棧的詳細(xì)內(nèi)容,更多關(guān)于鴻蒙 JavaScript GUI 技術(shù)棧的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: 国模私拍高清大胆专业网站 | 伊人色综合97 | 日本免费黄色片 | 三级精品视频在线播放 | 18成人在线观看 | 欧美综合国产精品日韩一 | 亚洲精品美女久久久久 | 男女啪视频大全1000 | 日本高清一级做a爱过程免费视频 | 一级做a爱过程免费视频日本 | 成人欧美一区二区三区黑人免费 | 18黄网站| 经典香港一级a毛片免费看 春水堂在线 | 中文字幕日韩欧美一区二区三区 | 色yeye成人免费视频 | 人与牲动交xxxxbbbb高清 | 国产精品麻豆va在线播放 | 91亚洲精品一区二区福利 | 色射影院 | 最近手机中文在线视频 | 一本一道久久a久久精品综合 | 黄色网址日本 | 1024国产手机视频基地 | 香蕉视频网站 | 九一视频在线免费观看 | 老妇综合久久香蕉蜜桃 | 婷婷啪啪 | 九九色播 | 久久激情五月丁香伊人 | 国产在线午夜 | 久久精品是免费100 久久精品首页 | 网友自拍一区 | 美国黄色小视频 | 1000部羞羞禁止免费观看视频 | 国产妇女性爽视频免费 | 国产二区在线播放 | 国产成人精品福利网站人 | 国产精品无码久久综合网 | 亚洲精品国产第一区二区多人 | 黄色三级视频在线 | 久久精品国产福利 |