React Native

跨平台App開發培訓課程

簡報: https://goo.gl/HPpyka

Wifi SSID: trunk-studio

Wifi 密碼 8 個 0

上課環境:ftp://192.168.23.1

 

  • 基本介紹
  • 特色、優缺點
  • 其他類似的框架
  • RN 環境安裝、介紹

About Me

 

  • 傅耀德 (Dan、FuD、FuYaoDe)

    • 創科資訊全端工程師
    • 專注於 Node 生態體系
  • 教學 / 經歷
    • 經營 React Natvie Taiwan 社群
    • 2017 第 8 屆iT 邦鐵人賽 佳作
    • GitHub 1800 星星  react-native-app-intro 專案作者
    • 2017 HelloJS 講師 - React Native
    • 2016 Trunk Studio - 前端開發 React / Redux 起手式
    • 2016 HelloJS 講師 - React Native

簡單介紹一下

React Native 是什麼?

  • 2013 年夏天 Facebook 內部駭客松的 Project。
  • 2015 年 1 月 React.js Conf 發表, 2015 年 5 月正式發佈,當時只有 iOS 版本,2015 年 9 月 Android 才正式支援。
  • 現在已經能開發
    • iOS
    • Android
    • Apple TV
  • 用 JavaScript 撰寫真正的原生 App,不是所謂的 Mobile Web App、HTML5 App、 Hybrid App

特色

跨平台

寫一份程式碼
即可達成跨平台 App 開發建置與維護。
  • iOS
  • Android
  • Apple TV

高效能

效能與使用體驗接近原生開發

快速除錯

開發時可以快速的更新 UI

不用重新編譯

熟悉的開發方式

與 Web 前端相同的 REST API 存取後端資料服務。

只要上架一次

CodePush

免送審更新程式之機制

更新週期快速

每個月更新一版

活躍的社群

相較其他跨平台框架

使用人數更多

完整的文件

缺點

開發環境需求高

需要了解 React 

其他類似框架

  • Codova, AngularJS, Sass

  • 骨子裡還是網頁

  • 官方 market 有賣 theme、pulgin

  • 適合有網頁開發底子,要簡單 MVP 的產品

  • Angular, TypeScript, JavaScript

  • 適合熟悉 Angular 的開發者

  • 也有官方的 Market

  • 可擴展現有 APP

  • Vue 版本 preview 中

  • 適合熟悉物件導向的程式語言開發者的垮平台解決方案

  • 分為社區版、專業版、企業版

  • 最近剛發佈的 Google 跨平台 APP 解決方案

  • 使用 Dart 語言開發

  • Beta 中,還在觀望

PhoneGap

  • Html, CSS, JavaScript

那有誰在用?

Trunk Studio

環境設定

 

  • Android Studio @ 3.0+
  • JDK @ 1.8+
  • Node.js @ 8.9.x
  • python2
  • Xcode (Mac Only)
  • Homebrew (Mac Only)

基本系統環境

install NVM

Node

Node Version Manager, 管理 Node 的工具

 

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash

設定環境變數 (~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc)

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
source ~/.bashrc

install NodeJs

nvm install 8

Android SDK

React Native 目前需要 Android Studio2.0 或更高版本。

開發環境

SDK Platforms 選擇 Google APIs 、 Android SDK Platform 23 

Android 環境變數

確保 ANDROID_HOME 環境變數跟 SDK 路徑一致。

 

Windows
控制台 > 系統及安全性 > 進階系統設定 > 進階 > 環境變數 > 新增

控制台 > 系統及安全性 > 進階系統設定 > 進階 > 環境變數 > 選擇 PATH > 編輯 在後方新增

;C:\Users\使用者名稱\AppData\Local\Android\sdk\platform-tools;C:\Users

\使用者名稱\AppData\Local\Android\sdk\tools

Mac 開起 ~/.bashrc 或 ~/.zshrc 或 ~/.profile 在最後加上

export ANDROID_HOME=${HOME}/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools

Android 虛擬機

Xcode

  • 從 App Store 安裝 Xcode​新增虛擬機
  • 安裝 Xcode Command Line Tools, Xcode > ToolBar > Locations > Preferences ​
  • xcode-select -p,確認是否有安裝完成,安裝完成會回傳 /Applications/Xcode.app/Contents/Developer
  • brew install watchman

開發環境

ESLint

npm install --save-dev babel-eslint eslint-config-airbnb-base
eslint-plugin-react eslint-plugin-react-native
node_modules/.bin/eslint --init

選擇 style

? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Airbnb
? Do you use React? Yes
? What format do you want your config file to be in? JavaScript

ESLint

常見 Code Style 提醒

// bad
var count = 1;
if (true) {
  count += 1;
}

// good, use the let.
let count = 1;
if (true) {
  count += 1;
}
// bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';

免環境快速上手

Create React Native App

npm install -g create-react-native-app
create-react-native-app AwesomeProject
cd AwesomeProject
npm start

Amazon 版本 CRNA

RN Playground

網頁版淺嚐 RN

Expo

上課環境設置

環境使用教學影片 注意有先後順序之分

  • 開啟 Genymotion
  • 開啟 Genymotion 當中的 Android 虛擬機
  • Custom Phone - 7.1.0 - API 25 - 768x1280,等待開機完成
  • 在 本機 電腦執行命令提示字元執行指令

 

 

  • 開啟 VirtualBox 當中的 ReactNative 虛擬機
  • ReactNative 虛擬機出現 login 字樣,開啟網頁 http://localhost:9083/ide.html
  • 到 ReactNative 虛擬機網頁當中的 terminal 視窗輸入指令
     
$ adb devices
$ adb tcpip 5556
$ adb connect $ip:5556
/* $ip 請自行替換為 adb devices 回傳的 Android 虛擬機 IP
例如: adb connect 192.168.57.101:5556 */
$ adb devices
/* 測試是否有連上 */
$ adb shell am start -a android.settings.SETTINGS

React Native 專案環境

CLI

$ npm install -g react-native-cli

開新專案

$ react-native init demo
$ cd demo
$ npm start
$ react-native run-android

react-native init

  • 新增乾淨的專案
  • 使用模板新增專案

專案模板

react-native init ${project name} --template ${template name}

react-native init ${project name} --template file:///path_to/react-native-template-XXXX

react-native init ${project name} --template git://.../react-native-template-XXXX

定義模板

  • 放入模板的架構
  • 定義 package.json,使用 npm init,能快速產生 package.json
  • 模板命名遵守命名規則 react-native-template- 開頭
  • 發佈到 npm、git serve

定義模板優點

  • 程式碼共享,避免程式私有化
  • 減少不必要的重工
  • 快速開發 React native module
  • 新人能快速上手新專案

npm start

  • 開啟 Bundle server
  • Bundle server 負責把 JavaScaript 程式碼轉為 Native Code
  • 負責接收、傳送 Debug 訊息

react-native run-android

react-native run-ios

  • 負責安裝開發模式的 react native app 至專案
  • 出現 BUILD SUCCEEDED 字樣代表完成
  • Android 編譯過 dev apk 後可直接使用下列指令安裝 apk 節省時間
adb install ./android/app/build/outputs/apk/app-debug.apk

React Native 架構

React Native 底層架構

專案結構

.
├── App.js
├── __tests__
├── android
├── app.json
├── index.js
├── ios
├── node_modules
├── package.json
└── yarn.lock

App 名稱位置 :

./android/app/src/main/res/values/strings.xml

 ./android/app/src/main/res/mipmap-*

App icon:

進入點

除錯

開啟開發人員選單

  • iOS simulator: Cmd + D
  • iOS device: 搖晃手機
  • Android emulator: Cmd + M
  • Android Devices: 搖晃手機

除錯選單功能

與後端交互

Fetch

  1. 跟 Server 獲取、提交資料的方法

  2. 提供了和 web 標準一至的 Fetch API

  3. 安全機制與網頁不同,在 RN 中沒有 CROS 的限制

異步操作,使用 await/async 

async function getMoviesFromApi() {
  try {
    let response = await fetch(
      'https://facebook.github.io/react-native/movies.json'
    );
    let responseJson = await response.json();
    return responseJson.movies;
  } catch (error) {
    console.error(error);
  }
}
 getMoviesFromApiAsync() {
    return fetch('https://facebook.github.io/react-native/movies.json')
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson.movies;
      })
      .catch((error) => {
        console.error(error);
      });
  }

異步操作,返回 Promise

使用 Fetch 時機

  • componentWillMount ?
  • componentDidMount ?

iOS 設定例外網域

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>rn.fuyaode.me</key>
    <dict>
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <true/>
    </dict>
    <key>localhost</key>
    <dict>
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <true/>
    </dict>
  </dict>
</dict>

Timeout

NetInfo

handleConnectivityChange = async (isConnected) => {
 ....
}

NetInfo.addEventListener(
  'connectionChange',
  this.handleConnectivityChange
);

Android 記得加上權限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Fetch Debug

if (__DEV__) {
  GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest
}

Fetch 使用方法

getData = async (page) => {
  try {
    let response = await fetch(`http://rn.fuyaode.me/users/1`);
    let responseJson = await response.json();
    console.log(responseJson);
    this.setState({
      name: responseJson.name
    })
    return responseJson;
  } catch (e) {
    console.error(e);
  }
}

Get

fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue',
  }),
});

Post

const formData = new FormData();
form.append('id', 'A123123123');
form.append('password', '0000');
fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'multipart/form-data',
  },
  body: formData,
});

Form

const formData = new FormData();
formData.append('file', {
  uri: filepath,
  type: `${fileType}/${ext}`,
  name: fileName,
})
fetch('https://s3.us-east-2.amazonaws.com/test.png', {
  method: 'POST',
  headers: {
    'Content-Type': 'multipart/form-data',
  },
  body: formData,
});

檔案上傳

檔案上傳

選擇圖片、影片

react-native-image-picker

cosnt options = {
  title: '選擇頭貼',
  storageOptions: {
    skipBackup: true,
    path: 'images'
  }
};
ImagePicker.showImagePicker(options, (response) => {
  if (response.didCancel) {
    console.log('User cancelled image picker');
  } else if (response.error) {
    console.log('ImagePicker Error: ', response.error);
  } else {
    let source = { uri: response.uri };
    this.setState({
      avatarSource: source
    });
  }
});

檔案上傳

影片縮圖

react-native-thumbnail


RNThumbnail.get(filepath).then((result) => {
  console.log(result.path);
})
<Image source={{uri : this.state.videoUri}} />

Image 原生元件支援顯示 Video 縮圖

檔案上傳

import { 
  AudioPlayer, 
  AudioRecorder,
  AudioUtils,
} from 'react-native-audio-player-recorder';

AudioRecorder.prepareRecordingAtPath(
  AudioUtils.DocumentDirectoryPath + '/record.aac', {
    SampleRate: 22050,
    Channels: 1,
    AudioQuality: 'Low',
    AudioEncoding: 'aac',
    AudioEncodingBitRate: 32000
  }
);

AudioRecorder.startRecording();

檔案上傳

檢查檔案大小

react-native-fs

import RNFS from 'react-native-fs';

const fileStat = await RNFS.stat(filepath);
if (fileStat.size < 1024 * 1024 * 50) {
   ...
} else {
  // 檔案超過限制大小
}

Websocket

WebSocket 使用方法

var ws = new WebSocket('ws://host.com/path');

ws.onopen = () => {
  // connection opened
  ws.send('something'); // send a message
};

ws.onmessage = (e) => {
  // a message was received
  console.log(e.data);
};

ws.onerror = (e) => {
  // an error occurred
  console.log(e.message);
};

ws.onclose = (e) => {
  // connection closed
  console.log(e.code, e.reason);
};

現代 APP 完全開發工作坊

By fuyaode

現代 APP 完全開發工作坊

  • 2,207