让你的iOS应用程序支持运行JavaScript脚本:JavaScriptCore框架详解(四)

简介: 让你的iOS应用程序支持运行JavaScript脚本:JavaScriptCore框架详解

八、Hybird App 构建思路


   Hybird App是指混合模式移动应用,即其中既包含原生的结构有内嵌有Web的组件。这种App不仅性能和用户体验可以达到和原生所差无几的程度,更大的优势在于bug修复快,版本迭代无需发版。3月8日苹果给许多开发者发送了一封警告邮件,主要是提示开发者下载脚本动态更改App原本行为的做法将会被提审拒绝。其实这次邮件所提内容和Hybird App并无太大关系(对ReactNative也没有影响),苹果警告的是网络下发脚本并且使用runtime动态修改Native行为的应用,Hybird App的实质并没有修改原Native的行为,而是将下发的资源进行加载和界面渲染,类似WebView。


   关于混合开发,我们有两种模式:


   1.Native内嵌WebView,通过JS与OC交互实现业务无缝的衔接。


   无论是UIWebView还是WKWebKit,我们都可以在其中拿到当前的JSContext,然是使用前面介绍的方法便可以实现数据互通与交互。这种方式是最简单的混合开发,但其性能和原生相比要差一些。示意图如下:


image.png


   2.下发JS脚本,使用类似ReactNative的框架进行原生渲染


   这是一种效率非常高的混合开发模式,并且ReactNative也本身支持android和iOS公用一套代码。我们也可以使用JavaScriptCore自己实现一套解析逻辑,使用JavaScript来编写Native应用,要完整实现这样一套东西太复杂了,我们也没有能力完成一个如此庞大的工程,但是我们可以做一个小Demo来模拟其原理,这样可以更好的帮助我们理解Hybird App的构建原理。


我们打算实现这样的功能:通过下发JS脚本创建原生的UILabel标签与UIButton控件,首先编写JS代码如下:


(function(){

   console.log("ProgectInit");

   //JS脚本加载完成后 自动render界面

   return render();

})();


//JS标签类

function Label(rect,text,color){

   this.rect = rect;

   this.text = text;

   this.color = color;

   this.typeName = "Label";

}

//JS按钮类

function Button(rect,text,callFunc){

   this.rect = rect;

   this.text = text;

   this.callFunc = callFunc;

   this.typeName = "Button";

}

//JS Rect类

function Rect(x,y,width,height){

   this.x = x;

   this.y = y;

   this.width = width;

   this.height = height;

}

//JS颜色类

function Color(r,g,b,a){

   this.r = r;

   this.g = g;

   this.b = b;

   this.a = a;

}

//渲染方法 界面的渲染写在这里面

function render(){

   var rect = new Rect(20,100,280,30);

   var color = new Color(1,0,0,1);

   var label = new Label(rect,"Hello World",color);

   var rect2 = new Rect(20,150,280,30);

   var color2 = new Color(0,1,0,1);

   var label2 = new Label(rect2,"Hello Native",color2);

   var rect3 = new Rect(20,200,280,30);

   var color3 = new Color(0,0,1,1);

   var label3 = new Label(rect3,"Hello JavaScript",color3);

   var rect4 = new Rect(20,240,280,30);

   var button = new Button(rect4,"我是一个按钮",function(){

                           var randColor = new Color(Math.random(),Math.random(),Math.random(),1);

                           Globle.changeBackgroundColor(randColor);

                           });

   //将控件以数组形式返回

   return [label,label2,label3,button];

}


创建一个Objective-C类绑定到JS全局对象上,作为OC方法的桥接器:


//.h

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

#import <JavaScriptCore/JavaScriptCore.h>

@protocol GloblePrptocol <JSExport>

-(void)changeBackgroundColor:(JSValue *)value;

@end

@interface Globle : NSObject<GloblePrptocol>

@property(nonatomic,weak)UIViewController * ownerController;

@end

//.m

#import "Globle.h"


@implementation Globle

-(void)changeBackgroundColor:(JSValue *)value{

   self.ownerController.view.backgroundColor = [UIColor colorWithRed:value[@"r"].toDouble green:value[@"g"].toDouble blue:value[@"b"].toDouble alpha:value[@"a"].toDouble];

}

@end

在ViewController中实现一个界面渲染的render解释方法,并建立按钮的方法转换,如下:


//

//  ViewController.m

//  JavaScriptCoreTest

//

//  Created by vip on 17/3/6.

//  Copyright © 2017年 jaki. All rights reserved.

//


#import "ViewController.h"

#import <JavaScriptCore/JavaScriptCore.h>

#import "Globle.h"

@interface ViewController ()


@property(nonatomic,strong)JSContext * jsContext;

@property(nonatomic,strong)NSMutableArray * actionArray;

@property(nonatomic,strong)Globle * globle;

@end


@implementation ViewController


- (void)viewDidLoad {

   [super viewDidLoad];

   //创建JS运行环境

   self.jsContext = [JSContext new];

   //绑定桥接器

   self.globle =  [Globle new];

   self.globle.ownerController = self;

   self.jsContext[@"Globle"] = self.globle;

   self.actionArray = [NSMutableArray array];

   [self render];

}

//界面渲染解释器

-(void)render{

   NSString * path = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"js"];

   NSData * jsData = [[NSData alloc]initWithContentsOfFile:path];

   NSString * jsCode = [[NSString alloc]initWithData:jsData encoding:NSUTF8StringEncoding];

   JSValue * jsVlaue = [self.jsContext evaluateScript:jsCode];

   for (int i=0; i<jsVlaue.toArray.count; i++) {

       JSValue * subValue = [jsVlaue objectAtIndexedSubscript:i];

       if ([[subValue objectForKeyedSubscript:@"typeName"].toString isEqualToString:@"Label"]) {

           UILabel * label = [UILabel new];

           label.frame = CGRectMake(subValue[@"rect"][@"x"].toDouble, subValue[@"rect"][@"y"].toDouble, subValue[@"rect"][@"width"].toDouble, subValue[@"rect"][@"height"].toDouble);

           label.text = subValue[@"text"].toString;

           label.textColor = [UIColor colorWithRed:subValue[@"color"][@"r"].toDouble green:subValue[@"color"][@"g"].toDouble blue:subValue[@"color"][@"b"].toDouble alpha:subValue[@"color"][@"a"].toDouble];

           [self.view addSubview:label];

       }else if ([[subValue objectForKeyedSubscript:@"typeName"].toString isEqualToString:@"Button"]){

           UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem];

           button.frame = CGRectMake(subValue[@"rect"][@"x"].toDouble, subValue[@"rect"][@"y"].toDouble, subValue[@"rect"][@"width"].toDouble, subValue[@"rect"][@"height"].toDouble);

           [button setTitle:subValue[@"text"].toString forState:UIControlStateNormal];

           button.tag = self.actionArray.count;

           [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];

           [self.actionArray addObject:subValue[@"callFunc"]];

           [self.view addSubview:button];

         

       }

   }

}

//按钮转换方法

-(void)buttonAction:(UIButton *)btn{

   JSValue * action  = self.actionArray[btn.tag];

   //执行JS方法

   [action callWithArguments:nil];

}




@end

运行工程,效果如下图所示,点击按钮即可实现简单的界面颜色切换:

image.png



上面的示例工程我只实现了UILabel类与UIButton类的JS-OC转换,如果将原生控件和JS对象再进行一层绑定,并且实现大部分JS类与原生类和他们内部的属性,则我们就开发了一套Hybird App开发框架,但并没有这个必要,如果你对更多兴趣,可以深入学习下ReactNative。


   文中的示例Demo我放在了Github上,地址如下:https://github.com/ZYHshao/Demo-Hybird

目录
相关文章
|
29天前
|
JavaScript 前端开发 中间件
探索后端技术:Node.js与Express框架的完美融合
【10月更文挑战第7天】 在当今数字化时代,Web应用已成为日常生活不可或缺的一部分。本文将深入探讨后端技术的两大重要角色——Node.js和Express框架,分析它们如何通过其独特的特性和优势,为现代Web开发提供强大支持。我们将从Node.js的非阻塞I/O和事件驱动机制,到Express框架的简洁路由和中间件特性,全面解析它们的工作原理及应用场景。此外,本文还将分享一些实际开发中的小技巧,帮助你更有效地利用这些技术构建高效、可扩展的Web应用。无论你是刚入门的新手,还是经验丰富的开发者,相信这篇文章都能为你带来新的启发和思考。
|
6天前
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
在浏览器执行js脚本的两种方式
|
25天前
|
JavaScript 前端开发 API
Vue.js:现代前端开发的强大框架
【10月更文挑战第11天】Vue.js:现代前端开发的强大框架
63 41
|
2天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端框架
【10月更文挑战第34天】在数字化时代,后端开发如同一座桥梁,连接着用户界面与数据处理的两端。本文将通过Node.js这一轻量级、高效的平台,带领读者领略后端框架的魅力。我们将从基础概念出发,逐步深入到实战应用,最后探讨如何通过代码示例来巩固学习成果,使读者能够在理论与实践之间架起自己的桥梁。
|
4天前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
|
8天前
|
JavaScript 中间件 API
Node.js进阶:Koa框架下的RESTful API设计与实现
【10月更文挑战第28天】本文介绍了如何在Koa框架下设计与实现RESTful API。首先概述了Koa框架的特点,接着讲解了RESTful API的设计原则,包括无状态和统一接口。最后,通过一个简单的博客系统示例,详细展示了如何使用Koa和koa-router实现常见的CRUD操作,包括获取、创建、更新和删除文章。
27 3
|
9天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
23 1
|
18天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
42 5
|
15天前
|
Web App开发 JavaScript 中间件
构建高效后端服务:Node.js与Express框架的完美结合
【10月更文挑战第21天】本文将引导你走进Node.js和Express框架的世界,探索它们如何共同打造一个高效、可扩展的后端服务。通过深入浅出的解释和实际代码示例,我们将一起理解这一组合的魅力所在,并学习如何利用它们来构建现代Web应用。
38 1
|
1月前
|
设计模式 JavaScript 前端开发
浅谈JavaScript 框架在现代 Web 开发中的作用
浅谈JavaScript 框架在现代 Web 开发中的作用
35 12
下一篇
无影云桌面