此文是我的出版書籍[《React Native 精解與實戰》](http://rn.parryqiu.com/)連載分享,此書由機械工業出版社出版,書中詳解了 React Native 框架底層原理、React Native 組件佈局、組件與 API 的介紹與代碼實戰,以及 React Native... ...
此文是我的出版書籍《React Native 精解與實戰》連載分享,此書由機械工業出版社出版,書中詳解了 React Native 框架底層原理、React Native 組件佈局、組件與 API 的介紹與代碼實戰,以及 React Native 與 iOS、Android 平臺的混合開發底層原理講解與代碼實戰演示,精選了大量實例代碼,方便讀者快速學習。
書籍還配套了視頻教程「80 節實戰課精通 React Native 開發」,此視頻課程建議配合書籍學習,書籍中原理性的東西講解的比較清晰,而視頻教程對於組件、API 等部分的代碼實戰開發講解比較直觀。
書籍相關所有資料請訪問:http://rn.parryqiu.com
本章將深入講解 React Native 的底層原理,萬丈高樓平地起,非常深入地理解 React Native 底層的實現,在你開發或遇到難題調試時非常有幫助。
此部分包含 React Native 的框架構成、工作原理、UI 層的渲染與重繪以及組件間通信、React Native 與各個平臺的通信實現以及 React Native 中的生命周期。
如果需要直接開始 React Native 的開發與實戰,請直接跳至第四章開始學習。
3.1 React Native 框架構成
React Native 框架內部已提供了很多的內置組件,如圖 3-1 所示。如 View、Text 等基本組件,用於一些功能佈局的 Button、Picker 等,用於列表展示的各種 List 組件和對應 iOS 平臺與 Android 平臺的特定組件、API 等。同時也提供了供編寫與原生平臺交互的介面,在後續的章節我們會進行與原生平臺的混合實戰開發實戰。
圖 3-1 React Native 框架構成
3.2 React Native 工作原理
在 React 框架介紹的章節,我們理解瞭如何將代碼渲染至虛擬 DOM 並更新到真實 DOM 的過程。在 React Native 框架中,渲染到 iOS 平臺與 Android 平臺的過程如圖 3-2 所示。
圖 3-2 React Native 渲染
在 React 框架中,JSX 源碼通過 React 框架最終渲染到了瀏覽器的真實 DOM 中,而在 React Native 框架中,JSX 源碼通過 React Native 框架編譯後,通過對應平臺的 Bridge 實現了與原生框架的通信。如果我們在程式中調用了 React Native 提供的 API,那麼 React Native 框架就通過 Bridge 調用原生框架中的方法。
因為 React Native 的底層為 React 框架,所以如果是 UI 層的變更,那麼就映射為虛擬 DOM 後進行 diff 演算法,diff 演算法計算出變動後的 JSON 映射文件,最終由 Native 層將此 JSON 文件映射渲染到原生 App 的頁面元素上,最終實現了在項目中只需要控制 state 以及 props 的變更來引起 iOS 與 Android 平臺的 UI 變更。
編寫的 React Native代碼最終會打包生成一個 main.bundle.js 文件供 App 載入,此文件可以在 App 設備本地,也可以存放於伺服器上供 App 下載更新,後續章節講解的熱更新就會涉及到 main.bundle.js 位置的設置問題。
3.3 React Native 與原生平臺通信
在與原生框架通信中,如圖 3-3 所示,React Native 採用了 JavaScriptCore 作為 JS VM,中間通過 JSON 文件與 Bridge 進行通信。而如果在使用 Chrome 瀏覽器進行調試時,那麼所有的 JavaScript 代碼都將運行在 Chrome 的 V8 引擎中,與原生代碼通過 WebSocket 進行通信。
圖 3-3 React Native 與原生平臺的通信
關於 React Native 框架與原生平臺的通信原理的詳細介紹,後續的混合開發章節將會有詳細的講解與實戰開發。
3.4 組件間通信
React Native 開發最基本的元素就是組件,React Native 與 React 一樣,也會涉及到組件之間的通信,用於數據在組件之間的傳遞,下麵列出了幾種常用的組件間通信的方式。
父子組件的通信
如同之前的章節介紹 React 組件間傳遞參數一樣,在 React Native 中,父組件向子組件傳遞值,可以通過 props 的形式。
在下例中,父組件通過調用子組件並賦值子組件的 name 為 React,子組件通過 this.props.name 獲取父組件傳遞過來的 name 的字元串值 React。
完整代碼在本書配套源碼的 03-04 文件夾。
/**
* 章節: 03-04
* 父子組件通信,在父組件中調用子組件
* FilePath: /03-04/parent-2-child.js
* @Parry
*/
<ChildComponent name='React'/>
/**
* 章節: 03-04
* 子組件實現,通過 props 獲取父頁面傳遞的值
* FilePath: /03-04/parent-2-child.js
* @Parry
*/
class ChildComponent extends Component {
render() {
return (
<Text>Hello {this.props.name}!</Text>
);
}
}
子父組件的通信
在開發過程中,不僅有父子之間的通信,有時還會有子組件向父組件通信傳遞值的需求,比如當子組件的某個值變更後,需要通知到父組件做相應的變更與響應,那麼就會需要子父組件之間的通信。
示例代碼如下,在父組件的定義中,在調用子組件時,同樣向子組件傳遞了一個參數,不過這個參數是一個函數,此函數用於接收後續子組件向父組件傳遞過來的數據,與之前父組件向子組件傳遞數據不太一樣。
完整代碼在本書配套源碼的 03-04 文件夾。
/**
* 章節: 03-04
* 子父組件通信,父組件的實現
* FilePath: /03-04/child-2-parent.js
* @Parry
*/
import React, {Component} from 'react';
import ChildComponent from './ChildComponent'
class App extends Component {
constructor(props) {
super(props)
this.state = {
name: 'React'
}
}
//傳遞到子組件的參數,不過參數是一個函數。
handleChangeName(nickName) {
this.setState({name: nickName})
}
render() {
return (
<div>
<p>父組件的 name:{this.state.name}</p>
<ChildComponent
onChange={(val) => {
this.handleChangeName(val)
}}/>
</div>
);
}
}
export default App;
下麵為子組件的定義,子組件在頁面中定義了一個按鈕,點擊此按鈕後,調用自身的一個函數 handleChange,修改了自身 state 中的值 name 為 nickName 定義的值 Parry,那麼此子組件的頁面上的字元串將由之前的 Hello React! 變為 Hello Parry!,同時使用了 this.props.changeName,也就是父組件調用時傳遞過來的函數,向父組件傳遞了 nickName 的值 Parry。
父組件在接收到子組件的調用後,調用了父組件自身的函數 handleChangeName 修改了自身的 state 中的 name 的值為 Parry,也就是子組件傳遞過來的 Parry,所以同時,父組件的頁面上的值也同時由之前的 React 變更成了 Parry。
/**
* 章節: 03-04
* 子父組件通信,子組件的實現
* FilePath: /03-04/child-2-parent.js
* @Parry
*/
import React, {Component} from 'react'
export default class ChildComponent extends Component {
constructor(props) {
super(props)
this.state = {
name: 'React'
}
}
handleChange() {
const nickName = 'Parry';
this.setState({name: nickName})
//調用父組件傳遞過來的函數參數,傳遞值到父組件去。
this
.props
.changeName(nickName)
}
render() {
const {name} = this.state;
return (
<div>
<p>Hello {name}!</p>
<Button
onPress={this
.handleChange
.bind(this)}
title="修改一下 name 為 Parry"/>
</div>
)
}
}
多級組件之間的通信
如果組件之間的父子層級非常多,需要進行組件之間的傳遞,這時候當然可以通過上面介紹的方法進行一級一級的傳遞,但是當這種組件間層級很深的時候,這樣的傳遞方法不是一個太好的方法。
解決的方法是首先要在設計 App 時,需要註意不能讓組件之間的層級關係太深,一是為了避免組件之間通信的冗長,還有一個原因是太深的嵌套邏輯,用戶體驗上也不會很好,可以想象一下用戶從最底層一層層操作返回到最頂層時的體驗。
第二就是可以使用如 context 對象或 global 等方式進行多級組件間的通信,但是這種方式不推薦。
無直接關係組件間通信
前面提到的都是有層級關係的組件間的通信方式,而如果組件間沒有層級的關係的話,可以通過如 AsyncStorage 或 JSON 文件等方式進行無直接關係組件間的通信。
當然,還可以使用 EventEmitter / EventTarget / EventDispatcher繼承或實現介面的方式、Signals 模式或 Publish / Subscribe 的廣播形式,都可以達到無直接關係組件間的通信。
這些組件間的通信方式使得組件之間的數據可以傳遞起來,後續的實戰章節會有詳細的代碼實現,這裡主要進行了理論部分的介紹。掌握這部分知識後才可以將 App 開發中的基本單位,也就是組件串聯起來。