Weex嘗鮮
Weex是阿里開源的類React Native技術,其實按知乎上的討論基本可以說是整合Vue.js+React Native造的輪子( 如何看待阿里無線前端發布的Weex? )。Weex是一款輕量級的移動端跨平臺動態性技術解決方案,主要致力于使用Web方式開發出Native性能的App。Weex學習成本較RN相對較低, 可以說就是使用簡易版的HTML/CSS/JavaScript以及自定義的一些組件和規則開發.we文件,完成界面控件布局、樣式、數據綁定以及簡單的事件綁定等。Weex提供了node.js的小工具可以將.we文件轉換成js bundle 文件,客戶端引入了weex sdk就可以解析js bundle文件,最終完成頁面渲染。接下來在簡單嘗試下weex。
weex配置
.we文件開發其實任何編輯器都可以,不過需要使用Node安裝Weex提供的.we文件轉換工具,該工具可以將.we文件轉換成weex sdk能夠識別和解析的js bundle文件。
npm install -g weex-toolkit
主要介紹下Android端配置:
1、首先引入weex sdk,gradle添加weex依賴
compile 'com.taobao.android:weex_sdk:0.5.1@aar'
2、確保聲明了網絡權限
<uses-permission android:name="android.permission.INTERNET" />
3、配置ImageView加載網絡圖片形式
weex需要我們手動配置網絡圖片加載,否則imageview將無法正常工作。通常我們可以使用第三方圖片加載庫,這里我引入了Picasso來幫助我們加載網絡圖片。配置時機可以放在Application初始化中,這樣全局有效。
private void initWeex(){ InitConfig.Builder configBuilder = new InitConfig.Builder().setImgAdapter(new IWXImgLoaderAdapter() { @Override public void setImage(String url, ImageView view, WXImageQuality quality, WXImageStrategy strategy) { Picasso.with(getApplicationContext()).load(url).into(view); } }); WXSDKEngine.initialize(this, configBuilder.build()); }
通過以上配置,客戶端基本的weex環境也就配置好了。
.we文件開發
.we文件主要包括三大部分, <template></template> 聲明組件, <style></style> 定義組件樣式, <script></script> 聲明組件data、events,業務邏輯處理等,基本類似web端開發。template中可以使用{{}}進行data binding,將script中的data和events綁定到相應的組件中。具體語法不再介紹,下面是一個類似ViewPager的自動輪播banner例子。
<template> <div style="flex-direction: column;"> <slider class="slider" interval="2000" auto-play="true"> <div class="slider-pages" repeat="{{headline}}" onclick="openUrl(headline[$index].url)"> <image class="image" src="{{image}}"></image> <text class="title">{{title}}</text> </div> <indicator class="indicator" if="shouldShowIndicators()"></indicator> </slider> </div> </template> <style> .image { width: 750; height: 260; } .title { margin-top: 20; margin-bottom: 20; text-align: left; flex: 1; color: black; font-size: 35; } .slider { width: 750; height: 450; } .slider-pages { padding-top: 30; flex-direction: column; width: 750; height: 400; } .indicator { height: 20; width: 750; position:absolute; left: 1; bottom: 1; item-color: grey; item-selectedColor: orange; item-size: 20; } </style> <script> var weexModule = require('@weex-module/weexModule'); module.exports = { data: { headline:[] }, methods: { openUrl: function (url) { weexModule.startActivity(url, function(err){ console.log(err); }); }, shouldShowIndicators: function(){ return this.headline.length > 1; } } } </script>
style默認屏幕寬度為750px,所以如果組件寬度為整屏寬度,直接定義為750即可。綁定onclick事件,其實只是屬性設置并不是方法調用,如果不帶對應方法不帶參數直接使用方法名即可,但是其他地方如果進行方法調用,必須得加(),表示方法的調用,例如 if="shouldShowIndicators()" 。
自定義Module
在script中我們引用了自定義的Module,負責與Native端通信,處理具體的業務邏輯。要引用自定義Module需要事先使用WXSDKEngine的registerModule方法進行注冊,可以在Application啟動時注冊一些通用的Module,也可以在需要使用時再去注冊一些具體業務邏輯Module。
try { WXSDKEngine.registerModule("weexModule", WeexModule.class); } catch (WXException e) { e.printStackTrace(); }
自定義Module時,方法訪問權限必須聲明為public,并且必須使用@WXModuleAnno注解標識。這里,weexModule是個簡單的負責Activity跳轉的Module,并且回調了Activity啟動結果。對應的WeexModule代碼如下:
public class WeexModule extends WXModule { @WXModuleAnno public void startActivity(String url, String cb){ Log.d("weex", "========" + url); boolean error = false; try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); mWXSDKInstance.getContext().startActivity(intent); } catch (ActivityNotFoundException e) { error = true; } Map<String, Object> result = new HashMap<>(1); result.put("error", error); WXBridgeManager.getInstance().callback(mWXSDKInstance.getInstanceId(), cb, result); } }
自定義Component
weex目前只支持一些常用的組件,如果有需要,只要遵循weex規范,我們完全可以自定義組件。在配置weex客戶端環境時,我們使用了第三方圖片請求庫Picasso使得imageview能夠直接加載url。其實公司項目里已經有了強大的自造輪子——NetworkImageView,我們并不想引用其他庫增加App size,但是template中無法直接使用Native組件,我們需要自定義Component進行簡單的包裝。例如:
@Component(lazyload = false) public class NetworkImageViewComponent extends WXImage { public WeexComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) { super(instance, dom, parent, isLazy); } @Override protected void initView() { if (mContext != null) { mHost = new NetworkImageView(mContext); // 替換原生組件 ((NetworkImageView) mHost).setScaleType(ImageView.ScaleType.CENTER_CROP); } } @Override public View getView() { return super.getView(); } @WXComponentProp(name = "url") public void setImageUrl(String url) { ((NetworkImageView) mHost).setImage(url); } }
如上代碼所示,通常自定義Component只需要重寫initView方法,替換mHost為所需的原生組件即可。使用@WXComponentProp注解可以為組件添加自定義屬性,在template中聲明組件時設置屬性就可以調用對應方法。最后和Module類似,自定義Component也需要向weex注冊,可以在Application啟動時注冊通用Component,也可以在需要時注冊業務耦合較大的Component。
try { WXSDKEngine.registerComponent("myimageview", NetworkImageViewComponent.class); } catch (WXException e) { e.printStackTrace(); }
經過以上操作我們已經可以在template中使用我們自定義的MyImageView了。簡單修改下.we文件:
<template> <div style="flex-direction: column;"> <slider class="slider" interval="2000" auto-play="true"> <div class="slider-pages" repeat="{{headline}}" onclick="goWeexSite(headline[$index].url)"> <MyImageView class="image" url="{{image}}"></MyImageView> <text class="title">{{title}}</text> </div> <indicator class="indicator" if="shouldShowIndicators()"></indicator> </slider> </div> </template>
這里值得注意的是,.we文件中的MyImageView在通過weex自動轉換工具轉換成的js bundle文件中type被標識為"myimageview",也就是說會轉換為全小寫,因此在注冊Component時應該盡量使用小寫key。當然我們也可以手動修改js bundle文件,不過自定義組件多了會比較繁瑣。
Native端渲染
客戶端渲染工作主要包括解析js bundle文件還原Native端組件,至于數據請求可以直接在Native端發送網絡請求,也可以在script中通過js調用weex內置的網絡請求Module——WXStreamModule的sendHttp方法進行網絡請求,請求到的數據會通過WXBridgeManager回調給js端。測試時為了方便,直接在Native端進行網絡請求,然后將請求到的數據塞給weex,weex進行render渲染,渲染成功后更新listview,將最終得到的native view塞進listview。調用weex渲染的主要代碼如下:
JSONObject json = new JSONObject(); json.put("headline", array); //key和js端對應,相當于將數據塞進.we文件的data中 String template = WXFileUtils.loadFileContent("weex/index.js", getContext()); if (weexLayout == null) { wxsdkInstance.render("headline", template, null, json.toString(), -1, -2, WXRenderStrategy.APPEND_ASYNC); } else { wxsdkInstance.refreshInstance(json.toString()); }
測試時直接使用的客戶端本地的由.we文件轉換來的js bundle文件,生產環境通常應該從服務端拉取。需要注意的是,一個WXSDKInstance實例只負責一次頁面渲染,如果想重新加載模版渲染頁面,需要將WXSDKInstance實例destroy再重新new一個,如果后期只是更新數據直接refresh即可。另外,使用WXSDKInstance.registerRenderListener可以注冊對渲染結果的監聽。
@Override public void onViewCreated(WXSDKInstance instance, View view) { weexLayout = view; // 首次渲染成功拿到native view } @Override public void onRenderSuccess(WXSDKInstance instance, int width, int height) { adapter.notifyDataSetChanged(); // 更新listview } @Override public void onRefreshSuccess(WXSDKInstance instance, int width, int height) { adapter.notifyDataSetChanged(); // 更新listview } @Override public void onException(WXSDKInstance instance, String errCode, String msg) { Log.d("weex", "=======" + msg); }
onViewCreated會在首次渲染成功后回調,拿到了native view,剩下的一切都非常熟悉了,放到你期望的容器中顯示即可。
來自:http://www.jianshu.com/p/18fac5db8429