前端开发一直有它自己的特殊性。要做开发不仅仅要学 HTML,还要学 CSS、JS 等,甚至还有 Node.js 这种更全面的语言。大量的学习占用了大量的精力,导致很多人难以提升自己的核心能力,逐渐沦为了框架熟练工。
我总结了过往的一些常见场景,从中提取出了一种专属于前端的编程思想。它不能很快的提升你的技术能力,也有一个学习和理解的过程。但是如果你习惯使用这种思想,或者你已经在无意识的使用这种思想。那么恭喜你,你的开发效率会非常高,产出的页面也会更流畅,更健壮。
本文主要内容:
1.流式编程简介
2.流式编程是从哪儿来的
3.根据案例讲述流式思想的构思和落地实现
1 流式编程
有时候也可以成为函数式编程。它的主要目的是在数据拉取的过程中渲染页面,即边处理数据边渲染页面。
类似概念:
1.流式应用:Rxjs
2.流式列表
3.流式处理:大数据、实时计算
4.文件处理、字节流、加密、网络
xJS本身一直试图将数据、dom等前端元素看做流处理。它的思想其实就是流式编程思想的一种。如果你使用过RxJS,你会非常快速的理解流式编程的核心理念。
但是RxJS更多的是将操作,包括http请求、dom操作等看做流的形式,在普通请求、操作的基础上封装出了一个流的上层对象。它只能算做流式编程思想的其中一部分。流式编程更加的靠前,它包含了页面结构设计、请求顺序等一些列的东西。
瀑布流
开发前端的同学或多或少都会遇到瀑布流页面,哪怕没开发过也见过、研究过。
瀑布流完全是将页面(一部分)看过了一个流式页面,如果页面向下流动,页面能够自然达到的顺序就是瀑布流希望/想要达到的效果。
流式编程思想更多的是提出页面整体上的流式,而不仅仅是页面的某一部分。假如你做的瀑布流页面是完全靠position绝对定位实现的。很抱歉,这样的页面仅仅是实现了瀑布流,但它不属于流式编程。流式编程提倡使用正常的文档流去开发。让页面渲染能够一泻而下,通畅而不阻塞。不需要重新绘制dom树才是流式编程在页面开发上的核心要点。
这里拿出实时计算就是为了让开发者能够从不同的方面去理解流式编程的思想。
不管是页面还是请求、甚至是各种操作等,将它们看做一个流,顺着网络请求的顺序,页面渲染逻辑的顺序向一点去前进。这个点就是流式编程。所有的东西都通过流式编程思想去处理,将它们混杂在一起,然后处理成一个一个单独块。这个块包含当时情况下最应该做的操作(dom、http、click等),然后打包出现在页面上。这个就是流式编程的核心思想。
流处理
这里使用了流去举例是因为流的读写操作从概念上是非常贴合流式编程思想的。
从文件上开一个通道去获取数据,然后拿到的同时就做相应的处理,同时在处理完就将它们放出来。这个过程也就是流式编程思想在构思页面结构的时候想要做的。
来源和结论
2.1 页面结构
原始页面是嵌套的,但是去掉嵌套来看,整个页面是一个从上到下的渲染过程。在渲染的过程中如果触发页面重绘,或者是出现脱离文档流的样式,整个页面的性能就会下降。由此可见,前端结构是需要尽量流式的展示的。
从这个点出发就是流式编程中的dom结构流式编程。
从页面结构设计上考虑,首屏渲染的时间把握其实就是在考验开发者的流式编程思想。让页面尽可能早的呈现第一屏的dom结构,同时尽量早的加载完成第一屏的图片等资源文件。其他会对页面渲染阻塞都要往后排。
如果使用流式编程思想重新设计页面结构,单从结构上来说,一个首屏的渲染了不起20ms就到顶了,再加上各种资源加载,这些综合下来首页的加载速度一般可以提到500ms以内。这个时间才是首页渲染应该有的时间。如果是手机上,不考虑网络差的情况甚至可以做到300ms内打开页面。
从图中可以看到,一部分请求在蓝色线左边,也就是页面必须使用到的。后面的4个请求是页面加载之后用到的,这几个请求是可以后加载的。其中1个是接口,然后几个是渲染页面用到的js文件。他们的顺序就是先渲染页面框架,然后再填充下面的内容。
真正的让页面飞起来,从页面结构上开始考虑。
打开页面甚至在页面什么也没有展示的时候,接口就会请求数据。从上面的截图可以看到一个页面首次打开可以请求多少接口,甚至截图中的接口请求个数都不是非常多的那种。
有的页面请求3、5个,有的请求10多个。从整体上看,大多数接口是一个顺序请求的过程。它受到页面结构的影响,过快的请求反而渲染的不是所需要的地方。
页面的请求顺序就可以看做是一个流式的过程。将需要同步渲染的接口放在一起,等待结果返回之后再展示页面。剩下的几个不需要优先渲染的往后推,通过事件触发等方式在页面渲染之后再继续执行。
tips:有些接口可以同时异步执行,使用并行请求,将页面渲染分离,可以加速页面的呈现。 tips:多次请求需要多次握手,使用http2.0可以加速这个时间。合并接口可以直接去掉多余时间。
2.3 优化顺序
通过上面的分析,我们可以对流式编程有一个大概的印象,一些想法思路可以总结下来:
1.按照页面渲染顺序做开发。没必要呈现的后渲染,可以加速页面展示。避免在页面加载过程中不断重绘。
2.多余接口后请求,优先渲染第一屏。接口请求往往意味着后面有一些逻辑需要执行,将不需要第一时间请求的接口往后放可以节省很多加载时间,就像分页加载一样加载页面,页面可以更快打开。
3.从结构上优化。先加载的先请求,先呈现的先计算,后加载的排队来,后呈现的叠加计算。这里的排队可以类比分页加载来思考怎么实现。叠加计算更多的是在当前状态上更新新的页面。
3 实例分析
3.1 单接口情况
单接口的时候会遇到数据量大,结构复杂。页面结构也会相应的增长复杂度。面对这种一次性接收大量数据的情况下,最优先考虑是优化数据结构。将大的复杂的结构分化成合适的小的结构,这样分解之后再使用会减少很多开发过程中的麻烦。把问题前置,预先处理复杂的数据,后面用到的时候才会更合适。同时,如果数据结构还有变化,也能够及时的调整应对。
应对这种情况,我们可以从几个方面入口去优化。
1.加速接口返回速度。这个往往比较容易。
2.分析页面结构,将页面分成不同侧重点的部分。比如商品详情这个页面,可以分成商品简略信息和详情2个部分。其中简略信息包含几个头图、价格、卖点等信息。详情包含规格、复杂的介绍内容。(如果有需要,价格也可以分开,将价格单独分成一个接口)
3.数据适配。我们已经将页面分成了2个部分,这里就可以单独把第一个部分放在一个对象中做渲染。这里使用vue、react等框架会得到非常及时的渲染结果,省下了大量的dom操作时间。剩下的部分放在滑动事件中,滑动页面的时候再渲染。
- 异步加载。这里继续使用商品详情的例子。当用户点击或者下滑的时候需要展示商品的各种规格、宣传图等。我们可以先渲染容器,保证页面结构的统一化。然后再根据页面当前位置逐步加载需要显示的图片等资源。这里同时也会涉及到超长列表的优化。其实非常简单,就是保持容器不变,内部渲染内容在超出屏幕之后删除,保证页面上的dom数量不至于太多。
上面的几点初步展示了怎么样优化一个大数据块的页面。正在遇到这个问题的同学可以根据这个思路去思考解决方案了。
3.2 多接口
多接口的情况往往是需要并行的和需要并发的接口齐上阵,一不注意就造成接口阻塞。
1.首先分析出页面具有哪些不同的部分。这些不同的部分的结构要隔离开,方便做不同的逻辑处理。这里推荐组件化形式处理,可以更方便的隔离不同的逻辑结构
2.接口处理。后端基于某些(不靠谱)考虑会出几种不同的接口。这个时候前端更多的是区分接口的优先级和功能域。相同功能依赖的接口同步等待。不同的功能域之间梳理加载顺序。如果发现功能域不是优先展示的,可以放在异步或者触发式功能域中。
3.先返回的数据先渲染,后返回的数据后渲染。多种渲染使用组件分离。
这里提到了一个功能域,这其实是一个领域的概念。一个功能域包含特定的页面结构、依赖的接口。这里可以看做一个组件,内部包含了页面结构接口请求。
3.3 实时+触发
这个场景更多的出现在交互比较频繁的地方。比如一个按钮可以无限次的点击,滚动的时候有一个出现隐藏的动画/结构等。
1.如果一个操作多次触发,为了保持反馈/动画的稳定性,这里是一定要做控制的。典型的解决方式就是节流函数控制。或者也可以考虑从展示逻辑上处理,实现更简单。上滑的时候出现滑动小动画,如果不懂上滑其实只需要做一次动画即可。只有上一次动画做完才会执行下一个动画。这其实也是最简单的流式思想。
2.如果一个页面可能会多次展示隐藏,同时内部的数据是异步获取的。这个场景下优先考虑缓存渲染结果,第一次的时候加载数据,后面在展示的时候其实一直在用一个缓存来渲染结果。从流程上看就简化为了data=>dom。
3.4 复杂页面
复杂的页面跟多的是上面几种的组合。只要能够识别到那些地方归属那种情况,再去优化就有一个清晰的目标了。
1.产品级分析。看到原型或者UI的时候基本就能够知道页面未来会长什么样子了。这个时候也就是一个复杂的页面最应该开始构思结构的时候。分析页面功能点,区分哪些是先展示,哪些是触发之后展示。然后设计页面结构,隔离不同展示区域。
2.不同的结构使用不同的接口,接口之间也是分属不同的功能域。如果一个接口内容特别多,可以从逻辑上将一个接口分成好几个部门处理。尤其是要渲染很多内容的时候,分开渲染速度更快。
3.有些结构完全可以设计到触发之后再加载。比如第二屏展示的内容,点击之后才能看到的地方等。
4 总结
流式编程将页面结构看做数据,多个数据并行展示看做一个整体。通过这样宏观的抽象整理,将页面变成了一个流动的对象。从根本上改变了开发的形态。
在实际开发中使用流式编程思想可以很容易的开发出高性能的页面。页面渲染更快,展示更合理。同时反过来加速了代码结构的进化,促进开发者思考和成长。