咱們都知道React Navite在開發的時候,須要在React Native根目錄下運行react-native run-ios(或run-android)
,或者在Xcode中運行原生iOS項目(對於Android則是在Android Studio中運行原生Android項目),而後在對應的React Native根目錄下運行npm start
(開啓nodejs服務,開啓JS Server)。javascript
一、梳理react-native run-ios(android)的完整流程,並識別ios和android的區別。
二、理解debug下ios的詭異現象,在沒開JS Server時,有時加載條爲何會變成load pre bundle
並能正常運行,有時爲何閃崩。
三、幫助不懂原生的朋友快速進入code狀態,不要每次都等待react-native run-ios(android)
四、最後的黑魔法,在團隊協做下,寫js的能夠根本不須要iOS和Android環境(固然,這須要原生開發夥伴的幫助),原生也不須要裝nodejs。java
react-native run-ios
:
在控制檯能夠看到輸出:node
> $ react-native run-ios
Found Xcode project LayoutDemo.xcodeproj
We couldn't boot your defined simulator due to an already booted simulator. We are limited to one simulator lau
nched at a time.
Launching iPhone 6 (iOS 10.3)...
Building using "xcodebuild -project LayoutDemo.xcodeproj -configuration Debug -scheme LayoutDemo -destination i
d=BB4E36F2-D6B3-447F-91E9-8D1F5B56022E -derivedDataPath build"複製代碼
一、使用xcodebuild來編譯項目
在輸出中能夠看到React Native首先是尋找Xcode project(尋找方式後面會說明),而後使用 xcodebuild來編譯項目。你能夠想象就是用xcode打開項目,而後按運行,同樣的效果。
二、定義了打bundle包的腳本
在編譯參數中設置了React Native的編譯腳本(能夠在project.pbxproj中查看到react-native-xcode.sh腳本的設置),目錄是在:/node_modules/react-native/packager/react-native-xcode.sh。react-native-xcode.sh
:react
case "$CONFIGURATION" in
Debug) //在Debug模式下
# Speed up build times by skipping the creation of the offline package for debug
# builds on the simulator since the packager is supposed to be running anyways.
if [[ "$PLATFORM_NAME" == *simulator ]]; then
echo "Skipping bundling for Simulator platform" //使用模擬器跑時不打bundle包,退出sh腳本
exit 0;
fi
DEV=true //設置DEV爲true
;;
"")
echo "$0 must be invoked by Xcode"
exit 1
;;
*)
DEV=false //設置DEV爲false
;;
esac
...
# Xcode project file for React Native apps is located in ios/ subfolder
cd ${REACT_NATIVE_DIR}/../.. //進入React Native根目錄
...
if [[ "$CONFIGURATION" = "Debug" && ! "$PLATFORM_NAME" == *simulator ]]; then
...
//若是在Debug環境在,而且不是由模擬器運行則對localhost和ip.txt中的內容設置ATS爲容許http(由於node.js服務是開在http://localhost:8081上的)
NSAppTransportSecurity:NSExceptionDomains:localhost:NSTemporaryExceptionAllowsInsecureHTTPLoads bool true" "$PLIST"
$PLISTBUDDY -c "Add NSAppTransportSecurity:NSExceptionDomains:$IP.xip.io:NSTemporaryExceptionAllowsInsecureHTTPLoads bool true" "$PLIST"
echo "$IP.xip.io" > "$DEST/ip.txt"
fi
...
BUNDLE_FILE="$DEST/main.jsbundle" //設置輸出main.jsbundle的目錄
//運行react-native下的bundle命令,至關於本身運行`react-native bundle`
$NODE_BINARY "$REACT_NATIVE_DIR/local-cli/cli.js" bundle \
--entry-file "$ENTRY_FILE" \
--platform ios \
--dev $DEV \
--reset-cache \
--bundle-output "$BUNDLE_FILE" \
--assets-dest "$DEST"複製代碼
從腳本能看出在Debug模式下,不會爲模擬器打bundle包,可是會爲真機打bundle包,這也就是爲何咱們在真機調試後,而後斷開nodejs服務(不能打開remote debug js模式,否則會閃崩),從新進入應用時也會正確的加載bundle,屏幕上方會出現'load pre bundle'的字樣,而且是黑色的背景條,若是是加載nodejs服務器的則會是綠色的背景條並出現加載進度。可是當咱們在模擬器上作一樣的操做時,好比先正常打開nodejs服務器加載調試,而後在從新打開應用,它加載不到bundle會崩潰。
當咱們設置Rlease模式時,必須用xcode編譯纔會打bundle包,此時不管是在模擬器仍是在真機運行,都會打bundle包。
輸出以下:android
/Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Intermediates/LayoutDemo.build/Debug-iphoneos/LayoutDemo.build/Script-00DD1BFF1BD5951E006B06BC.sh
+DEST=/Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/Debug-iphoneos/LayoutDemo.app
+ [[ Debug = \D\e\b\u\g ]]
+ [[ ! iphoneos == *simulator ]]
+ PLISTBUDDY=/usr/libexec/PlistBuddy
+PLIST=/Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/Debug-iphoneos/LayoutDemo.app/Info.plist
++ ipconfig getifaddr en0
+ IP=192.168.16.111
+ '[' -z 192.168.16.111 ']'
+ /usr/libexec/PlistBuddy -c 'Add NSAppTransportSecurity:NSExceptionDomains:localhost:NSTemporaryExceptionAllow
sInsecureHTTPLoads bool true' /Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/Debug
-iphoneos/LayoutDemo.app/Info.plist
+ /usr/libexec/PlistBuddy -c 'Add NSAppTransportSecurity:NSExceptionDomains:192.168.16.111.xip.io:NSTemporaryEx
ceptionAllowsInsecureHTTPLoads bool true' /Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Pr
oducts/Debug-iphoneos/LayoutDemo.app/Info.plist
+ echo 192.168.16.111.xip.io
+BUNDLE_FILE=/Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/Debug-iphoneos/LayoutDemo.app/main.jsbundle
+ node /Users/lyxia/Documents/ios/React_Native/LayoutDemo/node_modules/react-native/local-cli/cli.js bundle --entry-file index.ios.js --platform ios --dev true --reset-cache --bundle-output/Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/Debug-iphoneos/LayoutDemo.app/main.jsbundle --assets-dest /Users/ly
xia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/Debug-iphoneos/LayoutDemo.app
[2017-04-01 11:26:20] <START> Initializing Packager
[2017-04-01 11:26:21] <START> Building Haste Map
[2017-04-01 11:26:21] <END> Building Haste Map (575ms)
[2017-04-01 11:26:21] <END> Initializing Packager (1644ms)
[2017-04-01 11:26:21] <START> Transforming files
Warning: The transform cache was reset.
[2017-04-01 11:26:40] <END> Transforming files (18750ms)
bundle: start
bundle: finish
bundle: Writing bundle output to: /Users/lyxia/Documents/ios/React_Native/LayoutDemo/ios/build/Build/Products/D
ebug-iphoneos/LayoutDemo.app/main.jsbundle
bundle: Copying 5 asset files
bundle: Done writing bundle output
bundle: Done copying assets
+ [[ ! -n true ]]複製代碼
三、自動彈出一個框來啓動nodejs服務
在../node_modules/react-native/React/React.xcodeproj/project.pbxproj
文件中咱們能夠看到PBXShellScriptBuildPhase section
中定義了Start Packager
的shell執行,最終會執行到../node_modules/react-native/packager/launchPackager.command
。一路追溯,能夠看到最後執行到node "$THIS_DIR/../local-cli/cli.js" start "$@"
也就是咱們經常使用的npm start
。ios
總結:因此如今咱們整理一下這整個流程:shell
react-native init <項目名>
生成的項目中,project.pbxproj
裏面會添加生成bundle的Bundle React Native code and images
編譯參數,因此咱們在真機完成調試後,nodejs關了,也能去加載本地的bundle,由於它已經幫咱們打包好了。React
的project.pbxproj
中添加了Start Packager
的編譯參數,因此它會判斷是否已經開啓nodejs服務,若是沒有則會幫咱們開啓。組合:按需求組合使用
這個流程使咱們只要運行react-native run-ios
就能夠編譯項目,開啓nodejs服務。
所以咱們能夠換個方向來想,若是咱們是在原生的iOS項目中接入RN,那應該怎麼作呢?
只需用Xcode運行原生項目,而後npm start
便可。固然咱們也能夠在編譯參數中加入Bundle React Native code and images
讓它每次執行爲咱們自動打包最新的bundle,這樣當咱們調試好後,就算關了nodejs服務,也能繼續運行。npm
問題:如何尋找.xcodeproj文件
在../node_modules/react-native/local-cli/runIOS/runIOS.js
裏面能夠找到react-native run-ios
的實現,而且有各類參數的舉例說明:react-native
{
desc: 'Pass a non-standard location of iOS directory',
cmd: 'react-native run-ios --project-path "./app/ios"',
}
...
{
command: '--project-path [string]',
description: 'Path relative to project root where the Xcode project '
+ '(.xcodeproj) lives. The default is \'ios\'.',
default: 'ios',
}複製代碼
能夠看到默認是在\ios.目錄下尋找以.xcworkspace
或者.xcodeproj
結尾的文件,不過可使用--project-path [string]
來指定文件目錄。xcode
一樣咱們在控制檯運行react-native run-android
,看輸出:
> $ react-native run-android
Starting JS server...
Running /Users/lyxia/Documents/Android/adt-bundle-mac-x86_64-20140702/sdk//platform-tools/adb -s C
oolpad5890-a1778fd9 reverse tcp:8081 tcp:8081
Building and installing the app on the device (cd android && ./gradlew installDebug)...
Starting a new Gradle Daemon for this build (subsequent builds will be faster).
> Configuring > 1/2 projects > :app > Compiling script into cache^C
:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:checkDebugManifest
:app:preReleaseBuild UP-TO-DATE
:app:prepareComAndroidSupportAppcompatV72301Library
:app:prepareComAndroidSupportRecyclerviewV72301Library
:app:prepareComAndroidSupportSupportV42321Library複製代碼
是的,Android和咱們的iOS寶寶是不同的,他一開始就去查看JS Server是否開啓,若是沒開他就會去開啓,而後使用gradle來編譯項目並安裝。
能夠在../node_modules/react-native/local-cli/runAndroid/runAndroid.js
中查看react-native run-android
的實現,這裏不貼出來了。
注意點
一、在Release下,會打包bundle:if (args.configuration.toUpperCase() === 'RELEASE') { console.log(chalk.bold( 'Generating the bundle for the release build...' )); child_process.execSync( 'react-native bundle ' + '--platform android ' + '--dev false ' + '--entry-file index.android.js ' + `--bundle-output ${androidProjectDir}/app/src/main/assets/index.android.bundle ` + `--assets-dest ${androidProjectDir}/app/src/main/res/`, { stdio: [process.stdin, process.stdout, process.stderr], } );複製代碼
二、如何驗證android項目是否存在:
在React Native根目錄下尋找android/gradlew
,是的,你沒看錯,全程不可配,乖乖放好路徑吧,否則就使用Android Studio運行,而後執行npm start
。
總結:整理react-native run-android的完整流程:
條理比iOS清晰許多,由於都寫在一個文件中。
相同點:
不一樣點:
--project-path
修改。而Android不能夠指定項目路徑,若是/android/gradlew不存在,則不可使用react-native run-android
react-native run-ios --device
),也會咱們打包bundle,可是Android不會。問題1:
我需不須要天天開機都react-native run-ios(android)
回答:
在沒有更改原生代碼的狀況下,是不須要的,只要設備或者是模擬器上安裝了應用,只須要npm start
開啓JS Server便可,讓應用能加載到nodejs服務上的js bundle便可。
問題2:
若是我電腦上沒有Android和iOS環境,能不能調試RN項目。
回答:
能夠跑,只須要手機上已經裝好RN的Debug版的項目,而且電腦開啓了JS Server。Android機須要在開發者選項裏把ip和端口改爲開了JS Server的ip和端口便可。iOS就要麻煩些了,須要在原生添加ip.txt文件,而後指定ip爲開了JS Server的ip,重編,便可(這裏就須要有Xcode了)。
問題3:
可不可使用1個JS Server同時調試iOS和Android項目。
回答:
能夠的。
歡迎在評論區提出問題和錯誤。
簡書同步更新地址:www.jianshu.com/u/b92ab7b3a…