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