跳到主要內容

介紹新的 iOS WebViews

·2 分鐘閱讀
Facebook 軟體工程師

長期以來,Apple 一直不鼓勵使用 UIWebView,而是傾向使用 WKWebView。在即將於未來幾個月發布的 iOS 12 中,UIWebView 將被正式棄用。React Native 的 iOS WebView 實作很大程度上依賴 UIWebView 類別。因此,鑑於這些發展,我們為 WebView React Native 組件建置了一個新的原生 iOS 後端,該後端使用 WKWebView。

這些變更的尾聲已在 此提交中完成,並將在 0.57 版本中提供。

若要選擇加入此新實作,請使用 useWebKit 屬性

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

改進

UIWebView 沒有合法的方式來促進在 WebView 中執行的 JavaScript 與 React Native 之間的通訊。當訊息從 WebView 傳送時,我們依賴一種駭客手法將它們傳遞到 React Native。簡而言之,我們將訊息資料編碼為具有特殊架構的 URL,並將 WebView 導航到該 URL。在原生端,我們攔截並取消此導航,從 URL 中解析資料,最後呼叫到 React Native。此實作容易出錯且不安全。我很高興宣布,我們已利用 WKWebView 功能完全取代它。

WKWebView 相較於 UIWebView 的其他優點包括更快的 JavaScript 執行速度和多進程架構。請參閱此 2014 年 WWDC 以了解更多詳細資訊。

注意事項

如果您的組件使用以下屬性,則在切換到 WKWebView 時可能會遇到問題。目前,我們建議您避免使用這些屬性

行為不一致

automaticallyAdjustContentInsetscontentInsets (commit)

當您將 contentInsets 新增到 WKWebView 時,它不會變更 WKWebView 的視口。視口大小與框架大小相同。使用 UIWebView,視口大小實際上會變更(如果內容插頁為正數,則會變小)。

backgroundColor (commit)

使用 WebView 的新 iOS 實作,如果您使用此屬性,則您的背景顏色可能會閃爍到檢視中。此外,WKWebView 以不同於 UIWebView 的方式呈現透明背景。請查看提交描述以了解更多詳細資訊。

不支援

scalesPageToFit (commit)

WKWebView 不支援 scalesPageToFit 屬性,因此我們無法在 WebView React Native 組件上實作此屬性。

無障礙功能 API 更新

·7 分鐘閱讀
Ziqi Chen
加州大學柏克萊分校學生

動機

隨著技術進步和行動應用程式在日常生活中變得越來越重要,建立無障礙應用程式的必要性也同樣變得重要。

React Native 有限的無障礙功能 API 一直是開發人員的一大痛點,因此我們對無障礙功能 API 進行了一些更新,以使其更容易建立包容性行動應用程式。

現有 API 的問題

問題一:兩個完全不同但相似的屬性 - accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

accessibilityComponentTypeaccessibilityTraits 是用於告知 Android 上的 TalkBack 和 iOS 上的 VoiceOver 使用者正在與哪種類型的 UI 元素互動的兩個屬性。這些屬性的兩個最大問題是

  1. 它們是兩個具有不同使用方法的不同屬性,但具有相同的目的。 在先前的 API 中,它們是兩個獨立的屬性(每個平台一個),這不僅不方便,而且對許多開發人員來說也很困惑。iOS 上的 accessibilityTraits 允許 17 個不同的值,而 Android 上的 accessibilityComponentType 僅允許 4 個值。此外,這些值在很大程度上沒有重疊。即使這兩個屬性的輸入類型也不同。accessibilityTraits 允許傳入特徵陣列或單一特徵,而 accessibilityComponentType 僅允許單一值。
  2. Android 上的功能非常有限。 使用舊屬性,Talkback 能夠識別的唯一 UI 元素是「button」、「radiobutton_checked」和「radiobutton_unchecked」。

問題二:不存在的無障礙功能提示:

無障礙功能提示可協助使用 TalkBack 或 VoiceOver 的使用者了解,當他們對無障礙功能元素執行動作時會發生什麼情況,而這並非僅透過無障礙功能標籤即可顯而易見。這些提示可以在設定面板中開啟和關閉。先前,React Native 的 API 完全不支援無障礙功能提示。

問題三:忽略反轉顏色:

一些視力障礙使用者在其行動電話上使用反轉顏色,以獲得更高的螢幕對比度。Apple 為 iOS 提供了一個 API,允許開發人員忽略某些檢視。這樣,當使用者開啟反轉顏色設定時,影像和視訊就不會失真。React Native 目前不支援此 API。

新 API 的設計

解決方案一:合併 accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

為了消除 accessibilityComponentTypeaccessibilityTraits 之間的混淆,我們決定將它們合併為單一屬性。這很有意義,因為它們在技術上具有相同的預期功能,並且透過合併它們,開發人員在建置無障礙功能時不再需要擔心平台特定的複雜性。

背景

在 iOS 上,UIAccessibilityTraits 是可以在任何 NSObject 上設定的屬性。透過 javascript 屬性傳遞到原生的 17 個特徵中的每一個都對應到 Objective-C 中的 UIAccessibilityTraits 元素。特徵各自由一個長整數表示,並且每個設定的特徵都進行 OR 運算。

然而,在 Android 上,AccessibilityComponentType 是 React Native 建立的概念,並不直接對應到 Android 中的任何屬性。無障礙功能由無障礙功能委派處理。每個檢視都有預設的無障礙功能委派。如果您想要自訂任何無障礙功能動作,您必須建立新的無障礙功能委派,覆寫您想要自訂的特定方法,然後將您正在處理的檢視的無障礙功能委派設定為與新委派相關聯。當開發人員設定 AccessibilityComponentType 時,原生程式碼會根據傳入的組件建立新的委派,並將檢視設定為具有該無障礙功能委派。

已做的變更

對於我們的新屬性,我們想要建立兩個屬性的超集。我們決定將新屬性主要仿照現有的屬性 accessibilityTraits 建模,因為 accessibilityTraits 具有明顯更多的值。這些特徵的 Android 功能將透過修改無障礙功能委派來進行 polyfill。

accessibilityTraits 在 iOS 上可以設定為 17 個 UIAccessibilityTraits 值。但是,我們並未將它們全部包含為新屬性的可能值。這是因為設定其中一些特徵的效果實際上並不是很廣為人知,而且這些值中的許多值實際上從未使用過。

UIAccessibilityTraits 值通常用於兩種用途之一。它們要么描述 UI 元素具有的角色,要么描述 UI 元素所處的狀態。我們觀察到先前屬性的大多數用法通常使用一個表示角色的值,並將其與「state selected」、「state disabled」或兩者結合使用。因此,我們決定建立兩個新的無障礙功能屬性:accessibilityRoleaccessibilityState

accessibilityRole

新屬性 accessibilityRole 用於告知 Talkback 或 Voiceover UI 元素的角色。此新屬性可以採用以下值之一

  • none
  • button
  • link
  • search
  • image
  • keyboardkey
  • text
  • adjustable
  • header
  • summary
  • imagebutton

此屬性僅允許傳入一個值,因為 UI 元素通常在邏輯上不會採用多個值。例外情況是影像和按鈕,因此我們新增了一個角色 imagebutton,它是兩者的組合。

accessibilityStates

新屬性 accessibilityStates 用於告知 Talkback 或 Voiceover UI 元素所處的狀態。此屬性採用包含以下一個或兩個值的陣列

  • selected
  • disabled

解決方案二:新增無障礙功能提示

為此,我們新增了一個新屬性 accessibilityHint。設定此屬性將允許 Talkback 或 Voiceover 向使用者朗讀提示。

accessibilityHint

此屬性以字串形式接收要讀取的無障礙功能提示。

在 iOS 上,設定此屬性將在檢視上設定對應的原生屬性 AccessibilityHint。然後,如果 iPhone 中開啟了無障礙功能提示,Voiceover 將讀取提示。

在 Android 上,設定此屬性會將提示的值附加到無障礙功能標籤的末尾。此實作的優點是它模仿了 iOS 上提示的行為,但此實作的缺點是這些提示無法在 Android 上的設定中關閉,就像它們在 iOS 上可以關閉一樣。

我們在 Android 上做出此決定的原因是,通常,無障礙功能提示對應於特定動作(例如,點擊),並且我們希望保持跨平台行為一致。

問題三的解決方案

accessibilityIgnoresInvertColors

我們將 Apple 的 api AccessibilityIgnoresInvertColors 公開給 JavaScript,因此現在當您有一個不希望顏色反轉的檢視(例如影像)時,您可以將此屬性設定為 true,它將不會被反轉。

新用法

這些新屬性將在 React Native 0.57 版本中提供。

如何升級

如果您目前正在使用 accessibilityComponentTypeaccessibilityTraits,以下是您可以升級到新屬性的步驟。

1. 使用 jscodeshift

最簡單的用例可以使用執行 jscodeshift 指令碼來取代。

script 取代以下實例

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

取代為

accessibilityRole= “trait”

此指令碼也會移除 AccessibilityComponentType 的實例(假設您在設定 AccessibilityComponentType 的每個位置,也會設定 AccessibilityTraits)。

2. 使用手動程式碼修改

對於使用 AccessibilityTraits 但沒有對應的 AccessibilityRole 值的情況,以及將多個特徵傳遞到 AccessibilityTraits 的情況,必須進行手動程式碼修改。

一般而言,

accessibilityTraits= {[“button”, “selected”]}

將手動取代為

accessibilityRole=“button”
accessibilityStates={[“selected”]}

這些屬性已在 Facebook 的程式碼庫中使用。Facebook 的程式碼修改非常簡單。jscodeshift 指令碼修正了我們大約一半的實例,而另一半則手動修正。總體而言,整個過程花費不到幾個小時。

希望您會發現更新後的 API 非常有用!並請繼續使應用程式更易於存取!#包容性

發布 0.56

·5 分鐘閱讀
Lorenzo Sciandra
Drivetribe 的核心維護者與 React Native 開發人員

期待已久的 React Native 0.56 版本現已推出 🎉。此部落格文章重點介紹了此新版本中引入的變更。我們也想藉此機會說明自 3 月以來我們一直在忙什麼。

重大變更的兩難困境,或者「何時發布?」

貢獻者指南說明了所有對 React Native 的變更都必須經過的整合過程。此專案由許多不同的工具組成,需要協調和持續支援才能使一切正常運作。再加上回饋專案的活躍開源社群,您將感受到這一切令人難以置信的規模。

隨著 React Native 的驚人採用,必須非常謹慎地進行重大變更,並且此過程並不像我們希望的那樣順利。我們決定跳過 4 月和 5 月的版本,以便核心團隊能夠整合和測試一組新的重大變更。專用的社群溝通管道沿途被使用,以確保 2018 年 6 月 (0.56.0) 版本盡可能輕鬆地被那些耐心等待穩定版本的人採用。

0.56.0 完美嗎?不,就像所有軟體一樣:但是我們已經達到了一個平衡點,即「等待更多穩定性」與「測試導致成功結果,因此我們可以向前推進」之間的權衡,我們覺得已經準備好發布它了。此外,我們意識到 一些 幾個 問題 在最終的 0.56.0 版本中尚未解決。大多數開發人員應該可以順利升級到 0.56.0。對於那些因上述問題而受阻的人,我們希望在我們的討論中看到您,並且我們期待與您合作解決這些問題。

您可以將 0.56.0 視為朝向更穩定框架的基本建構區塊:在所有邊緣情況都被消除之前,可能需要一兩週的廣泛採用,但這將帶來更好的 2018 年 7 月 (0.57.0) 版本。

在本文的結尾,我們要感謝 總共 818 次提交的 67 位貢獻者 (!),這將有助於使您的應用程式變得更好 👏。

現在,事不宜遲...

重大變更

Babel 7

您可能知道,允許我們所有人使用 JavaScript 最新且最棒功能的轉譯器工具 Babel 正在遷移到 v7 即將推出。由於這個新版本帶來了一些重要的變更,我們認為現在是升級的好時機,讓 Metro 能夠 利用其改進

如果您在升級時遇到麻煩,請參閱與其相關的文件章節

現代化 Android 支援

在 Android 上,許多周邊工具都已變更。我們已更新到 Gradle 3.5Android SDK 26Fresco 到 1.9.0 和 OkHttp 到 3.10.0,甚至 NDK API 目標到 API 16。這些變更應該可以順利進行,並加快建置速度。更重要的是,它將幫助開發人員遵守下個月生效的新的 Play 商店要求

與此相關,我們要特別感謝 Dulmandakh 提交的許多 PR,使其成為可能 👏。

在這個方向上還有一些步驟需要採取,您可以關注 專用問題(以及 JSC 的附帶問題)中更新 Android 支援的未來規劃和討論。

新的 Node、Xcode、React 和 Flow – 我的天啊!

Node 8 現在是 React Native 的標準。它實際上已經在測試中,但隨著 Node 6 進入維護模式,我們已經全力以赴。React 也已更新到 16.4,其中帶來了大量修正。

我們正在放棄對 iOS 8 的支援,使 iOS 9 成為可以作為目標的最舊 iOS 版本。我們不認為這會是一個問題,因為任何可以執行 iOS 8 的裝置都可以升級到 iOS 9。此變更允許我們移除很少使用的程式碼,這些程式碼為執行 iOS 8 的舊裝置實作了變通方法。

持續整合工具鏈已更新以使用 Xcode 9.4,確保所有 iOS 測試都在 Apple 提供的最新開發人員工具上執行。

我們已升級到 Flow 0.75 以使用新的錯誤格式 許多開發人員都讚賞這種格式。我們還為更多組件建立了類型。如果您尚未在專案中強制執行靜態類型,請考慮使用 Flow 在編碼時而不是在執行階段識別問題。

以及許多其他事項...

例如,YellowBox 已取代為新的實作,這使得偵錯變得更好。

如需完整的版本資訊,請參考此處的完整變更記錄。並記得關注升級指南,以避免移動到這個新版本時出現問題。


最後一點:從本週開始,React Native 核心團隊將恢復舉行每月會議。我們將確保讓所有人隨時了解涵蓋的內容,並確保將您的回饋意見放在手邊,以供未來會議使用。

祝大家編碼愉快!

LorenzoRyan 和整個 React Native 核心團隊

附註: 與往常一樣,我們要提醒大家,React Native 仍處於 0.x 版本控制中,因為仍有許多變更正在進行中 - 因此請記住,在升級時,是的,可能仍然會發生崩潰或損壞的情況。在問題和提交 PR 時互相幫助 - 並記得遵守強制執行的 CoC:螢幕的另一側始終是人。

2018 年 React Native 的狀態

·5 分鐘閱讀
Sophie Alpert
Facebook React 工程經理

自我們上次發布有關 React Native 的狀態更新以來已經有一段時間了。

在 Facebook,我們比以往任何時候都更多地使用 React Native,並將其用於許多重要的專案。我們最受歡迎的產品之一是 Marketplace,它是我們應用程式中的頂層標籤之一,每月有 8 億人使用。自 2015 年創建以來,Marketplace 的所有內容都是使用 React Native 建置的,包括應用程式不同部分的一百多個全螢幕檢視。

我們也將 React Native 用於應用程式的許多新部分。如果您觀看了上個月的 F8 主題演講,您會認出 Blood Donations、Crisis Response、Privacy Shortcuts 和 Wellness Checks – 所有這些都是最近使用 React Native 建置的功能。主要 Facebook 應用程式以外的專案也在使用 React Native。新的 Oculus Go VR 頭戴裝置包括 完全使用 React Native 建置的配套行動應用程式,更不用說 React VR 為頭戴裝置本身中的許多體驗提供支援。

當然,我們也使用許多其他技術來建構我們的應用程式。 LithoComponentKit 是我們在應用程式中廣泛使用的兩個函式庫;兩者都提供類似 React 的元件 API,用於建構原生畫面。React Native 從未打算取代所有其他技術 – 我們專注於讓 React Native 本身變得更好,但我們樂於見到其他團隊借鑒 React Native 的想法,例如將 即時重新載入 帶入非 JavaScript 程式碼中。

架構

當我們在 2013 年開始 React Native 專案時,我們將其設計為在 JavaScript 和原生之間具有單一「橋樑」,該橋樑是非同步、可序列化且批次處理的。正如 React DOM 將 React 狀態更新轉換為對 DOM API(例如 document.createElement(attrs).appendChild())的命令式、變異呼叫一樣,React Native 的設計目的是傳回單一 JSON 訊息,其中列出要執行的變異,例如 [["createView", attrs], ["manageChildren", ...]]。我們將整個系統設計為永遠不依賴取得同步回應,並確保該清單中的所有內容都可以完全序列化為 JSON 並再序列化回來。我們這樣做是為了它給予我們的彈性:在這個架構之上,我們能夠建構諸如 Chrome 除錯 之類的工具,這些工具可以透過 WebSocket 連線非同步執行所有 JavaScript 程式碼。

在過去 5 年中,我們發現這些初始原則使得建構某些功能變得更加困難。非同步橋樑意味著您無法將 JavaScript 邏輯與許多期望同步回應的原生 API 直接整合。批次處理的橋樑會將原生呼叫排隊,這意味著讓 React Native 應用程式呼叫以原生方式實作的函式變得更加困難。而可序列化的橋樑意味著不必要的複製,而不是在兩個世界之間直接共享記憶體。對於完全以 React Native 建構的應用程式來說,這些限制通常是可以忍受的。但對於在 React Native 和現有應用程式程式碼之間進行複雜整合的應用程式來說,這些限制令人沮喪。

我們正在進行 React Native 的大規模架構重構,以使該框架更具彈性,並在混合 JavaScript/原生應用程式中與原生基礎架構更好地整合。 透過這個專案,我們將應用我們在過去 5 年中學到的知識,並逐步將我們的架構帶向更現代化的架構。我們正在重寫 React Native 的許多內部組件,但大多數變更都在幕後進行:現有的 React Native 應用程式將繼續運作,幾乎沒有或沒有任何變更。

為了使 React Native 更輕量級並更好地融入現有的原生應用程式,這次架構重構有三個主要的內部變更。首先,我們正在變更執行緒模型。不再是每次 UI 更新都需要在三個不同的執行緒上執行工作,而是可以在任何執行緒上同步呼叫 JavaScript 以進行高優先級更新,同時仍然將低優先級工作從主執行緒中移開以保持回應能力。其次,我們正在將 非同步渲染 功能整合到 React Native 中,以允許多個渲染優先級並簡化非同步資料處理。最後,我們正在簡化我們的橋樑,使其更快、更輕量;原生和 JavaScript 之間的直接呼叫更有效率,並將更容易建構諸如跨語言堆疊追蹤之類的除錯工具。

一旦這些變更完成,更緊密的整合將成為可能。今天,如果不進行複雜的駭客技巧,就不可能整合原生導航和手勢處理或原生元件(例如 UICollectionView 和 RecyclerView)。在我們變更執行緒模型之後,建構像這樣的功能將會變得簡單明瞭。

隨著這項工作接近完成,我們將在今年稍後發布有關這項工作的更多詳細資訊。

社群

除了 Facebook 內部的社群之外,我們很高興在 Facebook 外部擁有蓬勃發展的 React Native 使用者和協作者群體。我們希望更多地支援 React Native 社群,包括更好地服務 React Native 使用者,以及使該專案更易於貢獻。

正如我們的架構變更將幫助 React Native 與其他原生基礎架構更清晰地互操作一樣,React Native 應該在 JavaScript 端更精簡,以更好地適應 JavaScript 生態系統,其中包括使 VM 和 bundler 可交換。我們知道重大變更的步調可能難以跟上,因此我們希望找到減少主要發行版本的方法。最後,我們知道有些團隊正在尋找更詳盡的文件,例如啟動最佳化等主題,而我們在這方面的專業知識尚未寫下來。預計在未來一年中會看到其中一些變更。

如果您正在使用 React Native,您就是我們社群的一份子;請持續讓我們知道如何讓 React Native 對您更好。

React Native 只是行動開發人員工具箱中的一個工具,但我們堅信它 – 而且我們每天都在使其變得更好,在過去一年中,有來自 500 多位貢獻者的 2500 多次提交。

將 TypeScript 與 React Native 搭配使用

·8 分鐘閱讀時間
Ash Furrow
Artsy 軟體工程師

JavaScript!我們都喜歡它。但我們有些人也喜歡 型別。幸運的是,存在將更強型別添加到 JavaScript 的選項。我最喜歡的是 TypeScript,但 React Native 開箱即用就支援 Flow。您偏好哪個是個人喜好問題,它們各有自己添加型別魔法到 JavaScript 的方法。今天,我們將研究如何在 React Native 應用程式中使用 TypeScript。

這篇文章使用 Microsoft 的 TypeScript-React-Native-Starter 儲存庫作為指南。

更新:自撰寫這篇部落格文章以來,事情變得更加容易。您可以透過執行一個命令來取代這篇部落格文章中描述的所有設定

npx react-native init MyAwesomeProject --template react-native-template-typescript

但是,Babel 的 TypeScript 支援確實有一些限制,上面的部落格文章詳細介紹了這些限制。篇文章中概述的步驟仍然有效,Artsy 仍然在生產環境中使用 react-native-typescript-transformer,但開始使用 React Native 和 TypeScript 最快的方法是使用上面的命令。如果必須,您可以隨時稍後切換。

無論如何,玩得開心!原始部落格文章繼續如下。

先決條件

由於您可能在多個不同的平台上開發,並針對多種不同類型的裝置,因此基本設定可能會很複雜。您應該首先確保您可以執行沒有 TypeScript 的純 React Native 應用程式。請依照 React Native 網站上的說明開始使用。當您成功部署到裝置或模擬器時,您就可以開始 TypeScript React Native 應用程式了。

您還需要 Node.jsnpmYarn

初始化

一旦您嘗試搭建一個普通的 React Native 專案,您就可以開始新增 TypeScript 了。讓我們繼續執行此操作。

react-native init MyAwesomeProject
cd MyAwesomeProject

新增 TypeScript

下一步是將 TypeScript 新增到您的專案。以下命令將會

  • 將 TypeScript 新增到您的專案
  • React Native TypeScript Transformer 新增到您的專案
  • 初始化一個空的 TypeScript 設定檔,我們接下來將設定它
  • 新增一個空的 React Native TypeScript Transformer 設定檔,我們接下來將設定它
  • 新增 React 和 React Native 的 型別定義

好的,讓我們繼續執行這些命令。

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

tsconfig.json 檔案包含 TypeScript 編譯器的所有設定。上面命令建立的預設值大部分都很好,但開啟該檔案並取消註解以下行

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

rn-cli.config.js 包含 React Native TypeScript Transformer 的設定。開啟它並新增以下內容

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

遷移到 TypeScript

將產生的 App.js__tests__/App.js 檔案重新命名為 App.tsxindex.js 需要使用 .js 副檔名。所有新檔案都應使用 .tsx 副檔名(如果檔案不包含任何 JSX,則使用 .ts)。

如果您現在嘗試執行應用程式,您會收到類似 object prototype may only be an object or null 的錯誤。這是由於無法從 React 匯入預設匯出以及在同一行上的具名匯出所致。開啟 App.tsx 並修改檔案頂部的匯入

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

其中一些與 Babel 和 TypeScript 如何與 CommonJS 模組互操作的差異有關。將來,兩者將在相同的行為上趨於穩定。

此時,您應該能夠執行 React Native 應用程式。

新增 TypeScript 測試基礎架構

React Native 隨附 Jest,因此為了使用 TypeScript 測試 React Native 應用程式,我們需要將 ts-jest 新增到我們的 devDependencies 中。

yarn add --dev ts-jest

然後,我們將開啟我們的 package.json 並將 jest 欄位替換為以下內容

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

這會將 Jest 設定為使用 ts-jest 執行 .ts.tsx 檔案。

安裝相依性型別宣告

為了在 TypeScript 中獲得最佳體驗,我們希望型別檢查器能夠理解我們相依性的形狀和 API。某些函式庫將會發布帶有 .d.ts 檔案(型別宣告/型別定義檔案)的套件,這些檔案可以描述底層 JavaScript 的形狀。對於其他函式庫,我們需要明確地在 @types/ npm 範圍中安裝適當的套件。

例如,在這裡我們需要 Jest、React 和 React Native 以及 React Test Renderer 的型別。

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

我們將這些宣告檔案套件儲存到我們的開發相依性中,因為這是一個 React Native應用程式,僅在開發期間而不是在執行期間使用這些相依性。如果我們要將函式庫發布到 NPM,我們可能必須將其中一些型別相依性新增為常規相依性。

您可以在這裡閱讀更多關於取得 .d.ts 檔案的資訊

忽略更多檔案

對於您的原始碼控制,您需要開始忽略 .jest 資料夾。如果您正在使用 git,我們可以只將條目新增到我們的 .gitignore 檔案中。

# Jest
#
.jest/

作為檢查點,請考慮將您的檔案提交到版本控制。

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

新增元件

讓我們將元件新增到我們的應用程式。讓我們繼續建立一個 Hello.tsx 元件。這是一個教學元件,而不是您實際會在應用程式中撰寫的元件,但它是一個非平凡的元件,展示了如何在 React Native 中使用 TypeScript。

建立一個 components 目錄並新增以下範例。

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

哇!很多,但讓我們分解一下

  • 我們不是渲染像 divspanh1 等 HTML 元素,而是渲染像 ViewButton 這樣的元件。這些是跨不同平台運作的原生元件。
  • 樣式是使用 React Native 給我們的 StyleSheet.create 函式指定的。React 的樣式表允許我們使用 Flexbox 控制我們的版面配置,並使用其他類似於 CSS 中的結構來設定樣式。

新增元件測試

現在我們有了一個元件,讓我們嘗試測試它。

我們已經安裝了 Jest 作為測試執行器。我們將為我們的元件編寫快照測試,讓我們新增快照測試所需的附加元件

yarn add --dev react-addons-test-utils

現在讓我們在 components 目錄中建立一個 __tests__ 資料夾,並為 Hello.tsx 新增一個測試

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

第一次執行測試時,它將建立渲染元件的快照並將其儲存在 components/__tests__/__snapshots__/Hello.tsx.snap 檔案中。當您修改元件時,您需要更新快照並檢查更新是否有意外變更。您可以在這裡閱讀更多關於測試 React Native 元件的資訊

後續步驟

查看官方 React 教學課程 和狀態管理函式庫 Redux。這些資源在撰寫 React Native 應用程式時可能會有所幫助。此外,您可能需要查看 ReactXP,這是一個完全用 TypeScript 撰寫的元件函式庫,它同時支援 Web 上的 React 和 React Native。

在更型別安全的 React Native 開發環境中玩得開心!

使用 React Native 建置 - Build.com 應用程式

·5 分鐘閱讀
Garrett McCullough
資深行動工程師

Build.com 總部位於加州奇科,是最大的居家修繕用品線上零售商之一。該團隊擁有 18 年以網路為中心的業務經驗,並於 2015 年開始考慮行動應用程式。由於我們團隊規模小且原生經驗有限,因此建構獨特的 Android 和 iOS 應用程式並不實際。相反,我們決定冒險嘗試非常新的 React Native 框架。我們的初始提交是在 2015 年 8 月 12 日,使用的是 React Native v0.8.0!我們於 2016 年 10 月 15 日在兩個應用程式商店中上線。在過去兩年中,我們持續升級和擴展應用程式。我們目前使用的是 React Native 版本 0.53.0。

您可以在 https://www.build.com/app 查看該應用程式。

功能

我們的應用程式功能齊全,包含您對電子商務應用程式所期望的一切:產品列表、搜尋和排序、配置複雜產品的能力、我的最愛等等。我們接受標準信用卡付款方式以及 PayPal 和 iOS 使用者的 Apple Pay。

您可能沒想到的幾個突出功能包括

  1. 約 40 種產品提供 3D 模型,並有 90 種表面處理
  2. 擴增實境 (AR) 讓使用者可以查看燈具和水龍頭在他們家中的外觀,準確度達 98%。Build.com React Native 應用程式在 Apple App Store 中被推薦用於 AR 購物!AR 現在適用於 Android 和 iOS!
  3. 協作專案管理功能,讓使用者可以為專案的不同階段整理購物清單,並圍繞選擇進行協作

我們正在開發許多新的和令人興奮的功能,這些功能將繼續改善我們的應用程式體驗,包括下一階段的沉浸式 AR 購物。

我們的開發工作流程

Build.com 允許每位開發人員選擇最適合他們的工具。

  • IDE 包括 Atom、IntelliJ、VS Code、Sublime、Eclipse 等。
  • 對於單元測試,開發人員負責為任何新元件建立 Jest 單元測試,我們正在努力使用 jest-coverage-ratchet 提高應用程式舊部分的覆蓋率。
  • 我們使用 Jenkins 來建構我們的 Beta 版和候選發行版本。這個流程對我們來說運作良好,但仍然需要大量工作才能建立發行說明和其他成品。
  • 整合測試包括跨桌面、行動裝置和網路工作的共享測試人員池。我們的自動化工程師正在使用 Java 和 Appium 建構我們的自動化整合測試套件。
  • 工作流程的其他部分包括詳細的 eslint 配置、強制執行測試所需屬性的自訂規則以及阻止違規變更的 pre-push hooks。

應用程式中使用的函式庫

Build.com 應用程式依賴許多常見的開放原始碼函式庫,包括:Redux、Moment、Numeral、Enzyme 和一堆 React Native 橋接模組。我們也使用許多 fork 的開放原始碼函式庫;fork 的原因是因為它們被放棄了,或者因為我們需要自訂功能。快速統計顯示大約有 115 個 JavaScript 和原生相依性。我們希望探索可以移除未使用函式庫的工具。

我們正在透過 TypeScript 新增靜態型別,並研究可選鏈結。這些功能可以幫助我們解決我們仍然看到的一些類別的錯誤

  • 型別錯誤的資料
  • 由於物件不包含我們預期的內容而未定義的資料

開放原始碼貢獻

由於我們如此嚴重地依賴開放原始碼,我們的團隊致力於回饋社群。Build.com 允許團隊開放原始碼我們建構的函式庫,並鼓勵我們回饋我們使用的函式庫。

我們已發布和維護了許多 React Native 函式庫

  • react-native-polyfill
  • react-native-simple-store
  • react-native-contact-picker

我們還為長長的函式庫列表做出了貢獻,包括:React 和 React Native、react-native-schemes-managerreact-native-swipeablereact-native-galleryreact-native-view-transformerreact-native-navigation

我們的旅程

在過去幾年中,我們看到了 React Native 和生態系統的蓬勃發展。早期,似乎每個版本的 React Native 都會修復一些錯誤,但會引入更多錯誤。例如,遠端 JS 除錯在 Android 上中斷了好幾個月。值得慶幸的是,事情在 2017 年變得穩定得多。

我們反覆遇到的一大挑戰是導航函式庫。長期以來,我們一直使用 Expo 的 ex-nav 函式庫。它對我們來說運作良好,但最終被棄用了。但是,當時我們正處於繁重的功能開發階段,因此花時間更換導航函式庫是不可行的。這意味著我們必須 fork 該函式庫並修補它以支援 React 16 和 iPhone X。最終,我們能夠遷移到 react-native-navigation,並希望它能持續獲得支援。

橋接模組

另一個重大挑戰是橋接模組。當我們剛開始時,很多關鍵的橋接都不見了。我的其中一位隊友撰寫了 react-native-contact-picker,因為我們需要在我們的應用程式中存取 Android 聯絡人選擇器。我們也看到許多橋接被 React Native 內部的變更所破壞。例如,React Native v40 內部發生了重大變更,當我們升級我們的應用程式時,我不得不提交 PR 以修復 3 或 4 個尚未更新的函式庫。

展望未來

隨著 React Native 持續成長,我們對社群的願望清單包括

  • 穩定和改進導航函式庫
  • 維護對 React Native 生態系統中函式庫的支援
  • 改善將原生函式庫和橋接模組新增到專案的體驗

React Native 社群中的公司和個人一直很樂意貢獻他們的時間和精力來改進我們大家使用的工具。如果您尚未參與開放原始碼,我希望您能看看改進您使用的某些函式庫的程式碼或文件。有很多文章可以幫助您入門,而且它可能比您想像的要容易得多!

為 React Native 建置 <InputAccessoryView>

·6 分鐘閱讀時間
Peter Argany
Facebook 軟體工程師

動機

三年前,有人開啟了一個 GitHub issue,以支援來自 React Native 的輸入輔助視圖。

在隨後的幾年中,出現了無數的「+1」、各種變通方法,以及這個 issue 上 RN 的零具體變更 - 直到今天。從 iOS 開始,我們正在公開一個 API 以存取原生輸入輔助視圖,我們很高興分享我們是如何建構它的。

背景

輸入輔助視圖到底是什麼?閱讀 Apple 的開發人員文件,我們了解到它是一個自訂視圖,當接收器成為第一響應者時,它可以錨定到系統鍵盤的頂部。任何繼承自 UIResponder 的事物都可以將 .inputAccessoryView 屬性重新宣告為讀寫,並在此處管理自訂視圖。響應者基礎架構會掛載該視圖,並使其與系統鍵盤保持同步。解除鍵盤的手勢(例如拖曳或點擊)會在框架層級應用於輸入輔助視圖。這使我們能夠建構具有互動式鍵盤解除功能的內容,這是 iMessage 和 WhatsApp 等頂級訊息應用程式中不可或缺的功能。

將視圖錨定到鍵盤頂部有兩種常見的用例。第一種是建立鍵盤工具列,例如 Facebook 作曲家背景選擇器。

在這種情況下,鍵盤專注於文字輸入欄位,而輸入輔助視圖用於提供額外的鍵盤功能。此功能與輸入欄位的類型相關。在對應應用程式中,它可以是地址建議,或者在文字編輯器中,它可以是富文本格式工具。


在這種情況下,擁有 <InputAccessoryView> 的 Objective-C UIResponder 應該很清楚。<TextInput> 已成為第一響應者,而在底層,這會變成 UITextViewUITextField 的實例。

第二種常見情況是黏性文字輸入

在這裡,文字輸入實際上是輸入輔助視圖本身的一部分。這通常用於訊息應用程式中,在訊息應用程式中,可以在捲動瀏覽先前訊息的執行緒時撰寫訊息。


在此範例中,誰擁有 <InputAccessoryView>?它可以再次是 UITextViewUITextField 嗎?文字輸入位於輸入輔助視圖內,這聽起來像是循環相依性。僅解決這個問題本身就是 另一篇部落格文章 的主題。劇透:擁有者是一個通用的 UIView 子類別,我們手動告訴它 becomeFirstResponder

API 設計

我們現在知道 <InputAccessoryView> 是什麼,以及我們想要如何使用它。下一步是設計一個對這兩種用例都有意義的 API,並且可以與現有的 React Native 元件(例如 <TextInput>)良好地協同工作。

對於鍵盤工具列,我們需要考慮一些事項

  1. 我們希望能夠將任何通用的 React Native 視圖層級結構提升到 <InputAccessoryView> 中。
  2. 我們希望這個通用的和分離的視圖層級結構能夠接受觸控並能夠操作應用程式狀態。
  3. 我們希望將 <InputAccessoryView> 連結到特定的 <TextInput>
  4. 我們希望能夠在多個文字輸入之間共享 <InputAccessoryView>,而無需複製任何程式碼。

我們可以使用類似於 React portals 的概念來實現 #1。在這個設計中,我們將 React Native 視圖傳送到由響應者基礎架構管理的 UIView 層級結構。由於 React Native 視圖渲染為 UIView,因此這實際上非常簡單 - 我們可以只覆寫

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex

並將所有子視圖管道傳輸到新的 UIView 層級結構。對於 #2,我們為 <InputAccessoryView> 設定了一個新的 RCTTouchHandler。狀態更新是透過使用常規事件回呼實現的。對於 #3 和 #4,我們在建立 <TextInput> 元件期間,使用 nativeID 欄位在原生程式碼中定位輔助視圖 UIView 層級結構。此函式使用底層原生文字輸入的 .inputAccessoryView 屬性。這樣做有效地將 <InputAccessoryView> 連結到它們的 ObjC 實作中的 <TextInput>

支援黏性文字輸入(情境 2)增加了一些限制。對於此設計,輸入工具列視圖將文字輸入作為子元件,因此無法透過 nativeID 連結。取而代之的是,我們將通用螢幕外 UIView.inputAccessoryView 設定為我們的原生 <InputAccessoryView> 階層。透過手動告知此通用 UIView 成為第一響應者,階層會由響應者基礎架構掛載。此概念已在前述的部落格文章中完整說明。

缺陷

當然,在建構此 API 時並非一帆風順。以下是我們遇到的一些缺陷,以及我們如何修復它們。

建構此 API 的最初想法涉及監聽 NSNotificationCenter 以取得 UIKeyboardWill(Show/Hide/ChangeFrame) 事件。此模式用於某些開源函式庫,以及 Facebook 應用程式的某些內部部分。不幸的是,UIKeyboardDidChangeFrame 事件並未及時呼叫,以在滑動時更新 <InputAccessoryView> 框架。此外,鍵盤高度的變更未被這些事件捕獲。這產生了一類像這樣的錯誤

在 iPhone X 上,文字和表情符號鍵盤的高度不同。大多數使用鍵盤事件來操作文字輸入框架的應用程式都必須修復上述錯誤。我們的解決方案是承諾使用 .inputAccessoryView 屬性,這表示響應者基礎架構會處理像這樣的框架更新。


我們遇到的另一個棘手錯誤是避免 iPhone X 上的主畫面指示條。您可能會想:「Apple 開發 safeAreaLayoutGuide 就是為了這個原因,這很簡單!」。我們也一樣天真。第一個問題是,原生 <InputAccessoryView> 實作在即將出現的那一刻之前,沒有視窗可以錨定。沒關係,我們可以覆寫 -(BOOL)becomeFirstResponder 並在那裡強制佈局約束。遵守這些約束會將工具列視圖向上推,但另一個錯誤出現了:

輸入工具列視圖成功避開了主畫面指示條,但現在不安全區域後面的內容可見了。解決方案在於這個 radar。我將原生 <InputAccessoryView> 階層包裝在不符合 safeAreaLayoutGuide 約束的容器中。原生容器覆蓋了不安全區域中的內容,而 <InputAccessoryView> 則保持在安全區域邊界內。


範例用法

這是一個範例,它建構了一個鍵盤工具列按鈕來重置 <TextInput> 狀態。

class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}

render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}

另一個 黏性文字輸入的範例可以在儲存庫中找到

我何時可以使用它?

此功能實作的完整提交在這裡:這裡<InputAccessoryView> 將在即將發布的 v0.55.0 版本中提供。

鍵盤操作愉快:)

將 AWS 與 React Native 搭配使用

·9 分鐘閱讀
Richard Threlkeld
AWS Mobile 資深技術產品經理

AWS 在科技產業中以雲端服務供應商而聞名。這些服務包括運算、儲存和資料庫技術,以及完全託管的無伺服器產品。AWS Mobile 團隊一直與客戶和 JavaScript 生態系統的成員密切合作,以使雲端連線的行動和 Web 應用程式更安全、可擴展且更易於開發和部署。我們從 完整的入門套件開始,但最近有了一些新的發展。

這篇部落格文章討論了 React 和 React Native 開發人員感興趣的一些事項

  • AWS Amplify,一個用於使用雲端服務的 JavaScript 應用程式的宣告式函式庫
  • AWS AppSync,一個完全託管的 GraphQL 服務,具有離線和即時功能

AWS Amplify

使用 Create React Native App 和 Expo 等工具可以非常輕鬆地啟動 React Native 應用程式。但是,當您嘗試將用例與基礎架構服務匹配時,將它們連接到雲端可能具有挑戰性。例如,您的 React Native 應用程式可能需要上傳照片。這些照片應該按使用者保護嗎?這可能表示您需要某種註冊或登入程序。您想要自己的使用者目錄還是使用社群媒體供應商?也許您的應用程式還需要在使用者登入後呼叫具有自訂業務邏輯的 API。

為了幫助 JavaScript 開發人員解決這些問題,我們發布了一個名為 AWS Amplify 的函式庫。該設計分為「類別」任務,而不是 AWS 特定的實作。例如,如果您希望使用者註冊、登入,然後上傳私人照片,您只需將 AuthStorage 類別拉入您的應用程式即可

import { Auth } from 'aws-amplify';

Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));

Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));

在上面的程式碼中,您可以看到 Amplify 幫助您完成的一些常見任務的範例,例如將多因素驗證 (MFA) 程式碼與電子郵件或 SMS 結合使用。目前支援的類別有

  • Auth:提供憑證自動化。開箱即用的實作使用 AWS 憑證進行簽署,以及來自 Amazon Cognito 的 OIDC JWT 權杖。支援常見功能,例如 MFA 功能。
  • Analytics:只需一行程式碼,即可在 Amazon Pinpoint 中取得已驗證或未驗證使用者的追蹤。您可以根據自己的喜好擴展此功能以取得自訂指標或屬性。
  • API:提供以安全方式與 RESTful API 互動,利用 AWS Signature Version 4。API 模組非常適合具有 Amazon API Gateway 的無伺服器基礎架構。
  • Storage:簡化了在 Amazon S3 中上傳、下載和列出內容的命令。您還可以輕鬆地將資料按使用者分組為公開或私人內容。
  • Caching:跨 Web 應用程式和 React Native 的 LRU 快取介面,使用特定於實作的持久性。
  • i18n 和 Logging:提供國際化和在地化功能,以及偵錯和記錄功能。

Amplify 的優點之一是它在設計中編碼了針對您的特定程式設計環境的「最佳實務」。例如,我們在與客戶和 React Native 開發人員合作時發現,在開發期間為快速讓事情運作而採取的捷徑會一直持續到生產堆疊中。這些可能會損害可擴展性或安全性,並強制基礎架構重新架構和程式碼重構。

我們如何幫助開發人員避免這種情況的一個範例是 使用 AWS Lambda 的無伺服器參考架構。這些架構向您展示了在建構後端時,圍繞一起使用 Amazon API Gateway 和 AWS Lambda 的最佳實務。此模式編碼到 Amplify 的 API 類別中。您可以使用此模式與多個不同的 REST 端點互動,並將標頭一直傳遞到您的 Lambda 函數以取得自訂業務邏輯。我們還發布了 AWS Mobile CLI,用於使用這些功能啟動新的或現有的 React Native 專案。若要開始使用,只需透過 npm 安裝,然後按照組態提示操作即可

npm install --global awsmobile-cli
awsmobile configure

另一個特定於行動生態系統的編碼最佳實務範例是密碼安全性。預設的 Auth 類別實作利用 Amazon Cognito 使用者集區進行使用者註冊和登入。此服務實作 安全遠端密碼協定,作為在驗證嘗試期間保護使用者的一種方式。如果您傾向於閱讀 協定的數學原理,您會注意到在計算原始根上的密碼驗證器以產生群組時,必須使用大的質數。在 React Native 環境中,JIT 已停用。這使得用於此類安全性操作的 BigInteger 計算效能較差。為了解決這個問題,我們在 Android 和 iOS 中發布了原生橋接器,您可以在專案內部連結這些橋接器

npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js

我們也很高興看到 Expo 團隊已將此功能包含在 他們的最新 SDK 中,以便您可以在不退出的情況下使用 Amplify。

最後,針對 React Native(和 React)開發,Amplify 包含 高階元件 (HOC),用於輕鬆包裝功能,例如用於應用程式的註冊和登入

import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends React.Component {
...
}

export default withAuthenticator(App);

底層元件也以 <Authenticator /> 的形式提供,這讓您可以完全控制自訂 UI。它還為您提供了一些關於管理使用者狀態的屬性,例如他們是否已登入或正在等待 MFA 確認,以及您可以在狀態變更時觸發的回呼。

同樣地,您會找到可以針對不同用例使用的一般 React 元件。您可以根據您的需求自訂這些元件,例如,在 Storage 模組中顯示來自 Amazon S3 的所有私人圖片

<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>

您可以透過 props 控制許多元件功能,如先前所示,具有公開或私人儲存選項。甚至還有在使用者與某些 UI 元件互動時自動收集分析的功能

return <S3Album track/>

AWS Amplify 偏好慣例優於組態的開發風格,具有全域初始化常式或類別層級的初始化。最快的入門方法是使用 aws-exports 檔案。但是,開發人員也可以使用具有現有資源的函式庫。

若要深入了解理念並觀看完整的演示,請查看來自 AWS re:Invent 的影片。

AWS AppSync

在 AWS Amplify 發布後不久,我們也發布了 AWS AppSync。這是一項完全託管的 GraphQL 服務,具有離線和即時功能。雖然您可以在不同的用戶端程式設計語言(包括原生 Android 和 iOS)中使用 GraphQL,但它在 React Native 開發人員中非常受歡迎。這是因為資料模型非常適合單向資料流和元件階層。

AWS AppSync 使您能夠連線到您自己的 AWS 帳戶中的資源,這表示您擁有並控制您的資料。這是透過使用資料來源完成的,該服務支援 Amazon DynamoDBAmazon ElasticsearchAWS Lambda。這使您能夠在單個 GraphQL API 中將功能(例如 NoSQL 和全文檢索)組合為結構描述。這使您可以混合和匹配資料來源。AppSync 服務也可以 從結構描述佈建,因此如果您不熟悉 AWS 服務,您可以編寫 GraphQL SDL,按一下按鈕,您就可以自動啟動並執行。

AWS AppSync 中的即時功能透過 具有眾所周知的、基於事件的模式的 GraphQL 訂閱 來控制。由於 AWS AppSync 中的訂閱是 在結構描述上透過 GraphQL 指令控制的,並且結構描述可以使用任何資料來源,這表示您可以從具有 Amazon DynamoDB 和 Amazon Elasticsearch Service 的資料庫操作或從基礎架構的其他部分使用 AWS Lambda 觸發通知。

以與 AWS Amplify 類似的方式,您可以在具有 AWS AppSync 的 GraphQL API 上使用 企業安全性功能。該服務可讓您快速開始使用 API 金鑰。但是,當您進入生產環境時,它可以轉換為使用 AWS Identity and Access Management (IAM) 或來自 Amazon Cognito 使用者集區的 OIDC 權杖。您可以使用類型上的原則在解析器層級控制存取權。您甚至可以使用邏輯檢查來進行 細緻的存取控制 檢查,例如偵測使用者是否是特定資料庫資源的擁有者。還有一些功能圍繞檢查群組成員資格以執行解析器或個別資料庫記錄存取。

為了幫助 React Native 開發人員更了解這些技術,AWS AppSync 主控台首頁上有一個 內建的 GraphQL 範例結構描述,您可以啟動它。此範例部署 GraphQL 結構描述、佈建資料庫表,並自動為您連線查詢、變更和訂閱。還有一個運作中的 AWS AppSync 的 React Native 範例,它利用這個內建結構描述(以及 React 範例),使您能夠在幾分鐘內啟動並執行用戶端和雲端元件。

當您使用 AWSAppSyncClient 時,入門很簡單,它插入到 Apollo Client 中。AWSAppSyncClient 處理 GraphQL API 的安全性和簽署、離線功能以及訂閱交握和協商程序

import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from 'aws-appsync-react';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";

const client = new AWSAppSyncClient({
url: awsconfig.graphqlEndpoint,
region: awsconfig.region,
auth: {type: AUTH_TYPE.API_KEY, apiKey: awsconfig.apiKey}
});

AppSync 主控台提供一個組態檔供下載,其中包含您的 GraphQL 端點、AWS 區域和 API 金鑰。然後,您可以將用戶端與 React Apollo 結合使用

const WithProvider = () => (
<ApolloProvider client={client}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
);

此時,您可以使用標準 GraphQL 查詢

query ListEvents {
listEvents{
items{
__typename
id
name
where
when
description
comments{
__typename
items{
__typename
eventId
commentId
content
createdAt
}
nextToken
}
}
}
}

上面的範例顯示了使用 AppSync 佈建的範例應用程式結構描述的查詢。它不僅展示了與 DynamoDB 的互動,還包括資料分頁(包括加密權杖)以及 EventsComments 之間的類型關係。由於應用程式已使用 AWSAppSyncClient 進行組態,因此資料會自動離線持久保存,並在裝置重新連線時同步。

您可以在此影片中看到 此影片中用戶端技術背後的深入探討以及 React Native 演示

意見反應

函式庫背後的團隊渴望聽到這些函式庫和服務如何為您工作。他們也想聽聽我們還可以做些什麼,讓您更輕鬆地使用雲端服務進行 React 和 React Native 開發。請在 GitHub 上聯繫 AWS Mobile 團隊,以取得 AWS AmplifyAWS AppSync

在 React Native 中實作 Twitter 的應用程式載入動畫

·11 分鐘閱讀
Eli White
Eli White
Meta 軟體工程師

Twitter 的 iOS 應用程式有一個我非常喜歡的載入動畫。

應用程式準備就緒後,Twitter 標誌會愉快地展開,顯示應用程式。

我想弄清楚如何使用 React Native 重新建立此載入動畫。


為了了解如何建構它,我首先必須了解載入動畫的不同部分。查看細微之處的最簡單方法是放慢速度。

其中有幾個主要部分,我們需要弄清楚如何建構。

  1. 縮放小鳥。
  2. 隨著小鳥變大,顯示下方的應用程式
  3. 在最後稍微縮小應用程式

我花了一段時間才弄清楚如何製作這個動畫。

我從一個不正確的假設開始,即藍色背景和 Twitter 小鳥是位於應用程式頂部的圖層,並且隨著小鳥變大,它會變得透明,從而顯示下方的應用程式。這種方法行不通,因為 Twitter 小鳥變得透明會顯示藍色圖層,而不是下方的應用程式!

親愛的讀者,您很幸運,不必經歷我經歷過的挫折。您可以獲得這篇不錯的教學課程,直接跳到重點!


正確的方法

在我們開始編碼之前,重要的是要了解如何分解它。為了幫助視覺化此效果,我在 CodePen 中重新建立了它(嵌入在幾個段落中),以便您可以互動式地查看不同的圖層。

此效果有三個主要圖層。第一個是藍色背景圖層。即使這似乎出現在應用程式的頂部,但它實際上是在後面。

然後我們有一個純白色圖層。最後,在最前面是我們的應用程式。


此動畫的主要技巧是使用 Twitter 標誌作為 遮罩,並遮罩應用程式和白色圖層。我不會深入探討遮罩的細節,有很多 資源 在線上 說明。

此內容中遮罩的基本知識是具有影像,其中遮罩的不透明像素顯示它們正在遮罩的內容,而遮罩的透明像素隱藏它們正在遮罩的內容。

我們使用 Twitter 標誌作為遮罩,並使其遮罩兩個圖層;純白色圖層和應用程式圖層。

為了顯示應用程式,我們將遮罩放大,直到它大於整個螢幕。

當遮罩放大時,我們淡入應用程式圖層的不透明度,顯示應用程式並隱藏其後面的純白色圖層。為了完成效果,我們將應用程式圖層的比例設定為 > 1,並在動畫結束時將其縮小到 1。然後,我們隱藏非應用程式圖層,因為它們永遠不會再被看到。

俗話說,一張圖片勝過千言萬語。互動式視覺化值多少字?按一下「下一步」按鈕以逐步瀏覽動畫。顯示圖層可讓您從側面視角觀察。網格在那裡是為了幫助視覺化透明圖層。

現在,針對 React Native

好的。現在我們知道我們正在建構什麼以及動畫如何運作,我們可以開始編碼了 — 這才是您真正來這裡的原因。

此難題的主要部分是 MaskedViewIOS,一個核心 React Native 元件。

import {MaskedViewIOS} from 'react-native';

<MaskedViewIOS maskElement={<Text>Basic Mask</Text>}>
<View style={{backgroundColor: 'blue'}} />
</MaskedViewIOS>;

MaskedViewIOS 接受 props maskElementchildren。子元件會被 maskElement 遮罩。請注意,遮罩不需要是影像,它可以是任何任意視圖。上述範例的行為是呈現藍色視圖,但僅在 maskElement 中的「Basic Mask」文字所在的位置可見。我們只是製作了複雜的藍色文字。

我們要做的Render是藍色圖層,然後在頂部 Render 使用 Twitter 標誌遮罩的應用程式和白色圖層。

{
fullScreenBlueLayer;
}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Image source={twitterLogo} />
</View>
}>
{fullScreenWhiteLayer}
<View style={{flex: 1}}>
<MyApp />
</View>
</MaskedViewIOS>;

這將為我們提供以下看到的圖層。

現在是動畫部分

我們擁有使此工作運作所需的所有部分,下一步是為它們製作動畫。為了使此動畫感覺良好,我們將使用 React Native 的 Animated API。

Animated 讓我們可以在 JavaScript 中宣告式地定義動畫。預設情況下,這些動畫在 JavaScript 中執行,並告知原生圖層在每個影格上要進行哪些變更。即使 JavaScript 會嘗試在每個影格更新動畫,它也可能無法足夠快地執行此操作,並會導致影格丟失(卡頓)發生。這不是我們想要的!

Animated 具有特殊行為,可讓您獲得沒有這種卡頓的動畫。Animated 有一個名為 useNativeDriver 的標誌,它會在動畫開始時將您的動畫定義從 JavaScript 傳送到原生,從而允許原生端處理動畫的更新,而無需在每個影格都來回 JavaScript。useNativeDriver 的缺點是您只能更新一組特定的屬性,主要是 transformopacity。您無法使用 useNativeDriver 為背景顏色等項目製作動畫,至少目前還不行 — 我們會隨著時間的推移新增更多項目,當然,您始終可以為您的專案提交您需要的屬性的 PR,造福整個社群 😀。

由於我們希望此動畫流暢,我們將在這些約束條件下工作。若要更深入了解 useNativeDriver 在底層的工作方式,請查看我們的 宣布它的部落格文章

分解我們的動畫

我們的動畫有 4 個元件

  1. 放大小鳥,顯示應用程式和純白色圖層
  2. 淡入應用程式
  3. 縮小應用程式
  4. 完成後隱藏白色圖層和藍色圖層

使用 Animated,有兩種主要方法可以定義動畫。第一種是使用 Animated.timing,它可以讓您準確地說出動畫將執行多長時間,以及平滑運動的緩和曲線。另一種方法是使用基於物理的 API,例如 Animated.spring。使用 Animated.spring,您可以指定彈簧中的摩擦力和張力等參數,並讓物理定律運行您的動畫。

我們有多個動畫要同時運行,這些動畫都彼此密切相關。例如,我們希望應用程式在遮罩正在中間顯示時開始淡入。由於這些動畫密切相關,我們將使用具有單個 Animated.ValueAnimated.timing

Animated.Value 是 Animated 用於了解動畫狀態的原生值的包裝器。對於完整的動畫,您通常只想擁有其中一個。大多數使用 Animated 的元件都會將值儲存在狀態中。

由於我將此動畫視為在完整動畫的不同時間點發生的步驟,因此我們的 Animated.Value 將從 0 開始,表示完成 0%,並將值結束於 100,表示完成 100%。

我們的初始元件狀態將如下所示。

state = {
loadingProgress: new Animated.Value(0),
};

當我們準備好開始動畫時,我們告訴 Animated 將此值動畫化為 100。

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true, // This is important!
}).start();

然後,我嘗試粗略估計動畫的不同部分以及我希望它們在整體動畫的不同階段具有的值。以下是動畫的不同部分的表格,以及我認為它們在我們隨著時間推移的不同點上的值。

Twitter 小鳥遮罩應從比例 1 開始,並且在向上射擊之前會變小。因此,在動畫進行到 10% 時,它的比例值應為 0.8,然後在結束時向上射擊到比例 70。老實說,選擇 70 非常隨意,它需要足夠大,以便小鳥完全顯示螢幕,而 60 不夠大 😀。不過,關於這部分有趣的是,數字越高,看起來成長速度就越快,因為它必須在相同的時間內到達那裡。這個數字經過一些反覆試驗才能使此標誌看起來不錯。不同大小的標誌/裝置將需要不同的結束比例,以確保顯示整個螢幕。

應用程式應保持不透明一段時間,至少在 Twitter 標誌變小期間。根據官方動畫,我希望在小鳥處於中間向上縮放時開始顯示它,並在相當快地完全顯示它。因此,在 15% 時我們開始顯示它,在整體動畫的 30% 時它完全可見。

應用程式比例從 1.1 開始,並在動畫結束時縮小到其正常比例。

現在,在程式碼中。

我們基本上在上面做的是將動畫進度百分比的值對應到各個部分的值。我們使用 Animated 和 .interpolate 來做到這一點。我們使用基於 this.state.loadingProgress 的內插值,為動畫的每個部分建立 3 個不同的樣式物件。

const loadingProgress = this.state.loadingProgress;

const opacityClearToVisible = {
opacity: loadingProgress.interpolate({
inputRange: [0, 15, 30],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
// clamp means when the input is 30-100, output should stay at 1
}),
};

const imageScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 10, 100],
outputRange: [1, 0.8, 70],
}),
},
],
};

const appScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 100],
outputRange: [1.1, 1],
}),
},
],
};

現在我們有了這些樣式物件,我們可以在 Render 先前文章中的視圖片段時使用它們。請注意,只有 Animated.ViewAnimated.TextAnimated.Image 才能使用使用 Animated.Value 的樣式物件。

const fullScreenBlueLayer = (
<View style={styles.fullScreenBlueLayer} />
);
const fullScreenWhiteLayer = (
<View style={styles.fullScreenWhiteLayer} />
);

return (
<View style={styles.fullScreen}>
{fullScreenBlueLayer}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Animated.Image
style={[styles.maskImageStyle, imageScale]}
source={twitterLogo}
/>
</View>
}>
{fullScreenWhiteLayer}
<Animated.View
style={[opacityClearToVisible, appScale, {flex: 1}]}>
{this.props.children}
</Animated.View>
</MaskedViewIOS>
</View>
);

耶!我們現在讓動畫片段看起來像我們想要的那樣。現在我們只需要清理我們的藍色和白色圖層,它們永遠不會再被看到。

為了知道我們何時可以清理它們,我們需要知道動畫何時完成。幸運的是,在我們呼叫 Animated.timing 的地方,.start 接受一個可選的回呼,該回呼在動畫完成時運行。

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true,
}).start(() => {
this.setState({
animationDone: true,
});
});

現在我們在 state 中有一個值來知道我們是否已完成動畫,我們可以修改我們的藍色和白色圖層以使用它。

const fullScreenBlueLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenBlueLayer]} />
);
const fullScreenWhiteLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenWhiteLayer]} />
);

瞧!我們的動畫現在可以運作了,並且我們在動畫完成後清理了我們未使用的圖層。我們已經建構了 Twitter 應用程式載入動畫!

但是等等,我的無法運作!

親愛的讀者,別擔心。我也討厭指南只提供您程式碼片段,而不提供您完整的原始碼。

此元件已發布到 npm,並在 GitHub 上以 react-native-mask-loader 的形式提供。若要在您的手機上試用,它在 Expo 上可用

更多閱讀/額外加分

  1. 這本 gitbook 是在您閱讀 React Native 文件後,進一步了解 Animated 的絕佳資源。
  2. 實際的 Twitter 動畫似乎在接近尾聲時加快了遮罩顯示速度。嘗試修改載入器以使用不同的緩和函數(或彈簧!)以更好地匹配該行為。
  3. 目前的遮罩結束比例是硬式編碼的,並且可能無法在平板電腦上顯示整個應用程式。根據螢幕尺寸和影像尺寸計算結束比例將是一個很棒的 PR。

React Native 每月精選 #6

·4 分鐘閱讀
Tomislav Tenodi
Speck 創辦人

React Native 每月會議仍在如火如荼地進行中!請務必查看本文底部的註解,以了解下一次會議。

Expo

  • 恭喜 Devin AbbottHoussein Djirdeh 預先發布了「Full Stack React Native」書籍!它引導您透過建構幾個小型應用程式來學習 React Native。
  • 發布了 reason-react-native-scripts 的第一個(實驗性)版本,以幫助人們輕鬆試用 ReasonML
  • Expo SDK 24 已發布!它使用 React Native 0.51,並包含許多新功能和改進:在獨立應用程式中捆綁圖片(無需在首次載入時快取!)、圖像處理 API(裁剪、調整大小、旋轉、翻轉)、臉部偵測 API、新的發布管道功能(為給定管道設定活動發布和回滾)、用於追蹤獨立應用程式建置的 Web 儀表板,以及修復了 OpenGL Android 實作和 Android 多工處理器的長期錯誤,僅舉幾例。
  • 我們將從今年一月開始為 React Navigation 分配更多資源。我們堅信,僅使用 React 組件和基本元件(如 Animated 和 react-native-gesture-handler)來構建 React Native 導航是可行且理想的,並且我們對已計劃的一些改進感到非常興奮。如果您希望為社群做出貢獻,請查看 react-native-mapsreact-native-svg,它們都需要一些幫助!

Infinite Red

Microsoft

  • 已啟動一個 pull request,將核心 React Native Windows 橋接遷移到 .NET Standard,使其有效地與作業系統無關。希望許多其他 .NET Core 平台可以使用自己的執行緒模型、JavaScript 運行時和 UIManager(例如 JavaScriptCore、Xamarin.Mac、Linux Gtk# 和 Samsung Tizen 選項)擴展此橋接。

Wix

  • Detox
    • 為了讓我們能夠擴展 E2E 測試,我們希望盡可能減少在 CI 上花費的時間,我們正在為 Detox 開發平行化支援。
    • 提交了一個 pull request,以啟用對自訂 flavor 建置的支援,以便更好地支援 E2E 上的模擬。
  • DetoxInstruments
    • 開發 DetoxInstruments 的殺手級功能被證明是一項非常具有挑戰性的任務,在任何給定時間取得 JavaScript 回溯需要自訂的 JSCore 實作來支援 JS 執行緒暫停。在 Wix 的應用程式上內部測試效能分析器揭示了關於 JS 執行緒的有趣見解。
    • 該專案仍然不夠穩定,無法供一般使用,但正在積極開發中,我們希望很快宣布它。
  • React Native Navigation
    • V2 的開發速度已大幅提高,到目前為止,我們只有 1 位開發人員以 20% 的時間投入,現在我們有 3 位開發人員全職投入!
  • Android 效能
    • 將 RN 中捆綁的舊 JSCore 替換為其最新版本(webkitGTK 專案的 tip,具有自訂 JIT 配置)使 JS 執行緒的效能提高了 40%。下一步是編譯其 64 位元版本。這項工作基於 Android 的 JSC 建置腳本。在此處追蹤其目前狀態 here

下次會議

最近有人討論重新安排本次會議的用途,以討論單一且特定的主題(例如導航、將 React Native 模組移至單獨的 repo、文件...)。這樣我們覺得我們可以為 React Native 社群做出最大的貢獻。這可能會在下次會議中進行。歡迎在 Twitter 上發布您希望看到涵蓋的主題。