eros widget提供了請求,路由,本地儲存,原生導航,發佈訂閱等功能,下面將從請求功能去分析eros widget源碼實現。前端
在eors官網中介紹的請求的定義是axios,前端的開發者看到這個會以爲很熟悉,由於有個著名的前端開源Http請求框架也叫做這個名字,我剛開始看到這個也是很是興奮,由於axios開源框架功能很強大,因此以爲基於業務的Http請求封裝很easy,能夠直接複用了,可是用了以後發現不是那麼回事,後面研究了源碼以後,才知道eros只是把請求功能叫axios這個名字,可是跟開源框架axios背後實現原理,實現功能徹底不同!因此你們不要誤解了!vue
// 配置方法
import './src/mixins.js'
import './src/font.js'
// 彈窗
import './src/notice.js'
// 獲取地理位置
import './src/geo.js'
// 獲取相機信息
import './src/camera.js'
// 圖片操做相關
import './src/image.js'
// 設置導航
import './src/nav.js'
// 支付相關(已抽離第三方插件)
// import './src/pay.js'
// bindingx
import './src/bindingx.js'
// 存儲相關
import './src/storage.js'
// 全局事件
import './src/events.js'
// 分享(已抽離第三方插件)
// import './src/share.js'
// 工具方法
import './src/tools.js'
import './src/coms.js'
// 路由
import Router from './src/router.js'
// 發送請求
import Axios from './src/axios.js'
let instance = null
export default class Widget {
constructor ({ router, ajax }) {
if (!instance) {
//這裏是咱們須要關注的,這裏建立的請求對象,而且掛載在Vue裏
Vue.use(new Axios(ajax))
Vue.use(new Router(router))
instance = this
}
return instance
}
}
複製代碼
2.代碼咱們能夠很清楚的看到,整個widget的功能都是在這裏被引入的,相關的配置信息也是經過widget的構造函數傳到對應功能模塊的。咱們能夠看到咱們分析的Axios定義就在./src/axios.js這個文件裏。繼續往下分析axios.js代碼:java
import _isFunction from 'lodash/isFunction'
const bmAxios = weex.requireModule('bmAxios')//請求的真正執行者,這裏對應一個weex原生自定義module,註冊名字叫bmAxios
export default class Axios {
//構造函數,經過外部傳遞請求相關配置參數
constructor ({ timeout, apis, baseUrl = '', requestHandler, responseHandler }) {
this.apis = apis //apis是配置請求別名的到路徑的映射,別名經過它能夠找到真實路徑
this.timeout = timeout//請求的超時時間
this.baseUrl = baseUrl//請求的基礎url
this.requestHandler = requestHandler//請求的攔截器
this.responseHandler = responseHandler//響應的攔截器
return this
}
//vue自定義組件安裝函數,前面說的vue.use()會調用該函數進行自定義組件的添加
install (Vue) {
/**
* Contributor: Eric Xiao.
* Description: extend promise.
* Eros thanks every contributor.
*/
Promise.prototype.finally = function (callback) {
const P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// Throw a global error
setTimeout(() => { throw reason }, 0);
});
};
const self = this
//此處是在vue原型中掛載一個$fetch函數,掛載以後在vue實例中能夠經過this.$fetch進行調用該函數,該函數是eros http請求的調用函數,支持多種http請求方式。
Vue.prototype.$fetch = (options) => {
return new Promise((resolve, reject) => {
//判斷requestHandle是不是函數,若是是函數則先執行requestHandle對請求進行處理,處理完成後再由handleAxios處理,不然直接由handleAxios處理。
if (_isFunction(self.requestHandler)) {
self.requestHandler(options, () => {
//統一的http功能處理
handleAxios(options, resolve, reject)
})
} else {
//統一的http功能處理
handleAxios(options, resolve, reject)
}
})
}
//vue原型上提供this.$get發起get請求的調用,邏輯同上
Vue.prototype.$get = (options) => {
options.method = 'GET'
return new Promise((resolve, reject) => {
if (_isFunction(self.requestHandler)) {
self.requestHandler(options, () => { handleAxios(options, resolve, reject) })
} else {
handleAxios(options, resolve, reject)
}
})
}
//vue原型上提供this.$post發起Post請求的調用,邏輯同上
Vue.prototype.$post = (options) => {
options.method = 'POST'
return new Promise((resolve, reject) => {
if (_isFunction(self.requestHandler)) {
self.requestHandler(options, () => { handleAxios(options, resolve, reject) })
} else {
handleAxios(options, resolve, reject)
}
})
}
//請求js端真正處理者
function handleAxios (options, resolve, reject) {
//首先對請求參數進行解析
const { name, url, data, method, header, params } = options
//經過name去apis配置裏找真正請求路徑
const requestPath = name && pathFormater(name, params)
//這裏纔是真正的請求功能的發起者,經過eros自定義weex的bmAxios原生模塊真正發起請求,經過傳遞迴調函數給原生從而在請求執行完以後獲取到請求結果,再由promise對結果進行分發,若是本身定義了responseHandler函數,則由調用者在responseHandler函數中根據http status和業務code去分發請求結果
bmAxios.fetch({
url: url || (self.baseUrl + requestPath),
data: data || {},
method: method && method.toUpperCase() || 'GET',
header: header || {},
timeout: self.timeout || 30000
}, (resData) => {
// 統一的監控
if (_isFunction(self.responseHandler)) {
self.responseHandler(options, resData, resolve, reject)
} else {
resolve(resData)
}
})
}
//此函數是用來根據name去映射apis路徑,以及對路徑參數進行賦值替換
function pathFormater (name, params) {
let _path = self.apis[name]
const matcher = _path.match(/[^{][a-zA-Z0-9]+(?=\})/g)
if (matcher && matcher.length) {
matcher.forEach(item => {
if (!params[item]) throw new Error(`you are using dynamic params, but ${item} not existed in your params`)
_path = _path.replace(`{${item}}`, params[item] || 'undefined')
})
}
return _path
}
}
}
複製代碼
3.經過上面的代碼分析咱們知道了在vue中經過this.$fetch調用真正的js處理邏輯,實際上js只是對請求參數進行定義和簡單功能的封裝。真正實現http調用處仍是在原生,這個原生是相對的,若是jsbundle是運行在android上則是經過android weex模塊去調用,若是運行在ios則經過ios weex模塊去調用。android
package com.benmu.framework.extend.module;
import com.benmu.framework.constant.WXConstant;
import com.benmu.framework.constant.WXEventCenter;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.WeexEventBean;
import com.taobao.weex.annotation.JSMethod;
import com.taobao.weex.bridge.JSCallback;
import com.taobao.weex.common.WXModule;
/**
* Created by Carry on 17/1/16.
*/
public class AxiosModule extends WXModule {
@JSMethod(uiThread = false)
public void fetch(String params, final JSCallback jsCallback) {
WeexEventBean eventBean = new WeexEventBean();
eventBean.setContext(mWXSDKInstance.getContext());
eventBean.setKey( WXEventCenter.EVENT_FETCH);
eventBean.setJsParams(params);
eventBean.setJscallback(jsCallback);
ManagerFactory.getManagerService(DispatchEventManager.class).getBus().post
(eventBean);
}
//其餘省略
....
}
複製代碼
package com.benmu.framework.event;
import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import com.benmu.framework.adapter.router.RouterTracker;
import com.benmu.framework.constant.WXEventCenter;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.BaseEventBean;
import com.benmu.framework.model.WeexEventBean;
imporot cm.benmu.framework.utils.JsPoster;
import com.benmu.wxbase.EventGate;
import com.benmu.wxbase.EventGateFactory;
import com.squareup.otto.Subscribe;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.bridge.JSCallback;
import java.util.ArrayList;
/**
* Created by Carry on 2017/8/23.
*/
public class DispatchEventCenter {
//這裏DispatchEventCenter建立了一個單例對象
private static DispatchEventCenter mInstance = new DispatchEventCenter();
private DispatchEventCenter() {
}
public static DispatchEventCenter getInstance() {
return mInstance;
}
//這裏對DispatchEventCenter對象進行註冊,註冊以後能夠接收Bus分發的事件
public void register() {
ManagerFactory.getManagerService(DispatchEventManager.class).getBus().register(this);
}
//這裏對DispatchEventCenter對象進行解注,解注以後取消接收Bus分發的事件
public void unregister() {
ManagerFactory.getManagerService(DispatchEventManager.class).getBus().unregister(this);
}
//這裏是WeexEventBean事件的接收,全部eros module post出來的WeexEventBean類型對象都在這裏接收以後進行轉發
@Subscribe
public void onWeexEvent(WeexEventBean weexEventBean) {
if (weexEventBean == null) return;
Context context = safeContext(weexEventBean.getContext());
if (context == null) return;
String params = weexEventBean.getJsParams();
switch (weexEventBean.getKey()) {
case WXEventCenter.EVENT_IMAGE_UPLOAD:
//這裏就是咱們要找的key爲EVENT_FETCH的處理地方
case WXEventCenter.EVENT_FETCH:
//這裏咱們直接傳遞了一個class絕對路徑的string,此處的作法值得商榷。下面咱們繼續看reflectionClazzPerform方法
reflectionClazzPerform("com.benmu.framework.event.http.EventFetch", context
, weexEventBean
, "", weexEventBean.getKey());
break;
...中間省略
default:
reflectionClazzPerform(weexEventBean.getKey()
, context
, weexEventBean
, "");
break;
}
}
//真正反射建立對象的調用邏輯在這個函數裏
private void reflectionClazzPerform(String clazzName, Context context, WeexEventBean weexEventBean, String errosMsg, String type) {
//這裏EventGateFactory根據clazzName反射去建立對象,可是爲啥對象是EventGate類型的呢,由於eros全部的Event都實現了EventGate接口,因此建立出來的對象都是EventGate實現類的對象。
EventGate event = EventGateFactory.getEventGate(clazzName);
String params = weexEventBean.getJsParams();
if (null != event) {
//根據上面調用邏輯,type爲WXEventCenter.EVENT_FETCH,不爲空,直接走else邏輯
if (TextUtils.isEmpty(type)) {
event.perform(context, weexEventBean);
} else {
//event.perform是全部實現EventGate接口的Event對象執行功能的入口,本文分析的是com.benmu.framework.event.http.EventFetch這個類,下面我去找EventFetch對象的perform方法。
event.perform(context, weexEventBean, type);
}
} else {
//若是找不到對應的EVENT處理類,則給js回調失敗處理
if (TextUtils.isEmpty(params)) {
JsPoster.postFailed(weexEventBean.getJscallback());
} else {
JsPoster.postFailed(errosMsg, weexEventBean.getJscallback());
}
}
}
}
複製代碼
package com.benmu.framework.event.http;
import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.benmu.framework.constant.Constant;
import com.benmu.framework.constant.WXEventCenter;
import com.benmu.framework.http.okhttp.OkHttpUtils;
import com.benmu.framework.http.okhttp.callback.StringCallback;
import com.benmu.framework.http.okhttp.exception.CancelException;
import com.benmu.framework.http.okhttp.exception.HttpException;
import com.benmu.framework.http.okhttp.exception.IrregularUrlException;
import com.benmu.framework.http.okhttp.utils.L;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.AxiosManager;
import com.benmu.framework.manager.impl.ImageManager;
import com.benmu.framework.manager.impl.ModalManager;
import com.benmu.framework.manager.impl.ParseManager;
import com.benmu.framework.manager.impl.PersistentManager;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.AxiosGet;
import com.benmu.framework.model.AxiosPost;
import com.benmu.framework.model.AxiosResultBean;
import com.benmu.framework.model.UploadImageBean;
import com.benmu.framework.model.UploadResultBean;
import com.benmu.framework.model.WeexEventBean;
import com.benmu.framework.utils.JsPoster;
import com.benmu.framework.utils.TextUtil;
import com.benmu.wxbase.EventGate;
import com.lzy.imagepicker.bean.ImageItem;
import com.squareup.otto.Subscribe;
import com.taobao.weex.bridge.JSCallback;
import java.util.ArrayList;
import okhttp3.Call;
/**
* Created by Carry on 2017/8/16.
*/
public class EventFetch extends EventGate {
private JSCallback mUploadAvatar;
private Context mUploadContext;
//根據上面分析執行到這裏
@Override
public void perform(Context context, WeexEventBean weexEventBean, String type) {
String params = weexEventBean.getJsParams();pe
//此處type爲WXEventCenter.EVENT_FETCH,因此直接執行if裏面的fetch方法
if (WXEventCenter.EVENT_FETCH.equals(type)) {
fetch(params, context, weexEventBean.getJscallback());
} else if (WXEventCenter.EVENT_IMAGE_UPLOAD.equals(type)) {
uploadImage(params, context, weexEventBean.getJscallback());
}
}
//js端fetch請求分發到這裏
public void fetch(String params, final Context context, final JSCallback jscallback) {
//建立參數解析管理類
ParseManager parseManager = ManagerFactory.getManagerService(ParseManager.class);
//建立Axios執行管理類
AxiosManager axiosManager = ManagerFactory.getManagerService(AxiosManager.class);
//把js 參數解析成Json對象
JSONObject object = parseManager.parseObject(params);
//獲取請求的Url
final String mUrl = object.getString("url");
Boolean noRepeat = object.getBoolean("noRepeat");
//若是重複發了則取消上一個請求
if (noRepeat != null && noRepeat) {
axiosManager.cancel(mUrl);
}
//此處提供了全部的restful請求方法,說明js端能夠發起全部的restful請求,根據對應的method進行分發處理
switch (object.getString("method")) {
case OkHttpUtils.METHOD.GET:
AxiosGet axiosGet = parseManager.parseObject(params, AxiosGet.class);
get(context, axiosManager, axiosGet, jscallback);
break;
case OkHttpUtils.METHOD.POST:
AxiosPost axiosPost = parseManager.parseObject(params, AxiosPost.class);
post(context, axiosManager, axiosPost, jscallback);
break;
case OkHttpUtils.METHOD.HEAD:
AxiosGet axiosHead = parseManager.parseObject(params, AxiosGet.class);
head(context, axiosManager, axiosHead, jscallback);
break;
case OkHttpUtils.METHOD.DELETE:
AxiosPost axiosDelete = parseManager.parseObject(params, AxiosPost.class);
delete(context, axiosManager, axiosDelete, jscallback);
break;
case OkHttpUtils.METHOD.PUT:
AxiosPost axiosPut = parseManager.parseObject(params, AxiosPost.class);
put(context, axiosManager, axiosPut, jscallback);
break;
case OkHttpUtils.METHOD.PATCH:
AxiosPost axiosPatch = parseManager.parseObject(params, AxiosPost.class);
patch(context, axiosManager, axiosPatch, jscallback);
break;
}
}
//暫時只分析get請求,其餘是同樣的。
private void get(final Context context, AxiosManager axiosManager, AxiosGet axiosGet, final
JSCallback jscallback) {
//此處只是調用axiosManager.get()方法,真正的http調用發起邏輯是在axiosManager類裏面,經過給該方法傳遞StringCallback()回調獲取到http執行以後的結果,而後對結果進行解析以後回調到Js端。
axiosManager.get(axiosGet.url, axiosGet.data, axiosGet.header, new
StringCallback() {
@Override
public void onError(Call call, Exception e, int id) {
//請求出現error以後的處理
parseError(context, e, jscallback);
}
@Override
public void onResponse(String response, int id) {
//請求成功以後的處理
parseResponse(response, jscallback, code);
}
}, axiosGet.url, axiosGet.timeout);
}
//請求錯誤的解析
private void parseError(Context context, Exception e, JSCallback callback) {
if (e instanceof CancelException) {
//request canceled
ModalManager.BmLoading.dismissLoading(context);
return;
}
AxiosResultBean bean = new AxiosResultBean();
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
bean.status = httpException.getmErrorCode();
bean.errorMsg = httpException.getmErrorMessage();
} else if (e instanceof IrregularUrlException) {
IrregularUrlException irregularUrlException = (IrregularUrlException) e;
bean.status = 9;
bean.errorMsg = irregularUrlException.getmErrosMeeage();
}
//請求錯誤以後給js端回調,回調的data爲AxiosResultBean,到這裏js——>java——>js 整個調用過程結束了
if (callback != null) {
callback.invoke(bean);
}
}
//請求正常的解析
private void parseResponse(String response, JSCallback callBack, int code) {
try {
AxiosResultBean bean = new AxiosResultBean();
if (callBack != null && !TextUtils.isEmpty(response)) {
bean.status = code;
bean.errorMsg = "";
bean.data = JSON.parse(response);
//請求正常返回以後給js端回調,回調的data爲AxiosResultBean,到這裏js——>java——>js 整個調用過程結束了
callBack.invoke(bean);
}
} catch (Exception e) {
e.printStackTrace();
L.e("json 解析錯誤");
AxiosResultBean bean = new AxiosResultBean();
bean.status = -1;
bean.errorMsg = "json 解析錯誤";
if (callBack != null) {
//請求錯誤以後給js端回調,回調的data爲AxiosResultBean,到這裏js——>java——>js 整個調用過程結束了
callBack.invoke(bean);
}
}
}
}
複製代碼
package com.benmu.framework.manager.impl;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import com.benmu.framework.BMWXEnvironment;
import com.benmu.framework.extend.adapter.WeexOkhttp3Interceptor;
import com.benmu.framework.http.Api;
import com.benmu.framework.http.BMPersistentCookieStore;
import com.benmu.framework.http.okhttp.OkHttpUtils;
import com.benmu.framework.http.okhttp.builder.GetBuilder;
import com.benmu.framework.http.okhttp.builder.OkHttpRequestBuilder;
import com.benmu.framework.http.okhttp.builder.OtherRequestBuilder;
import com.benmu.framework.http.okhttp.builder.PostFormBuilder;
import com.benmu.framework.http.okhttp.callback.FileCallBack;
import com.benmu.framework.http.okhttp.callback.StringCallback;
import com.benmu.framework.http.okhttp.cookie.CookieJarImpl;
import com.benmu.framework.http.okhttp.exception.IrregularUrlException;
import com.benmu.framework.http.okhttp.log.LoggerInterceptor;
import com.benmu.framework.manager.Manager;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.UploadResultBean;
import com.benmu.framework.utils.AppUtils;
import com.benmu.framework.utils.DebugableUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
/**
* Created by Carry on 2017/8/7. default implement by okHttpUtils
*/
public class AxiosManager extends Manager {
private static final String DEFAULT_MEDIATYPE = "application/json; charset=utf-8";
private static final String DEFAULT_HOST = "http://app.weex-eros.com";
//建立OkHttpClient對象
public OkHttpClient createClient(Context context, long timeout) {
CookieJarImpl cookieJar = new CookieJarImpl(new BMPersistentCookieStore
(context));
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new LoggerInterceptor("TAG"))
//接口超時時間 默認3000毫秒
.connectTimeout(timeout == 0 ? 3000L : timeout, TimeUnit.MILLISECONDS)
.readTimeout(timeout == 0 ? 30000L : timeout, TimeUnit.MILLISECONDS).cookieJar
(cookieJar);
if (DebugableUtil.isDebug()) {
builder.addNetworkInterceptor(new WeexOkhttp3Interceptor());
}
return builder.build();
}
public void initClient(Context context) {
OkHttpUtils.initClient(createClient(context, 0));
}
public void cancel(Object tag) {
}
//上面EventFetch get請求調用到這裏,這裏是真正發起請求的地方,最終經過okhttpUtil發送的請求。
public void get(String mUrl, HashMap<String, String> params, HashMap<String, String> header,
StringCallback stringCallback, Object tag, long timeout) {
mUrl = safeUrl(mUrl);
if (mUrl == null) {
if (stringCallback != null) {
stringCallback.onError(null, new IrregularUrlException("url不合法"), 0);
}
return;
}
if (header == null) {
header = new HashMap<>();
}
setTimeout(timeout);
//此處使用的是對okhttp進行封裝的OkHttpUtils框架發送的get請求,並經過stringCallback回調真正的請求結果。
GetBuilder builder = OkHttpUtils.get().url(mUrl).tag(tag).headers(header);
generateParams(params, builder);
builder.build().execute(stringCallback);
}
private void setTimeout(long timeout) {
if (timeout != 0) {
OkHttpUtils.getInstance().updateHttpClient(createClient(BMWXEnvironment
.mApplicationContext, timeout));
}
}
private RequestBody createRequestBodyByMediaType(Map<String, String> header, String content) {
if (header != null && !TextUtils.isEmpty(header.get("Content-Type"))) {
String s = header.get("Content-Type");
MediaType mediaType = null;
try {
mediaType = MediaType.parse(s);
} catch (Exception e) {
e.printStackTrace();
}
if (mediaType == null) {
mediaType = MediaType.parse(DEFAULT_MEDIATYPE);
}
return RequestBody.create(mediaType, content);
}
return RequestBody.create(MediaType.parse(DEFAULT_MEDIATYPE), content);
}
private String safeUrl(String origin) {
if (origin == null) return null;
Uri parse = Uri.parse(origin);
StringBuilder builder = new StringBuilder();
Uri requestHost = Uri.parse(TextUtils.isEmpty(Api.BASE_URL) ? DEFAULT_HOST : Api.BASE_URL);
if (TextUtils.isEmpty(parse.getScheme())) {
builder.append(requestHost.getScheme());
} else {
builder.append(parse.getScheme());
}
builder.append("://");
if (TextUtils.isEmpty(parse.getHost())) {
builder.append(requestHost.getHost());
} else {
builder.append(parse.getHost());
}
if (parse.getPort() != -1) {
builder.append(":").append(parse.getPort());
}
if (!TextUtils.isEmpty(parse.getPath())) {
builder.append(origin.substring(origin.indexOf(parse.getPath())));
}
String finalUrl = builder.toString();
return HttpUrl.parse(finalUrl) == null ? null : builder.toString();
}
private void generateParams(Map<String, String> params, OkHttpRequestBuilder builder) {
if (params == null) {
params = new HashMap<>();
}
if (builder instanceof GetBuilder) {
GetBuilder getBuilder = (GetBuilder) builder;
for (Map.Entry<String, String> entry : params.entrySet()) {
getBuilder.addParams(entry.getKey().trim(), entry.getValue().trim());
}
}
}
/**
* 組合返回給前端的Js 數據
*/
public UploadResultBean resultBean(int code, String message, ArrayList<String> arrayList) {
UploadResultBean uploadResultBean = new UploadResultBean();
uploadResultBean.resCode = code;
uploadResultBean.msg = message;
uploadResultBean.setData(arrayList);
return uploadResultBean;
}
}
複製代碼
整個eros js端和原生功能封裝的比較完整,基本上是按照weex原生功能風格去擴展的。對於前端同窗進行純weex項目開發來講,eros提供了一站式解決方案仍是比原生weex開發方便不少。可是也有一些不足的地方,好比說在js 端在非Vue實例中調用widget功能(儲存功能可能js裏調用)仍是稍微有點不方便,須要本身去擴展。還有提供的http模塊可配置性和攔截器功能比原生js http框架弱不少,對於複雜的js請求處理起來很彆扭,須要本身擴展等等,android原生端實現模塊功能切分的比較清晰,不過感受整個調用邏輯有點繞,還有就是經過絕對路徑Key去反射建立對象這個作法有隱患。固然,可以在weex原生的基礎上擴展出來這麼多功能,仍是比較讚的,不少地方仍是值得學習的!ios
下次分析一下eros是如何在weex多頁應用中插入js widget功能,多頁應用中如何在js端進行全局生命週期監聽和全局配置處理。git