DaLei 2019-06-30
哇,2019年了,时间总是那么快,快过新年了,忙点了也懒点了,还有点想家了,先祝大家新年快乐吧。
这一章官网上有介绍,但还是单独拎出来讲一讲,因为后期这块用的还是挺多的。
官网的所有组件和模块的截图:
在官网 扩展版块,是可以找到封装的方法步骤的。
iOS:
第一步:
新建 myModule.h
#import <Foundation/Foundation.h> #import <WeexSDK/WXModuleProtocol.h> @interface myModule : NSObject<WXModuleProtocol> @end
新建 myModule.m
#import "myModule.h" @implementation myModule WX_EXPORT_METHOD(@selector(log:)) - (void)log:(NSString *)inputParam { NSLog(@"%@",inputParam); } @end
第二步:
AppDelegate.m里面注册module
[WXSDKEngine registerModule:@"myModule" withClass:[myModule class]];
Android:
第一步:
新建myModule.java
public class MyModule extends WXModule { //run JS thread @JSMethod (uiThread = false) public void log(String inputParam) { Log.d("自定义模块:", inputParam); } }
第二步:
WXApplication.java里面注册module
WXSDKEngine.registerModule("MyModule", MyModule.class);
最后:
在上层vue里面,我们可以require我们自己封装的module,就能够调用原生的log方法,分别在xcode和Android Studio的控制台,看到hello weex的消息了。
这里需要强调一点的是:iOS和Android的module的名字方法要一致,这样在vue里面才能统一的。
weex.requireModule("myModule").log("hello weex")
组件封装起来比模块是麻烦许多的,一开始也是摸不着头脑,后来就找到weexsdk里面封装的组件,依样画葫芦的开始了。
iOS:
第一步:
新建myComponent.h
#import "WXComponent.h" @interface myComponent : WXComponent<UIWebViewDelegate> - (void)notifyWebview:(NSDictionary *) data; - (void)reload; - (void)goBack; - (void)goForward; @end
新建myComponent.m
#import "myComponent.h" #import <WeexSDK/WXComponent.h> #import <WeexSDK/WXComponentManager.h> #import "WXUtility.h" #import "WXURLRewriteProtocol.h" #import "WXSDKEngine.h" #import <JavaScriptCore/JavaScriptCore.h> @interface WXWebView : UIWebView @end @implementation WXWebView - (void)dealloc { if (self) { // self.delegate = nil; } } @end @interface myComponent () @property (nonatomic, strong) JSContext *jsContext; @property (nonatomic, strong) WXWebView *webview; @property (nonatomic, strong) NSString *url; @property (nonatomic, assign) BOOL startLoadEvent; @property (nonatomic, assign) BOOL finishLoadEvent; @property (nonatomic, assign) BOOL failLoadEvent; @property (nonatomic, assign) BOOL notifyEvent; @end @implementation myComponent WX_EXPORT_METHOD(@selector(goBack)) WX_EXPORT_METHOD(@selector(reload)) WX_EXPORT_METHOD(@selector(goForward)) - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance { if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) { self.url = attributes[@"src"]; } return self; } - (UIView *)loadView { return [[WXWebView alloc] init]; } - (void)viewDidLoad { _webview = (WXWebView *)self.view; _webview.delegate = self; _webview.allowsInlineMediaPlayback = YES; _webview.scalesPageToFit = YES; [_webview setBackgroundColor:[UIColor clearColor]]; _webview.opaque = NO; _jsContext = [_webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; __weak typeof(self) weakSelf = self; _jsContext[@"$notifyWeex"] = ^(JSValue *data) { if (weakSelf.notifyEvent) { [weakSelf fireEvent:@"notify" params:[data toDictionary]]; } }; if (_url) { [self loadURL:_url]; } } - (void)updateAttributes:(NSDictionary *)attributes { if (attributes[@"src"]) { self.url = attributes[@"src"]; } } - (void)addEvent:(NSString *)eventName { if ([eventName isEqualToString:@"pagestart"]) { _startLoadEvent = YES; } else if ([eventName isEqualToString:@"pagefinish"]) { _finishLoadEvent = YES; } else if ([eventName isEqualToString:@"error"]) { _failLoadEvent = YES; } } - (void)setUrl:(NSString *)url { NSString* newURL = [url copy]; WX_REWRITE_URL(url, WXResourceTypeLink, self.weexInstance) if (!newURL) { return; } if (![newURL isEqualToString:_url]) { _url = newURL; if (_url) { [self loadURL:_url]; } } } - (void)loadURL:(NSString *)url { if (self.webview) { NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:url]]; [self.webview loadRequest:request]; } } - (void)reload { [self.webview reload]; } - (void)goBack { if ([self.webview canGoBack]) { [self.webview goBack]; } } - (void)goForward { if ([self.webview canGoForward]) { [self.webview goForward]; } } - (void)notifyWebview:(NSDictionary *) data { NSString *json = [WXUtility JSONString:data]; NSString *code = [NSString stringWithFormat:@"(function(){var evt=null;var data=%@;if(typeof CustomEvent==='function'){evt=new CustomEvent('notify',{detail:data})}else{evt=document.createEvent('CustomEvent');evt.initCustomEvent('notify',true,true,data)}document.dispatchEvent(evt)}())", json]; [_jsContext evaluateScript:code]; } #pragma mark Webview Delegate - (NSMutableDictionary<NSString *, id> *)baseInfo { NSMutableDictionary<NSString *, id> *info = [NSMutableDictionary new]; [info setObject:self.webview.request.URL.absoluteString ?: @"" forKey:@"url"]; [info setObject:[self.webview stringByEvaluatingJavaScriptFromString:@"document.title"] ?: @"" forKey:@"title"]; [info setObject:@(self.webview.canGoBack) forKey:@"canGoBack"]; [info setObject:@(self.webview.canGoForward) forKey:@"canGoForward"]; return info; } - (void)webViewDidStartLoad:(UIWebView *)webView { } - (void)webViewDidFinishLoad:(UIWebView *)webView { if (_finishLoadEvent) { NSDictionary *data = [self baseInfo]; [self fireEvent:@"pagefinish" params:data domChanges:@{@"attrs": @{@"src":self.webview.request.URL.absoluteString}}]; } } - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { if (_failLoadEvent) { NSMutableDictionary *data = [self baseInfo]; [data setObject:[error localizedDescription] forKey:@"errorMsg"]; [data setObject:[NSString stringWithFormat:@"%ld", (long)error.code] forKey:@"errorCode"]; NSString * urlString = error.userInfo[NSURLErrorFailingURLStringErrorKey]; if (urlString) { // webview.request may not be the real error URL, must get from error.userInfo [data setObject:urlString forKey:@"url"]; if (![urlString hasPrefix:@"http"]) { return; } } [self fireEvent:@"error" params:data]; } } - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (_startLoadEvent) { NSMutableDictionary<NSString *, id> *data = [NSMutableDictionary new]; [data setObject:request.URL.absoluteString ?:@"" forKey:@"url"]; [self fireEvent:@"pagestart" params:data]; } return YES; } @end
第二步:
AppDelegate.m里面注册component
[WXSDKEngine registerComponent:@"myComponent" withClass:[myComponent class]];
这里需要说明:上面基本上是照着weexsdk里面的webview组件改的,而且就是改了一下名字,方法什么的大家就可以自由发挥了。
Android:
第一步:
新建myComponent.java
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.taobao.weex.ui.component; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; import android.text.TextUtils; import android.view.View; import com.taobao.weex.WXSDKInstance; import com.taobao.weex.annotation.Component; import com.taobao.weex.adapter.URIAdapter; import com.taobao.weex.common.Constants; import com.taobao.weex.dom.WXDomObject; import com.taobao.weex.ui.view.IWebView; import com.taobao.weex.ui.view.WXWebView; import com.taobao.weex.utils.WXUtils; import java.util.HashMap; import java.util.Map; @Component(lazyload = false) public class myComponent extends WXComponent { public static final String GO_BACK = "goBack"; public static final String GO_FORWARD = "goForward"; public static final String RELOAD = "reload"; protected IWebView mWebView; @Deprecated public myComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, String instanceId, boolean isLazy) { this(instance,dom,parent,isLazy); } public myComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) { super(instance, dom, parent, isLazy); createWebView(); } protected void createWebView(){ mWebView = new WXWebView(getContext()); } @Override protected View initComponentHostView(@NonNull Context context) { mWebView.setOnErrorListener(new IWebView.OnErrorListener() { @Override public void onError(String type, Object message) { fireEvent(type, message); } }); mWebView.setOnPageListener(new IWebView.OnPageListener() { @Override public void onReceivedTitle(String title) { if (getDomObject().getEvents().contains(Constants.Event.RECEIVEDTITLE)) { Map<String, Object> params = new HashMap<>(); params.put("title", title); fireEvent(Constants.Event.RECEIVEDTITLE, params); } } @Override public void onPageStart(String url) { if ( getDomObject().getEvents().contains(Constants.Event.PAGESTART)) { Map<String, Object> params = new HashMap<>(); params.put("url", url); fireEvent(Constants.Event.PAGESTART, params); } } @Override public void onPageFinish(String url, boolean canGoBack, boolean canGoForward) { if ( getDomObject().getEvents().contains(Constants.Event.PAGEFINISH)) { Map<String, Object> params = new HashMap<>(); params.put("url", url); params.put("canGoBack", canGoBack); params.put("canGoForward", canGoForward); fireEvent(Constants.Event.PAGEFINISH, params); } } }); return mWebView.getView(); } @Override public void destroy() { super.destroy(); getWebView().destroy(); } @Override protected boolean setProperty(String key, Object param) { switch (key) { case Constants.Name.SHOW_LOADING: Boolean result = WXUtils.getBoolean(param,null); if (result != null) setShowLoading(result); return true; case Constants.Name.SRC: String src = WXUtils.getString(param,null); if (src != null) setUrl(src); return true; } return super.setProperty(key,param); } @WXComponentProp(name = Constants.Name.SHOW_LOADING) public void setShowLoading(boolean showLoading) { getWebView().setShowLoading(showLoading); } @WXComponentProp(name = Constants.Name.SRC) public void setUrl(String url) { if (TextUtils.isEmpty(url) || getHostView() == null) { return; } if (!TextUtils.isEmpty(url)) { loadUrl(getInstance().rewriteUri(Uri.parse(url), URIAdapter.WEB).toString()); } } public void setAction(String action) { if (!TextUtils.isEmpty(action)) { if (action.equals(GO_BACK)) { goBack(); } else if (action.equals(GO_FORWARD)) { goForward(); } else if (action.equals(RELOAD)) { reload(); } } } private void fireEvent(String type, Object message) { if (getDomObject().getEvents().contains(Constants.Event.ERROR)) { Map<String, Object> params = new HashMap<>(); params.put("type", type); params.put("errorMsg", message); fireEvent(Constants.Event.ERROR, params); } } private void loadUrl(String url) { getWebView().loadUrl(url); } private void reload() { getWebView().reload(); } private void goForward() { getWebView().goForward(); } private void goBack() { getWebView().goBack(); } private IWebView getWebView() { return mWebView; } }
第二步:
WXApplication.java里面注册component
WXSDKEngine.registerComponent("myComponent", myComponent.class);
最后:
在上层vue里面,我们就可以直接使用封装好的组件。
这里需要强调一点的是:iOS和Android的组件名字一定要一致,这样在vue里面才能统一的。
<myComponent src="url" class="webview" :style="{ height : screenHeight+'px' }"></myComponent>
1、从上面可以看出不管是组件还是模块,都是要iOS和Android各封装一套的,而且名字还要一致,如果兼容web端,还要做web的扩展,这样才能三端统一的。
2、封装组件的版块,我把weex sdk里面的web组件代码拿出来了,也是为了后面webview章节做铺垫吧。
3、建议大家可以多看看weex sdk的源码,(这里请忘掉我只是一个前端,我干嘛还要学习oc、java的这些想法吧)其实也还好,也可能是目前我们的项目没有太复杂,封装的还不是很多,也还算简单,谷歌上一搜基本都能解决吧。
最后祝大家新的一年,少点bug,多点money,越来越好吧。
如果喜欢就请点个赞收藏一下啦~~~