GIC
從0.3.0版本開始正式支持JavaScript
,也就意味你能夠直接使用JavaScript
來寫業務邏輯,至此開始,結合XML
、js文件
、圖片資源
等靜態文件,徹底能夠將整個的APP作成一個能夠熱更新的應用。另外,在開發的時候也能夠經過HotReload
的方式,無需編譯整個APP就能實時刷新應用,進一步的加快應用的開發效率。javascript
在最新的0.4.8
版本中,GIC
已經支持大部分的ES6特性,包括但不限於yield
、generator
、Promise
等等,而且也支持了ES8
中的async
、await
html
GIC
在最初的架構中根本就沒有考慮對JavaScript
提供支持,數據綁定
、事件綁定
等等功能通通都是爲native code
設計的。然後來當我考慮想要對JavaScript
進行支持的時候,也僅僅是經過behavior
來實現。事實上,若是你看過GIC
的源碼,你會發現GIC
能夠實現對任意腳本的支持,而實現的方式也僅僅只是經過自定義behavior
來實現。從這一點來講,側面反映了behavior
功能的強大之處。java
GIC
對於JavaScript
的支持是基於JavaScriptCore
這個蘋果官方framework
實現的,其實這個framework
原本就是開源的,並且仍是屬於大名鼎鼎的Webkit其中的模塊。再加上Webkit
的跨平臺能力,也就意味着只要是基於JavaScriptCore
開發的功能,一樣能夠移植到安卓上面,也就是意味着GIC
在對JavaScript
的支持上具有了跨平臺的潛力。node
首先各位要知道的是,iOS對於Es6
規範的支持程度在不一樣的iOS版本中是不一樣的,iOS8
對於ES6
是徹底不支持的,iOS9
部分支持,最新的iOS12
基本已經徹底支持了ES6
的規範。固然,這裏就不列出不一樣的iOS版本具體支持哪些ES6特性,你只須要知道,不一樣的iOS版本對於不一樣的ES6規範支持程度不一就好了。ios
然而咱們的app確定是要運行在低版本上的,那麼如何解決這個問題?git
事實上,GIC
自己並無提供原生的解決方案,雖然也嘗試過內置babel
來實現實時轉碼,可是後來發現性能太差,對於複雜的JS文件進行轉碼會耗費大量的CPU資源。所以就斷了內置babel
的想法。github
可是GIC
經過爲VSCode
開發插件的方法,曲線實現了對ES6
的完整支持,VSCode的插件(GICVSCodeExtension
)能夠一次性將整個工程的JS代碼編譯成ES5
的代碼。也就意味你能夠放心的在你的工程中編寫ES6
的代碼,而後經過VSCode進行編譯,進而使得你的JS代碼能夠運行在不一樣的iOS版本上。web
事實上,VSCode插件也是經過babel來轉碼的,而且GIC也自定義了babel插件。objective-c
你只須要經過GIC
提供的腳手架來建立項目就能得到這樣的能力,詳細的腳手架以及IDE支持的介紹能夠查看這篇文章。npm
在最新的0.4.8
版本中,新增了對yield
、generator
、Promise
、async
、await
的支持。
在GIC
中,打開一個新頁面至關於在瀏覽器中打開一個新頁面,頁面跟頁面之間並不能共享JavaScript
執行環境(JSContext
),每一個頁面都有一個獨立的sand box
(JSContext
)。所以,當一個頁面須要某個功能的時候你須要經過require
方法手動的將這個功能引入,每一個頁面都是如此,同一個功能若是在不一樣的頁面中使用,那麼意味着你須要在不一樣的頁面單獨引入。
當前require
的功能更多的相似Nodejs
的用法,若是你之前接觸過nodejs的話,那麼很容易理解。
其實,GIC
提供的require
函數相較於Nodejs
中的require
函數來講只是一個簡化版的實現,功能比較簡單。
使用module.exports
來導出,使用require
函數來導入,當前require
函數支持js
和json
文件的導入。
好比: 在a.js
中定義
class ClassA {
}
module.exports = ClassA;
複製代碼
在b.js
中引用
const ClassA = require('/js/a.js');
複製代碼
或者一次性導出多個,好比: 在a.js
中定義
class ClassA {
}
class ClassB extends ClassA {
}
class ClassC {
}
module.exports = { ClassA, ClassB, ClassC };
複製代碼
在b.js
中引用
const { ClassA, ClassB, ClassC } = require('/js/a.js');
複製代碼
以上用法,寫過node.js的都很容易明白。惟一的區別就是,GIC
中的路徑所有是絕對路徑。可是若是您是使用VSCode開發的話,那麼能夠藉助插件自動將相對路徑編譯成絕對路勁。
起初設計require
函數實現的時候並無參考nodejs
,可是後來發現功能實在太弱,沒法實現模塊化,而後又從新設計了require
的實現,可是神奇的是,等我寫完之後才發現,這他喵的不就是nodejs中的require
嘛!若是你用VSCode開發,那麼你會發現,當你使用require
導入JS文件後,VSCode居然也能準確的識別,而且提供了完整的智能提示
功能。
GIC
不支持npm包管理工具
,若是你想在工程中使用第三方庫的話只能直接將JS文件引入的方式來使用。拿axios
舉例
require('./axios.js', false);
複製代碼
axios.get('https://www.sojson.com/open/api/lunar/json.shtml')
.then((response) => {
console.log(JSON.stringify(response));
})
.catch(function (err) {
console.log(err);
});
複製代碼
其餘的第三方庫均可以採用這樣的方式引入。可是須要注意的是,第三方庫可能用到了GIC
自己不支持的API,這時候就須要你本身以JSAPI擴展
的方式來提供支持了。
很遺憾,當前因爲JavaScriptCore
的限制,GIC
並不具有JS
調試的能力。目前調試手段只能是經過console.log來追蹤,更進一步的是經過直接打印調用堆棧的方式來實現方法調用追蹤,可是調用堆棧的信息在JSContext
中並不詳細。
另外,你也能夠經過safari
來查看某個JSContext
對象,來查看JSContext
的內容。
目前的調試手段有限,而做者也在研究如何配合VSCode實現調試功能,不過目前進展不大。若是有哪位大能之士有方案的話還請告知下。
上面已經提過,GIC
對JavaScript
的支持,是經過JavaScriptCore
來實現的。具體一點是經過JSContext
、JSValue
來實現的。然而JavaScriptCore
自己提供的API是有限的,好比console
、XMLHttpRequest
、setTimeout
等API是沒有的,只能經過擴展來實現。GIC
已經提供了一些JavaScriptCore
所沒有的API,而其餘的API就須要你本身來實現了。
然而對於目前不少從npm安裝的庫來講,很遺憾,GIC
不支持。可是你可使用直接加載js文件的方式來引入您的工程,可是注意這些庫有可能用到了其餘的一些api,那麼這時候就須要你本身擴展實現這些API以便提供支持。
其實對於第三方庫的支持已經跟GIC
自己沒有關係了,徹底能夠經過擴展+後期編譯
的方式來實現支持。不過這樣的工程會比較大,你不可能作到對全部庫的支持,只能針對特定庫作有限的支持。
在實際的項目開發過程當中,免不了要自定義JSAPI的擴展,而實現JSAPI的擴展那麼你就回避不了對JSValue
的使用,可是JSValue
仍是有些地方須要注意的,不然會爲你的代碼埋下內存管理的隱患。
各位在看這篇博客以前若是沒有接觸過JavaScriptCore
相關的內容,我仍是建議各位先去了解下JavaScriptCore
,尤爲是裏面的JSContext
和JSValue
。
JSValue
的最大問題就在於內存管理的問題。
JS和OC在內存管理方面是不同的,JS的內存管理機制被叫作垃圾回收
,而OC的內存管理是基於引用計數
的,所以,這兩種語言若是想要實現互調,那麼必須得解決內存管理的問題。而JSValue
就是幹這個事的,可是千萬不要覺得只要使用了JSValue
就萬事無憂了。
JSValue
是OC的對象,須要遵循引用計數
的內存管理機制,而JSValue
指向的JS對象
倒是垃圾回收的,若是你在代碼中直接將JSValue
做爲變量保存下來那麼等待你的有可能就是循環引用。這時候爲了解決循環引用的問題,就須要JSManagedValue
出場了,JSManagedValue
專門爲了解決這個問題的,即能讓你擁有JS對象,也不會引發循環引用的問題,能夠理解爲OC對JSValue
的弱引用。
若是出現了循環引用,系統有可能報Attempting to release access but the mutator does not have access
這樣的錯誤,這時候App會直接崩潰。那麼這時候就須要檢查你的代碼中是否循環引用了JSValue
若是要在XML中直接寫JS代碼,這裏幾個概念須要注意的。具體參考文檔
RootDataContext
--根數據源,也便是你在一個頁面中第一個設置的數據源。好比:
<page title="Home">
<behaviors>
<script path="./Home.js" /></script>
</behaviors>
</page>
複製代碼
上面的JS代碼$el.dataContext = new Home();
就是爲page
元素設置數據源,而且這時候,RootDataContext
就指向Home的實例
。
在實際的開發過程當中你不會直接接觸RootDataContext
,而是經過事件
、綁定
等形式間接的接觸。你能夠在數據綁定的表達式
、事件表達式
中經過this指針
來訪問。好比:
<lable text="按鈕" event-tap="js:this.onClicked()"/>
複製代碼
這樣,在tap
事件中,綁定了一個JS回調,this.onClicked()
指向classHome
的onClicked
方法。
而有的時候你可能須要直接在事件回調中修改元素的屬性,那麼能夠經過$el
來訪問該元素自己,而後設置屬性。好比:
<lable text="按鈕" event-tap="js:$el.text = 'i clicked'"/>
複製代碼
這樣,當該lable
被點擊的時候,文本內容就會被修改爲i clicked
。
事實上,上面在爲
page
設置數據源的時候,就是經過$el
來設置的。
GIC
在最新的版本中已經提供了對yield
、generator
的支持,並且generator
是由native code
開發的,並非babel
轉碼支持的,babel
轉碼只能提供對yield
的支持,可是generator
轉碼事後就沒法在iOS上運行了,所以我參考了generator
的API,以JSAPI擴展
的方式,用objective-c
實現了generator
整套API。實現過程我後面準備單寫一篇博客來介紹如何實現。
GIC
也已經提供了對Promise
的支持,所以在Promise
和yield
的基礎上,提供對async
、await
支持就變得瓜熟蒂落了。事實上,GIC
已經支持ES8
中的async
、await
功能了。
GIC
自己是支持數據綁定的,在引入JavaScript
後也提供了數據綁定的功能。可是JS的數據綁定跟naitve coed
的數據綁定在實現方式上是不同的,GIC
在實現JS的數據綁定過程當中,充分參考了VUE
中的實現方式,而且研究了VUE
的源碼,最終將VUE
的實現方式移植到了GIC
中。能夠參考這篇文章