Hybrid App 應用 開發中 9 個必備知識點複習(WebView / 除錯 等)

pingan8787發表於2019-07-13

前言

我們大前端團隊內部 ?每週一練 的知識複習計劃繼續加油,本篇文章是 《Hybrid APP 混合應用專題》 主題的第二期和第三期的合集。

這一期共整理了 10 個問題,和相應的參考答案,文字和圖片較多,建議大家可以收藏,根據文章目錄來閱讀

之前分享的每週內容,我都整理到掘金收藏集 ?《EFT每週一練》 上啦,歡迎點贊收藏咯??。

內容回顧:

  1. 《EFT 每週分享 —— Hybrid App 應用開發中 5 個必備知識點複習》

  2. 《EFT 每週分享 —— HTTP 的15個常見知識點複習》

  3. 《EFT 每週分享 —— 資料結構與演算法合集》

文章收錄:

本系列所有文章,都將收錄在 Github 上,歡迎點選查閱

注:本文整理部分資料來源網路,有些圖片/段落找不到原文出處,如有侵權,聯絡刪除。

一、iOS 平臺中 UIWebView 與 WKWebView 有什麼區別?

參考文章:《UIWebView與WKWebView》

UIWebView 是蘋果繼承於 UIView 封裝的一個載入 web 內容的類,它可以載入任何遠端的web資料展示在你的頁面上,你可以像瀏覽器一樣前進後退重新整理等操作。不過蘋果在 iOS8 以後推出了 WKWebView 來載入 Web,並應用於 iOS 和 OSX 中,它取代了 UIWebViewWebView ,在兩個平臺上支援同一套 API。

它脫離於 UIWebView 的設計,將原本的設計拆分成14個類,和3個代理協議,雖然是這樣但是瞭解之後其實用法比較簡單,依照職責單一的原則,每個協議做的事情根據功能分類。

UIWebView

WKWebViewUIWebView 的區別:

  • WKWebView 的記憶體遠遠沒有 UIWebView 的開銷大,而且沒有快取;

  • WKWebView 擁有高達 60FPS 滾動重新整理率及內建手勢;

  • WKWebView 支援了更多的 HTML5 特性;

  • WKWebView 高效的 app 和 web 資訊交換通道;

  • WKWebView 允許 JavaScriptNitro 庫載入並使用, UIWebView 中限制了;

  • WKWebView 目前缺少關於頁碼相關的 API;

  • WKWebView 提供載入網頁進度的屬性;

  • WKWebView 使用 Safari 相同的 JavaScript 引擎;

  • WKWebView 增加載入進度屬性: estimatedProgress

  • WKWebView 不支援頁面快取,需要自己注入 cookie , 而 UIWebView 是自動注入 cookie

  • WKWebView 無法傳送 POST 引數問題;

  • WKWebView 可以和js直接互調函式,不像 UIWebView 需要第三方庫 WebViewJavascriptBridge 來協助處理和 js 的互動;

注意:

大多數App需要支援 iOS7 以上的版本,而 WKWebView 只在 iOS8 後才能用,所以需要一個相容性方案,既 iOS7 下用 UIWebViewiOS8 後用 WKWebView 。但是目前 IOS10 以下的系統以及很少了,

小結:

WKWebView 相較於 UIWebView 在整體上有較大的提升,滿足 iOS 上面使用同一套控制元件的功能,同時對整個記憶體的開銷以及滾動重新整理率和 JS 互動做了優化的處理。

依據職責單一原則,拆分成了三個協議去實現 WebView 的響應,解耦了 JS 互動和載入進度的響應處理。

WKWebView 沒有做快取處理,所以對網頁需要快取的載入效能要求沒那麼高的還是可以考慮 UIWebView

二、WKWebView 有哪一些坑?

參考文章:《WKWebView 那些坑》

1. WKWebView 白屏問題

WKWebView 實際上是個多程式元件,這也是它載入速度更快,記憶體暫用更低的原因。

UIWebView 上當記憶體佔用太大的時候,App Process 會 crash;而在 WKWebView 上當總體的記憶體佔用比較大的時候,WebContent Process 會 crash,從而出現白屏現象。

解決辦法:

  1. 藉助 WKNavigtionDelegate

WKWebView 總體記憶體佔用過大,頁面即將白屏的時候,系統會呼叫上面的回撥函式,我們在該函式裡執行[webView reload](這個時候 webView.URL 取值尚不為nil)解決白屏問題。在一些高記憶體消耗的頁面可能會頻繁重新整理當前頁面,H5側也要做相應的適配操作。

  1. 檢測 webView.title 是否為空

並不是所有 H5 頁面白屏的時候都會呼叫上面的回撥函式,比如,最近遇到在一個高記憶體消耗的 H5 頁面上 present 系統相機,拍照完畢後返回原來頁面的時候出現白屏現象(拍照過程消耗了大量記憶體,導致記憶體緊張,WebContent Process 被系統掛起),但上面的回撥函式並沒有被呼叫。在 WKWebView 白屏的時候,另一種現象是 webView.titile 會被置空, 因此,可以在 viewWillAppear 的時候檢測 webView.title 是否為空來 reload 頁面。

2. WKWebView Cookie 問題

WKWebView Cookie 問題在於 WKWebView 發起的請求不會自動帶上儲存於 NSHTTPCookieStorage 容器中的 Cookie,而在 UIWebView 會自動帶上 Cookie

原因是:

WKWebView 擁有自己的私有儲存,不會將 Cookie 存入到標準的 Cookie 容器NSHTTPCookieStorage 中。

實踐發現 WKWebView 例項其實也會將 Cookie 儲存於 NSHTTPCookieStorage 中,但儲存時機有延遲,在 iOS 8上,當頁面跳轉的時候,當前頁面的 Cookie 會寫入 NSHTTPCookieStorage 中,而在 iOS 10 上,JS 執行 document.cookie 或伺服器 set-cookie 注入的 Cookie 會很快同步到 NSHTTPCookieStorage 中,FireFox 工程師曾建議通過 reset WKProcessPool 來觸發 Cookie 同步到 NSHTTPCookieStorage 中,實踐發現不起作用,並可能會引發當前頁面 session cookie丟失等問題。

解決辦法1:

WKWebView loadRequest 前,在 request header 中設定 Cookie, 解決首個請求 Cookie 帶不上的問題;

解決辦法2:

通過 document.cookie 設定 Cookie 解決後續頁面(同域)Ajax``、iframe 請求的 Cookie 問題;(注意:document.cookie() 無法跨域設定 cookie)。

3. WKWebView loadRequest 問題

WKWebView 上通過 loadRequest 發起的 post 請求 body 資料會丟失,同樣是由於程式間通訊效能問題HTTPBody 欄位被丟棄。

4. WKWebView NSURLProtocol問題

WKWebView 在獨立於 app 程式之外的程式中執行網路請求,請求資料不經過主程式,因此,在 WKWebView 上直接使用 NSURLProtocol 無法攔截請求。

解決辦法:

由於 WKWebView 在獨立程式裡執行網路請求。一旦註冊 http(s) scheme 後,網路請求將從 Network Process 傳送到 App Process,這樣 NSURLProtocol 才能攔截網路請求。在 webkit2 的設計裡使用 MessageQueue 進行程式之間的通訊,Network Process 會將請求 encode 成一個 Message,然後通過 IPC 傳送給App Process。出於效能的原因,encode 的時候 HTTPBodyHTTPBodyStream 這兩個欄位會被丟棄掉了。

5. WKWebView 頁面樣式問題

WKWebView 適配過程中,我們發現部分 H5 頁面元素位置向下偏移或被拉伸變形,追蹤後發現主要是 H5 頁面高度值異常導致。

解決辦法:

調整 WKWebView 佈局方式,避免調整webView.scrollView.contentInset 。實際上,即便在 UIWebView 上也不建議直接調整 webView.scrollView.contentInset 的值,這確實會帶來一些奇怪的問題。如果某些特殊情況下非得調整 contentInset 不可的話,可以通過下面方式讓H5頁面恢復正常顯示。

6. WKWebView 截圖問題

WKWebView 下通過 -[CALayer renderInContext:]實現截圖的方式失效,需要通過以下方式實現截圖功能:

@implementation UIView (ImageSnapshot) 
- (UIImage*)imageSnapshot { 
    UIGraphicsBeginImageContextWithOptions(self.bounds.size,YES,self.contentScaleFactor);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return newImage; 
} 
@end
複製程式碼

然而這種方式依然解決不了 webGL 頁面的截圖問題,截圖結果不是空白就是純黑圖片。

解決辦法:

無奈之下,我們只能約定一個JS介面,讓遊戲開發商實現該介面,具體是通過 canvas getImageData()方法取得圖片資料後返回 base64 格式的資料,客戶端在需要截圖的時候,呼叫這個JS介面獲取base64 String並轉換成 UIImage

7. WKWebView crash問題

如果 WKWebView 退出的時候,JS剛好執行了 window.alert(), alert 框可能彈不出來,completionHandler 最後沒有被執行,導致crash

另一種情況是在 WKWebView 一開啟,JS就執行 window.alert(),這個時候由於 WKWebView 所在的 UIViewController 出現( pushpresent )的動畫尚未結束,alert 框可能彈不出來,completionHandler最後沒有被執行,導致 crash

8. 視訊自動播放

WKWebView 需要通過 WKWebViewConfiguration.mediaPlaybackRequiresUserAction 設定是否允許自動播放,但一定要在 WKWebView 初始化之前設定,在 WKWebView 初始化之後設定無效。

9. goBack API問題

WKWebView 上呼叫 -[WKWebView goBack], 回退到上一個頁面後不會觸發window.onload() 函式、不會執行JS。

10. 頁面滾動速率

WKWebView 需要通過 scrollView delegate 調整滾動速率:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; 
}
複製程式碼

三、Crosswalk 是什麼,它有什麼作用?

參考網站: 《Crosswalk Github》 參考文章: 《Crosswalk入門》

Crosswalk 是一款開源的 web 引擎。目前 Crosswalk 正式支援的移動作業系統包括 Android 和 Tizen ,在 Android 4.0 及以上的系統中使用 Crosswalk 的 Web 應用程式在 HTML5 方面可以有一致的體驗,同時和系統的整合互動方面(比如啟動畫面、許可權管理、應用切換、社交分享等等)可以做到類似原生應用

現在 Crosswalk 已經成為眾多知名 HTML5 平臺和應用的推薦引擎,包括 Google Mobile Chrome App 、 Intel XDK 、 Famo.us 和 Construct2 等等,未來的 Cordova 4.0 也計劃整合 Crosswalk

四、常見的 WebView 效能優化方案有哪一些?

0. 問題分析

首先需要了解,對於一個普通使用者來講,開啟一個 WebView 通常會經歷哪幾個階段,一般有這些:

  1. 互動無反饋;

  2. 到達新的頁面,頁面白屏;

  3. 頁面基本框架出現,但是沒有資料;頁面處於loading狀態;

  4. 出現所需的資料;

webview1

當 App 首次開啟時,預設是並不初始化瀏覽器核心的;只有當建立 WebView 例項的時候,才會建立 WebView 的基礎框架。

所以與瀏覽器不同,App 中開啟 WebView 的第一步並不是建立連線,而是啟動瀏覽器核心。

於是我們找到了“為什麼WebView總是很慢”的原因之一:

  • 在瀏覽器中,我們輸入地址時(甚至在之前),瀏覽器就可以開始載入頁面。

  • 而在客戶端中,客戶端需要先花費時間初始化 WebView 完成後,才開始載入。

而這段時間,由於WebView還不存在,所有後續的過程是完全阻塞的。因此由於這段過程發生在 native 的程式碼中,單純靠前端程式碼是無法優化的;大部分的方案都是前端和客戶端協作完成,以下是幾個業界採用過的方案:

1. 全域性 WebView

在客戶端剛啟動時,就初始化一個全域性的 WebView 待用,並隱藏,當使用者訪問了 WebView 時,直接使用這個 WebView 載入對應網頁,並展示。

這種方法可以比較有效的減少 WebView 在App中的首次開啟時間。當使用者訪問頁面時,不需要初始化 WebView 的時間。

當然這也帶來了一些問題,包括:

  • 額外的記憶體消耗。

  • 頁面間跳轉需要清空上一個頁面的痕跡,更容易記憶體洩露。

2. WebView 動態載入

參考文章:《WebView常用優化方案》

WebView 動態載入。就是不在 xml 中寫 WebView ,寫一個 layout ,然後把 WebView add 進去。

WebView mWebView = new WebView(getApplicationgContext());
LinearLayout mll = findViewById(R.id.xxx);
mll.addView(mWebView);

protected void onDestroy() {
    super.onDestroy();
    mWebView.removeAllViews();
    mWebView.destroy()
}
複製程式碼

這裡用的 getApplicationContext() 也是防止記憶體溢位,這種方法有一個問題。如果你需要在 WebView 中開啟連結或者你開啟的頁面帶有 flash,獲得你的 WebView 想彈出一個 dialog ,都會導致從 ApplicationContextActivityContext 的強制型別轉換錯誤,從而導致你應用崩潰。

這是因為在載入 flash 的時候,系統會首先把你的 WebView 作為父控制元件,然後在該控制元件上繪製 flash ,他想找一個 ActivityContext 來繪製他,但是你傳入的是 ApplicationContext 。然後就崩潰了。

3. 獨立的web程式,與主程式隔開

參考文章:《WebView常用優化方案》

這個方法被運用於類似 qq ,微信這樣的超級 app 中,這也是解決任何 WebView 記憶體問題屢試不爽的方法 對於封裝的 webactivity ,在manifest.xml 中設定。

<activity 
    android:name=".webview.WebViewActivity" 
    android:launchMode="singleTop" 
    android:process=":remote" 
    android:screenOrientation="unspecified" 
/>
複製程式碼

然後在關閉 webactivity 時銷燬程式:

@Overrideprotected void onDestroy() {                
super.onDestroy(); 
    System.exit(0);
}
複製程式碼

關閉瀏覽器後便銷燬整個程式,這樣一般 95% 的情況下不會造成記憶體洩漏之類的問題,但這就涉及到 android 程式間通訊,比較不方便處理,優劣參半,也是可選的一個方案。

4. WebView 釋放

參考文章:《WebView常用優化方案》

public void destroy() {
    if (mWebView != null) {
        // 如果先呼叫destroy()方法,則會命中if (isDestroyed()) return;這一行程式碼,需要先onDetachedFromWindow(),再
        // destory()
        ViewParent parent = mWebView.getParent();
        if (parent != null) {
            ((ViewGroup) parent).removeView(mWebView);
        }
​
        mWebView.stopLoading();
        // 退出時呼叫此方法,移除繫結的服務,否則某些特定系統會報錯
        mWebView.getSettings().setJavaScriptEnabled(false);
        mWebView.clearHistory();
        mWebView.clearView();
        mWebView.removeAllViews();
​
        try {
            mWebView.destroy();
        } catch (Throwable ex) {
​
        }
    }
}
複製程式碼

五、在 Android 平臺下如何除錯 WebView?

1. 在 Chrome 瀏覽器上除錯

參考文章:《Android除錯webview》

1.1 條件:

  • 在 Android 裝置或模擬器執行 Android4.4 或更高版本,Android 裝置上啟用 USB除錯模式

  • Chrome 30 或更高版本。更強大的 WebView 介面除錯功能需要 Chrome31 或更高版本。

  • Android 應用程式中的 WebView 配置為可除錯模式

1.2 Android 程式碼中配置 WebView 為可除錯:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  
    WebView.setWebContentsDebuggingEnabled(true);  
}  
複製程式碼

注意 web 調測不受 app manifest 檔案中 debuggable 標記狀態的影響,如果希望僅 debuggabletrue 時才能使用 web 調測,那麼執行時檢測此標記,如下:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  
    if ( 0 != ( getApplcationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) ) {  
        WebView.setWebContentsDebuggingEnabled(true);  
    }  
}  
複製程式碼

1.3 手機開啟 USB 除錯選項,並用 USB 連線電腦:

開啟 Android 手機的開發者選項,一般在 系統設定 - Android版本 進行多次點選會觸發開啟開發者選項,然後進入開發者選項頁面,開啟USB除錯。

為了避免每次除錯時看到此警告,勾選“總是允許從這臺計算機”,並單擊“確定”。

1.4 在 Chrome 中啟用設定“USB web debugging”(不會影響WebView):

在 Chrome 上訪問 chrome://inspect/#devicesabout:inspect 訪問已啟用除錯的 WebView 列表,需要翻牆

然後在 WebView 列表中選擇你要除錯的頁面,點選“ Inspect ”選項,跟除錯 PC 網頁一樣,使用 Chrome 控制檯進行除錯。

Chrome 控制檯

Chrome 控制檯

1.5 小技巧:

(1)訪問 chrome://inspect/#devices 如果 chrome 沒有檢測到 Remote Target 中的頁面,可能需要安裝一下 Chrome 的 ADB 外掛,也可以在 Chrome 翻牆訪問 https://chrome-devtools-frontend.appspot.com

(2)對於騰訊系的 APP,預設採用 X5核心 ,我們可以在 APP 內部開啟 https://debugx5.qq.com/ 來使用它的Vconsole除錯工具進行除錯。

2. 使用 DebugGap 除錯

參考文章:《Android下的webview除錯》

2.1 Windows 下載 DebugGap 並配置:

在電腦上面下載 Windows 版本的 DebugGap 軟體包(下載連結:DebugGap),下載完成後解壓下來。

安裝完成後,執行 DebugGap ,開始配置:

  • 通常情況下,DebugGap 可以自動獲取IP,並設定預設的埠,如果沒有,你可以手動設定;
  • 點選“連線”按鈕啟動各種客戶端的偵聽器;

DebugGap

2.2 在客戶端上配置:

在除錯專案中要進行測試的 HTML 介面中引入 debuggap.js

<script src="debuggap.js" type="text/javascript"></script>
複製程式碼

當除錯專案的載入時,您的應用程式將會有一個藍色的地方,點選會出現一個四葉三葉草的東西,點選“配置”,顯示配置頁面。輸入與遠端 DebugGap 上的主機和埠相同的主機和埠,例如 192.168.1.4:11111,然後點選“連線”按鈕。

DebugGap

1.4電腦端遠端 DebugGap 將檢測即將到來的客戶端,開發人員可以單擊每個客戶端進行除錯。

DebugGap

六、在 iOS 平臺下如何除錯 WebView?

參考文章:《iOS之Safari除錯webView/H5頁面》

一般我們通過 Mac 的 Safari瀏覽器 來除錯,但是要注意兩點:

  • 如果除錯的是 APP 中 WebView 的頁面,則需要這個 APP 的包支援除錯,如果不能除錯,需要讓 iOS 開發人員重簽名 APP(可能需要將我們 iOS 裝置的 ID 寫入到可信任裝置列表中,然後使用 iTunes 安裝客戶端提供的測試包即可)。

  • 如果除錯的是 H5 頁面,可以直接在手機的 Safari瀏覽器 開啟直接除錯。

下面開始說說在 Mac 上如何除錯:

1. 開啟 Safari 開發選單

先將 iPhone 連線到 Mac,在 Mac 的 Safari 偏好設定中,開啟開發選單。具體步驟為:Safari -> 偏好設定… -> 高階 -> 勾選在選單欄顯示“開發”選單。

Safari

2. iPhone 開啟 Web檢查器

具體步驟為:設定 -> Safari -> 高階 -> Web 檢查器。

Safari

3. 除錯 APP 內的 WebView

參考文章:《前端 WEBVIEW 指南之 IOS 除錯篇》

在 Safari-> 開發中,看到自己的裝置以及 WebView 中網頁,點選後即可開啟對應頁面的 Inspector ,可以用來進行斷點除錯。

Safari

Safari

七、在內嵌版除錯過程中,Fiddler 或 Charles 能起到什麼作用?

這兩者都是強大的抓包工具,原理是以web代理伺服器的形式進行工作的,使用的代理地址是:127.0.0.1,埠預設為8888,我們也可以通過設定進行修改。

代理就是在客戶端和伺服器之間設定一道關卡,客戶端先將請求資料傳送出去後,代理伺服器會將資料包進行攔截,代理伺服器再冒充客戶端傳送資料到伺服器;同理,伺服器將響應資料返回,代理伺服器也會將資料攔截,再返回給客戶端。

Fiddler 或 Charles 的主要作用有:

  • 可以代理請求,用來檢視頁面傳送的請求和接收的響應;
  • 可以修改請求的響應,用來模擬自己想要的資料;
  • 可以模擬網路請求的速度;
  • 可以代理伺服器的靜態資源請求,指向本地檔案,省去頻繁釋出 H5 包,達到快速除錯目的;

補充連結:《Fiddler工具使用介紹一》

八、除錯企業微信、微信和釘釘版時,可以使用哪些工具?

1. 除錯企業微信、微信

2. 除錯釘釘

3. 通用

  • Fiddler 或 Charles,可以攔截介面替換檔案,來除錯應用;

九、常見的除錯技巧有哪些?

1. Chrome 控制檯除錯

參考文章:《前端常見除錯技巧篇總結(持續更新...)》

1.1 Source 皮膚斷點除錯 JS
從左到右,各個圖示表示的功能分別為:

  • Pause/Resume script execution:暫停/恢復指令碼執行(程式執行到下一斷點停止)。
  • Step over next function call:執行到下一步的函式呼叫(跳到下一行)。
  • Step into next function call:進入當前函式。
  • Step out of current function:跳出當前執行函式。
  • Deactive/Active all breakpoints:關閉/開啟所有斷點(不會取消)。
  • Pause on exceptions:異常情況自動斷點設定。

1.2 Element 皮膚除錯 DOM:

右擊元素,選擇 break on 選項:

break on

  • Subtree modifications 選項,是指當節點內部子節點變化時斷點

break on

  • Attributes modifications 選項,是指當節點屬性發生變化時斷點

break on

  • node removal 選項,是指當節點被移除時斷點

break on

2. console 除錯

參考文章:《Console除錯常用用法》

2.1 顯示資訊的命令:

console.log("normal");         // 用於輸出普通訊息
console.info("information");   // 用於輸出提示性資訊
console.error("error");        // 用於輸出錯誤資訊
console.warn("warn");          // 用於輸出警示資訊
console.clear();               // 清空控制檯資訊
複製程式碼

2.2 計時功能:

console.time()console.timeEnd()

console.time("控制檯計時器");
for(var i = 0; i < 10000; i++){
    for(var j = 0; j < 10000; j++){}       
}
console.timeEnd("控制檯計時器")
複製程式碼

2.3 資訊分組:

console.group()console.groupEnd()

console.group("第一組資訊");
    console.log("第一組第一條:我的部落格");
    console.log("第一組第二條:CSDN");
console.groupEnd();

console.group("第二組資訊");
    console.log("第二組第一條:程式愛好者QQ群");
    console.log("第二組第二條:歡迎你加入");
console.groupEnd();
複製程式碼

2.4 將物件以樹狀結構展現:

console.dir() 可以顯示一個物件所有的屬性和方法:

var info = {
    name : "Alan",
    age : "27",
    grilFriend : "nothing",
    getName : function(){
        return this.name;
    }
}
console.dir(info);
複製程式碼

2.5 顯示某個節點的內容:

console.dirxml() 用來顯示網頁的某個節點( node) 所包含的 html/xml 程式碼:

var node = document.getElementById("info");
node.innerHTML += "<p>追加的元素顯示嗎</p>";
console.dirxml(node);
複製程式碼

2.5 統計程式碼被執行的次數:

使用 console.count()

function myFunction(){
    console.count("myFunction 被執行的次數");
}
myFunction();       //myFunction 被執行的次數: 1
myFunction();       //myFunction 被執行的次數: 2
myFunction();       //myFunction 被執行的次數: 3
複製程式碼

2.6 輸出表格:

console.table(mytable);
複製程式碼

3. 除錯各種頁面尺寸

雖然把各種各樣的手機都擺在桌子上看起來很酷,但卻很不現實。但是,瀏覽器內卻提供了你所需要的一切。進入檢查皮膚點選“切換裝置模式”按鈕。這樣,就可以在視窗內調整視窗的大小。

4. debugger 斷點

具體的說就是通過在程式碼中新增" debugger;"語句,當程式碼執行到該語句的時候就會自動斷點。

debugger

結語

對於初入混合應用開發的小夥伴,還有經常需要除錯混合應用的小夥伴,相信會有幫助?

大家加油~

關於我

本文首發在 pingan8787個人部落格,如需轉載請保留個人介紹。

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 github.com/pingan8787/…
ES小冊 js.pingan8787.com

微信公眾號

bg

相關文章