目录
- Weex系列(序) —— 总要知道原生的一点东东(iOS)
- Weex系列(序) —— 总要知道原生的一点东东(Android)
- Weex系列(1) —— Hello World项目
- Weex系列(2) —— 页面跳转和通信
- Weex系列(3) —— 单页面还是多页面
- Weex系列(4) —— 老生常谈的三端统一
- Weex系列(5) —— 封装原生组件和模块
- [Weex系列(6) —— css相关小结]
- [Weex系列(7) —— web组件和webview]
- [Weex系列(8) —— 是时候简析一下流程原理了]
- [Weex系列(9) —— 踩坑填坑的集锦]
- [Weex系列(10) —— 先这么多吧想到再写。。。]
哇,2019年了,时间总是那么快,快过新年了,忙点了也懒点了,还有点想家了,先祝大家新年快乐吧。
这一章官网上有介绍,但还是单独拎出来讲一讲,因为后期这块用的还是挺多的。
官网的所有组件和模块的截图:
在官网 扩展版块,是可以找到封装的方法步骤的。
自定义模块
iOS:
第一步:
新建 myModule.h
#import
#import
@interface myModule : NSObject
@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
- (void)notifyWebview:(NSDictionary *) data;
- (void)reload;
- (void)goBack;
- (void)goForward;
@end
新建myComponent.m
#import "myComponent.h"
#import
#import
#import "WXUtility.h"
#import "WXURLRewriteProtocol.h"
#import "WXSDKEngine.h"
#import
@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 *)baseInfo
{
NSMutableDictionary *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 *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 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 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 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 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里面才能统一的。
小结
1、从上面可以看出不管是组件还是模块,都是要iOS和Android各封装一套的,而且名字还要一致,如果兼容web端,还要做web的扩展,这样才能三端统一的。
2、封装组件的版块,我把weex sdk里面的web组件代码拿出来了,也是为了后面webview章节做铺垫吧。
3、建议大家可以多看看weex sdk的源码,(这里请忘掉我只是一个前端,我干嘛还要学习oc、java的这些想法吧)其实也还好,也可能是目前我们的项目没有太复杂,封装的还不是很多,也还算简单,谷歌上一搜基本都能解决吧。
最后祝大家新的一年,少点bug,多点money,越来越好吧。
如果喜欢就请点个赞收藏一下啦~~~