zoukankan      html  css  js  c++  java
  • weex

    Weex学习与实践(一):Weex,你需要知道的事基本介绍A framework for building Mobile cross-platform UI怎么解释它呢?我的理解就是weex = react-native +vue ,使用vue的API风格,两端的实现方式则和react-native,weex 比rn的优点就是一次编写三端运行。IDE: Sublime Text + vue-syntax-highlight命令行工具:weex-toolkit调试工具: weex-devtoolweex分为组件component和模块(module)以及事件weex-components : weex-components组件 就是各种标记组件,比如div 、slider、indicator等 通过下面这种方式使用 

    <div>
      <image src="..."></image>
      <text>...</text>
    </div>
    

    js模块

    let modal = require('@weex-module/modal');
    modal.toast({
      "message":"我是提示框",
      "duration":2
    });  
    

    其他的还有stream,dom,animation之类的事件

    <div onviewappear="viewappear" onviewdisappear="viewdisappear">
      ...
    </div>
    

    Weex源码结构package.jsonnode_modules依赖,更重要的是里面包含了npm run xxx 等快捷命令。比如之前我们运行node.js程序是这样的:$ node xx.js。这里我们可以把它配置化,例如package.json文件中scripts的 “build:config”: “node build/config.frameworks.js”,其实就是npm run build:config 相当于执行了node build/config.frameworks.jsstart文件: 启动程序文件,里面包换编译和启动脚本:examples: 示例Demoandroid/ios/html: 各平台代码build:打包各平台的脚本,配置在package.json中。第2篇 了解Weex源码结构,修改example初始化工程初始化工程前需要先安装  homebrew,然后按照下面步骤创建一个工程。

    $ brew install node             //通过brew安装node
    $ npm install -g weex-toolkit   //通过node安装 weex-toolkit  
    $ sudo gem install cocoapods    //安装iOS包管理工具 cocoapods
    $ weex init                     //创建项目的文件
    $ npm install                   //依赖安装 package.json文件
    $ npm run dev                   //项目编译
    $ npm run serve                 //启动轻量服务器      
    

    这时有可能提示

    npm WARN babel-loader@6.2.5 requires a peer of babel-core@^6.0.0 but none was installed.
    

    你需要再

    npm install babel-core
    

    这时,打开浏览器,输入http://127.0.0.1:8080, 就会看到这个项目的效果:第3篇 初始化工程npm run dev 干了什么呢?先看 package.json 文件{ "name": "demo1", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack", "dev": "webpack --watch", "serve": "serve -p 8080", "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "serve": "^1.4.0", "webpack": "^1.13.1", "weex-html5": "0.2.18", "weex-loader": "^0.1.5" } } npm run dev实际上相当于 webpack –watchwebpack实际上是执行了默认的webpack.config.js配置文件webpack.config.js 引入webpack和weex-loader,entry属性是表示入口文件,output表示输出文件,默认输出到dist文件夹。require('webpack') require('weex-loader') var path = require('path') module.exports = { entry: { main: path.join(__dirname, 'src', 'main.we?entry=true') }, output: { path: 'dist', filename: '[name].js' }, module: { loaders: [ { test: /.we(?[^?]+)?$/, loaders: ['weex-loader'] } ] } } 不过这个自动产生的webpack.config.js的文件有个坑就是,你添加一个新的we文件,他不会自动build为js文件 可以手动添加 entry: { main: path.join(__dirname, 'src', 'main.we?entry=true'), translate: path.join(__dirname, 'src', 'translate.we?entry=true') }不过推荐的是自己遍历所有的we文件

    require('webpack')
    require('weex-loader')
    
    var path = require('path')
    var fs = require('fs');
    
    var entry = {};
    
    function walk(dir, root) {
      var directory = path.join(__dirname, root, dir);
      fs.readdirSync(directory)
        .forEach(function(file) {
          var fullpath = path.join(directory, file);
          var stat = fs.statSync(fullpath);
          var extname = path.extname(fullpath);
          if (stat.isFile() &&
                 (extname === '.we')) {
            var name = path.join(root, 'build', dir, path.basename(file, extname));
            entry[name] = fullpath + '?entry=true';
          } else if (stat.isDirectory() &&
                      file !== 'build') {
            var subdir = path.join(dir, file);
            walk(subdir, root);
          }
        });
    }
    walk('./', 'src');
    module.exports = {
      entry: entry,
      output: {
        path: '.',
        filename: '[name].js'
      },
      module: {
        loaders: [
          {
            test: /.we(?[^?]+)?$/,
            loaders: ['weex-loader']
          }
        ]
      }
    }
    

    hugojing - webpack.config.jsduqian291902259 - webpack.config.js 第3篇 初始化工程入口文件index.html这里可以参考 Integrate Weex HTML5 to your projectweex_extend_demowe代码结构template内必须包含唯一的根节点作为父容器, div就是一个很好的选择,里面则是一些Native Componentsstyle 支持盒子模型和Flexboxweex内置了响应式的支持,页面的宽度是以750来做为标准,自动适配所有手机;

    <template>
      <div>
        <div>子组件</div>
        <div>子组件</div>
      </div>
    </template>
    
    <style>
    </style>
    <script>
    module.exports = {
    
      data: function () {
        return {
          x: 1,
          y: 2
        }
      }
    
      methods: {
        foo: function () {
          console.log('foo')
        }
      },
    
      computed: {
        z: function () {
          return this.x + this.y
        }
      },
    
      events: {
        custom: function (e) {
          console.log(e)
        }
      },
    
      init: function () {},
      created: function () {},
      ready: function () {}
    }
    
    </script>
    

    script里面包含很多ViewModel Options,data methods computed init, created, ready events如果需要在模板里实现更多的逻辑判断,你可以使用’computed property’.created是生命周期函数,这个时候模板还没有被渲染,常用来在这里定义数据的更新和获取;ready是生命周期函数,这个时候模板被渲染,常用来做一些自己上报等;weex- references - Weex Cheat Sheet显然we文件的这些代码是不会被 native app 识别的,我们要想办法让这些代码可运行。所以我们同时做了三件事:1.在本地用一个叫做 transformer 的工具把这套代码转成纯 JavaScript 代码2.在客户端运行一个 JavaScript 引擎,随时接收 JavaScript 代码3.在客户端设计一套 JS Bridge,让 native 代码可以和 JavaScript 引擎相互通信所以紧接着第二步,就是用 transformer 对代码进行转换,变成客户端可运行的 JavaScript 代码 原图:本地开发时的 Weex Transformer 工作原理在 transformer 中,我们主要的工作就是对 HTML、CSS、JavaScript 代码进行解析和重组。这里我们用到了三个非常重要的库:HTML 解析工具:htmlparserCSS 解析工具:cssomJavaScript 解析工具:uglify-js对无线电商动态化方案的思考(二) 对无线电商动态化方案的思考(三) Weex的生命周期

    <script>
      module.exports = {
        data: {},
        methods: {},
    
        init: function () {
          console.log('在初始化内部变量,并且添加了事件功能后被触发');
        },
        created: function () {
          console.log('完成数据绑定之后,模板编译之前被触发');
        },
        ready: function () {
          console.log('模板已经编译并且生成了 Virtual DOM 之后被触发');
        },
        destroyed: function () {
          console.log('在页面被销毁时调用');
        }
      }
    </script>
    

    init内一般用于初始化一些内部变量,绑定一些自定义事件,这时还没有数据绑定,没有创建vdom,所以不能通过this获取到data和methods,也不能获取vdom的节点created 完成了数据绑定 ,但还未开始编译模板,可以通过this获取data和methods,但不能获取vdom的节点ready表示渲染完成 ,从子组件往上触发destroyed 组件销毁,比如页面跳转,从子组件开始往上触发Weex的工作原理 Weex详解:灵活的移动端高性能动态化方案 - 勾股&鬼道对无线电商动态化方案的思考(二) 页面间通信页面跳转是通过指定下一个页面的url,然后通过openurl或者push的方式来跳转获取url的方式可以通过下面这段JS代码

    function getAppBaseUrl(self) {
        var dir ='examples'
        var url = self.$getConfig().bundleUrl;
        var bundleUrl = url;
        bundleUrl = new String(bundleUrl);
    
        var nativeBase;
        var isAndroidAssets = bundleUrl.indexOf('file://assets/') >= 0;
    
        var isiOSAssets = bundleUrl.indexOf('file:///') >= 0;
        if (isAndroidAssets) {
          nativeBase = 'file://assets/';
        }
        else if (isiOSAssets) {
          nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1);
        }
        else {
          var host = 'localhost:12580';
          var matches = ///([^/]+?)//.exec(self.$getConfig().bundleUrl);
          if (matches && matches.length >= 2) {
            host = matches[1];
          }
          nativeBase = 'http://' + host + '/' + dir + '/build/';
        }
        var h5Base = './index.html?page=./' + dir + '/build/';
        //Native端
        var base = nativeBase;
        //H5端
        if (typeof window === 'object') {
          base = h5Base;
        }
        return base
    }
    

    第六篇 导航、页面跳转、stream、webview页面通信有两种方式1.通过 url 参数传递。

    /**
     * 获取URL参数
     */
    getUrlParam: function (key) {
        var t = this.$getConfig().bundleUrl;
        var reg = new RegExp('[?|&]' + key + '=([^&]+)');
        var match = t.match(reg);
        return match && match[1];
    }
    

    2.通过 localStorage 数据存储。如果是组件间通信不是页面通信,则参考:组件之间通信 - (Communicate Between Components)boxmodel & flexboxweex支持boxmodel 和flexbox下面这个是boxmodel 关于flexbox,可以看我的这篇文章react-native的第一课 - flexbox布局weex的缺点1.Weex将整个app的宽度定死在750px,然后其他都是根据scale进行计算的,会导致适配不方便。2.目前不支持iOS的presentViewController方法3.很多组件和模块需要自己扩展(比如datepicker,iconfont,摄像头,二维码等)几个小问题1.之前weex是只支持es5,现在可以支持es6了。精华 新版weex-loader@0.3.0-alpha,欢迎试用2.怎么断点调试?目前是可以断点调试的,可以参考下面文章Weex调试神器——Weex Devtools使用手册 线上调试3.weex支持本地图片吗?根据官方答疑是可以的,但是我目前还没有尝试成功。faq:use-local-image4.weex-x的使用?5.promise怎么使用?issues - 12696.热更新方案是什么?最后,希望有越来越多的人把weex用起来。http://coderyi.com/posts/weex1/ Weex学习与实践(二):iOS集成的tipsiOS上集成Weex集成weex,需要WeexSDK、WXDevtool两个库以及阿里未开源的ATSDK-Weex。目前官方的alibaba/Weex仓库里面

    pod 'WeexSDK', :path=>'../sdk/'
    pod 'WXDevtool', :path=>'../WXDevtool/'
    pod 'ATSDK-Weex', '0.0.1'
    

    它们直接使用的weex仓库的sdk,但是weex主仓库的WXDevtool已经不维护了,需要替换成,weexteam/weex-devtool-iOS的代码。另外也可以直接从cocoapods的源pod仓库,但是cocoapods的源都是打包成framwork,很多文件并没有设置为public,所以导致很多头文件没有暴露出来。当然你也可以不用通过cocoapods集成,直接把代码拉进工程就可以,如果发生 Unknown type name 'NSString'你可能需要把layout.c文件右侧的type改为Objective-C Source,或者直接修改为layout.m。基本上JS页面是在WXDemoViewController工作的,你可能需要接收页面刷新的通知,以支持实时刷新[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationRefreshInstance:) name:@"RefreshInstance" object:nil];页面会维护一个WXSDKInstance实例,WXSDKInstance就是weex渲染的实例对象,提供了很多页面渲染相关的接口,比如renderWithURL、refreshInstance、destroyInstance等weex SDK 集成到工程 (integrate to ios) Weex iOS SDK 集成指南iOS上扩展组件目前官方iOS这一块组件的代码在WeexSDK的component里面,组件有限,只有image,list,scroller等,如果想要实现自己的组件,首先需要继承WXComponent类。然后实现方法

    - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
    {}
    

    注意这个方法不在主线程,这里面接收一些js传过来的参数,以在js端写的image标记为例子

    <image style=" 100;height: 100;margin-top:20;margin-right:20;margin-left:220" src=></image>
    

    到objc端就是

    ref:701
    type:image
    styles:
    {
        height = 100;
        marginLeft = 220;
        marginRight = 20;
        marginTop = 20;
        width = 100;
    }
    attributes:
    {
        src = "https://avatars.githubusercontent.com/u/9892522?v=3";
    }
    

    ref(结点的唯一标识符)然后在loadView的时候可能需要返回自己的objc组件

    - (UIView *)loadView
    {
        return [[WXImageView alloc] init];
    }
    

    然后你可以通过复写addEvent方法来增加一个change(UIControlEventValueChanged)、click(UIControlEventTouchUpInside)等事件如果是image组件的话,你可能需要接收图片地址,这个时候需要通过实现了WXImgLoaderProtocol的WXImgLoaderDefaultImpl来处理,WXImgLoaderDefaultImpl实现了downloadImageWithURL方法,这里面通过SDWebImage来下载一张图片。iOS 扩展 (extend to ios)iOS上扩展module这一块的代码在module分组里面,包括网络库stream,持久化storage等,你可以扩展自己module。需要做的是实现WXModuleProtocol协议,并且写自己的方法就可以了,这里需要通过weex的宏把需要public的方法导出

    WX_EXPORT_METHOD(@selector(fetch:callback:progressCallback:))
    

    在module中目前是没有view的,但是你可以通过由js传过来的ref值拿到

    WXComponent *targetComponent = [self.weexInstance componentForRef:nodeRef];
    CALayer *layer = targetComponent.layer;
    UIView *view = targetComponent.view;
    

    注意点1.如果设置js文件在bundle中载入的话,需要把build的js文件拖入工程,如果的你的we里面一开始就调用了js文件,由于只会buildwe文件,所以还需要把之前的js文件拖入工程,另外examples里面判断iOSAssets是这样的

    var isiOSAssets = bundleUrl.indexOf('file:///') >= 0 && bundleUrl.indexOf('WeexDemo.app') > 0;
    

    你如果拖入自己的工程需要把WeexDemo.app的判断去掉http://coderyi.com/posts/weex2/  3、Weex学习与实践(三):iOS原理篇主要类WXSDKEngineWXSDKEngine主要用于初始化WeexSDK的环境一开始会载入配置文件main.js并且注册一些默认的组件、模块以及handler+ (void)initSDKEnviroment:(NSString *)script { [self _registerDefaultComponents]; [self _registerDefaultModules]; [self _registerDefaultHandlers]; [[WXSDKManager bridgeMgr] executeJsFramework:script]; } WXSDKInstance一个WXSDKInstance就对应一个UIViewController,对应一个weex页面。主要用来渲染页面,一般通过renderWithURL方法,然后能够接收一些回调和一些视图相关的方法

    onCreate //根视图rootView创建的时候
    renderFinish//视图渲染完成
    componentForRef //通过视图索引拿到对应的组件视图
    

    WXBridgeManagerWXBridgeManager 是JS与iOS通过JSCore交互的类,相关的类还有WXBridgeContext、WXJSCoreBridge。比如调用JS

    - (void)executeJsMethod:(WXBridgeMethod *)method
    {
        if (!method) return;
        
        __weak typeof(self) weakSelf = self;
        WXPerformBlockOnBridgeThread(^(){
            [weakSelf.bridgeCtx executeJsMethod:method];
        });
    }
    

    JS调用native的话需要通过WXJSCoreBridge的registerCallNative方法WXComponent组件基类,自己实现iOS端的组件需要继承它。相关的还有负责组件初始化的工厂类WXComponentFactory,以及WXComponentManagerWXModuleProtocol自定义module需要实现的协议Weex页面iOS端渲染流程首先在ViewController里的render放初始化WXSDKInstance,因为render会支持实时刷新,所以每次都需要先销毁这个实例。

    [_instance destroyInstance];
    _instance = [[WXSDKInstance alloc] init];
    

    然后WXSDKManager会保存instanceId

    [WXSDKManager storeInstance:self forID:_instanceId];
    

    然后会调用renderWithURL方法来载入script,在这里会判断是本地文件还是需要从服务器下载,

    - (void)renderWithURL:(NSURL *)url options:(NSDictionary *)options data:(id)data{
        if ([url isFileURL]) {
            //from local
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSString *path = [url path];
                NSData *scriptData = [[NSFileManager defaultManager] contentsAtPath:path];
                NSString *script = [[NSString alloc] initWithData:scriptData encoding:NSUTF8StringEncoding];
                [weakSelf renderView:script options:newOptions data:data];
            });
        }else{
        	//from server
        }
    }
    

    然后就会根据script文件渲染视图

    [weakSelf renderView:script options:newOptions data:data];
    

    在这个方法里面首先会创建根视图,当创建完成时WXSDKInstance会收到onCreate的回调

    //TODO WXRootView
    WXPerformBlockOnMainThread(^{
        self.rootView = [[WXView alloc] initWithFrame:self.frame];
        if(self.onCreate) {
            self.onCreate(self.rootView);
        }
    });
    

    之后再通过bridge调用JS方法来开始创建实例

    [self callJSMethod:@"createInstance" args:args];
    

    然后这里会判断JSFramework也就是main.js有没有加载完成,然后再通过WXJSBridge的JSContext来执行js方法

    - (void)callJSMethod:(NSString *)method args:(NSArray *)args
    {
        [[_jsContext globalObject] invokeMethod:method withArguments:args];
    }
    

    最后main.js会调用WXSDKInstance的createFinish方法来结束页面的渲染JS调用iOS方法首先要注册一个组件

    [self registerModule:@"dom" withClass:NSClassFromString(@"WXDomModule")];
    

    注册module的时候 会通过下面方法+ (void)registerModule:(NSString *)name withClass:(Class)clazz { WXAssert(name && clazz, @"Fail to register the module, please check if the parameters are correct !"); NSString *moduleName = [WXModuleFactory registerModule:name withClass:clazz]; NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName]; [[WXSDKManager bridgeMgr] registerModules:dict]; } 把所有通过宏注册的方法发送给js端WX_EXPORT_METHOD(@selector(createBody:)) 这会把方法暴露出来,并且方法名字是”wx_export_method_“加代码所在行号,wx_export_method_25组件、模块 是给js端用的,而handler则是给objc自己用的,所以不用发送消息给js端然后通过methodForSelector拿到WX_EXPORT_METHOD方法的返回值,并且保存到methods中

    - (void)registerModuleMethods {
        
        if ([currentClass respondsToSelector:selector]) {
            method = ((NSString* (*)(id, SEL))[currentClass methodForSelector:selector])(currentClass, selector);
        }
        [_methods setObject:method forKey:name];
        
    }
    

    然后拿到WXModuleConfig组成的_moduleMap之后再发送给JS端

    [[WXSDKManager bridgeMgr] registerModules:dict];
    

    最后需要自己callNative的回调,当JS调用时就会传值到这里

    - (void)registerCallNative:(WXJSCallNative)callNative
    {
        NSInteger (^callNativeBlock)(JSValue *, JSValue *, JSValue *) = ^(JSValue *instance, JSValue *tasks, JSValue *callback){
            NSString *instanceId = [instance toString];
            NSArray *tasksArray = [tasks toArray];
            NSString *callbackId = [callback toString];
            
            return callNative(instanceId, tasksArray, callbackId);
        };
        
        _jsContext[@"callNative"] = callNativeBlock;
    }
    

    tasks里面包括方法的一些相关信息,包括module(比如dom),method(比如updateFinish),argsweex-devtool-iOSweex-devtool-iOS 其实是 PonyDebugger的衍生品。使用PonyDebugger调试iOS应用FLEXNetworkObserverhttp://coderyi.com/posts/weex3/

  • 相关阅读:
    springboot实现redis的分布式锁
    剑指offer--二维数组中查找
    剑指offer--二维数组中查找
    对JDK动态代理的模拟实现
    Spring(4)AOP
    设计模式之单例模式(Java)
    【Java并发系列】--Java内存模型
    maven 解决jar包冲突及简单使用
    基于注解的SpringAOP源码解析(三)
    Java代码中可以优化性能的小细节
  • 原文地址:https://www.cnblogs.com/caolei1108/p/6255504.html
Copyright © 2011-2022 走看看