哇,2019年了,時間老是那麼快,快過新年了,忙點了也懶點了,還有點想家了,先祝你們新年快樂吧。css
這一章官網上有介紹,但仍是單獨拎出來說一講,由於後期這塊用的仍是挺多的。前端
官網的全部組件和模塊的截圖:vue
在官網 擴展版塊,是能夠找到封裝的方法步驟的。java
iOS:android
第一步:web
新建 myModule.hexpress
#import <Foundation/Foundation.h> #import <WeexSDK/WXModuleProtocol.h> @interface myModule : NSObject<WXModuleProtocol> @end
新建 myModule.mapache
#import "myModule.h" @implementation myModule WX_EXPORT_METHOD(@selector(log:)) - (void)log:(NSString *)inputParam { NSLog(@"%@",inputParam); } @end
第二步:json
AppDelegate.m裏面註冊modulesegmentfault
[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>
一、從上面能夠看出無論是組件仍是模塊,都是要iOS和Android各封裝一套的,並且名字還要一致,若是兼容web端,還要作web的擴展,這樣才能三端統一的。
二、封裝組件的版塊,我把weex sdk裏面的web組件代碼拿出來了,也是爲了後面webview章節作鋪墊吧。
三、建議你們能夠多看看weex sdk的源碼,(這裏請忘掉我只是一個前端,我幹嗎還要學習oc、java的這些想法吧)其實也還好,也多是目前咱們的項目沒有太複雜,封裝的還不是不少,也還算簡單,谷歌上一搜基本都能解決吧。
最後祝你們新的一年,少點bug,多點money,愈來愈好吧。
若是喜歡就請點個贊收藏一下啦~~~