JSPatch 是 iOS 平臺(tái)上的一個(gè)開(kāi)源庫(kù),只需接入極小的三個(gè)引擎文件,即可以用 JavaScript 調(diào)用和替換任意 Objective-C 方法,也就是說(shuō)可以在 App 上線后通過(guò)下發(fā) JavaScript 腳本,實(shí)時(shí)修改任意 Objective-C 方法的實(shí)現(xiàn),達(dá)到修復(fù) Bug 或動(dòng)態(tài)運(yùn)營(yíng)的目的。目前 JSPatch 被大規(guī)模應(yīng)用于熱修復(fù)(Hotfix),已有超過(guò) 2500 個(gè) App 接入。
雖然 JSPatch 目前大部分只用于熱修復(fù),但因?yàn)槠淇梢哉{(diào)用任意 Objective-C 方法,實(shí)際上它也可以做熱更新的工作,也就是動(dòng)態(tài)為 App 添加功能模塊,并對(duì)這些功能模塊進(jìn)行實(shí)時(shí)更新,可以起到跟 React Native 一樣的作用。我們從學(xué)習(xí)成本、接入成本、開(kāi)發(fā)效率、熱更新能力和性能體驗(yàn)這幾個(gè)方面來(lái)對(duì)比一下使用 React Native 和 JSPatch 做熱更新的差異。
學(xué)習(xí)成本
React Native 是從 Web 前端開(kāi)發(fā)框架 React 延伸出來(lái)的解決方案,主要解決的問(wèn)題是 Web 頁(yè)面在移動(dòng)端性能低的問(wèn)題,React Native 讓開(kāi)發(fā)者可以像開(kāi)發(fā) Web 頁(yè)面那樣用 React 的方式開(kāi)發(fā)功能,同時(shí)框架會(huì)通過(guò) JavaScript 與 Objective-C 的通信讓界面使用原生組件渲染,讓開(kāi)發(fā)出來(lái)的功能擁有原生APP的性能和體驗(yàn)。
這里會(huì)有一個(gè)學(xué)習(xí)成本的問(wèn)題,大部分 iOS 開(kāi)發(fā)者并不熟悉 Web 前端開(kāi)發(fā),當(dāng)他們需要一個(gè)動(dòng)態(tài)化的方案去開(kāi)發(fā)一個(gè)功能模塊時(shí),若使用 React Native,就意味著他需要學(xué)習(xí) Web 前端的一整套開(kāi)發(fā)技能,學(xué)習(xí)成本很高。所以目前一些使用 React Native 的團(tuán)隊(duì)里,這部分功能的開(kāi)發(fā)是由前端開(kāi)發(fā)者負(fù)責(zé),而非終端開(kāi)發(fā)者。
但前端開(kāi)發(fā)者負(fù)責(zé)這部分功能也會(huì)有一些學(xué)習(xí)成本的問(wèn)題,因?yàn)?React Native 還未十分成熟,出了 Bug 或有性能問(wèn)題需要深入 React Native 客戶端代碼去排查和優(yōu)化。也就是說(shuō) React Native 是跨 Web 前端開(kāi)發(fā)和終端開(kāi)發(fā)的技術(shù),要求使用者同時(shí)有這兩方面能力才能使用得當(dāng),這不可避免地帶來(lái)學(xué)習(xí)成本的提高。
而 JSPatch 是從終端開(kāi)發(fā)出發(fā)的一種方案,JSPatch 寫(xiě)出來(lái)的代碼風(fēng)格與 Objective-C 原生開(kāi)發(fā)一致,使用者不需要有 Web 前端的知識(shí)和經(jīng)驗(yàn),只需要有 iOS 開(kāi)發(fā)經(jīng)驗(yàn),再加上一點(diǎn) JavaScript 語(yǔ)法的了解,就可以很好地使用,對(duì)終端開(kāi)發(fā)來(lái)說(shuō)學(xué)習(xí)成本很低。
可以看一下同樣實(shí)現(xiàn)一個(gè)簡(jiǎn)單的界面,React Native 和 JSPatch 代碼的對(duì)比:
//React Native
class HelloWorld extends Component {
render() {
return (
<View style={styles.btnArea}>
<View style={styles.btnWrapper}>
<TouchableHighlight underlayColor="#ED5F37” onPress={this.login}
activeOpacity={0.7}>
<Text style={styles.btn}>登錄</Text>
</TouchableHighlight>
</View>
</View>
);
}
login(){
};
}
var styles = StyleSheet.create({
btnArea: {
justifyContent: 'center',
marginLeft: 20,
marginRight: 20,
marginTop: 100,
flexDirection: 'row',
},
btnWrapper: {
backgroundColor: '#FC6E50',
borderRadius: 5,
flex: 1
},
btn: {
paddingTop: 10,
paddingBottom: 10,
color: '#ffffff',
textAlign: 'center',
},
});
//JSPatch
require('UIColor, UIScreen, UIButton')
defineClass('HelloWord : UIView', {
initWithFrame: function(frame) {
if(self = super.initWithFrame(frame)){
var screenWidth = UIScreen.mainScreen().bounds().width
var loginBtn = UIButton.alloc().initWithFrame({x: 20, y: 50, width: screenWidth - 40, height: 30});
loginBtn.setBackgroundColor(UIColor.greenColor())
loginBtn.setTitle_forState("Login", 0)
loginBtn.layer().setCornerRadius(5)
loginBtn.addTarget_action_forControlEvents(self, 'handleBtn', 1<<6);
self.addSubview(loginBtn);
}
return self;
},
handleBtn: function() {
}
})
接入成本
接入成本上,React Native 是比較大的框架,據(jù)統(tǒng)計(jì)目前核心代碼里Objective-C 和 JavaScript 代碼加起來(lái)有 4w 行,接入后安裝包體積增大 1.8M 左右。而 JSPatch 是微型框架,只有 3 個(gè)文件 2k 行代碼,接入后增大 100K 左右。另外 React Native 需要搭建一套開(kāi)發(fā)環(huán)境,有很多依賴的庫(kù),環(huán)境的搭建是一個(gè)痛點(diǎn)。而 JSPatch 無(wú)需搭建環(huán)境,只需拖入三個(gè)文件到工程中即可使用。
React Native 是大框架,維護(hù)起來(lái)成本也會(huì)增大,在性能調(diào)優(yōu)和 Bug 查找時(shí),必須深入了解整個(gè)框架的原理和執(zhí)行流程,此外 React Native 目前還未達(dá)到穩(wěn)定狀態(tài),升級(jí)時(shí)踩坑不可避免。相對(duì)來(lái)說(shuō) JSPatch 接入后的維護(hù)成本會(huì)低一些,因?yàn)?JSPatch 只是作為很薄的一層轉(zhuǎn)接口,沒(méi)有太多規(guī)則和框架,也就沒(méi)有太多坑,本身代碼量小,需要深入了解去調(diào)試 Bug 或性能調(diào)優(yōu)時(shí)成本也低。
開(kāi)發(fā)效率
在 UI 層上目前 HTML + CSS 的方式開(kāi)發(fā)效率是比手寫(xiě)布局高的,React Native 也是用近似 HTML+CSS 去繪制 UI,這方面開(kāi)發(fā)效率相對(duì) JSPatch 會(huì)高一些,但 JSPatch 也可以借助 iOS 一些成熟的庫(kù)去提高效率,例如使用 Masonry,讓 UI 的開(kāi)發(fā)效率不會(huì)相差太多。邏輯層方面的開(kāi)發(fā)效率雙方是一樣的。
此外,React Native 在開(kāi)發(fā)效率上的另一個(gè)優(yōu)勢(shì)是支持跨平臺(tái),React Native 本意是復(fù)用邏輯層代碼,UI 層根據(jù)不同平臺(tái)寫(xiě)不同的代碼,但 UI 層目前也可以通過(guò) ReactMix 之類的工具做到跨平臺(tái),所以UI層和邏輯層代碼都能得到一定程度的復(fù)用。而 JSPatch 目前只能用于 iOS 平臺(tái),沒(méi)有跨平臺(tái)能力。
實(shí)際上跨平臺(tái)有它適用和不適用的場(chǎng)景,跨平臺(tái)有它的代價(jià),就是需要兼顧每個(gè)平臺(tái)的特性,導(dǎo)致效果不佳。
跨平臺(tái)典型的適用場(chǎng)景是電商活動(dòng)頁(yè)面,以展示為主,重開(kāi)發(fā)效率輕交互體驗(yàn),但不適用于功能性的模塊。對(duì) Android 來(lái)說(shuō)目前熱更新方案十分成熟,Android 十分自由,可以直接用原生開(kāi)發(fā)后生成 diff 包下發(fā)運(yùn)行,這種無(wú)論是開(kāi)發(fā)效率和效果都是最好的。所以若是重體驗(yàn)的功能模塊,Android 使用原生的熱更新方案,iOS 使用 JSPatch 開(kāi)發(fā),會(huì)更適合。
JSPatch 也做了一些事情嘗試提高開(kāi)發(fā)效率,例如做了 Xcode 代碼提示插件 JSPatchX,讓用 JavaScript 調(diào)用 Objective-C 代碼時(shí)會(huì)出現(xiàn)代碼提示,另外跟 React Native 一樣有開(kāi)發(fā)時(shí)可以實(shí)時(shí)刷新界面查看修改效果的功能,目前仍在繼續(xù)做一些措施和工具提高開(kāi)發(fā)效率。
熱更新能力
React Native 和 JSPatch 都能對(duì)用其開(kāi)發(fā)出來(lái)的功能模塊進(jìn)行熱更新,這也是這種方案最大的好處。不過(guò) React Native 在熱更新時(shí)無(wú)法使用事先沒(méi)有做過(guò)橋接的原生組件,例如需要加一個(gè)發(fā)送短信功能,需要用到原生 MessageUI.framework 的接口,若沒(méi)有在編譯時(shí)加上提供給 JavaScript 的接口,是無(wú)法調(diào)用到的。而 JSPatch 可以調(diào)用到任意已在項(xiàng)目里的組件,以及任意原生 framework 接口,不需要事先做橋接,在熱更新的能力上,相對(duì)來(lái)說(shuō) JSPatch 的能力和自由度會(huì)更高一些。
性能體驗(yàn)
使用 React Native 和 JSPatch 性能上會(huì)比原生差點(diǎn),但都能得到比純 H5 頁(yè)面或 hybrid 更好的性能和體驗(yàn)。
JSPatch 的性能問(wèn)題主要在于 JavaScript 和 Objective-C 的通信,每次調(diào)用 Objective-C 方法都要通過(guò) Objective-C Runtime 接口,并進(jìn)行參數(shù)轉(zhuǎn)換。runtime 接口調(diào)用帶來(lái)的耗時(shí)一般不會(huì)成為瓶頸,參數(shù)轉(zhuǎn)換則需要注意避免在 JavaScript 和 Objective-C 之間傳遞大的數(shù)據(jù)集合對(duì)象。JSPatch 在性能方面也針對(duì)開(kāi)發(fā)功能做了不少優(yōu)化,盡力減少了 JavaScript 和 Objective-C 的通信,GitHub 項(xiàng)目主頁(yè)上有完整的小 App Demo,目前來(lái)看并沒(méi)有碰到太多性能問(wèn)題。
React Native 的性能問(wèn)題會(huì)復(fù)雜一些,因?yàn)榭蚣鼙旧淼哪K初始化/React 組件初始化/JavaScript 渲染邏輯等會(huì)消耗不少時(shí)間和內(nèi)存,這些地方若使用或優(yōu)化不當(dāng)都會(huì)對(duì)性能和體驗(yàn)造成影響。JavaScript 和 Objective-C 的通信也是一個(gè)耗性能的點(diǎn),不過(guò)這點(diǎn)上 React Native 優(yōu)化得比較好,沒(méi)有成為主要消耗點(diǎn)。
在性能和體驗(yàn)問(wèn)題上,兩者有不同的性能消耗點(diǎn),從最終效果來(lái)看兩者差別不大。
總結(jié)

總的來(lái)說(shuō),JSPatch在學(xué)習(xí)成本、接入成本、熱更新能力上占優(yōu),而 React Native 在開(kāi)發(fā)效率和跨平臺(tái)能力上占優(yōu)(見(jiàn)表 1),大家可以根據(jù)需求的不同選用不同的熱更新方案。JSPatch 目前仍在不斷發(fā)展中,后續(xù)會(huì)致力于提高開(kāi)發(fā)效率,完善周邊支持,歡迎參與這個(gè)開(kāi)源項(xiàng)目的開(kāi)發(fā)。