横向对比浏览器,RN以及Flutter

跨平台是最近几年很火的技术栈,也是未来的趋势,一次开发,多出处行,能节约大量的人力和时间成本。目前主要的跨平台方案有三种

1. 以原生组件为基础,对接web生态RN,快应用,weex这这一类为代表
2. 以Web为代表
3. 以独立绘,自有生态的flutter为代表

这里来通过横向对比这三类技术,来有一个深度和广度的认识。对于这三种技术,我这都通过介绍他们的渲染流程,优点,缺点,如何优化来进行一个横向的比较。在这里插入图片描述

浏览器Web

在了解web是如何渲染之前,我们要先了解web是什么?一个完整的web页面由html,css和js这三部分组成。html负责描述页面结构和内容,css设定页面样式风格,js控制页面动态逻辑,通过这三部分,一个web页面就可以渲染成开发预期的ui效果。

渲染

我们接下来看看浏览器web是如何渲染的。
在这里插入图片描述
通过上图可以看到,一个web页面的完整渲染流程是这样的
1. 通过网络或者缓存加载我们想要展示的web页面及资源
2. 将我们获取到的web页面解析,遇到html的部分,就采用htmlparser来解析,遇到js的内容,就采用jsengine来解析,遇到css部分就采用css parser来解析。解析后的最终的结果是一棵节点带CSS Style、会响应自定义事件的Styled DOM树
3. 将我们生产的dom数执行layout过程,这个过程主要涉及生成渲染树,以及根据盒模型来进行测量和布局
4. 将经过测量和布局的渲染数绘制出来

我们来详细看一下上面的流程

parser过程

webkit在解析网页内容时,通过解码,分词,解析和建树四个步骤,将html最终转换成dom树。

  • 解码:将网络上接收到的经过编码的字节流,解码成Unicode字符。
  • 分词:按照一定的切词规则,将Unicode字符流切成一个个的词语(Tokens)。
  • 解析:根据词语的语义,创建相应的节点(Node)。
  • 建树:将节点关联到一起,创建DOM树、Render树和RenderLayer树。
<html> 
    <body>
        <p>Hello, World</p>
        <div><img src=”example.png”/><div/>
    </body>
</html>

对于上面的一段html代码,解析过程会经历下面两个阶段
在这里插入图片描述
通过上面的html和dom的对比,其实可以发现dom和html是一一对应的。每有个html的标签,都有对应的dom element来表示。

我们在通过一张图看看parser的流程
在这里插入图片描述

layout过程

layout分为两个步骤,一是创建渲染(Render)树,二是执行布局,我们分别来看这两个过程。

1,创建布局树主要有下面几个步骤

  • 在创建dom树的同时,创建render树。render树并不是在dom树创建完成之后才创建的,而是同时创建的。当调用创建节点的attach函数时,render树就会创建。
  • 绑定css属性。render树创建完成后,会attach上css的规则
    在这里插入图片描述
    通过上图可以看看,render树与dom树在很大程度上也是一一对应的,但是dom树中一些节点,比如head,不需要渲染的是不会生成render节点的。

2,计算布局

  • 布局主要基于css的盒子模型,通过margin,padding,border,content以及static,releative,absolute,fixed等定位属性来进行测量和布局计算。

3,paint

  • Render树映射成可视的图形,它会遍历Render树调用每个Render节点的绘制方法将其内容显示在一块画布或者位图上,并最终呈现在浏览器应用窗口中成为用户看到的实际页面

我们在通过下面的流程图回顾一下web的绘制流程
在这里插入图片描述

优缺点

我们接着看看web的优缺点

优点

  • 稳定而且一致的跨平台能力

缺点

  • js的执行和DOM的计算都在主线程中,如果js频繁改动dom或者js有耗时操作,就会阻塞主线程。
  • js是解释性语言,解释型语言的程序不需要在运行前编译,在运行程序的时候才翻译,专门的解释器负责在每个语句执行的时候解释程序代码。这样解释型语言每执行一次就要翻译一次,效率比较低。

优化

那么我们如何来优化web呢?我这里将web的流程分成了三个步骤,网页资源准备,运行,渲染

  • 下载主要是html,css,js等资源的下载流程;
  • 运行表示web网页的运行流程,包括dom的 解析,js的运行等;
  • 渲染主要是页面的渲染及绘制流程

资源准备

资源准备过程是很依赖网络的,所以可以通过http2.0,cdn,加服务器,加带宽等优化服务器的速度。除掉网络的条件,我们可以通过资源管理管理来优化这个过程,主要包括细粒度控制有效资源的加载时机,预加载,懒加载,资源压缩,资源分割,分包,缓存等。

运行

在对scripting流程进行优化时,我们先看看httparchive官方统计数据
在这里插入图片描述
可以看到,最耗时的是布局,其次是执行脚本和重新计算样式

在布局上,为了兼容W3C标准,导致非常繁杂。开发者除了优化页面结构布局外,比较难做优化。所以我们主要看js脚本执行这一块。

其次就是针对js脚本运行慢,这一点主要是因为语言执行慢,针对这一瓶颈,退出了webassembly技术。webassembly可以直接用c++等高效语言写脚本,然后编译成机器码,V8是能比较好的支持webassembly的。我们大概来了解一下webassembly。

在这里插入图片描述

通过上图可以看到,当v8执行一个js脚本时,首先会将js转换成字节码,也就是parse过程,其次,通过compile和optimize将字节码转换成机器码,最后在运行。而采用webassembly,运行前就已经将脚本转换成了机器码,直接省去了parser和很大部分的compile过程。而且webassembly用的是强语言,运行的速度也会比js快很多。

渲染

和硬件运算速度有关,开发者比较难优化

其他优化方案

服务端渲染主要是将动态的数据在服务端渲染出来,返回给客户端静态页面。节约客户端再去请求数据的时间。
在这里插入图片描述

RN

我们接着开始看看以快应用,weex,rn这这一类为代表的跨平台技术,这类技术最大的优势是既能使用系统原生组件,又能对接前端生态。我们来看一下这类方案通用的渲染流程。

渲染

rn,weex和快应用,都主要由四部分组成,JsFramework,jsbridge,v8,和客户端组成。我分别讲一下这四个部分的作用

  • JsFramework主要在于创建Vdom,以及向端侧发送指令(render,method),响应端侧发过来的指令(call function)
  • jsbridge主要做jsframework和端测的通信桥联
  • v8或者jscore主要用来执行js文件
  • 端侧则响应jsframework传来的渲染指令,创建vdom,进行渲染工作
    在这里插入图片描述

优缺点

我们接着再了解一下,这类技术的优缺点

优点

  • 能使用系统原生组件和方法
  • 渲染速度比web快
  • 稳定的跨平台能力

缺点

  • 不能完全兼容W3C的规范,比如W3C里面,可以轻易设置圆角的大小,粗细,边框是实现和虚线,但是在客户端,这个实现起来都比较难。所以这类技术都只能有限的支持W3C的标准。
  • js运行性能瓶颈
  • 数据通信的性能瓶颈

优化

上面讲了web的优化分为下载,运行,渲染这三个步骤,RN,WEEX的优化同样也需要将拆分流程,因为weex和rn都是开源,我们可以改动的控件更大一点,所以这里我会把把这个流程分的更细一点。打开一个rn页面,主要有:

  • 资源准备
  • JS文件执行
  • JsFramework创建VDom并生成对应的组件数据指令
  • 指令数据传输
  • 客户端解析指令
  • 客户端执行组件数据指令创建VDom
  • 客户端渲染VDom

资源准备

资源准备的优化,主要有两个点,一是提高网络性能,二是减少下载的资源大小

提高网络性能这一点在介绍浏览器的时候说过,快应用也是需要同样的方法,对于客户端开发来说,是很难控制网络的条件的,所以这一方面能做出的优化有限

减少下载的资源大小也和web的方案是通用的,我这里说一下这几种通用的方式

  • 数据缓存,数据缓存相当于将资源下载的大小将为零
  • 分批或者按需下载,首次启动时,只下载首页的数据,然后后台去下载其他页面的数据
  • 资源的压缩,选择压缩率更高的压缩算法
  • 预下载,预下载就是在我们使用资源前就将资源下载至本地

JS文件执行

执行js文件的主要是v8引擎,所以想要优化js文件的运行效率,就需要优化v8引擎,weex,rn相比web页面的优势是集成了独立的V8引擎,我们可以对v8做出优化,而web页面的v8受制于浏览器的限制,我们没法做出修改。

在这里插入图片描述

在讲浏览器的时候,提到过webassembly,那么weex和rn可以用这个技术吗?这个是比较难的,因为webassembly并不支持js语言,如果想要使用webassembly,需要让第三方开发换强语言,这对于已有的生态来说,是不可能实现的。

但是我们可以提前将js文件提前编译成字节码或者机器码,就能省掉js文件的编译时间。

JsFramework创建VDom并生成渲染指令

在这个过程,需要把握渲染指令的传递时机

在前面的渲染流程我们可以知道,页面的js文件会将页面的dom数据传到jsframework,jsframework此时会根据这些数据创建VirtualDom,然后将dom节点创建指令发送到native端。

发送时机有两种选择

  1. 一是jsframework一边创建VitrualDom的同时,将dom节点的指令发送到native端,这种方式的好处是会减少白屏时间,因为native端可以更早的开始创建dom,但是缺点是native端频繁的重绘会影响性能
  2. 第二种方式是等jsframework层的virtdom创建完毕后,在将dom节点的指令传递到native层,这样会减轻native层的性能消耗,但是会增加页面白屏时间,如果jsframework创建vdom耗费的时间太久,native的ui表现上就会一直白屏。

这两种方式我们要根据具体的页面,均衡的选择。

vdom创建

vdom创建是一个耗时的操作,jsframework层需要不断的优化,采用更搞笑的diff算法,优化dom创建流程等方式需要对这个创建过程不断的优化。但我们都知道这个过程也是有瓶颈的,但jsframework创建vdom到达瓶颈后怎么办呢?我们知道js语言的速度和性能远远要低于c++,如果将vdom的创建过程,移动到c++层,那么就可以突破瓶颈,将vdom的创建速度大大的提升。

客户端解析指令

native创建页面的过程,会收到大量的createbody,addelement等指令数据,因为指令的数量比较多,在数据的序列化与反序列上,也必定会耗费不少的性能,为了减少在数据解析上的性能消耗,我们可以采用更高效的数据协议,如二进制,protobuf等。同样,为了突破数据解析的性能瓶颈,我们也可以将数据解析移到c++层。

客户端执行组件数据指令创建VDom

此时,客户端开始根据jsframework传来的dom节点数据,开始创建Vdom了。vom的创建,一定不能阻塞主线程,这里需要采取多线程创建组件,所以weex和rn在创建组件时都是才用的多线程模型。

其次,我们如果能将vdom的数据序列化缓存至本地,那么下次打开时,也能节约vdom的创建时间,极大的提高页面打开时间。

客户端渲染VDom

渲染过程主要依赖客户端的原生ui渲染,这个过程中,我们能优化的是我们组件,如Div,Text,Video,ListView等这些我们自定义的组件,那么如何优化呢?我们知道android在渲染ui时,会经历measure,layou,draw这个三个过程,那么我们对我们的自定义组件的优化也主要围绕着三方面,我们在可以子线程创建vdom的同时,将组件的布局测量出来,那么在子线程中渲染组件是,就可以省掉测量布局的流程,直接复用我们已经测量好的布局信息。也可以优化自定义组件的层级结构,或者尽量减少ondraw方法里面的逻辑。

其他优化方案

这一类跨平台技术架构的瓶颈在于native测绘制ui的绘制依赖于js的运行,而js的运行又是比较耗时。这也是为weex,rn,快应用这类跨平台方案无法达到原生的渲染速度。

在这里插入图片描述
通过上图可以看到,客户端排版是强依赖js执行的。

我们知道一个页面的js文件里面既包含了逻辑数据,也包含了ui数据,如果我们能把这些ui数据直接拆分出来,交给native端直接去处理,而不是交给jsframework去处理,然后jsframework在将ui的指令数据传到native端。这样在渲染ui的时候,就可以直接跨过对jsframework的依赖,达到接近原生的渲染速度。

对于这种方案,我们可以看看weex是如何实现的。
在这里插入图片描述
可以看到weex将页面的js里面的ui部分拆分了出来,这样客户端就能直接拿这一部分ui数据渲染界面,不再依赖jsframework的运行,这种方案能极大的提高页面的渲染速度。

Flutter

快应用的部分讲完了,我们接着看看第三种跨平台的方案,也就是也flutter为代表的,自建生态的技术。

渲染

我们先看一下flutter的架构图
在这里插入图片描述
从架构图,我们可以知道,flutter的架构由上到下分别是:

  1. 代表Android和iOS的Material及CupertinoUI设计风格
  2. Flutter以Dart语言实现的Widget生态
  3. 渲染模块
  4. 动画,手势,还有基于底层绘制接口实现的绘制模块
  5. 公共和通用类库
  6. c++实现的flutter引擎,包括skia,dart虚拟机等。
  7. 嵌入层,基于平台差异,做一些线程,渲染等配置工作。

那Flutter是如何渲染的呢?这个流程主要涉及到三棵树

  • Widget树。Widget树是开发用dart开发的,描述页面和逻辑等信息,类似于web的html
  • Element树。flutter会根据widget树,生成element树和renderobject树,element类似于Dom,用来描述节点信息,还有做diff。
  • RenderObject树。renderobject则会进行布局测量,进行渲染处理。

优缺点

优点

  • 因为dart语言执行速度比js快,而且没有过高的通信成本,渲染速度接近原生效果

缺点

  • 无法动态更新
  • 内存和包大小占用
  • 学习成本高,生态不足。

总结

通过横向对比,我们会发现这三种跨平台方案有很多相同点,比如都有类似于dom的树机制,都有用于render的渲染树,都需要经过测量,布局,绘制这三个流程。每一种方案也都有向其他方案借鉴经验,比如web的同层渲染,这个技术可以在浏览器中嵌入原生平台的view,起源于以weex为代表的跨平台方案,比如weex,rn中使用的Dom,为了兼容web生态,起源于Web。这三种方式各有各的使用场景,我们需要根据不同的场景和需求,选择适合我们的跨平台方案。

猜你喜欢

转载自blog.csdn.net/tyuiof/article/details/105595253