Weex學習與實踐

Weex學習與實踐(一):Weex,你須要知道的事javascript

本文主要介紹包括Weex基本介紹、Weex源碼結構、初始化工程、we代碼結構、Weex的生命週期、Weex的工做原理、頁面間通訊、boxmodel & flexbox、weex的缺點css

基本介紹

A framework for building Mobile cross-platform UIhtml

怎麼解釋它呢?個人理解就是weex = react-native +vue ,使用vue的API風格,兩端的實現方式則和react-native,weex 比rn的優勢就是一次編寫三端運行。vue

IDE: Sublime Text + vue-syntax-highlighthtml5

命令行工具:weex-toolkitjava

調試工具: weex-devtoolnode

weex分爲組件component和模塊(module)以及事件react

weex-components : weex-componentsandroid

組件 就是各類標記組件,好比div 、slider、indicator等 經過下面這種方式使用webpack

 

<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.json

node_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.js

start文件: 啓動程序文件,裏面包換編譯和啓動腳本:

examples: 示例Demo

android/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 –watch

webpack其實是執行了默認的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.js

duqian291902259 - webpack.config.js 第3篇 初始化工程

入口文件index.html

這裏能夠參考 Integrate Weex HTML5 to your project

weex_extend_demo

we代碼結構

template內必須包含惟一的根節點做爲父容器, div就是一個很好的選擇,裏面則是一些Native Components

style 支持盒子模型和Flexbox

weex內置了響應式的支持,頁面的寬度是以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 解析工具:htmlparser

CSS 解析工具:cssom

JavaScript 解析工具: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 & flexbox

weex支持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-image

4.weex-x的使用?

5.promise怎麼使用?

issues - 1269

6.熱更新方案是什麼?

最後,但願有愈來愈多的人把weex用起來。

轉載請注原文連接:http://coderyi.com/posts/weex1/

 

Weex學習與實踐(二):iOS集成的tips

本文主要介紹包括iOS上集成Weex、iOS上擴展組件、iOS上擴展module

iOS上集成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="width: 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/

 

 

三、Weex學習與實踐(三):iOS原理篇

本文主要介紹包括WeexSDK-iOS主要類介紹、Weex頁面iOS端渲染流程、JS調用iOS方法

主要類

WXSDKEngine

WXSDKEngine主要用於初始化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 //經過視圖索引拿到對應的組件視圖

WXBridgeManager

WXBridgeManager 是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,以及WXComponentManager

WXModuleProtocol

自定義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),args

weex-devtool-iOS

weex-devtool-iOS 實際上是 PonyDebugger的衍生品。

使用PonyDebugger調試iOS應用

FLEXNetworkObserver

轉載請注原文連接:http://coderyi.com/posts/weex3/

相關文章
相關標籤/搜索