Weex系列(5) —— 封装原生组件和模块

58次阅读

共计 11071 个字符,预计需要花费 28 分钟才能阅读完成。

目录

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 <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,越来越好吧。
如果喜欢就请点个赞收藏一下啦~~~

正文完
 0