此系列文章將整合我的 React 視頻教程與 React Native 書籍中的精華部分,給大家介紹 React Native 源碼學習方法及其他資源。
最后的章節給大家介紹 React Native 源碼的查閱方法,以便你進行更加高階的開發與研究時參閱,並分享了開發過程中可能遇到的眾多問題的解決方案,以及與 React Native 開發相關、本書相關的一些線上資源。
15.6 React Native 源碼剖析
我們在學習了 React Native 開發的方方面面之后,我們再次回到 React Native 的本質,給大家簡要介紹 React Native 源碼的學習方法,對 React Native 源碼的整體框架做一個簡單介紹,后續如果大家想深入閱讀 React Native 框架的源碼,希望這部分對你有所幫助,並且能從源碼中學習到復雜框架的設計思想,希望大家也能“造出復雜的輪子”。
React Native 項目的 GitHub 地址為:https://github.com/facebook/react-native,源碼的基本結構如圖 A-1 所示。
圖 A-1 React Native 源碼結構
- 根目錄中主要包含了項目的一些配置文件和一些描述性文檔;
- 初始化項目的 React Native CLI 定義在 react-native-cli 文件夾下;
- RNTester 文件夾包含了 React Native 項目的單元測試用例以及組件、API 的使用示例代碼,是一個學習 React Native 組件與 API 使用方法的寶庫,這個在之前的章節有過介紹;
- React 文件夾是 iOS 原生平台的項目文件夾,用於與 React Native 的 JavaScript 代碼通信;
- ReactAndroid 文件夾是 Android 原生平台的項目文件夾,用於與 React Native 的 JavaScript 代碼通信;
- babel-preset 文件夾是 React Native 項目的 Babel 預配置;
- Libraries 文件夾是 React Native 源碼的核心,所有的 React Native 組件與 API 的實現都在此文件夾中。
接下來我們就隨便找一個組件來看看 React Native 是如何進行實現的,假設我們就來看看 Alert 組件的實現,其實通過我們在 React Native 與原生平台混合開發章節的學習,我們已經大概知道了 React Native 是如何來實現的。
我們先來看 Alert 組件 JavaScript 端的實現,Alert 組件包含的文件如圖 A-2 所示。
圖 A-2 Alert 組件源碼結構
源碼在 https://github.com/facebook/react-native/blob/master/Libraries/Alert/Alert.js。
1. ......
2. class Alert {
3.
4. /**
5. * Launches an alert dialog with the specified title and message.
6. *
7. * See http://facebook.github.io/react-native/docs/alert.html#alert
8. */
9. static alert(
10. title: ?string,
11. message?: ?string,
12. buttons?: Buttons,
13. options?: Options,
14. type?: AlertType,
15. ): void {
16. if (Platform.OS === 'ios') {
17. if (typeof type !== 'undefined') {
18. console.warn('Alert.alert() with a 5th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.');
19. AlertIOS.alert(title, message, buttons, type);
20. return;
21. }
22. AlertIOS.alert(title, message, buttons);
23. } else if (Platform.OS === 'android') {
24. AlertAndroid.alert(title, message, buttons, options);
25. }
26. }
27. }
28.
29. /**
30. * Wrapper around the Android native module.
31. */
32. class AlertAndroid {
33.
34. static alert(
35. title: ?string,
36. message?: ?string,
37. buttons?: Buttons,
38. options?: Options,
39. ): void {
40. var config = {
41. title: title || '',
42. message: message || '',
43. };
44.
45. if (options) {
46. config = {...config, cancelable: options.cancelable};
47. }
48. // At most three buttons (neutral, negative, positive). Ignore rest.
49. // The text 'OK' should be probably localized. iOS Alert does that in native.
50. var validButtons: Buttons = buttons ? buttons.slice(0, 3) : [{text: 'OK'}];
51. var buttonPositive = validButtons.pop();
52. var buttonNegative = validButtons.pop();
53. var buttonNeutral = validButtons.pop();
54. if (buttonNeutral) {
55. config = {...config, buttonNeutral: buttonNeutral.text || '' };
56. }
57. if (buttonNegative) {
58. config = {...config, buttonNegative: buttonNegative.text || '' };
59. }
60. if (buttonPositive) {
61. config = {...config, buttonPositive: buttonPositive.text || '' };
62. }
63. NativeModules.DialogManagerAndroid.showAlert(
64. config,
65. (errorMessage) => console.warn(errorMessage),
66. (action, buttonKey) => {
67. if (action === NativeModules.DialogManagerAndroid.buttonClicked) {
68. if (buttonKey === NativeModules.DialogManagerAndroid.buttonNeutral) {
69. buttonNeutral.onPress && buttonNeutral.onPress();
70. } else if (buttonKey === NativeModules.DialogManagerAndroid.buttonNegative) {
71. buttonNegative.onPress && buttonNegative.onPress();
72. } else if (buttonKey === NativeModules.DialogManagerAndroid.buttonPositive) {
73. buttonPositive.onPress && buttonPositive.onPress();
74. }
75. } else if (action === NativeModules.DialogManagerAndroid.dismissed) {
76. options && options.onDismiss && options.onDismiss();
77. }
78. }
79. );
80. }
81. }
82.
83. module.exports = Alert;
84. ......
此段代碼省略了頭部的相關內容,在代碼的第 16 行通過 Platform 變量判斷當前所運行的平台,如果是 iOS 平台,那么就調用 AlertIOS 文件中定義的代碼,如果是 Android 平台就調用代碼第 32 行定義的 AlertAndroid 用於實現對 Android 原生平台 Alert 的調用,注意代碼的第 63 行,是不是和我們實戰 React Native 與 Android 平台混合開發的實現一樣?所以 React Native 的所有組件與 API 基本都是通過此種方法進行了封裝后提供給了開發者,所以我們可以說 iOS 原生平台與 Android 原生平台具備的功能都可以通過封裝后在 React Native 框架中使用。
對應的 Android 原生端的實現代碼在:https://github.com/facebook/react-native/blob/26684cf3adf4094eb6c405d345a75bf8c7c0bf88/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java。
86. ......
87. @ReactMethod
88. public void showAlert(
89. ReadableMap options,
90. Callback errorCallback,
91. final Callback actionCallback) {
92. final FragmentManagerHelper fragmentManagerHelper = getFragmentManagerHelper();
93. if (fragmentManagerHelper == null) {
94. errorCallback.invoke("Tried to show an alert while not attached to an Activity");
95. return;
96. }
97.
98. final Bundle args = new Bundle();
99. if (options.hasKey(KEY_TITLE)) {
100. args.putString(AlertFragment.ARG_TITLE, options.getString(KEY_TITLE));
101. }
102. if (options.hasKey(KEY_MESSAGE)) {
103. args.putString(AlertFragment.ARG_MESSAGE, options.getString(KEY_MESSAGE));
104. }
105. if (options.hasKey(KEY_BUTTON_POSITIVE)) {
106. args.putString(AlertFragment.ARG_BUTTON_POSITIVE, options.getString(KEY_BUTTON_POSITIVE));
107. }
108. if (options.hasKey(KEY_BUTTON_NEGATIVE)) {
109. args.putString(AlertFragment.ARG_BUTTON_NEGATIVE, options.getString(KEY_BUTTON_NEGATIVE));
110. }
111. if (options.hasKey(KEY_BUTTON_NEUTRAL)) {
112. args.putString(AlertFragment.ARG_BUTTON_NEUTRAL, options.getString(KEY_BUTTON_NEUTRAL));
113. }
114. if (options.hasKey(KEY_ITEMS)) {
115. ReadableArray items = options.getArray(KEY_ITEMS);
116. CharSequence[] itemsArray = new CharSequence[items.size()];
117. for (int i = 0; i < items.size(); i ++) {
118. itemsArray[i] = items.getString(i);
119. }
120. args.putCharSequenceArray(AlertFragment.ARG_ITEMS, itemsArray);
121. }
122. if (options.hasKey(KEY_CANCELABLE)) {
123. args.putBoolean(KEY_CANCELABLE, options.getBoolean(KEY_CANCELABLE));
124. }
125.
126. UiThreadUtil.runOnUiThread(new Runnable() {
127. @Override
128. public void run() {
129. fragmentManagerHelper.showNewAlert(mIsInForeground, args, actionCallback);
130. }
131. });
132.
133. }
134. ......
我們可以通過如上代碼看到整個 Android 端的 showAlert 實現完全就是我們平時進行 Android 原生開發的代碼實現,而通過 React Native 的封裝之后,可以輕松讓開發者在前端通過 JavaScript 的代碼調用原生平台的方法,還可以直接適配兩個平台,這樣的框架設計的確有魅力,源碼也值得好好閱讀。
以上主要是給大家把 React Native 源碼的基本結構告訴大家,空閑時間大家可以多去閱讀 React Native 的實現源碼,希望能對你 React Native 的學習再多一些幫助。
關於源碼學習過程中的任何問題都可以在本書的線上資源站點找到我的聯系方式和我交流。
15.7 難題解決方法與 Issues 重要作用
任何開發語言的學習,即使相關的書籍講解得再詳細,也不能完全覆蓋你在開發過程中遇到的種種問題,所以我們需要掌握一些查找疑難問題的基本方案。
關於大家在學習本書進行 React Native 開發的過程中,有幾個建議遵循的原則與查找問題的方案供大家參考。
1. 不要糾結於 React Native 的版本問題
很多時候我們在學習時糾結於 React Native 版本更新后,自己已學習的知識是否會落后,從而頻繁地在安裝最新版本的 React Native 框架、以及解決新版本與老的學習代碼沖突上浪費太多的時間。其實很多的前端框架的更新都比較激進,React 基本實現了兩周版本一更新,而每次的版本升級肯定會導致和你既有的項目代碼有稍許沖突的地方,而如果你花大量地時間去解決這些沖突沒有太大的意義。
所以一般的建議是你固定一個版本的 React Native 進行學習,因為版本的更新一般都很小,你只需要專注於框架的使用學習,盡快通過代碼實戰掌握框架的基本使用,后期可以認真研究框架的底層實現原理,而后期的版本更新基本都不會離開你已掌握的框架知識,更不會與你理解的實現原理有太大出入。
2. 單個平台進行問題定位
React Native 的開發因為涉及到 iOS 平台與 Android 平台的適配,有時一個問題可能影響到了兩個平台的表現,這時應該逐個平台突破,而不是兩個平台來一起調試,反而會造成代碼的邏輯混亂,如果需要在代碼上強制分離邏輯調試,可以通過 Platform 變量判斷當前是運行在哪個平台,進而編寫特定的平台代碼進行分離調試。
3. 善用官方的 Issues
React Native 因為源碼就發布在 GitHub 上,所以你可以直接在 GitHub 項目頁面上查找開發過程中遇到的問題,在 Issues 頁面中已包含了近萬個問題,基本上你使用過程中遇到的問題肯定有別人遇到過,所以要學會直接在 Issues 中查找問題的原因以及解決方案,實在找不到解決方案你還可以向 React Native 項目提交 Issue,並可以獲得 React Native 開發團隊的回復,我想應該沒有人比 React Native 開發團隊的人更了解 React Native 了吧,不過在提問前最好自己多動手查閱一遍所有的 Issues 是否已包含了你遇到的問題了。
React Native 的官方 Issues 地址為:https://github.com/facebook/react-native/issues,截圖如圖 A-3 所示。
圖 A-3 React Native 官方 Issues 頁面
15.8 書籍相關資源列表
-
本書配套源碼的 GitHub 地址
包含書籍中所有標注的完整代碼、代碼片段等,所有的章節代碼都進行了單獨文件夾存放,方便查閱,后續關於本書的相關更新也在此 GitHub 中更新。
地址:https://github.com/ParryQiu/ReactNative-Book-Demo -
React GitHub
地址:https://github.com/facebook/react/ -
React Native 官網
地址:https://facebook.github.io/react-native/ -
React Native GitHub
地址:https://github.com/facebook/react-native -
awesome-react-native GitHub
地址:https://github.com/jondot/awesome-react-native -
深入理解 React JS 中的 setState
地址:http://blog.parryqiu.com/2017/12/19/react_set_state_asynchronously/ -
從源碼的角度再看 React JS 中的 setState
地址:http://blog.parryqiu.com/2017/12/29/react-state-in-sourcecode/ -
從源碼的角度看 React JS 中批量更新 State 的策略(上)
地址:http://blog.parryqiu.com/2018/01/04/2018-01-04/ -
從源碼的角度看 React JS 中批量更新 State 的策略(下)
地址:http://blog.parryqiu.com/2018/01/08/2018-01-08/ -
Node.js 官網
地址:https://nodejs.org -
npm 官網
地址:https://www.npmjs.com/ -
Node.js 下載頁面
地址:https://nodejs.org/en/download/ -
Homebrew 官網
地址:https://brew.sh/ -
官方 UI 示例 App
地址:https://github.com/facebook/react-native/tree/master/RNTester -
react-native-elements
地址:https://github.com/react-native-training/react-native-elements -
react-native-tab-navigator
地址:https://github.com/happypancake/react-native-tab-navigator -
react-native-navigation
地址:https://github.com/wix/react-native-navigation -
react-native-keychain
地址:https://github.com/oblador/react-native-keychain -
react-native-sensitive-info
地址:https://github.com/mCodex/react-native-sensitive-info -
react-native-image-picker
地址:https://github.com/react-community/react-native-image-picker -
Fetch API 文檔
地址:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch -
Awesome React Native
地址:https://github.com/jondot/awesome-react-native -
react-native-open-share
地址:https://github.com/ParryQiu/react-native-open-share -
新浪微博開放平台
地址:http://open.weibo.com/ -
微信開放平台
地址:https://open.weixin.qq.com/ -
QQ 開放平台
地址:http://open.qq.com/ -
React-Virgin
地址:https://github.com/Trixieapp/react-virgin -
react-native-pathjs-charts
地址:https://github.com/capitalone/react-native-pathjs-charts -
react-native-gifted-listview
地址:https://github.com/FaridSafi/react-native-gifted-listview -
react-native-vector-icons
地址:https://github.com/oblador/react-native-vector-icons -
React Native metro
地址:https://github.com/facebook/metro -
Genymotion
地址:https://www.genymotion.com/ -
極光推送官網
地址:https://www.jiguang.cn/ -
jpush-react-native
地址:https://github.com/jpush/jpush-react-native -
極光推送 iOS 證書設置向導
地址:https://docs.jiguang.cn/jpush/client/iOS/ios_cer_guide/ -
Ape Tools
地址:http://apetools.webprofusion.com/tools/imagegorilla -
App 圖標生成工具
https://makeappicon.com/
http://ios.hvims.com/
https://romannurik.github.io/AndroidAssetStudio/ -
react-native-code-push
地址:https://github.com/Microsoft/react-native-code-push -
React Native Issues
地址:https://github.com/facebook/react-native/issues -
以上的所有鏈接匯總頁面
如果你覺得需要查閱以上的鏈接,手動在瀏覽器中輸入太麻煩,你可以直接訪問本書的線上所有鏈接匯總站點,在此站點中你可以看到以上的所有鏈接以及鏈接說明,直接點擊即可訪問、查閱,希望能幫助大家提高學習效率。
地址:http://rn.parryqiu.com