版權聲明:本文爲博主原創文章,未經博主容許不得轉載。javascript
原文地址:https://yq.aliyun.com/articles/57554css
相比較於React Native的「Learn once, write anywhere」,Weex的口號是「Write once, run everywhere」。考慮到React Native比較任性的向下兼容性,咱們也引入了Weex作一番瞭解。html
本文主要分爲如下幾個部分:前端
參考官方教程,咱們須要先安裝Node。在Mac上也能夠經過Homebrew直接進行安裝:brew install node
。java
接着咱們須要安裝Weex CLI:npm install -g weex-toolkit
,並確保版本號大於0.1.0:node
$ weex --version info 0.3.4
至此,準備工做已經到位,咱們能夠開始編寫Weex程序了。
建立一個名爲helloweex.we
的文件,並編寫如下代碼:react
<template> <div> <text>Hello Weex</text> </div> </template>
經過命令行在helloweex.we
文件所在的目錄下執行以下命令:android
$ weex helloweex.we
info Fri Jul 08 2016 14:30:31 GMT+0800 (CST)WebSocket is listening on port 8082 info Fri Jul 08 2016 14:30:31 GMT+0800 (CST)http is listening on port 8081
此時,瀏覽器會打開一個新的標籤頁展現helloweex.we
的執行效果:webpack
注意到此時地址欄的內容http://127.0.0.1:8081/weex_tmp/h5_render/?hot-reload_controller&page=helloweex.js&loader=xhr
包含着hot reload
字樣,因此能夠天然聯想到當咱們在源文件作修改並保存後,該頁面會自動刷新展現效果。
上面的示例只是一個很是簡單的雛形,而一個比較完整的Weex程序包括三個部分:模板(Template)、樣式(Style)和腳本(Script)。
好比咱們能夠利用上文提到的hot reload
,修改文本的顏色並實時查看效果:
<template> <div> <text class="title">Hello Weex</text> </div> </template> <style> .title { color: red; } </style>
接着咱們添加上第三組成部分:腳本(Script):
<template> <div> <text class="title" onclick="onClickTitle">Hello Weex</text> </div> </template> <style> .title { color: red; } </style> <script> module.exports = { methods: { onClickTitle: function (e) { console.log(e); alert('title clicked.'); } } } </script>
這樣一來,當咱們點擊文本的時候會出現以下效果:
更多語法相關內容能夠參考官方文檔。
上面是從前端的角度來初步看Weex的基礎效果,對於客戶端來說,這類框架的一個優點就是可以結合Native代碼發揮做用。好比在人手緊張的狀況下能夠一次開發,而後應用在不一樣平臺終端上。
因此,這裏討論下如何將其集成到現有的iOS工程項目當中。
ios/sdk
複製到現有的iOS工程目錄下,並根據相對路徑更新既有工程的podfile,而後執行pod update
將Weex iOS SDK集成進既有的iOS項目中;在如何集成的文檔中,前面說的比較清楚,可是在初始化Weex環境
和渲染Weex實例
這兩個小節中,多是因爲代碼是從比較大的項目源碼中摘錄出來的,因此存在一些沒必要要或沒有上下文的代碼。
這裏描述下在開發調試階段運行Weex程序。
建立一個WeexDebugViewController
,進行以下佈局:
經過填入IP和文件名來定位咱們要運行的Weex程序。此外,還能夠結合weex helloweex.we --qr -h {ip or hostname}
命令來生成二維碼,進行掃描演示,不過解析二維碼仍是爲了獲取到Weex程序所在位置。
開發調試階段咱們能夠先將Weex SDK的初始化放在這個WeexDebugViewController
中:
- (void)initWeex {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[WXAppConfiguration setAppGroup:@"AliApp"]; [WXAppConfiguration setAppName:@"WeexDemo"]; [WXAppConfiguration setAppVersion:@"1.0.0"]; [WXSDKEngine initSDKEnviroment]; [WXLog setLogLevel:WXLogLevelVerbose]; }); }
點擊ShowWeex
按鈕時,咱們能夠根據兩個輸入框的內容拼接出要運行的Weex程序的位置,而後將其賦值給用來渲染Weex實例的WeexShowcaseViewController
:
- (void)showWeex { NSString *str = [NSString stringWithFormat:@"http://%@:8081/%@", self.ipField.text, self.filenameField.text]; WeexShowcaseViewController *vc = [WeexShowcaseViewController new]; vc.weexUri = [NSURL URLWithString:str]; [self.navigationController pushViewController:vc animated:YES]; }
接着咱們來看看WeexShowcaseViewController
的源碼:
#import <WeexSDK/WeexSDK.h> @interface WeexShowcaseViewController () @property (nonatomic, strong) WXSDKInstance *weexSDK; @end @implementation WeexShowcaseViewController - (void)dealloc { [_weexSDK destroyInstance]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.weexSDK.viewController = self; self.weexSDK.frame = self.view.frame; [self.weexSDK renderWithURL:self.weexUri]; __weak typeof(self) weakSelf = self; self.weexSDK.onCreate = ^(UIView *view) { [weakSelf.view addSubview:view]; }; self.weexSDK.renderFinish = ^(UIView *view) { ; }; self.weexSDK.onFailed = ^(NSError *error) { NSLog(@"weexSDK onFailed : %@\n", error); }; } - (WXSDKInstance *)weexSDK { if (!_weexSDK) { _weexSDK = [WXSDKInstance new]; } return _weexSDK; }
回到終端上,切換到helloweex.we
文件所在的目錄,將Weex的dev server跑起來:
$ weex -s .
info Fri Jul 08 2016 15:38:59 GMT+0800 (CST)http is listening on port 8081 info we file in local path . will be transformer to JS bundle please access http://30.9.112.173:8081/
而後在Native上填入對應的IP和程序文件名:
到此,將Weex集成到現有iOS工程中算初步告一段落。
當集成工做完成後,會發覺現有功能不足以知足業務需求,因此Weex支持開發者作一些擴展。
以前的helloweex.we
示例中只有一個文本元素,如今再添加一個圖片元素:
<template> <div> <image class="thumbnail" src="http://image.coolapk.com/apk_logo/2015/0817/257251_1439790718_385.png"></image> <text class="title" onclick="onClickTitle">Hello Weex</text> </div> </template> <style> .title { color: red; } .thumbnail { width: 100; height: 100; } </style> <script> module.exports = { methods: { onClickTitle: function (e) { console.log(e); alert('title clicked.'); } } } </script>
而後再執行:$ weex helloweex.we
來運行查看效果:
能夠在瀏覽器裏看到此次多了一張圖片。可是若是是運行在Native端,圖片則得不到展現:
這是因爲Weex SDK沒有提供圖片下載能力,須要咱們來實現。
WXImgLoaderProtocol
這個基本能夠參考官方文檔來實現。
#import <WeexSDK/WeexSDK.h> @interface WeexImageDownloader : NSObject <WXImgLoaderProtocol> @end
這個類必須遵循WXImgLoaderProtocol
協議,並實現該協議定義的接口:
#import "WeexImageDownloader.h" #import <SDWebImage/SDWebImageManager.h> @implementation WeexImageDownloader - (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url imageFrame:(CGRect)imageFrame userInfo:(NSDictionary *)options completed:(void(^)(UIImage *image, NSError *error, BOOL finished))completedBlock { return (id<WXImageOperationProtocol>)[[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) { } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (completedBlock) { completedBlock(image, error, finished); } }]; } @end
[WXSDKEngine registerHandler:[WeexImageDownloader new] withProtocol:@protocol(WXImgLoaderProtocol)];
這樣一來,再次運行程序就能夠看到圖片了:
這樣設計的好處主要是考慮了不一樣App依賴的網絡庫或者圖片下載緩存庫不一樣,避免Weex強依賴於一些第三方庫,遵循依賴抽象而不是具體的原則。
BTW,我我的感受Weex
縮寫成WX
,WeexImageLoaderProtocol
縮寫成WXImgLoaderProtocol
,不是很好看。
若是Weex的內置標籤不足以知足要求時,咱們能夠自定義Native組件,而後暴露給.we文件使用。
好比咱們能夠定義一個WeexButton
,繼承自WXComponent
,而後將其註冊進Weex SDK:
[WXSDKEngine registerComponent:@"weex-button" withClass:[WeexButton class]];
這樣一來,咱們就能夠在.we文件中使用這個標籤了:
<weex-button class="button" title="hello"></weex-button>
標籤中的屬性咱們能夠在初始化函數中得到:
- (instancetype)initWithRef:(NSString *)ref type:(NSString*)type styles:(nullable NSDictionary *)styles attributes:(nullable NSDictionary *)attributes events:(nullable NSArray *)events weexInstance:(WXSDKInstance *)weexInstance { self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]; if (self) { _title = [WXConvert NSString:attributes[@"title"]]; } return self; }
經過這些屬性,咱們能夠在組件生命週期中修改組件的樣式,好比設置按鈕的title:
- (void)viewDidLoad { [super viewDidLoad]; self.innerButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; self.innerButton.frame = self.view.bounds; [self.view addSubview:self.innerButton]; [self.innerButton setTitle:self.title forState:UIControlStateNormal]; [self.innerButton addTarget:self action:@selector(onButtonClick:) forControlEvents:UIControlEventTouchUpInside]; }
除了UI組件以外,有些時候咱們但願JS層面可以調用Native的一些功能,好比經過JS代碼讓Native打開一個特定的ViewController。這時候,咱們能夠自定義一個模塊向JS層面暴露API:
@synthesize weexInstance; WX_EXPORT_METHOD(@selector(call:withParam:callback:)) - (void)call:(NSString *)api withParam:(NSDictionary *)param callback:(WXModuleCallback)callback {
注意點以下:
1. 須要遵循WXModuleProtocol
協議;
2. 須要合成(synthesize
)weexInstance
屬性;
3. 使用WX_EXPORT_METHOD
來暴露API;
4. 使用WXModuleCallback
進行回調;
完成以上編碼後,向Weex SDK註冊:[WXSDKEngine registerModule:
,就能夠在.we文件中使用了:
<script> module.exports = { methods: { onClickTitle: function (e) { var mymodule = require('@weex-module/mymodule'); mymodule.call('api', {}, function(ret) { }); } } } </script>
因爲Weex剛開源不久,若是開發者發現一些問題或者須要改善的地方,能夠直接在GitHub上進行fork,修改完後提交Pull Request。