很早的时候火了一阵子Jasonette,打出来的宣传语是用json写出纯native的app(牛皮其实有点大,其实只是写动态界面,彻底不是写动态App)。css
前一阵子,天猫又开源了跨多个平台的Tangram,一套通用的UI解决方案,仔细阅读文档咱们会发现,他们也是在用json来实现这套七巧板布局。一套灵活的跨平台的UI解决方案。html
Jasonette的牛皮其实有点大,不少人看到动态用Json写出纯native的app,就很激动,仿佛客户端也能有H5那样的能力,但其实他只是focus在解决app中的界面的问题。Tangram的定位就很精准了,是一套为业务出发的通用跨平台UI解决方案,把布局渲染性能与多段一致性考虑在框架内的UI框架。这两者有个共同点都是用json来描述界面与内容,从而用native进行呈现,json这种数据是一种自然便于下发与动态更新的数据,所以这些其实都能让客户端作到相似H5网页同样的赶脚。虽然没有使用WebView,但他们的设计思路和网页技术的发展历史一模一样,所以@响马大叔说过"这实际上是最纯正的网页技术,虽然他是native的"。前端
顺着这个话题继续问几个问题node
为何Jasonette与Tangram都是用json?css3
为何Jasonette写出来的json有些属性看着很像css?padding
& align
(拿Jasonette举例)git
Jasonette调用UIKit进行渲染,H5用WebView渲染,因此Jasonette就叫native?程序员
DSL 是 Domain Specific Language 的缩写,意思就是特定领域下的语言,与DSL对应的就是通用编程语言,好比Java/C/C++这种。换个通俗易懂的说法,DSL是为了解决某些特定场景下的任务而专门设计的语言。github
举几个很著名的DSL的例子web
经过一些规定好的符号和组合规则,经过正则表达式引擎来实现字符串的匹配正则表达式
虽然写的是相似XML 或者 .{} 同样的字符规则,可是最终都会被浏览器内核转变成Dom树,从而渲染到Webview上
虽然是一些诸如 create select insert 这种单词后面跟上参数,这样的语句实现了对数据库的增删改查一系列程序工做
计算机领域须要用代码解决不少专业问题,每每须要同时具有编码能力以及专业领域的能力,为了提升工做于生产效率,须要把一些复杂但更偏向专业领域的处理,以一种更简单,更容易学习的语言或者规范(即DSL),抽象提供给领域专家,交给不懂编码的领域专家编写。
而后代码编程能力者经过读取解析本身定制出来的这些语言规范,来领会领域专家的意图,最终转化成真正的通用编程语言代码实现,接入底层代码框架,从而实现让领域专家只须要学习更简单的DSL,就能影响代码程序的最终结果。
(虽然DSL的原始定义是为了非编程的专业领域人才使用,但到后来直接交给程序员使用,但能大幅度提升程序员编写效率的非通用语言,也被当作是DSL的一种)
设计师会设计出不少精美的界面,最后交给程序员去用代码编写成漂亮的网页或者App。每一个界面若是纯用代码编写,都会面临大量的代码工做量,这里面可能更多的是一些重复的机械性的代码工做。诸如:某个元素设置长宽,设置居中,设置文字,设置距离上个元素XX像素,N个元素一块儿纵向,横向平均排列等等。对于代码实现界面开发来讲,程序员须要编写一系列诸如:setFrame,setTitle,setColor,addSubview这样的代码,一边写这样的代码,一边查阅设计师给出的各类标注。
为了提升工做效率,若是能把一些设计师产出的长宽
,色值
,文字
,居中
,距上
等设计元数据(设计的标注信息等),以一种约定的简洁的语言规则(即DSL)输入给程序代码,由程序和代码自动的分析和处理,从而生成真正的界面开发代码setFrame,setTitle,setColor,addSubview,这样就能够大幅度的减小代码量与工做量,程序员来写这种简洁的语法规则会更快更高效,甚至能够把这种简洁的语法规则教会设计师,让设计师有能力直接写出DSL,而后输入给底层程序,这样界面就天然完成。
作iOS客户端开发的同窗有兴趣能够用文本编辑器打开如下XIB文件,你会看到咱们拖来拖去拖线出来的Xib,其实就是XML语法,而Jasonette就是Json语法,他们用XML/JSON这种通用的结构化语法来存储这些设计数据,用一些自定义的标签,来标记这些数据的用途,XIB/JSON通过解析后就会生成签到字典的树状结构,所以代码就能够进行遍历执行,从而转变成最终的UIKit的渲染代码。
HTML/CSS,是网页开发广泛使用的,他们也是一种DSL,你写出的每个HTML的DIV以及CSS的属性样式,最后都不是经过.html .css文件渲染到屏幕到浏览器上的,都是经过浏览器内核最后调用OpenGL,C++代码渲染上去的。从这个层面讲,Jasonette客户端框架用的json,native客户端开发的XIB,与网页浏览器的HTML/CSS是一回事。
XIB的XML代码其实也通常不会交给设计师去学习和掌握,可是在XIB的XML基础之上,制做出一个InterFaceBuilder,可让设计师用图形界面拖来拖去,这种UI编辑器式的设计其实都离不开DSL,都是规划出了一种比通用代码语言更简洁的DSL后,再辅助开发界面编辑器生成这种简洁DSL来实现的。
扯淡了这么多,亲自看看Jasonette源码是如何执行DSL的。
{
"$jason": {
"head": {
"title": "{ ˃̵̑ᴥ˂̵̑}",
"actions": {
"$foreground": {
"type": "$reload"
},
"$pull": {
"type": "$reload"
}
}
},
"body": {
"header": {
"style": {
"background": "#ffffff"
}
},
"style": {
"background": "#ffffff",
"border": "none"
},
"sections": [
{
"items": [
{
"type": "vertical",
"style": {
"padding": "30",
"spacing": "20",
"align": "center"
},
"components": [
{
"type": "label",
"text": "It's ALIVE!",
"style": {
"align": "center",
"font": "Courier-Bold",
"size": "18"
}
},
{
......省略
}
]
},{
......省略
},
{
"type": "label",
"style": {
"align": "right",
"padding": "10",
"color": "#000000",
"font": "HelveticaNeue",
"size": "12"
},
"text": "Watch the tutorial video",
"href": {
"url": "https://www.youtube.com/watch?v=hfevBAAfCMQ",
"view": "Web"
}
}
]
}
]
}
}
}复制代码
这是demo里的页面json代码,你会看到不少很像网页开发的东西,head,body,padding,align等等,是否是以为和CSS很像
这个demo写的json文件其实就是一个helloworld界面,里面有一些按钮,点击能够跳转,还有一些图片,我先简单介绍一下Jasonette DemoApp的启动流程
程序初始化,触发[[Jason client] start:nil]
,初始化Jason,在这个start里面,会建立JasonViewController,而且给这个VC设置rootUrl,设置这个VC做为Window的Key,从而进行App展示
这个KeyVC,当viewWillAppear的时候,触发[[Jason client] attach:self]
,这个函数内会调用[self reload]
来进行网络数据拉取,刚刚说的rootUrl实际上是一个json网络文件(也能够设置成bundle内文件),换句话说这个vc的json文件能够每次从网络上拉取最新的json文件来实现动态更新的(跟网页其实是同样的),这个过程就是触发网络框架AF去拉取最新的json
在网络数据拉取回来后,会通过一系列的处理,包括请求异步的其余相关json(像不像异步请求其余css),把请求到的json字典通过JasonParser
这个类的一些其余处理最后生成最终的Dom字典
(Dom这个词写在Jason drawViewFromJason的源码里,源码就将这个数据字典的变量起名叫dom,可见他作的和网页工做原理是一个思路)
找到Jason类的drawViewFromJason:
函数,这才是咱们DSL之因此能渲染成界面的最重要的一步,前面都是一直在下载DSL,处理DSL,结果就是json生成了最终须要的元数据字典--Dom字典,这一步就是将DSL转变成App界面
Dom字典生成界面的过程
简单的看看这个流程都分别依次调用了哪些函数,不一一讲解了,最后咱们挑最有表明的进行说明。
[Jason drawViewFromJason:DomDic]
[JasonViewController reload:DomDic]
Set Stylesheet //CSS
[JasonViewController setupSections:DomDic]
[JasonViewController setupLayers:DomDic]
setupSections与setupLayers基本上涵盖了页面主元素的全部渲染方式
先以简单的setupLayers的代码逻辑举例,先按着约定的标签从Dom字典中有目的的读取须要的数据字段Layers,循环遍历Layers字段数组下的全部数据,每一次都先判断子节点的Type属性,若是Type写了Image,就会建立UIImageView,若是Type写了Label,就会建立UILabel,根据子节点其余属性一一设置不一样的UIView的属性,最后AddSubview到界面上。(我会略过大量实际代码,以伪代码形式进行说明,实际代码能够看源码查看)
NSArray *layer_items = body[@"layers"];
NSMutableArray *layers = [[NSMutableArray alloc] init];
//循环遍历Dom树下的layer字段
if(layer_items && layer_items.count > 0){
for(int i = 0 ; i < layer_items.count ; i++){
NSDictionary *layer = layer_items[i];
layer = [self applyStylesheet:layer];
//设置Css
//判断type字段是否为image,是否有image url
if(layer[@"type"] && [layer[@"type"] isEqualToString:@"image"] && layer[@"url"]){
//NEW一个UIImageView
//设置UIImageView的style
//设置UIImageView的 image URL
//将UIImageView Add subview
//异步拉取图片回来后,经过style,运算UIImageView的frame
}
//判断type字段是否为label,是否有text
else if(layer[@"type"] && [layer[@"type"] isEqualToString:@"label"] && layer[@"text"]){
//NEW一个TTTAttributedLabel
//设置TTTAttributedLabel的style
//设置文本
//addSubview
}
}
}复制代码
再说说setupSections,他其实充分利用了tableview的能力,首先将Dom字典下的sections
字段进行保存与整理,而后并不马上进行渲染,而是直接调用[UITableview reloadData]
,触发heightForRowAtIndexPath
与cellForRowAtIndexPath
。(我会略过大量实际代码,以伪代码形式进行说明,实际代码能够看源码查看)
heightForRowAtIndexPath获取cell高度
//取出indexPath.section对应的dom节点数据
NSArray *rows = [[self.sections objectAtIndex:indexPath.section] valueForKey:@"items"];
//取出indexPath.row对应的dom节点数据
NSDictionary *item = [rows objectAtIndex:indexPath.row];
//取出样式属性
item = [JasonComponentFactory applyStylesheet:item];
NSDictionary *style = item[@"style"];
//经过JasonHelper传入style[@"height"]样式属性计算宽高
//一些样式算法算出
return [JasonHelper pixelsInDirection:@"vertical" fromExpression:style[@"height"]];复制代码
cellForRowAtIndexPath获取cell
NSDictionary *s = [self.sections objectAtIndex:indexPath.section];
NSArray *rows = s[@"items"];
//获取对应的Dom节点数据
iNSDictionary *item = [rows objectAtIndex:indexPath.row];
//渲染竖着滑的CELL
//只支持SWTableViewCell这一种客户端预先写好的这种通用cell
//支持Dom节点循环内嵌stackview,按着内嵌形式,横竖布局都支持
//stackview内的子元素经过JasonComponentFactory建立对应的UIKit UIView
//建立方式如同layer,判断type等于'image'建立UIImageView,判断等于'text'建立UILabel
//frame经过style等字段,进行系统autolayout计算
return [self getVerticalSectionItem:item forTableView:tableView atIndexPath:indexPath];复制代码
上面讲的其实力度很粗,而且不少代码没有详细展开,其实目的是让你们发现,Jasonette的源码持续在干一件事情:
从Dom字典中,读取约定好的固定字段
循环遍历Dom字典,遍历全部设计数据
而后用字符串匹配去判断每一个节点的key与值,指引OC代码应该怎么调用
匹配出label就建立UILabel
匹配出iamge就建立UIImageView
匹配出style就调用autolayout赋值属性进行autolayout计算,或者进行自行算法计算。
Jasonette的DSL工做特色就是这样,先从设计师给出的元数据入手,把全部的元数据抽象抽离出来,约定成固定的标签与值,而后客户端一一遍历整个Dom元数据的节点,一一解读这些标签与值,走入对应的客户端代码,从而调用对应的客户端代码功能。
客户端的这套框架写完以后,之后在写全新的界面,实际上是无需再重复写一套客户端代码,而是直接写全新的DSL也就是Jasonette的json文件就能够了。
拿JSON/HTML/CSS举例子其实,这些都是一种外部DSL
,与之对应的还有某些语言支持的内部DSL
,这里也就不展开了
XML DSL
不少常见的XML配置文件实际上就是DSL,但不是全部的配置文件都是DSL。好比“属性列表”和DSL是不一样的,那只是一份简单的“键-值对”列表,可能再加上分类。
XML不是编程语言,是一种没有语义的语法结构。XML是DSL的承载语法,可是它又引入了太多语法噪音—太多的尖括号、引号和斜线,每一个嵌套元素都必须有开始标签和结束标签。
自定义的外部DSL也带来了一个烦恼:它们处理引用、字符转义之类事情的方式老是难以统一。
因此DSL叫特殊领域语言,离开了为某一DSL专门开发的语言环境或者代码框架,DSL是没法运行的,没有效果的,没有正则表达引擎的源码,你写出来的正则表达式没人认识。没有底层数据库框架,sql语句就只是一行字符串,无法进行数据管理。没有Jasonette这个框架,你写出来json也不可能生成界面,有了Jasonette这个框架,你不按着约定的标签写,本身单纯的在json里凭空建立标签,也是不可能正确生成你想要的东西。
在最后,笔者想说的是,当咱们在某一个领域常常须要解决重复性问题时,能够考虑实现一个 DSL 专门用来解决这些相似的问题。
from 谈谈 DSL 以及 DSL 的应用
既然说到动态界面,那必定得聊屏幕适配,这其实无论是否是动态界面,无论用不用到DSL,作客户端都要考虑的一点,其实网页在这方面发展的更完善,毕竟客户端的屏幕尺寸就那么几种,就算安卓碎片化,也比不上PC电脑上,桌面浏览器用户能够任意伸缩窗口的大小,所以对于在不一样尺寸的限定屏幕大小(即排版区域)内,把设计出来的元素以最美观的形式进行展示,这就是布局与排版。
刚才在讲解DSL,讲解Jasonette的时候其实回避一些问题,咱们光提到了经过Dom的type信息,来建立不一样的UIView,可是每个UIView应该摆放在屏幕的什么位置,在哪进行展示,在上面的文章中被一带而过,有的描述,读取style字段后分别赋值给对应的autolayout,有的被我说成了进行必定的算法从而算出高度。这背后其实都是布局与排版的算法。
作iOS客户端的同窗不少会有感触,早些年的时候写绝对坐标,那时候iOS的屏幕尺寸还不是太多,用代码手写frame进行元素定位,试想一下若是纯用frame进行app开发,那么去开发一套对应的DSL动态界面其实更容易,咱们只须要给每一个字典节点,规定上{x:N,y:N,w:N,h:N}的属性,而后在框架里别的跟布局相关的style都不须要写了,只须要用xywh生成CGRect,而后调用setFrame就行了,想开发出这样一种绝对布局的动态界面框架
其实还真是挺简单的。
到后来有了iPad,有了iPhone5,有了iPhone6,6Plus,iOS的屏幕尺寸变的碎片化,若是继续使用frame,客户端同窗开发工做量会变的异常繁琐,因而在IOS7引入了苹果的autolayout,引入了VFL语言Visual Format Language。其实VFL也应该算是一种DSL吧,他不是用来绘制出一个个的界面元素,而是用来在绘制前,计算清楚每个元素在动态的屏幕区域下的最终位置。咱们学会了如何写VFL,或者说咱们学会了如何用masnory这个框架实现autolayout,但咱们并不须要深刻去了解这里面的排版布局算法。
须要记住的一点是,最终渲染必定是经过frame去页面上进行绘制,有了明确的坐标才能绘制出UI,手写frame式的绝对布局代码,直接由程序员指定,所以必定是性能开销最小的,能够说没有或者少许的布局运算开销直接进行渲染,但在多屏适配的需求下才引入了一整套庞大的布局算法体系(不必定非得是苹果的autolayout),引入庞大布局算法的目的是但愿根据可排区域动态的计算frame,但并不表明采用自动布局,就与frame无关,自动布局算法只是间接的运算出frame再渲染。
将HTML文件以字符串的形式输入,通过解析,生成了Dom树,Dom树就比如是iOS开发里面页面View的层级树,可是每一个View/div里面并无css信息,只写了每一个div所对应的css的名字
将CSS文件以字符串形式输入,通过解析,获得了一系列不一样名字的style rules,样式规则
Dom树上的div并不包含样式信息,而是只记录了样式的名字,而后从style rules里找到对应名字的具体样式信息,Attech到一块儿,生成了Render Tree渲染树,此时的渲染树只是Dom与CSS的合并,他依然不包含真正能够用于渲染的位置信息,由于他还没通过布局排版。
网页将View的层级,与View的样式进行了分离,View就是HTML,样式是CSS,可是Jasonette的Json没有作这样的分离,Json直接描述的就是view与style归并到一块儿的数据,所以在Jasonette通过了parse解析后直接就拿到了样式与视图的合体结构信息。咱们在Json里明显能够看到head
,footer
,layers
,sections
这种字段其实就是HTML里面的相似Dom的对象,而style
这个字段其实就是CSS里面的对象。Jasonette的源码里直把这个字典起名叫 NSDictionary * dom
,其实就能够感知到,虽然Jasonette使用的是json,可是他的思路跟浏览器内核是同样的。
当你使用代码执行addsubview的操做的时候,你其实就是在对一个view(一种节点),添加了一个子view(一个子节点),当全部subview添加完成的时候,你已经建立好了一个界面层级树,你addSubview一个子view之后,会对这个view要么设置VFL,要么使用masnory,总之会对这个view设置样式属性(其实就是在用oc代码,attach css),以后在layoutIfNeeded
的时候,autolayout开始本身闷头计算排版
换句话说iOS autolayout与HTML/CSS在解析上的区别是,iOS的布局是用代码写死的,生成一种界面层级树形结构,而网页HTML/CSS是用可随意下发的字符串,进行解析,从而生成了一种界面层级树形结构(RenderTree)
不管使用的是网页,仍是Jasonette,仍是iOS autolayout,当咱们拿到没有通过排版的Render Tree的时候,虽然里面的节点包含着样式信息,可是并无具体的绘制位置信息,所以须要从Tree的根节点开始依次遍历每一个节点,每一个节点都根据本身的样式信息以及子节点的样式信息进行排版算法计算。
在排版引擎的设计模式里(一种设计概念,不是指具体某个排版源码实现),一个RenderTree上每个节点是一种RenderNode,他多是不一样的界面元素,甚至是界面容器,每一个RenderNode均可以有本身的layout()方法用于计算本身和本身的子节点的算法,一个position绝对布局的节点,他及内部的子节点布局算法layout(),确定与一个listview,tableview那种有规律的排布容器节点布局算法layout()不同,从根节点rootNode开始,循环遍历递归下去,直到把Tree上的全部节点的位置信息都运行了layout(),就完成了布局排版。
咱们知道不一样的节点,是能够用不一样的算法进行他与内部子节点的布局计算的。
拿iOS开发举例子,咱们彻底能够同一个页面内,有的view是用frame方式写死的绝对布局,有的view是用masnory进行的autolayout,甚至父view是写死的绝对布局,子view是autolayou,或者反过来。
拿浏览器CSS来讲,浏览器内核C++代码里一个RenderObject的基本子类之一就是RenderBox,该类表示听从CSS盒子模型的对象,每个盒子有四条边界:外边距边界 margin edge, 边框边界 border edge, 内边距边界 padding edge 与内容边界 content edge。这四层边界,造成一层层的盒子包裹起来。这种基础RenderBox有着本身的layout()算法。而在新的CSS里引入了更多不一样的布局方式,好比运用很是普遍的Flexbox弹性盒子布局,Grid布局,多列布局等等。
在排版引擎的设计模式里,若是你想引入一种新的布局算法,或者一种新的专属布局效果锁对应的布局计算,你只须要建立一种新的RenderNode,而且实现这种node的layout()函数,你就能够为你的排版引擎,持续扩展支持更多的排版能力了
Jasonette是怎么作的?
jasonette其实根本没本身实现布局算法,也没有抽象出renderNode这种树状结构,他直接用原始的Dom字典直接开始遍历递归。
遍历到layers节点,就调用[JasonLayer setupLayers]
函数,内部是本身写的一套xywh的算法,有那么点像CSS盒子模型,但简单的多。
遍历到sections节点,就调用[JasonViewController setupSections]
函数,走系统的tableview的reload布局,在heightforrow的时候,用本身的一套算法计算高度,而在cellforrow的时候,他使用系统stackview与系统autolayoutAPI进行设置,最后走系统autolayout布局
Jasonette的布局过程看起来很山寨,从设计上把Dom字典直接快速遍历,识别标签,用if else直接对接到不一样的iOS代码里,有的布局代码是一些简单盒子运算,有的布局代码则是直接接入系统autolayout,能够看出来他从DSL的角度,多快好省的快速实现了一个界面DSL框架,但从代码架构设计的角度上,他距离完善庞大的排版引擎,从模块抽象以及功能扩展上,还欠缺很多。
这就不说了,固定精确的坐标,其实不须要计算了
Auto Layout 的原理就是对线性方程组或者不等式的求解。
这篇文章写得很是很是清楚,我就不详细展开了,简单的说一下就是,iOS会把父view,子view之间的坐标关系,样式信息,转化成一个N元一次方程组,子view越多,方程组的元数越多,方程组求解起来越耗时,所以运算性能也会愈来愈底下,这一点iOS 的Auto Layout其实被普遍吐槽,广受诟病。
传统的CSS盒子模型布局,这个前端开发应该是基本功级别的东西,能够自行查阅
CSS3被引入的更好更快更强的强力布局算法FlexBox,由于其优秀的算法效率,不只仅在浏览器标准协议里,被普遍运用,在natie的hyrbid技术方面,甚至纯native技术里也被普遍运用。
Facebook的ASDK也用的是Flexbox,一套纯iOS的彻底与系统UIKit不一样的布局方式
大前端Hybrid技术栈里,RN与Weex中都用的是FlexBox,阿里的另一套LuaView用Lua写热更新app的方案也用的是FlexBox算法
Grid布局被正式的归入了CSS3中的布局模块,但彷佛目前浏览器支持状况不佳,看起来从设计上补全了Flexbox的一些痛点。
CSS3的新布局方式,效果就好像看报刊杂志那样的分栏的效果。
通过了整个排版过程以后,renderTree上已经明确知道了每一个节点/每一个界面元素具体的位置信息,剩下的就是按着这个信息渲染到屏幕上。
Jasonette直接调用的addSubview来进行view的绘制,Dom字典遍历完了,view就已经被add到目标的rootview里面去了,渲染机制和正常客户端开发没区别,彻底交给系统在适当的时候进行屏幕渲染。
这两个Link其实介绍了,RN与Weex也是经过addSubview的方式,调用原生native进行渲染,在iOS上来讲就是addSubview
在绘制阶段,浏览器内核并不会直接使用RenderTree进行绘制,还会进一步将renderTree处理成LayerTree,遍历这个LayerTree,将内容显示在屏幕上。
浏览器自己并不能直接改变屏幕的像素输出,它须要经过系统自己的 GUI Toolkit。因此,通常来讲浏览器会将一个要显示的网页包装成一个 UI 组件,一般叫作 WebView,而后经过将 WebView 放置于应用的 UI 界面上,从而将网页显示在屏幕上。但具体浏览器内核内部的渲染机制是怎么工做的有什么弊端,还取决于各个浏览器的底层实现。
How Rendering Work (in WebKit and Blink)
从这里面能够详细看出来,浏览器内核的渲染实际上是能够作到下面这些多种功能的,但不一样平台,不懂浏览器内核的支持能力不一样,不是全部的WebView或者浏览器App都是一样的性能与效果
仔细想一想,真正到渲染这一步,你须要作的都是操做CPU和GPU去计算图形,而后提交给显示器进行逐帧绘制,webview与native其实异曲同工。
@响马大叔说过"这实际上是最纯正的网页技术,虽然他是native的"。
本文从Jasonette出发,从这个号称纯native,又动态,又用json写app的技术上入手,看看这native
+动态
的巨大吸引力到底有多神奇,挖下来看一看。
咱们看到了和浏览器内核一脉相承的技术方案
经过DSL,下发设计元数据信息
构建Dom树
遍历Dom树,排版(计算算法与接入autolayout)
遍历Dom树,渲染(addsubview接入系统渲染)
Webkit浏览器内核就是按着这样的结构分为2部分
绿色虚线部分是WebCore,HTML/CSS都是以String的形式输入,通过了parse,attach,layout,display,最终调用底层渲染api进行展示
红色部分是JSCore,JS以string的形式输入,输入JS虚拟机,造成JS上下文,将Dom的一些事件绑定到js上,将操做Dom的api绑定到js上,将一些浏览器底层native API绑定到js上
整个WebCore不是一个虚拟机,他里面都是C++代码,所以HTML/CSS在执行效率上,从原理上讲和native是一回事,没区别。
而咱们今天提到的动态界面,不管是Jasonette仍是Tangram,甚至把xib或者storyboard动态下发后动态展现,用iOS系统API就彻底能够作到动态界面(滴滴的DynamicCocoa里面提到把xib当作资源动态下发与装载),其实都和浏览器内涵的WebCore部分是一个思路与设计,没错。
本文从开头到如今,重点围绕着WebCore的设计思路,讲了N多,可是看到Webkit结构图的时候,你会发现,有个东西我始终没提到过--JSCore,但我在开头提到了一句话
Jasonette牛皮其实有点大,其实只是写动态界面,彻底不是写动态App
界面动态
这个词与App功能动态
有什么区别呢?
一个APP不只仅须要有漂亮的界面,还须要有业务处理的逻辑。
Jasonette号称是用json开发native app,可是json只是一种DSL,DSL是不具有命令和运算的能力的,DSL被誉为一种声明式编程,但这些业务逻辑运算,DSL这种领域专用语言是不可能知足的,他须要的是通用编程语言。
所以Jasonette对点击事件的处理,其实就是一种路由,json的对象里面有个标签约定为action,action的值是一个url字符串,url指向另外一个界面的json文件,也就是说,DSL能够把这个view的点击事件写死,一旦发生点击,固定会跳转到url所指向全新的json页面,换句话说,这就是网页开发的url跳转href
字段。
换个说法你就理解了,Jasonette在技术上至关于用iOS的native代码,仿写了一个处于刀耕火种的原始时代的浏览器内核思路,一个尚未诞生js技术,只是纯HTML的超文本连接的上个世纪的浏览器技术。那个时候网页里每个超连接,点进去都是一个新的网页。
因此这不叫App功能动态
,充其量只是界面动态
。
JSCore的引入给浏览器内核注入了动态执行逻辑脚本代码的能力,先不说脚本引擎执行起来效率不如native,但脚本引擎至少是一个通用编程语言,通用编程语言就有能力执行动态的通用代码(JS/LUA等),通用代码比DSL有更强大的逻辑与运算能力,所以能够更加灵活的扩展,甚至还能够将脚本语言对接native,这就是webkit架构图里提到的jsbinding。
将脚本语言对接到本地localstorage,js就有了本地存储能力,将脚本语言对接到network,js就有了网络的能力,将脚本语言对接上dom api,js就有了修改WebCore Dom树,从而实现业务逻辑二次改变界面的能力。
所以ReactNative & Weex 能够算做App功能动态
,他们不只仅巨有WebCore的能力,同时还巨有JSCore的能力(这里面其实有个区别,浏览器内核的WebCore是纯native环境C++代码,不依赖js虚拟机,但RN与Weex负责实现WebCore能力的代码,都是js代码,是运行在虚拟机环境之下的,但他们的渲染部分是bridge到native调用的系统原生api,有兴趣看我写的RN源码详解吧,ReactNative iOS源码解析(一),ReactNative iOS源码解析(二))
阿里的LuaView我没细看过源码,但其实内部机制和RN&WEEX没啥区别,用的是FlexBox排版,可是选用的是Lua Engine这个脚本引擎,而非JSCore
在native动态化的道路上,不论你们走哪条路,有一个共识仍是你们都找到了的,那就是看齐Web的几个标准。由于Web的技术体系在UI的描述能力以及灵活度上确实设计得很优秀的,并且相关的开发人员也好招。因此,若是说混合开发指的是Native里运行一个Web标准,来看齐Runtime来写GUI,并桥接一部分Native能力给这个Runtime来调用的话,那么它应该是一个永恒的潮流。
虽然有点扯远了,可是这句话确实又回到响马叔的那个思路,Jasonette用json写出native app,他的思路依然是web思路,RN用js写出native app,也不能改变他一整套web技术的基因。一个界面最终渲染是以native系统级Api实现,并不能说明什么,渲染只是庞大Web内核技术的末端模块,把末端渲染模块换成native的,其实说明不了什么。
这里就要强调一下了,浏览器内核在界面这块是纯C++实现,没有使用任何虚拟机,所谓的浏览器内核下的Dom环境是纯C++环境,也就是纯native环境,因此浏览器在单次渲染性能上,不见得比native慢。
CSS的布局排版兼容不少种布局算法,有些算法在保证前端开发人员以高素质高质量的开发前提下,一样的界面,其性能是彻底可能碾压autolayout的,因此单说布局这块,webview也不见得慢。
webview的渲染的时候还存在不少异步资源加载,但这个问题是动态能力带来的代价,啥都远端实时拉最新的资源固然会这样,若是在App下以hybrid的形式,内置本地静态资源,经过延迟更新本地资源缓存的方式,设计hybrid底层app框架,那么这种开销也能减小。更况且浏览器新技术PWA也好SW也好都从浏览器层面深度优化了WAP APP的资源与缓存。
webview性能慢的缘由不少,多方面综合来看确实很容写出性能不佳的页面,但话也不能绝对了,web技术所带来的灵活多变,是会给业务带来巨大收益的。在须要灵活多变,快速响应,敏捷迭代的业务场景下,web技术(泛指这类用web的思路作出来的范hybrid技术)所带来的优点也是巨大的
Jasonette写了这么多,虽然没有深度剖析每一行源码,但把他的实现思路讲解了一下,其实本身实现一个动态界面也不是不能够。
咱们的工做业务须要深度处理文字,咱们也有一套跨平台的C++排版引擎内核,思路是一脉相承的,区别是文字排版会比界面区块盒子排版更复杂的多,用的也是json当作DSL,可是咱们利用咱们的文字排版引擎,去实现相对简单的各类在native系统上的什么图片环绕,图文绕排,瀑布流界面UI等,其实很是的容易,甚至仍是跨平台的(比native代码实现要容易的多)。就连Jasonette代码里也就只支持section(tableview布局)和layer(盒子模型)2中常见形式,复杂页面同样实现不了。
可是!可是!可是!
DSL是领域专业语言,DSL就注定巨有着局限性,你为本身的排版引擎设计出一套DSL规则,就算都使用的是json,那又如何,新来的一我的能很快上手写出复杂页面?DSL的规则越庞大,引擎支持的能力越强,越表明着DSL的学习成本直线加大。
HTML/CSS已经发展成为一个国际标准,甚至是一种被普遍传播和学习的DSL,所以他有着不少技术资料,技术社区,方便这门语言的发展,而且随着应用越广,语言层面的抽象愈来愈合理,扩展能力也愈来愈强。
可是你本身设计出来的DSL能走多远?能应用多远?
学习成本大,哪怕只是在本身业务内,也很大的,须要有效的创建文档说明,维护业务迭代带来的功能变化,还要给新来的同事培训如何写这种DSL。
应用范围小,想应付本身一个业务,可能初步设计出来的接口和功能就知足需求了,但也只能本身使用,若是想推广,必然会带来更大的维护成本,须要更加精细化合理化的API设计,扩展性设计
人员的迁移成本大,DSL的特色是会让写DSL的人员屏蔽对底层代码的理解,甚至一些初中是为了能给一些不会编码的专业领域人员学习和运用,若是编程的人员长时间写这种专有的DSL,迁移到别的公司之后,该公司不用这种DSL,那么这些技能就完全废掉,若是开发者自身不保持一些对底层源码的自行探索,那么换工做将会带来很大的损失
前端人员在写各类HTML/CSS的时候,想要深入理解透其中的做用机制,也是须要深刻到浏览器内核层面去了解内部机制的
因此我以为天猫的Tangram,是很值得尊敬的,由于想作出一个动态界面框架,没那么难,想作大,作到通用性,扩展性,作到推广,作到持续维护,是很是艰难的,真的很赞!
谈谈 DSL 以及 DSL 的应用(以 CocoaPods 为例)
DSL(五)-内部DSL vs 外部DSL (N篇系列文章)
理解WebKit和Chromium: WebKit布局 (Layout)