Weex入門與進階指南

Weex入門與進階指南

標籤: WeexiOSNative
本文章已收錄於: 
 分類:
 
 

目錄(?)[+]php

 

原文地址:https://yq.aliyun.com/articles/57554css

前言

相比較於React Native的「Learn once, write anywhere」,Weex的口號是「Write once, run everywhere」。考慮到React Native比較任性的向下兼容性,咱們也引入了Weex作一番瞭解。html

本文主要分爲如下幾個部分:前端

  1. 構建Hello World程序;
  2. 集成到現有的iOS工程中;
  3. 使用Weex的高級特性;
  4. 如何爲Weex作貢獻;

1、Weex入門

1.1 Hello Weex

參考官方教程,咱們須要先安裝Node。在Mac上也能夠經過Homebrew直接進行安裝:brew install nodejava

接着咱們須要安裝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 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

此時,瀏覽器會打開一個新的標籤頁展現helloweex.we的執行效果:webpack

_2016_07_08_2_34_04

注意到此時地址欄的內容http://127.0.0.1:8081/weex_tmp/h5_render/?hot-reload_controller&page=helloweex.js&loader=xhr包含着hot reload字樣,因此能夠天然聯想到當咱們在源文件作修改並保存後,該頁面會自動刷新展現效果。

1.2 基礎結構

上面的示例只是一個很是簡單的雛形,而一個比較完整的Weex程序包括三個部分:模板(Template)、樣式(Style)和腳本(Script)。

好比咱們能夠利用上文提到的hot reload,修改文本的顏色並實時查看效果:

<template> <div> <text class="title">Hello Weex</text> </div> </template> <style> .title { color: red; } </style>

 

_2016_07_08_2_47_03

接着咱們添加上第三組成部分:腳本(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>

這樣一來,當咱們點擊文本的時候會出現以下效果:

_2016_07_08_2_50_33

更多語法相關內容能夠參考官方文檔

2、集成到iOS工程

2.1 概述

上面是從前端的角度來初步看Weex的基礎效果,對於客戶端來說,這類框架的一個優點就是可以結合Native代碼發揮做用。好比在人手緊張的狀況下能夠一次開發,而後應用在不一樣平臺終端上。

因此,這裏討論下如何將其集成到現有的iOS工程項目當中。

  1. 參考官方文檔,咱們先從GitHub下載Weex源碼
  2. 解壓後將目錄下的ios/sdk複製到現有的iOS工程目錄下,並根據相對路徑更新既有工程的podfile,而後執行pod update將Weex iOS SDK集成進既有的iOS項目中;
  3. 在iOS Native代碼中初始化Weex SDK,而後建立出要展現Weex程序的ViewController,具體見以下描述;

2.2 在iOS應用上運行Weex程序

如何集成的文檔中,前面說的比較清楚,可是在初始化Weex環境渲染Weex實例這兩個小節中,多是因爲代碼是從比較大的項目源碼中摘錄出來的,因此存在一些沒必要要或沒有上下文的代碼。

這裏描述下在開發調試階段運行Weex程序。

2.2.1 肯定要運行的Weex程序

建立一個WeexDebugViewController,進行以下佈局:

_2016_07_08_3_19_12

經過填入IP和文件名來定位咱們要運行的Weex程序。此外,還能夠結合weex helloweex.we --qr -h {ip or hostname}命令來生成二維碼,進行掃描演示,不過解析二維碼仍是爲了獲取到Weex程序所在位置。

2.2.2 初始化Weex SDK

開發調試階段咱們能夠先將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]; }); }

 

2.2.3 運行Weex程序的ViewController

點擊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; }

 

2.2.4 運行起來

回到終端上,切換到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和程序文件名:

_2016_07_08_3_47_33

_2016_07_08_3_47_43

到此,將Weex集成到現有iOS工程中算初步告一段落。

3、Weex進階

當集成工做完成後,會發覺現有功能不足以知足業務需求,因此Weex支持開發者作一些擴展。

3.1 實現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來運行查看效果:

_2016_07_08_4_28_01

能夠在瀏覽器裏看到此次多了一張圖片。可是若是是運行在Native端,圖片則得不到展現:

_2016_07_08_4_37_08

這是因爲Weex SDK沒有提供圖片下載能力,須要咱們來實現。

3.2 實現圖片下載協議WXImgLoaderProtocol

這個基本能夠參考官方文檔來實現。

3.2.1 定義圖片下載Handler

#import <WeexSDK/WeexSDK.h> @interface WeexImageDownloader : NSObject <WXImgLoaderProtocol> @end

 

3.2.2 實現協議接口

這個類必須遵循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


3.2.3 註冊Handler

[WXSDKEngine registerHandler:[WeexImageDownloader new] withProtocol:@protocol(WXImgLoaderProtocol)];

這樣一來,再次運行程序就能夠看到圖片了:

_2016_07_08_5_45_09

這樣設計的好處主要是考慮了不一樣App依賴的網絡庫或者圖片下載緩存庫不一樣,避免Weex強依賴於一些第三方庫,遵循依賴抽象而不是具體的原則。

BTW,我我的感受Weex縮寫成WXWeexImageLoaderProtocol縮寫成WXImgLoaderProtocol,不是很好看。

3.2 自定義UI組件

若是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]; }

3.3 自定義模塊

除了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
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

注意點以下: 
1. 須要遵循WXModuleProtocol協議; 
2. 須要合成(synthesizeweexInstance屬性; 
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>

4、爲Weex作貢獻

因爲Weex剛開源不久,若是開發者發現一些問題或者須要改善的地方,能夠直接在GitHub上進行fork,修改完後提交Pull Request

 

前端開發-Weex初試

 

1 Weex介紹

weex是阿里2016年開源的一套跨移動端(Andriod/IOS/Wap)的前端框架,採用VUE,較React Native入門相對簡單 

官網地址

2 Weex安裝與初始化

2.1 安裝NodeJS和NPM

略過,默認安裝了

注意:nodejs的版本須大於4.5.0

2.2 安裝weex

  • npm install -g week-toolkit,全局安裝week工具集合
  • 安裝完成後命令行輸入weex,查看是否有命令幫助內容,若是提示沒有weex命令,表示weex沒有安裝好,檢查一下nodejs的版本

2.3 初始化一個項目

  • 新建一個項目目錄
  • 命令行輸入 weex init,這時會自動下載和生成相關文件
  • 運行npm install,安裝相關依賴包

2.4 與IDE集成

我使用的是WebStrom
  • 將剛纔新建的工程導入webstrom中
  • 在setting->plugins中安裝weex的插件:weex、weex langugge support,用於建立we文件和支持weex語法(VUE)
  • 直接在webstrom的終端中運行weex相關命令

2.5 相關命令

  • weex ***.we : 運行調試xxx頁面,支持熱加載,默認端口是8081和8082 8082是熱加載端口
  • npm run build : build 在package.json定義的腳本命令,執行webpack
  • npm run dev : dev 在package.json定義的腳本命令,執行webpack --watch
  • npm run serve : serve package.json定義的腳本命令,啓動serve服務
  • weex xxx.we --qr: 運行調試xxx頁面,並依據地址url生成二維碼,主要是在iOS和Android上查看效果,設備須在同一個局域網中

webpack和serve的依賴包須要安裝

3 第一個Weex項目

3.1 主頁面

3.1.1 main.we

<template> <scroller> <text>用戶名:</text> <input id="top" type="text" autofocus="true" placeholder="請輸入用戶名" value="{{username}}" oninput="usernameoninput" style="margin-top: 200px;margin-left: 200px;font-size:32px;"> </input> <text>密碼:</text> <input type="password" autofocus="true" placeholder="請輸入密碼" value="{{password}}" oninput="passwordoninput" style="margin-top: 200px;margin-left: 200px;font-size:32px;"> </input> <input type="success" value="登陸" onclick="login" style="margin-top: 200px;margin-left: 200px;"> </input> </scroller> </template> <style> </style> <script> var common = require('./lib/common.js'); module.exports = { data: { root:"dist", username:"", password:"" }, ready: function () { }, methods:{ login:function(e){ var storage = require('@weex-module/storage'); var self = this; var bundleUrl = this.$getConfig().bundleUrl; var url = common.getUrl(bundleUrl,'mainindex.lib','dist'); storage.setItem('username', self.username, function(e) { self.$openURL(url) }); }, usernameoninput:function(e){ this.username = e.value; }, passwordoninput:function(e){ this.password = e.value; } } } </script>

3.1.2 內置組件使用

3.1.2.1 數據存儲與讀取

var storage = require('@weex-module/storage');//引入存儲 storage.setItem('username', self.username, function(e) {//將用戶名存進客戶端,對應的key是usernam }); var storage = require('@weex-module/storage'); var self = this; storage.getItem("username",function(e){//讀取數據 self.headinfo = e.data; });

3.1.2.2 數據請求

var stream = require('@weex-module/stream'); stream.fetch({ method: 'GET', url: "http://192.168.169.124:3000/testhttpget.do", type:'json' }, function(response) { self.body = response.data.info; },function(response){ });

其餘內置組件使用,請參看API

3.2 自定義組件

3.2.1 新建we文件

<template> <div class="headclass"> <text>{{headinfo}}</text> </div> </template> <script> module.exports = { data:{ headinfo:"welcome to this" }, ready:function(){ var storage = require('@weex-module/storage'); var self = this; storage.getItem("username",function(e){ self.headinfo = e.data; }); } } </script> <style> .headclass{ margin-top: 200px; } </style>

3.2.2 引入

<script> require('./components/headdiv.we') module.exports = { data:{ } } </script>

3.2.3 使用

<template> <div class="bg"> <headdiv></headdiv> </div> </template>

3.3 引用JS文件與打包

3.3.1 定義JS

var getUrl = function(bundleUrl,fileName,dir,host){ var nativeBase; var isAndroidAssets = bundleUrl.indexOf('file://assets/') >= 0; var isiOSAssets = bundleUrl.indexOf('file:///') >= 0 && bundleUrl.indexOf('WeexDemo.app') > 0; if (isAndroidAssets) { nativeBase = 'file://assets/'; } else if (isiOSAssets) { nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1); } else { host = host||'localhost:8080'; var matches = /\/\/([^\/]+?)\//.exec(bundleUrl); if (matches && matches.length >= 2) { host = matches[1]; } nativeBase = 'http://' + host + '/' + dir + '/'; } var h5Base = './index.html?page=./' + dir + '/'; // in Native var base = nativeBase; if (typeof window === 'object') { base = h5Base; } return base+fileName; }

3.3.2 引用

var common = require('./lib/common.js');

打包

require('webpack') require('weex-loader') var path = require('path') module.exports = { entry: {//主we頁面 main: path.join(__dirname, 'src', 'main.we?entry=true') }, output: { path: 'dist', filename: '[name].lib' }, module: { loaders: [ { test: /\.we(\?[^?]+)?$/, loaders: ['weex-loader'] }, { test: /\.js$/, loaders: ['weex-loader'] //將js文件打包 } ] }}

3.4 頁面跳轉

self.$openURL(url)

需要注意Android和iOS的跳轉,要提早定義好相關協議

4 與Android的集成

4.1 建立工程

建立Android 工程

4.2 引入weex

  • 下載源碼 git clone https://github.com/alibaba/weex
  • File->New-Import Module->選擇WEEX SDK Module(weex/android/sdk)->Finish
  • app 的build.gradle 中添加以下依賴:compile project(':weex_sdk')

4.3 引入相關組件

apply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "25.0.0" defaultConfig { applicationId "demo.android.weex.tomsnail.cn.weexandroiddemo" minSdkVersion 21 targetSdkVersion 24 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.2.1' compile 'com.android.support:design:24.2.1' compile 'com.taobao.android:dexposed:0.1.8' compile 'com.loopj.android:android-async-http:1.4.9@aar' compile 'com.facebook.fresco:fresco:0.12.0+' compile 'com.facebook.fresco:animated-gif:0.12.0' compile 'com.squareup.okhttp:okhttp:2.3.0' compile 'com.squareup.okhttp:okhttp-ws:2.3.0' compile 'com.squareup.okio:okio:1.0.1' compile 'com.alibaba:fastjson:1.1.46.android' compile 'com.android.support:support-annotations:23.2.1' compile 'com.jakewharton.scalpel:scalpel:1.1.2' compile 'com.squareup.picasso:picasso:2.5.2' //compile 'com.google.android.gms:play-services-appindexing:8.1.0' compile 'com.taobao.android:weex_inspector:0.0.8.1' compile project(':weex_sdk') testCompile 'junit:junit:4.12' compile 'com.google.android.gms:play-services-appindexing:8.4.0' }

4.4 建立基礎類

  • WXApplication
  • ImageAdapter
  • MainActivity

4.5 配置AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="demo.android.weex.tomsnail.cn.weexandroiddemo"> <!-- To auto-complete the email text field in the login form with the user's emails --> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"> </uses-permission> <application android:name=".WXApplication" android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="weexandroiddemo" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Main1Activity"> </activity> <!-- ATTENTION: This was auto-generated to add Google Play services to your project for App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. --> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> </manifest>

4.6 加入js

src下新建assets文件夾,將weex生成的dist下的文件放入以便加載 WXFileUtils.loadAsset(path, context)

4.7 運行

鏈接設備運行app,建議使用真機,使用模擬機佔用電腦資源較多

5 相關問題

5.1 升級

  • 依據版本號規劃進行升級
  • 打包下載,本地解壓存儲、文件緩衝

5.2 自定義事件

  • 定義好相關協議
  • SPA化

5.3 消息與推送

  • 只作Native功能

5.4 Native功能

  • 好比拍照上傳、相冊等功能仍是須要移動端開發人員開發和集成

5.5 持續集成

  • 從新定義集成流程,將weex的發佈與移動端的發佈聯合定義

相關代碼在整理後近期放在github上

相關文章
相關標籤/搜索