ReactNative分包實踐
1. 實現思路
1. RN從本地中讀取bundle檔案進行顯示
2. 將JS檔案進行分包打包
3. Native實現頁面跳轉,每個包跳轉都為一個新的Activity
4. 進行bundle檔案基礎包與功能包的拆分,使用Google的diff_match_patch演算法生成差異檔案
5. 網路下載差異檔案進行合併
6. 展示新頁面
2. 操作步驟
Android
1. 修改MainApplication
2. 修改MainActivity
3. 建立LocalReactActivityDelegate
4. 打第一bundle
react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle/index.android.bundle --assets-dest bundle/assets/
5. 打第二個bundle
react-native bundle --platform android --dev false --entry-file src/indexNet.js --bundle-output bundle/index1.android.bundle --assets-dest bundle/assets/
其中indexNet.js是新頁面的入口檔案
6. 使用google-diff-match-patch進行差異比對
3. 本地載入實現
1). 修改MainApplication
public class MainApplication extends Application {
private static MainApplication sInstance;
/**
* 獲取當前物件
*/
public static MainApplication getInstance() {
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
SoLoader.init(this, /* native exopackage */ false);
}
}
這裡主要是將Application中實現的介面取消,轉由MainActivity中提供.
2). 修改MainActivity
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "Unpacking";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new LocalReactActivityDelegate(this, getMainComponentName());
}
}
這裡實現ReactActivity中的createReactActivityDelegate方法,自定義代理設定
3). LocalReactActivityDelegate代理程式碼
/**
* 本地ReactActivity代理
*/
class LocalReactActivityDelegate(activity: Activity, @Nullable mainComponentName: String) :
ReactActivityDelegate(activity, mainComponentName) {
private val mReactNativeHost: ReactNativeHost = object : ReactNativeHost(MainApplication.getInstance()) {
/**
* 返回ReactPackage物件
*/
override fun getPackages(): MutableList<ReactPackage> = Arrays.asList(
MainReactPackage(),
CustomPackage()
)
/**
* 是否為開發
*/
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
/**
* 返回JSBundle檔案路徑
*/
override fun getJSBundleFile(): String? = "${Environment.getExternalStorageDirectory()}/bundle/index.android.bundle"
}
/**
* 返回ReactNativeHost物件
*/
override fun getReactNativeHost(): ReactNativeHost = mReactNativeHost
}
4). Native實現頁面跳轉
I. 建立CustomPackage
/**
* 自定義ReactPackage
*/
class CustomPackage : ReactPackage {
/**
* 建立本地模組
*/
override fun createNativeModules(reactContext: ReactApplicationContext?): MutableList<NativeModule>
= Arrays.asList(PageModule(reactContext))
/**
* 建立檢視管理者
*/
override fun createViewManagers(reactContext: ReactApplicationContext?): MutableList<ViewManager<View, ReactShadowNode<*>>>
= Collections.emptyList()
}
5). 建立PageMoudle
/**
* 頁面管理模組
*/
@ReactModule(name = "PageModule")
class PageModule(reactContext: ReactApplicationContext?) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String = "PageModule"
/**
* 開啟網路上的模組頁面
*/
@ReactMethod
fun startNetActivity() {
val intent = Intent(currentActivity, NetActivity::class.java)
currentActivity?.startActivity(intent)
}
}
6). NetActivity頁面
/**
* 新頁面
*/
class NetActivity : ReactActivity() {
override fun getMainComponentName(): String? {
return "NetActivity"
}
override fun createReactActivityDelegate(): ReactActivityDelegate =
object: ReactActivityDelegate(this, mainComponentName) {
override fun getReactNativeHost(): ReactNativeHost = object : ReactNativeHost(MainApplication.getInstance()) {
override fun getPackages(): MutableList<ReactPackage> = Arrays.asList(MainReactPackage())
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override fun getJSBundleFile(): String? = "${Environment.getExternalStorageDirectory()}/bundle/index1.android.bundle"
}
}
}
7). RN中使用
修改App.js檔案
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, {Component} from `react`;
import {Platform, StyleSheet, Text, View, NativeModules, TouchableOpacity} from `react-native`;
const PageModule = NativeModules.PageModule;
const instructions = Platform.select({
ios: `Press Cmd+R to reload,
` + `Cmd+D or shake for dev menu`,
android:
`Double tap R on your keyboard to reload,
` +
`Shake or press menu button for dev menu`,
});
type Props = {};
export default class App extends Component<Props> {
startPage() {
PageModule.startNetActivity()
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
<TouchableOpacity
onPress={() => this.startPage()}
>
<Text style={styles.instructions}>開啟新頁面</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: `center`,
alignItems: `center`,
backgroundColor: `#F5FCFF`,
},
welcome: {
fontSize: 20,
textAlign: `center`,
margin: 10,
},
instructions: {
textAlign: `center`,
color: `#333333`,
marginBottom: 5,
},
});
8). 執行打包
react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle/index.android.bundle --assets-dest bundle/assets/
–platform: 平臺
–dev: 是否為開發模式
–entry-file: 入口檔案
–bundle-output: bundle輸出路徑,這裡注意bundle資料夾必須存在
–assets-dest: 資原始檔存放路徑
9). 測試
I. 生成之後將bundle資料夾拷貝至sdcard下
II. 如果想要在除錯時可用,務必修改android/app/build.gradle檔案,將bundleInDebug設定為false, 設定為false之後即不將bundle檔案打包進app
project.ext.react = [
entryFile : "index.js",
bundleInDebug: false
]
III. 執行
注:此時無法開啟新的頁面,因為新頁面的js檔案不存在
4. 開啟新頁面
1). RN中建立入口檔案indexNet.js
/** @format */
import {AppRegistry} from `react-native`;
import NetJs from `./NetJs`;
import {name as appName} from `./NetActivity`;
AppRegistry.registerComponent(appName, () => NetJs);
2). RN中建立NetActivity.json檔案
{
"name": "NetActivity",
"displayName": "NetActivity"
}
注:此處的name和displayName應當與NetActivity.kt中的getMainComponentName方法返回的一致。
3). NetJs.js檔案內容
import React, {PureComponent} from `react`;
import {
StyleSheet, Text,
View
} from `react-native`;
/**
* @FileName: NetJs
* @Author: mazaiting
* @Date: 2018/10/9
* @Description:
*/
class NetJs extends PureComponent {
render() {
return (
<View style={styles.container}>
<Text>Welcome mazaiting!</Text>
</View>
)
}
}
/**
* 樣式屬性
*/
const styles = StyleSheet.create({
container: {
backgroundColor: `#DDD`
}
});
/**
* 匯出當前Module
*/
module.exports = NetJs;
4). 打包
react-native bundle --platform android --dev false --entry-file src/indexNet.js --bundle-output bundle/index1.android.bundle --assets-dest bundle/assets/
5). 拷貝檔案
將bundle資料夾直接拷貝到sdcard目錄下,此時再重新執行APP, 即可成功顯示頁面。
6). 帶來的問題
初始包太大,每個包都講RN基礎內容打包,此時應該比較差異,進行差異化分解。
5. 差異化
1). diff-match-patch主頁
2). 測試頁面
3). 依賴
implementation `google-diff-match-patch:google-diff-match-patch:0.1`
4). 程式碼使用
const val FILE1 = "index.android"
const val FILE2 = "index1.android"
const val FILE3 = "$FILE2-$FILE1.patch"
const val FILE_DIR = "E:\android\React-Native-Study\Unpacking\bundle\"
const val SUFFIX = ".bundle"
object Patch {
@JvmStatic
fun main(args: Array<String>) {
val file1 = "$FILE_DIR$FILE1$SUFFIX"
val file2 = "$FILE_DIR$FILE2$SUFFIX"
val file3 = "$FILE_DIR$FILE3"
val patch = productPatch(file1, file2)
productFile(file1, file3, patch)
}
/**
* 生成檔案
* @param fileOri 原始檔
* @param fileDest 目標檔案
* @param patchString 差異字串
*/
private fun productFile(fileOri: String, fileDest: String, patchString: String?) {
// 建立物件
val patch = diff_match_patch()
// 讀取檔案
val file1 = readFile(fileOri)
// 獲取補丁內容
val patchText = patch.patch_fromText(patchString)
// 應用補丁
val patchApply = patch.patch_apply(LinkedList(patchText), file1)
println("===============================================")
println("結果:" + patchApply[0])
// 寫入檔案內容
writeResult(fileDest, patchApply[0].toString())
println("===============================================")
// 獲取執行結果陣列,true為成功,false為失敗
val patchResult: BooleanArray = patchApply[1] as BooleanArray
val result = StringBuilder()
patchResult.forEach { result.append("$it ") }
println("result: $result")
}
/**
* 寫入檔案
* @param fileDest 目標檔案
* @param string 檔案內容
*/
private fun writeResult(fileDest: String, string: String) {
// Read a file from disk and return the text contents.
val output = FileWriter(fileDest)
val writer = BufferedWriter(output)
try {
writer.write(string)
} finally {
writer.close()
output.close()
}
}
/**
* 生成差異patch
* @param fileOri 原始檔
* @param fileDest 目標檔案
* @return 差異字串
*/
private fun productPatch(fileOri: String, fileDest: String): String? {
// 建立物件
val diff = diff_match_patch()
// 讀取基礎檔案內容
val file1 = readFile(fileOri)
// 讀取目標檔案內容
val file2 = readFile(fileDest)
// 進行差異化
val diffString = diff.diff_main(file1, file2, true)
// 陣列長度大於2執行
if (diffString.size > 2) {
diff.diff_cleanupSemantic(diffString)
}
println(diffString)
println("===============================================")
// 生成patch內容
val patchString = diff.patch_make(file1, diffString)
println(patchString)
println("===============================================")
// 將patch內容轉為字串
val patchText = diff.patch_toText(patchString)
println(patchText)
return patchText
}
/**
* 讀取檔案
* @param filename 檔名
* @return 檔案內容
*/
@Throws(IOException::class)
private fun readFile(filename: String): String {
// Read a file from disk and return the text contents.
val sb = StringBuilder()
val input = FileReader(filename)
val bufRead = BufferedReader(input)
try {
var line = bufRead.readLine()
while (line != null) {
sb.append(line).append(`
`)
line = bufRead.readLine()
}
} finally {
bufRead.close()
input.close()
}
return sb.toString()
}
}
6. [程式碼下載] (https://gitee.com/mazaiting/React-Native-Study/tree/master/Unpacking)
相關文章
- ReactNative學習實踐:Navigator實踐React
- ReactNative TextInput 常用方法實踐React
- ReactNative專案實踐編碼規範React
- 京東購物小程式 | Taro3 專案分包實踐
- ReactNative 多端程式碼覆蓋率調研及實踐React
- 分包基礎概念+使用分包
- ReactNative實現ToastReactAST
- Multidex(分包)實現簡要分析IDE
- Mybatis實現分包定義資料庫MyBatis資料庫
- mpvue 分包方案Vue
- 轉轉:微信小程式分包載入實戰微信小程式
- Android 分包策略Android
- 微信小遊戲分包遊戲
- ReactNative實現地圖導航React地圖
- [譯]按功能(特性)分包
- 小程式分包(原生+mpvue)Vue
- 小程式分包載入
- LayaAir IDE如何實現快遊戲分包與載入AIIDE遊戲
- uniapp分包(詳盡版)APP
- swoole 之 tcp 合包分包TCP
- ReactNative入門React
- ONE-ReactNativeReact
- ReactNative Demo -SearchInputReact
- Socket 粘包和分包問題
- mpvue + iview 專案分包優化VueView優化
- 遊戲轉化率②:分包策略篇遊戲
- Netty--粘包與分包Netty
- 小程式分包的一些思考及Uiniapp 分包優化邏輯的驗證UIAPP優化
- ReactNative初體驗React
- ReactNative版友盟推送React
- ReactNative環境搭建React
- ReactNative之NavigatorReact
- ReactNative 生命週期React
- ReactNative仿《ONE》APPReactAPP
- ReactNative呼叫原生模組React
- ReactNative Demo - ImageVIew 使用ReactView
- ReactNative flex 佈局ReactFlex
- Weex & ReactNative & JSPatchReactJS