zoukankan      html  css  js  c++  java
  • Weex入门与进阶指南

    Weex入门与进阶指南

    标签: WeexiOSNative
    本文章已收录于: 
     分类:
     
     

    目录(?)[+]

     

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

    前言

    相比较于React Native的“Learn once, write anywhere”,Weex的口号是“Write once, run everywhere”。考虑到React Native比较任性的向下兼容性,我们也引入了Weex做一番了解。

    本文主要分为以下几个部分:

    1. 构建Hello World程序;
    2. 集成到现有的iOS工程中;
    3. 使用Weex的高级特性;
    4. 如何为Weex做贡献;

    一、Weex入门

    1.1 Hello Weex

    参考官方教程,我们需要先安装Node。在Mac上也可以通过Homebrew直接进行安装:brew install node

    接着我们需要安装Weex CLI:npm install -g weex-toolkit,并确保版本号大于0.1.0:

    $ weex --version
    info 0.3.4

    至此,准备工作已经到位,我们可以开始编写Weex程序了。 
    创建一个名为helloweex.we的文件,并编写以下代码:

    <template>
      <div>
        <text>Hello Weex</text>
      </div>
    </template>

    通过命令行在helloweex.we文件所在的目录下执行如下命令:

    $ 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的执行效果:

    _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

    更多语法相关内容可以参考官方文档

    二、集成到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 : %@
    ", 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工程中算初步告一段落。

    三、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 {  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>

    四、为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上

     
  • 相关阅读:
    vector存入共享内存(了解)
    vector内存分配
    关于传值的小问题
    c++11 lambda(匿名函数)
    std::function,std::bind复习
    项目分析(人物上线消息)
    mongo 1067错误
    随笔1
    随笔
    交换机的体系结构和各部件说明
  • 原文地址:https://www.cnblogs.com/liangqihui/p/6866556.html
Copyright © 2011-2022 走看看