Weex嘗鮮

WinfredTrae 8年前發布 | 15K 次閱讀 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

 

 本文由用戶 WinfredTrae 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!