1. 程式人生 > >《React Native 精解與實戰》書籍連載「React Native 原始碼學習方法及其他資源」

《React Native 精解與實戰》書籍連載「React Native 原始碼學習方法及其他資源」

此係列文章將整合我的 React 視訊教程與 React Native 書籍中的精華部分,給大家介紹 React Native 原始碼學習方法及其他資源。

最後的章節給大家介紹 React Native 原始碼的查閱方法,以便你進行更加高階的開發與研究時參閱,並分享了開發過程中可能遇到的眾多問題的解決方案,以及與 React Native 開發相關、本書相關的一些線上資源。

15.6 React Native 原始碼剖析

我們在學習了 React Native 開發的方方面面之後,我們再次回到 React Native 的本質,給大家簡要介紹 React Native 原始碼的學習方法,對 React Native 原始碼的整體框架做一個簡單介紹,後續如果大家想深入閱讀 React Native 框架的原始碼,希望這部分對你有所幫助,並且能從原始碼中學習到複雜框架的設計思想,希望大家也能“造出複雜的輪子”。

截圖 圖 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 元件原始碼結構

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 框架中使用。

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 是否已包含了你遇到的問題了。

截圖 圖 A-3 React Native 官方 Issues 頁面

15.8 書籍相關資源列表

  1. 本書配套原始碼的 GitHub 地址 包含書籍中所有標註的完整程式碼、程式碼片段等,所有的章節程式碼都進行了單獨資料夾存放,方便查閱,後續關於本書的相關更新也在此 GitHub 中更新。 地址:https://github.com/ParryQiu/ReactNative-Book-Demo

  2. React GitHub 地址:https://github.com/facebook/react/

  3. React Native 官網 地址:https://facebook.github.io/react-native/

  4. React Native GitHub 地址:https://github.com/facebook/react-native

  5. awesome-react-native GitHub 地址:https://github.com/jondot/awesome-react-native

  6. 深入理解 React JS 中的 setState 地址:http://blog.parryqiu.com/2017/12/19/react_set_state_asynchronously/

  7. 從原始碼的角度再看 React JS 中的 setState 地址:http://blog.parryqiu.com/2017/12/29/react-state-in-sourcecode/

  8. 從原始碼的角度看 React JS 中批量更新 State 的策略(上) 地址:http://blog.parryqiu.com/2018/01/04/2018-01-04/

  9. 從原始碼的角度看 React JS 中批量更新 State 的策略(下) 地址:http://blog.parryqiu.com/2018/01/08/2018-01-08/

  10. Node.js 官網 地址:https://nodejs.org

  11. npm 官網 地址:https://www.npmjs.com/

  12. Node.js 下載頁面 地址:https://nodejs.org/en/download/

  13. Homebrew 官網 地址:https://brew.sh/

  14. 官方 UI 示例 App 地址:https://github.com/facebook/react-native/tree/master/RNTester

  15. react-native-elements 地址:https://github.com/react-native-training/react-native-elements

  16. react-native-tab-navigator 地址:https://github.com/happypancake/react-native-tab-navigator

  17. react-native-navigation 地址:https://github.com/wix/react-native-navigation

  18. react-native-keychain 地址:https://github.com/oblador/react-native-keychain

  19. react-native-sensitive-info 地址:https://github.com/mCodex/react-native-sensitive-info

  20. react-native-image-picker 地址:https://github.com/react-community/react-native-image-picker

  21. Fetch API 文件 地址:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

  22. Awesome React Native 地址:https://github.com/jondot/awesome-react-native

  23. react-native-open-share 地址:https://github.com/ParryQiu/react-native-open-share

  24. 新浪微博開放平臺 地址:http://open.weibo.com/

  25. 微信開放平臺 地址:https://open.weixin.qq.com/

  26. QQ 開放平臺 地址:http://open.qq.com/

  27. React-Virgin 地址:https://github.com/Trixieapp/react-virgin

  28. react-native-pathjs-charts 地址:https://github.com/capitalone/react-native-pathjs-charts

  29. react-native-gifted-listview 地址:https://github.com/FaridSafi/react-native-gifted-listview

  30. react-native-vector-icons 地址:https://github.com/oblador/react-native-vector-icons

  31. React Native metro 地址:https://github.com/facebook/metro

  32. Genymotion 地址:https://www.genymotion.com/

  33. 極光推送官網 地址:https://www.jiguang.cn/

  34. jpush-react-native 地址:https://github.com/jpush/jpush-react-native

  35. 極光推送 iOS 證書設定嚮導 地址:https://docs.jiguang.cn/jpush/client/iOS/ios_cer_guide/

  36. Ape Tools 地址:http://apetools.webprofusion.com/tools/imagegorilla

  37. App 圖示生成工具 https://makeappicon.com/ http://ios.hvims.com/ https://romannurik.github.io/AndroidAssetStudio/

  38. react-native-code-push 地址:https://github.com/Microsoft/react-native-code-push

  39. React Native Issues 地址:https://github.com/facebook/react-native/issues

  40. 以上的所有連結彙總頁面 如果你覺得需要查閱以上的連結,手動在瀏覽器中輸入太麻煩,你可以直接訪問本書的線上所有連結彙總站點,在此站點中你可以看到以上的所有連結以及連結說明,直接點選即可訪問、查閱,希望能幫助大家提高學習效率。 地址:http://rn.parryqiu.com