1. 程式人生 > >Flask核心機制--上下文源碼剖析

Flask核心機制--上下文源碼剖析

測試結果 遷移 rman 拆分 自定義 客戶端請求 了無 當我 計算方法

  在App開發中,內嵌WebView始終占有著一席之地。它能以較低的成本實現Android、iOS和Web的復用,也可以冠冕堂皇的突破蘋果對熱更新的封鎖。
  
  然而便利性的同時,WebView的性能體驗卻備受質疑,導致很多客戶端中需要動態更新等頁面時不得不采用其他方案。
  
  以發展的眼光來看,功能的動態加載以及三端的融合將會是大趨勢。
  
  那麽如何克服WebView固有的問題呢?
  
  我們將從性能、內存消耗、體驗、安全幾個維度,來系統的分析客戶端默認WebView的問題,以及對應的優化方案。
  
  性能
  
  對於WebView的性能,給人最直觀的莫過於:打開速度比native慢。
  
  是的,當我們打開一個WebView頁面,頁面往往會慢吞吞的loading很久,若幹秒後才出現你所需要看到的頁面。
  
  這是為什麽呢?
  
  對於一個普通用戶來講,打開一個WebView通常會經歷以下幾個階段:
  
  交互無反饋
  
  到達新的頁面,頁面白屏
  
  頁面基本框架出現,但是沒有數據;頁面處於loading狀態
  
  出現所需的數據
  
  如果從程序上觀察,WebView啟動過程大概分為以下幾個階段:
  
  WebView啟動時間
  
  如何縮短這些過程的時間,就成了優化WebView性能的關鍵。
  
  接下來我們逐一分析各個階段的耗時情況,以及需要註意的優化點。
  
  WebView初始化
  
  當App首次打開時,默認是並不初始化瀏覽器內核的;
  
  只有當創建WebView實例的時候,才會創建WebView的基礎框架。
  
  所以與瀏覽器不同,App中打開WebView的第一步並不是建立連接,而是啟動瀏覽器內核。
  
  我們來分析一下這段耗時到底需要多久。
  
  分析
  
  針對WebView的初始化時間,我們可以定義兩個指標:
  
  首次初始化時間: 客戶端冷啟動後,第一次打開WebView, 從開始創建WebView到開始建立網絡連接之間的時間。
  
  二次初始化時間: 在打開過WebView後,退出WebView, 再重新打開WebView, 從 開始創建WebView到開始建立網絡連接之間的時間。
  
  測試數據:
  
  測試系統1: iOS模擬器,Titans 10.0.7
  
  測試系統2: OPPO R829T Android 4.2.2
  
  測試方式:測試10次取平均值
  
  測試App:美團外賣
  
  單位:ms
  
  首次初始化時間 二次初始化時間
  
  iOS(UIWebView) 306.56 76.43
  
  iOS(WKWebView) 763.26 457.25
  
  Android 192.79 * 142.53
  
  *Android外賣客戶端啟動後會在後臺開啟WebView進程,故並不是完全新建WebView時間。
  
  這意味著什麽呢?
  
  作為前端工程師,統計了無數次的頁面打開時間,都是以網絡連接開始作為起點的。
  
  很遺憾的通知您:WebView中用戶體驗到的打開時間需要再增加70~700ms。
  
  於是我們找到了“為什麽WebView總是很慢”的原因之一:
  
  在瀏覽器中,我們輸入地址時(甚至在之前),瀏覽器就可以開始加載頁面。
  
  而在客戶端中,客戶端需要先花費時間初始化WebView完成後,才開始加載。
  
  而這段時間,由於WebView還不存在,所有後續的過程是完全阻塞的。可以這樣形容WebView初始化過程:
  
  WebView啟動過程
  
  那麽有哪些解決辦法呢?
  
  怎麽優化
  
  由於這段過程發生在native的代碼中,單純靠前端代碼是無法優化的;
  
  大部分的方案都是前端和客戶端協作完成,以下是幾個業界采用過的方案。
  
  全局WebView
  
  方法:
  
  在客戶端剛啟動時,就初始化一個 全局的WebView 待用,並隱藏;
  
  當用戶 訪問了 WebView時,直接使用這個 WebView 加載對應網頁,並展示。
  
  這種方法可以比較 有效的減少WebView在App中的首次打開時間。
  
  當用戶訪問頁面時,不需要初始化WebView的時間。
  
  當然這也帶來了一些問題,包括:
  
  額外的內存消耗。
  
  頁面間跳轉需要清空上一個頁面的痕跡,更容易內存泄露。
  
  【參考東軟專利 - 加載網頁的方法及裝置 CN106250434A】
  
  客戶端代理數據請求
  
  方法:
  
  在客戶端初始化WebView的同時,直接由native開始網絡請求數據;
  
  當頁面初始化完成後,向native獲取其代理請求的數據。
  
  此方法雖然不能減小WebView初始化時間,但 數據請求 和 WebView初始化 可以 並行進行,總體的頁面加載時間就縮短了;縮短總體的頁面加載時間:
  
  【參考騰訊分享:70%以上業務由H5開發,手機QQ Hybrid 的架構如何優化演進?】
  
  還有其他各種優化的方式,不再一一列舉,總結起來都是圍繞兩點:
  
  在使用前 預先初始化 好WebView,從而減小耗時。
  
  在初始化的同時,通過Native來完成一些網絡請求等過程,使得WebView初始化 不是完全的 阻塞後續過程。
  
  建立連接/服務器處理
  
  在頁面請求的數據返回之前,主要有以下過程耗費時間。
  
  DNS
  
  connection
  
  服務器處理
  
  分析
  
  以下為美團中活動頁面的鏈接時間統計:
  
  統計: 美團的活動頁面
  
  內容值: n%分位值(ms)
  
  DNS connection 獲取首字節
  
  50% 1.3 71 172
  
  90% 60 360 541
  
  優化
  
  這些時間都是發生在 網頁加載之前,但這並不意味著無法優化,有以下幾種方法。
  
  DNS采用和客戶端API相同的域名
  
  DNS會在系統級別進行緩存, 對於WebView的地址,如果使用的域名與native的API相同, 則可以直接使用緩存的DNS 而不用再發起請求圖片。
  
  以美團為例,美團的客戶端請求域名主要位於api.meituan.com,然而內嵌的WebView主要位於 i.meituan.com。
  
  當我們初次打開App時:
  
  客戶端首次打開都會請求api.meituan.com,其DNS將會被系統緩存。
  
  然而當打開WebView的時候,由於請求了不同的域名,需要重新獲取i.meituan.com的IP。
  
  根據上面的統計,至少10%的用戶打開WebView時耗費了60ms在DNS上面,
  
  如果WebView的域名與App的API域名統一,則可以讓WebView的DNS時間全部達到1.3ms的量級。
  
  靜態資源同理,最好與客戶端的資源域名保持一致。
  
  同步渲染采用chunk編碼
  
  同步渲染時如果後端請求時間過長,可以考慮采用chunk編碼,將數據放在最後,並優先將靜態內容flush。
  
  對於傳統的後端渲染頁面,往往都是使用的【瀏覽器】--> 【Web API】 --> 【業務 API】的加載模式,
  
  其中後端時間就指的是Web API的處理時間了。在這裏Web API一般有兩個作用:
  
  確定靜態資源的版本。
  
  根據用戶的請求,去業務API獲取數據。
  
  而一般確定靜態資源的版本往往是直接讀取代碼版本,基本無耗時;而主要的後端時間都花費在了業務API請求上面。
  
  那麽怎麽優化利用這段時間呢?
  
  在HTTP協議中,我們可以在header中設置 transfer-encoding:chunked 使得頁面可以分塊輸出。如果合理設計頁面,
  
  讓head部分都是確定的靜態資源版本相關內容,而body部分是業務數據相關內容,那麽我們可以在用戶請求的時候,
  
  首先將Web API可以確定的部分先輸出給瀏覽器,然後等API完全獲取後,再將API數據傳輸給瀏覽器。
  
  下圖可以直觀的看出分chunk輸出和一起輸出的區別:
  
  分chunk加載
  
  如果采用普通方式輸出頁面,則頁面會在服務器請求完所有API並處理完成後開始傳輸。瀏覽器要在後端所有API都加載完成後才能開始解析。
  
  如果采用chunk-encoding: chunked,並優先將頁面的靜態部分輸出;然後處理API請求,並最終返回頁面,可以讓後端的API請求和前端的資源加載同時進行。
  
  兩者的總共後端時間並沒有區別,但是可以提升首字節速度,從而讓前端加載資源和後端加載API不互相阻塞。
  
  頁面框架渲染
  
  頁面在解析到足夠多的節點,且所有CSS都加載完成後進行首屏渲染。
  
  在此之前,頁面保持白屏;在頁面完全下載並解析完成之前,頁面處於不完整展示狀態。
  
  分析
  
  我們以一個美團的活動頁面作為樣例:
  
  測試頁面:http://i.meituan.com/firework/meituanxianshifengqiang
  
  在Mac上面,模擬4G情況
  
  頁面樣式:
  
  頁面
  
  測試得到的時間耗費如下:
  
  表1
  
  階段 時間 大小 備註
  
  DOM下載 58ms 29.5?KB 4G網絡
  
  DOM解析 12.5ms 198?KB 根據估算,在手機上慢2~5倍不等
  
  CSS請求+下載 58ms 11.7?KB 4G網絡(包含鏈接時間,CDN)
  
  CSS解析 2.89ms 54.1?KB 根據估算,在手機上慢2~5倍不等
  
  渲染 23ms 1361節點 根據估算,在手機上慢2~5倍不等
  
  繪制 4.1ms 根據估算,在手機上慢2~5倍不等
  
  合成 0.23ms GPU處理
  
  同時,對HTML的加載時間進行分析,可以得到如下時間點。
  
  表2
  
  指標 時間 計算方法
  
  HTML加載完成時間 218 performance.timing.responseEnd - performance.timing.fetchStart
  
  HTML解析完成時間 330 performance.timing.domInteractive - performance.timing.fetchStart
  
  這意味著什麽呢?
  
  對於表1
  
  可以看到,隨著在網絡優良的情況下,Dom的解析所占耗時比例還是不算低的,對於低端機器更甚。
  
  Layout時間也是首屏前耗時的大頭,據猜測這與頁面使用了rem作為單位有關(待進一步分析)。
  
  對於表2,我們可以發現一個問題
  
  一般來說HTML在開始接收到返回數據的時候就開始解析HTML並構建DOM樹。如果沒有JS(JavaScript)阻塞的話一般會相繼完成。
  
  然而,在這裏時間相差了90ms……也就是說,解析被阻塞了。
  
  進一步分析可以發現,頁面的header部分有這樣的代碼:
  
  .....
  
  <link href="//ms0.meituan.net/css/eve.9d9eee71.css" rel="stylesheet" onload="MT.pageData.eveTime=Date.now()"/>
  
  <script>
  
  window.fk = function (callback) {
  
  require([‘util/native/risk.js‘], function (risk) {
  
  risk.getFk(callback);
  
  });
  
  }
  
  </script>
  
  </head>
  
  ....
  
  通常情況下,上面代碼的link部分和script部分如果單獨出現,都不會阻塞頁面的解析:
  
  CSS不會阻止頁面繼續向下繼續。
  
  內聯的JS很快執行完成,然後繼續解析文檔。
  
  然而,當這兩部分同時出現的時候,問題就來了。
  
  CSS加載阻塞了下面的一段內聯JS的執行,而被阻塞的內聯JS則阻塞了HTML的解析。
  
  通常情況下,CSS不會阻塞HTML的解析,但如果CSS後面有JS,
  
  則會阻塞JS的執行直到CSS加載完成(即便JS是內聯的腳本),從而間接阻塞HTML的解析。
  
  優化
  
  在頁面框架加載這一部分,能夠優化的點參照雅虎14條就夠了;但註意不要犯錯,一個小小的 內聯JS放錯位置 也會讓性能下降很多。
  
  CSS的加載 會在HTML解析 到CSS的標簽時開始,所以CSS的標簽要盡量靠前。
  
  但是,CSS鏈接下面不能有 任何的JS標簽( 包括很簡單的內聯JS),否則會阻塞HTML的解析。
  
  如果必須要在頭部增加內聯腳本, 一定要放在CSS標簽之前。 ???
  
  CSS帶來的阻塞解析
  
  JS加載
  
  對於大型的網站來說,在此我們先提出幾個問題:
  
  將全部JS代碼打成一個包,造成首次執行代碼過大怎麽辦?
  
  將JS以細粒度打包,造成請求過多怎麽辦?
  
  將JS按 "基礎庫" + "頁面代碼" 分別打包,要怎麽界定什麽是基礎代碼,什麽是頁面代碼;不同頁面用的基礎代碼不一致怎麽辦?
  
  單一文件的少量代碼改的是否會導致緩存失效?
  
  代碼模塊間有動態依賴,怎樣合並請求。
  
  關於這些問題的解決方案數量可能會比問題還多,而它們也各有優劣。
  
  具體分析太過復雜,鑒於篇幅原因在這裏不做具體分析了。
  
  您可以期待我們的後續計劃:BPM(瀏覽器包管理)。
  
  JS解析、編譯、執行
  
  在PC互聯網時代,人們似乎都快忘記了 JS的解析和執行 還需要消耗時間。
  
  確實,在幾年前網速還在用kb衡量的時代裏,JS的解析時間在整個頁面的打開時間裏只能算是九牛一毛。
  
  然而,隨著網速越來越快, 而 CPU的速度反而沒有提升(從PC到手機),JS的時間開銷就成為問題了。
  
  解析執行js的速度 依賴好的cpu
  
  那麽JS的編譯和解析,在當今的頁面上要消耗多少時間呢?
  
  分析
  
  我們用以下方式來檢驗JS代碼的解析/編譯和執行時間:
  
  <script>
  
  window.t1 = performance.now()
  
  </script>
  
  <script>
  
  window.test = function () {
  
  // test code
  
  }
  
  </script>
  
  <script>
  
  window.t2 = performance.now()
  
  test();
  
  window.t3 = performance.now();
  
  alert("編譯耗時:" + (t2 - t1));
  
  alert("執行耗時:" + (t3 - t2));
  
  </script>
  
  將測試代碼放入 【test code】 位置,然後在手機中執行;
  
  在t1~t2期間,JS代碼僅僅聲明了一個函數,主要時間會集中在 解析和編譯過程;
  
  在t2~t3時間段內,執行test時時間主要為代碼的執行時間
  
  在首次啟動客戶端後,打開WebView的測試頁面,我們可以得到如下的結果: vue在安卓下還是要100多毫秒去執行, 雖然只是react 2/5
  
  測試系統: iPhone6 iOS 10.2.1
  
  測試系統: OPPO R829T Android 4.2.2
  
  內容值: 編譯時間(ms)/執行時間(ms)
  
  系統 Zepto.js Vue.js React.js + ReactDOM.js
  
  iOS 5.2 / 8 12.8 / 16.1 13.7 / 43.3
  
  Android 13 / 40 43 / 127 26 / 353
  
  當保持客戶端進行不關閉情況下, 關閉WebView並重新 訪問測試頁面,再次測試得到如下結果:
  
  系統 Zepto.js Vue.js React.js + ReactDom.js
  
  iOS 0.9 / 1.9 5 / 7.4 3.5 / 23
  
  Android 5 / 9 17 / 12 25 / 60
  
  執行時間指的是框架代碼加載的頁面的初始化時間,沒有任何業務的調用。
  
  這意味著什麽
  
  經過測試可以得出以下結論:
  
  偏重的框架,例如React,僅僅初始化的時間就會達到50ms ~ 350ms,這在對性能敏感的業務中時比較不利的。
  
  在App的啟動周期內,統一域名下的代碼會 被緩存編輯 和 初始化結果,重復調用性能較好。
  
  所以,在移動瀏覽器上,JS的解析和執行時間 並不是不可忽略的。
  
  在低端安卓機上,(框架的初始化+異步數據請求+業務代碼執行)會遠高於幾KB網絡請求時間;
  
  高性能的Web網站需要仔細斟酌前端渲染帶來的性能問題。
  
  優化
  
  高性能要求頁面還是需要後端渲染。
  
  React還是太重了,面向用戶寫系統需要謹慎考慮。
  
  JS代碼的編譯和執行會有緩存,同 一個 App中 網頁 盡量統一框架。
  
  WebView性能優化總結
  
  一個加載網頁的過程中,native、網絡、後端處理、CPU都會參與,各自都有必要的工作和依賴關系;
  
  讓他們相互並行處理而不是相互阻塞才可以讓網頁加載更快:
  
  WebView初始化慢,可以在初始化同時先請求數據,讓後端和網絡不要閑著。
  
  後端處理慢,可以讓服務器分trunk輸出,在後端計算的同時前端也加載網絡靜態資源。
  
  腳本執行慢,就讓腳本在最後運行,不阻塞頁面解析。
  
  同時,合理的預加載、預緩存可以讓加載速度的瓶頸更小。
  
  WebView初始化慢,就隨時初始化好一個WebView待用。
  
  DNS和鏈接慢,想辦法復用客戶端使用的域名和鏈接。
  
  腳本執行慢,可以把框架代碼拆分出來,在請求頁面之前就執行好。
  
  WebView內存消耗
  
  分析
  
  為了測試WebView會消耗多少內存,我們設計了如下的測試方案:
  
  客戶端啟動後,記錄消耗的內存。
  
  打開空頁面,記錄內存的上漲。
  
  退出。
  
  打開空頁面,記錄內存上漲。
  
  spring:
  
  application:
  
  name: register-center
  
  server:
  
  port: 8761
  
  eureka:
  
  client:
  
  serviceUrl:
  
  defaultZone: www.michenggw.com http://www.ysyl157.com local.register.com:8761/eureka/,http://www.mcyllpt.com local.register.com:8762/eureka/ # 添加新的Eureka Server
  
  register-with-eureka: true
  
  fetch-registry: false
  
  registry-fetch-interval-seconds: 5
  
  instance:
  
  lease-expiration-duration-in-seconds: 15
  
  lease-renewal-interval-in-seconds: 5
  
  prefer-ip-address: true
  
  instance-id: ${spring.yongshiyule178.com application.name}:${server.port}
  
  server:
  
  enable-self-preservation: false
  
  eviction-interval-timer-in-ms: 3000
  
  退出。
  
  打開加載了代碼的頁面,記錄內存的額外增加。
  
  得到如下測試結果:
  
  測試系統: iOS模擬器,Titans 10.0.7
  
  測試系統: OPPO R829T Android 4.2.2
  
  測試方式:測試10次取平均值
  
  首次打開增加內存 二次打開增加內存 加載KNB+VUE+靈犀
  
  iOS UIWebView 31.1M 5.52M 2M
  
  iOS WKWebView 1.95M 1.6M 2M
  
  Android 32.2M 6.62M 1.7M
  
  WKWebView的內存消耗相比其他低了一個數量級,在此方面相當占優。
  
  UIWebView和Android的WebView在首次初始化時都要消耗大量內存,之後每次新建WebView會額外增加一些。
  
  UIWebView的內存占用不會在關閉WebView時主動回收,每次新開WebView都會消耗額外內存。
  
  相比於性能,對於內存的優化可以做的還是比較有限的。
  
  WKWebView的內存占用優勢比較大(代價是初始化比較慢)。
  
  頁面內代碼消耗的內存相比與WebView系統的內存消耗相比可以說是很低。
  
  WebView體驗
  
  除了打開的速度,WebView通常體驗也沒有native的實現更好,我們可以找到以下幾個例子:
  
  長按選擇
  
  在WebView中,長按文字會使得WebView默認開始選擇文字;長按鏈接會彈出提示是否在新頁面打開。
  
  解決方法:可以通過給body增加CSS來禁止這些默認規則。
  
  點擊延遲
  
  在WebView中,click通常會有大約300ms的延遲(同時包括鏈接的點擊,表單的提交,控件的交互等任何用戶點擊行為)。
  
  唯一的例外是設置的meta:viewpoint為禁止縮放的Chrome(然而並不是Android默認的瀏覽器)。
  
  解決方法:使用fastclick一般可以解決這個問題。
  
  頁面滑動期間不渲染/執行
  
  在很多需求中會有一些吸頂的元素,例如導航條,購買按鈕等;當頁面滾動超出元素高度後,元素吸附在屏幕頂部。
  
  這個功能在PC和native中都能夠實現,然而在WebView中卻成了難題:
  
  在頁面滾動期間,Scroll Event不觸發
  
  不僅如此,WebView在滾動期間還有各種限定:
  
  setTimeout和setInterval不觸發。
  
  GIF動畫不播放。
  
  很多回調會延遲到頁面停止滾動之後。
  
  background-position: fixed不支持。
  
  這些限制讓WebView在滾動期間很難有較好的體驗。
  
  這些限制大部分是不可突破的,但至少對於吸頂功能還是可以做一些支持:
  
  解決方法:
  
  在iOS上,使用position: sticky可以做到元素吸頂。
  
  在Android上,監聽touchmove事件可以在滑動期間做元素的position切換(慣性運動期間就無效了)。
  
  crash
  
  通常WebView並不能直接接觸到底層的API,因此比較穩定;但仍然有使用不當造成整個App崩潰的情況。
  
  目前發現的案例包括:
  
  使用過大的圖片(2M)
  
  不正常使用WebGL
  
  WebView安全
  
  WebView被運營商劫持、註入問題
  
  由於WebView加載的頁面代碼是從服務器動態獲取的,這些代碼將會很容易被中間環節所竊取或者修改,其中最主要的問題出自地方運營商(浙江尤其明顯)和一些WiFi。
  
  我們監測到的問題包括:
  
  無視通信規則強制緩存頁面。
  
  header被篡改。
  
  頁面被註入廣告。
  
  頁面被重定向。
  
  頁面被重定向並重新iframe到新頁面,框架嵌入廣告。
  
  HTTPS請求被攔截。
  
  DNS劫持。
  
  這些問題輕則影響用戶體驗,重則泄露數據,或影響公司信譽。
  
  針對頁面註入的行為,有一些解決方案:
  
  使用CSP(Content Security Policy)
  
  CSP可以有效的攔截頁面中的非白名單資源,而且兼容性較好。在美團移動版的使用中,能夠阻止大部分的頁面內容註入。
  
  但在使用中還是存在以下問題:
  
  由於業務的需要,通常inline腳本還是在白名單中,會導致完全依賴內聯的頁面代碼註入可以通過檢測。
  
  如果註入的內容是純HTML+CSS的內容,則CSP無能為力。
  
  無法解決頁面被劫持的問題。
  
  會帶來額外的一些維護成本。
  
  總體來說CSP是一個行之有效的防註入方案,但是如果對於安全要求更高的網站,這些還不夠。
  
  HTTPS
  
  HTTPS可以防止頁面被劫持或者註入,然而其副作用也是明顯的,網絡傳輸的性能和成功率都會下降,
  
  而且HTTPS的頁面會要求頁面內所有引用的資源也是HTTPS的,對於大型網站其遷移成本並不算低。
  
  HTTPS的一個問題在於:一旦底層想要篡改或者劫持,會導致整個鏈接失效,頁面無法展示。
  
  這會帶來一個問題:本來頁面只是會被註入廣告,而且廣告會被CSP攔截,而采用了HTTPS後,整個網頁由於受到劫持完全無法展示。
  
  對於安全要求不高的靜態頁面,就需要權衡HTTPS帶來的利與弊了。
  
  App使用Socket代理請求
  
  如果HTTP請求容易被攔截,那麽讓App將其轉換為一個Socket請求,並代理WebView的訪問也是一個辦法。
  
  通常不法運營商或者WiFi都只能攔截HTTP(S)請求,對於自定義的包內容則無法攔截,因此可以基本解決註入和劫持的問題。
  
  Socket代理請求也存在問題。
  
  首先,使用客戶端代理的頁面HTML請求將喪失邊下載邊解析的能力;
  
  根據前面所述,瀏覽器在HTML收到部分內容後就立刻開始解析,並加載解析出來的外鏈、圖片等,執行內聯的腳本
  
  ……而目前WebView對外並沒有暴露這種流式的HTML接口,只能由客戶端完全下載好HTML後,註入到WebView中。因此其性能將會受到影響。
  
  其次,其技術問題也是較多的,例如對跳轉的處理,對緩存的處理,對CDN的處理等等……稍不留神就會埋下若幹大坑。
  
  此外還有一些其他的辦法,例如頁面的MD5檢測,頁面靜態頁打包下載等等方式,具體如何選擇還要根據具體的場景抉擇。
  
  客戶端內打開第三方WebView
  
  一般來說,客戶端內的WebView都是可以通過客戶端的某個schema打開的,而要打開頁面的URL很多都並不寫在客戶端內,而是可以由URL中的參數傳遞過去的。
  
  那麽,一旦此URL可以通過外界輸入自定義,那麽就有可能在客戶端內部打開一個外部的網頁。
  
  例:作案過程
  
  某個App有個WebView,打開的schema為 appxx://web?url={weburl}。
  
  App中有個掃碼的功能,可以掃描某個二維碼並打開對應的schema鏈接。
  
  某個壞人制作了一個二維碼並張貼到街上,內容符合 : appxx://web?url={some_hack_weburl}。
  
  用戶掃碼打開了some_hack_weburl。
  
  如果some_hack_weburl是一個高仿的登錄頁面,那麽用戶將會很可能將用戶名密碼提交到其他網站。
  
  解決方法:在內嵌的WebView中應該限制允許打開的WebView的域名,並設置運行訪問的白名單。或者當用戶打開外部鏈接前給用戶強烈而明顯的提示。
  
  發展
  
  在一個客戶端內,native目前主要功能是提供高效而基礎的功能;內部的WebView則添加一些性能體驗要求不高但動態化要求高的能力。
  
  提高客戶端的動態能力,或者提高WebView的性能,都是提升App功能覆蓋的方式。
  
  而目前的各種框架,ReactNative、Week包括微信小程序,都是這個趨勢的嘗試。
  
  隨著技術的發展,WebView的性能、體驗和安全問題也將會逐漸的改善,在App中占有越來越多比重的同時,也將會為App開拓新的能力,為用戶帶來更優質的體驗。

Flask核心機制--上下文源碼剖析