Skip to content

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

关注 前端瓶子君,回复 “交流”

加入我们一起学习,天天进步

通常来讲前端性能优化是指从用户开始访问我们的网站到整个页面完整的展现出来的过程中,通过各种优化策略和优化方法让页面加载的更快,让用户的操作响应更及时,给用户更好的使用体验。

这里我们介绍的是前端性能优化知识的解决方案,从静态资源优化开始入手,从表象深入体系化的讲解页面渲染架构,掌握搞笑的技术方案。

本文并非细节的讲述如何实现性能优化,而是从各个方面介绍性能优化的方式方法,并且不仅限于 H5,因为当今的前端也不仅仅只有 H5。

图片资源优化

  1. 图片格式介绍

jpeg: 一种针对彩色照片而广泛使用的有损压缩图形格式。是一种栅格图形,常用文件扩展名为 jpg,jpeg,jpe。在互联网上常被应用于存储和传输照片。不适合线条图形和文字,图标图形,因为他的压缩算法不支持这些类型的图形,并且不支持透明度。常用于色彩丰富的照片,彩色图大焦点图 banner 等结构不规则的图形。

png: 便携式网络图形,是一种无损压缩的位图图形格式,支持索引、灰度、RGB 三种颜色,以及 Alpha 通道等特性。他最初的设计是为了代替 GIF, 能够支持半透明和透明特性,最高支持 24 位彩色图形和 8 位灰度图像。不过由于是无损压缩所以文件体积太大。比较适合纯色,透明,线条绘图,图标以及颜色较少的需要半透明的图片。

GIF: 位图图形文件格式,8 位色重现真彩色的图像,采用 LZW 压缩算法进行编码。支持 256 色,仅支持完全透明和完全不透明,可以支持动图,不过每个像素只有 8 比特,不适合存储彩色图片。常用与动画和图标。

webp: 是一种现代图像格式,可以提供无损压缩和有损压缩两种。可以同时办证一定程序上的图像质量和较小的体积,可以插入多帧,实现动画效果。支持透明度。采用 8 位压缩算法,无损的 webp 比 png 小 26%,有损的 webp 比 jpeg 小 25-34%,比 gif 有更好的动画。不过最多可以处理 256 色,不适合彩色图片。常用于图形和半透明图像。

  1. 图片优化

对于 png 图片来说,可以使用 jdf-png-native 进行压缩, 他是 node-pngquant-native 工具的封装包,这个工具跨平台,压缩比高,而且压缩 png24 也非常的好。

const pngquant = require('jdf-png-native');const fs = require('fs');fs.readFile('./in.png', (err, buffer) => {    if (err) {        throw err;    }    const resBuffer = pngquant.option({}).compress(buffer);    fs.writeFile('./out.png', resBuffer), {        flags: 'wb'    }, () => {})})复制代码

压缩 jpg 可以使用 jpegtran 这个工具,他也是一个 node 工具。使用方法比较简单,直接使用命令即可。大概压缩 10% 的占比。

jpegtran -copy node -optimize-outfile out.jpg in.jpg
复制代码

对于 gif 文件来说可以使用 gifsicle 工具,他是通过改变每帧比例,减小 gif 文件大小,同时可以使用透明来达到更小的文件体积。是一个公认的解决方案。可以去http://www.lcdf.org/gifsicle/中去安装。使用方式同样也是命令行方式。

gifsicle --optimize=3 --crop-transparency -o out.gif in.gif
复制代码

这里的优化级别不要小于 2,1 的话代表不压缩。压缩后基本不失帧。

还有一种压缩方式是图片可以根据网络环境来展示不同尺寸和像素的图片,通过在 url 后缀加不同参数来实现。比如下面的地址, 430 可以修改为 800 来获得不同体积的图片。

https://img.alicdn.com/imgextra/i1/2616970884/O1CN01x6HnoK1IOuj5IosXO_!!2616970884.jpg_430x430q90.jpg
  1. 响应式图片

响应式图片是我们可以在用户不同的窗口大小还有设备像素的情况下来展示不同大小的图片,可以用以下三种方式来实现, 第一种是可以使用 js 来绑定事件检测窗口大小,以此来设置图片的大小。第二种方式就是 css 的媒体查询。

@media screen and (max-width: 640) {    my_image { width: 640px; }}复制代码

第三种可以使用 html5 的 srcset 来设置,他会根据设备的像素比来自动选择需要的图片。而且不支持 srcset 的浏览器也可以正常展示 src 的属性。

<img   srcset="img-320w.jpg, img-640w.jpg 2x, img-960w.jpg 3x"   src="img-960w.jpg"   alt="img" />复制代码
  1. 逐步加载图片的方式

其实就是延迟加载,在真实的图片加载出来之前,可以使用一张公共的图片,一般是公司的 logo,先将布局撑起来,然后再换成真实的图片。

lqip 这个工具可以将真实的图片虚化,转换为很小的 base64 编码。这样我们可以先使用 base64 加载虚化的图片。

img

npm install lqip复制代码const lqip = require('lqip');const file = './in.png';// imagelqip,base64(file).then(res => {    console.log(res); // 输出base64})// colorlqip.paletter(file).then(res => {    console.log(res); // 图片颜色值})复制代码

也可以使用低质量图片占位符, 他是基于 SVG 的图像占位符实现的。

img

npm install sqip复制代码const sqip = require('sqip');const result = sqip({    filename: './in.png',    numberOfPrimitives: 10 // 效果值});console.log(result.final_svg); // 输出svg格式复制代码

相比 lqip 来说 sqip 效果会好很多,而且可以设置不同的大小。

  1. 其他方式

可以使用 web font 来代替图片,比如说小图标等业务小图片。

也可以用 dataurl 的方式,也就是前面的 base64 的方式来代替图片,这样用户就不需要发送 http 请求了。

也可以采用雪碧图将多个小图片合成一个大图,这样也会节省很多的图片请求。

  1. 图片服务器自动优化

图片服务器优化是指可以在图片 url 连接上增加不同特殊参数,让服务器自动生成不同格式,大小,质量的图片。

比如说可以对图片做一些裁剪,裁剪成我们需要的图片,也可以支持不同格式的转换,比如说 jpg,gif,png,webp 等也可以设置图片的压缩比。

也可以对图片添加一些水印,高斯模糊,重心处理等还可以增加一些 AI 的能力,比如说用户上传的图片是否涉黄。还可以通过智能抠图,智能排版,智能配色智能合成等功能完善图片。

HTML 的优化方法

  1. 精简 html 代码

可以减少 html 的嵌套也就是层级关系尽量减小,也可以减小 DOM 节点数也就是尽量压缩优化 DOM 的节点数, 让浏览器渲染的 DOM 节点数最少。

减少一些无语义的代码,比如说空标签清浮动那种代码<div></div>能不用最好不要用。

建议连接中删除 http 或者 https,因为一般链接的协议头和页面的协议头都是一致的,写他们多了 4-5 个字符其实是没有什么意义的。而且可以减少代码体积。

也可以删除多余的空格,换行符,缩进和不必要的注释,一般会用压缩工具来处理这个过程。可以省略一些标签和属性。使用相对路径的 url,最大范围的减少字节数。

  1. 文件位置

css 文件链接尽量放在页面头部,css 加载不会阻塞 DOM Tree 解析,但是会阻塞 DOM Tree 渲染,也会阻塞后面 js 执行。也就是说 DOM Tree 在渲染前就要解析好 CSS,从而减少浏览器重排文档的次数。而且 css 放在页面底部会导致页面白屏时间变长。

js 文件一般放在页面底部,这是防止 js 的加载和解析阻塞页面元素的正常渲染。

  1. 用户体验

设置 favicon.ico, 如果不设置控制台会报错,而且用户访问的时候地址栏也是空的,不利于品牌记忆。

增加首屏必要的 css 和 js,一般页面需要在等待所有的依赖加载完成才会展示,这样就会导致页面存在空白。永祥用户体验,可以增加背景图或者 loading 或者骨架屏,比空白页好很多。

CSS 优化细则

  1. 提升 css 渲染性能

谨慎使用一些 expensive 的属性,比如 nth-child 伪类或者 position:fixed 定位,因为这些比较消耗浏览器的渲染性能。

尽量减少一些样式层级的级数,比如,div ul li span i {color: red}, 其实我们可以给 i 标签设置 class,直接书写样式。

避免使用占用过多 cpu 和内存的属性,比如 text-indent 不要设置太大的值。

尽量避免使用耗电量大的属性,比较占用 GPU, 比如 transfrom 是,transitions, opacity.

合适的使用 css 选择器, 尽量避免使用通配符,避免使用 css 表达式。color: expression((new Date()).getHours() % 2 ? "#fff" : "#000")。

避免类正则的属性选择器。*=, |=, ^=, $=,使用外链的 css,可以单独形成文件放在 cdn,使用缓存形式加载。避免使用 @import 因为他的加载会阻塞进程,需要加载完毕才会向下执行。

精简 css 代码,使用缩写的语句,比如 margin-top 可以写在 margin 中,再者如果值为 0 能删除就删除,删除不必要的单位值,删除过多的分号,删除空格和注释。尽量减小样式表的体积。其实这些都可以使用压缩工具来处理,会方便很多。

  1. 合理使用 web fonts

可以将字体文件部署到 cdn 上,加快用户端的加载速度,也可以将字体以 base64 的形式保存在 css 中,并通过 localStorage 进行缓存。一些谷歌字体库应该使用国内托管服务不要直接使用源地址。

  1. css 动画优化

避免同时动画,也就是说用户访问的屏幕区间里面不要有过多的动画,动画太多会干扰用户正常浏览网站,而且动画多也影响浏览器的性能。

延迟动画的初始化,可以让其它 css 先渲染,让动画延迟,比如说 0.5 或 1。

可以借助 svg 去展示动画,样式放在 css 里面控制。

JavaScript 优化

首先我们是当需要的时候才去优化,不是为了优化而优化,一般的优化是在某一个时间点进行的,而且优化也需要考虑可维护性这是要结合团队的研发水平和代码的规范。

  1. 提升 js 文件的加载性能

这个基本每个人都知道,就是 css 文件放在 head 标签中,js 文件放在 body 结尾的地方。这个是 js 的加载不要影响 html 的渲染。

  1. 变量和函数方面的优化

尽量使用 id 选择器,因为 id 选择器在查询效果上效率最快。

避免使用 eval,这个方法比较消耗能行。

js 函数尽可能保持简洁,不要把太多内容写在一个函数中。也建议使用事件的节流函数。事件委托等等。

  1. js 动画

尽量避免添加大量的 js 动画,css3 动画和 canvas 动画都比 js 动画性能好。

使用 requestAnimationFrame 来代替 setTimeout 和 setInterval,因为 requestAnimationFrame 可以在正确的时间进行渲染,setTimout 和 setInterval 无法保证渲染时机。不要在定时器里面绑定事件。

  1. 使用逻辑缓存

缓存 dom 对象,也就是用一个变量来存储 dom 对象,不要每次使用都查询。

缓存列表长度,也就是说用变量存储 dom 元素的个数,而不是每次都重新计算。

比如百度 M 站,会把页面的 css 和 js 放在本地存储里面,这样后面再加载的时候就直接从本地存储里面取,实现秒考的效果。不过本地存储空间有限,要谨慎使用。

减少页面回流和重绘

  1. css

避免过多的样式嵌套,最好可以快速的定位到元素。

避免使用 css 表达式,css 表达式会在 css 绘制的过程中都会执行,会增加重排和回流的次数。

可以使用绝对定位让动画元素脱离文档流。

避免使用 table 布局他会引起浏览器的多次重绘,也不要使用 float 布局。

图片最好设置好设置 width 和 height,这样图片在加载之后布局就可以确定了。

简化浏览器不必要额任务,使用 viewport 设置屏幕缩放级别。

避免频繁设置样式,将多个样式操作合并修改, 一次性的更新。

  1. js

为了减少回流发生次数,应该避免频繁操作 DOM,可以合并多次对 DOM 的修改,一次性批量处理。

控制绘制过程和绘制区域,绘制过程开销比较大的属性设置应当避免使用。

简化 DOM 操作

众所周知,页面交互卡顿和流畅度很大一部分原因就是页面有大量 DOM 元素,想想一下,从一个上万节点的 DOM 树上,使用 querySelectorAll 或 getElementByTagName 方法查找某一个节点,是非常耗时的,另外元素绑定事件时,事件冒泡和事件捕获的执行也会相对耗时。所以一般我们应该合理的不熟业务逻辑,DOM 节点过多时应该延迟即将呈现的 DOM 内容。

对 DOM 的操作最好统一处理后再统一插入到 DOM Tree 中。可以使用 fragment 对 DOM 和样式设置好再统一放到页面中去。

目前比较流行的框架,比如 Angular,React 和 Vue 都是使用虚拟 DOM 技术,通过 diff 算法简化和减少 DOM 操作。

静态文件压缩工具

html-minifier: 压缩 html clean-css: css 的压缩工具 uglify-js: js 文件的压缩工具

浏览器渲染过程

首先浏览器会解析 HTML 生成 DOM Tree,然后解析 CSS 生成 CSSOM Tree。接着 JS 会通过 DOM Api 和 CSSOM Api 来操作 DOM Tree 和 CSS Rule Tree 将 DOM Tree 和 CSSOM Tree 合成一颗渲染树 Render Tree。

根据生成的渲染树进行回流,以计算每个节点的几何信息,包括位置,大小,样式等等。然后根据渲染树和回流得到的几何信息,得到每个节点上的绝对像素。

最后将像素发送给图片处理器也就是 GPU 进行页面展示。

前端页面渲染可以分为服务端渲染和客户端渲染。服务端渲染有传统的后端同步渲染,同构直出比如 php,java, .net 或者大家熟悉的 node。

客户端渲染也就是 js 渲染,前后端分离,单页面应用。react, vue, ios, 安卓,hybird app,flutter 等。

懒加载,预加载,预渲染

懒加载也叫延迟加载,指的是长网页中延迟加载特定元素,可以是图片也可以是 js 和 css。懒加载的好处是可以减少当前屏无效资源的加载。

一般我们会把 img 标签的 src 属性设置为空字符串,真实的图片地址放在 data-lazy 中,当页面 scroll 到对应的位置时再通过 DOM 操作将 src 的值替换为 data-lazy 的值。

预加载是让浏览器预先加载某些资源,同样也是图片,js 或者 css,这些资源是在将来才会被使用的。

简单来说就是讲所需要的资源提前加载到浏览器本地,后面在需要的时候可以直接从浏览器的缓存中获取,而不用再重新开始加载。好处是减少用户后续加载资源等待的时间。

可以使用 new Image 的方式也可以使用标签的方式 preload,prefetch, preconnect

<link rel="preload" href="src/style.css" /><link rel="prefetc" href="src/image.png" /><link rel="dns-prefetch" href="https://my.com" /> <!-- 提前将dns缓存--><link rel="preconnect" href="https://my.com" /> <!-- 提前加载需要的资源 -->复制代码

另一种预加载组件的方式就是提前渲染它,在页面中渲染组件,但是并不在页面中展示,也就是渲染好后先隐藏起来,用的时候再直接展示。可以使用 prerender 将 https://my.com 页面先提前渲染好。

<link rel="prerender" href="https://my.com" />复制代码

接口服务调用优化

  1. 接口合并,指一个页面的众多的业务接口和依赖的第三方接口统一使用一个部署在集群的接口统一调用,以减少页面接口请求数。

  2. 接口上 CDN,主要基于接口性能考虑,我们可以把不需要实时更新的接口同步至 CDN,接口内容变更自动同步 CDN。

  3. 接口域名上 CDN 可以增强可用性和稳定性。

  4. 接口降级,这个基于大促备战考虑,核心进行降级用基础接口进行业务实现,比如千人千面的推荐接口,在大促时间点可以直接运营编辑的数据,另外接口万一无法访问,使用预设好的垫底备份数据。

  5. 接口监控,监控接口的成功率不是常说的服务器 TP99,而是和用户实际情况一直的成功和失败监控,比如弱网,超时,网络异常,网络切换等情况。排查出来问题需要联合后端,运维,网络岗位人员一并解决。

接口缓存优化

1.ajax/fetch 缓存,前端请求的时候带上 cache,依赖浏览器本身的机制来请求接口,这个比较适用于不会经常变更的数据。

  1. 本地缓存,异步接口数据优先使用本地 localStorage 中缓存的数据。可以让服务端返回数据的时候再给一个 md5 值,然后将 md5 值和数据绑定存在本地,再次请求的时候对比这个 md5 值,如果相同就不要再请求获取数据的接口了,如果不同就请求更新。

webview

原生的 webview 对于 IOS 来说有两种,一种是 UIWebView,他从 IOS2 开始就作为 App 内展示 web 内容的容易,而且排版布局能力比较强。

不过 UIWebView 也有很多的问题,比如说内存泄漏,运行期间会有极高的内存峰值,Touch Delay 延迟 300 毫秒。js 运行性能不高,在 2018 年的 ios12 以后就不再维护了。

WKWebView 是苹果在 WWDC 2014 上推出的新一代 WebView 组件,WKWebView 的内存开销比 UIWebView 要小很多,而且在性能,稳定性,内存占用方面都有很大提升。可以实现 60fps 的滚动刷新率,自身就支持了右滑返回手势,支持更多的 HTML 属性。内存占用是 UIWebView 的 1/4 ~ 1/3, 加载速度比 UIWebView 提升了一倍左右。大幅度提升了 js 执行速度。允许 js 的 Nitro 库的使用,UIWebView 是不允许的。可以和 js 直接互调函数,不需要使用 jsbridge 来协助。

当然 WKWebView 不支持页面缓存,需要自己注入 cookie,而 UIWebView 是自动注入 cookie 的。他也无法发送 POST 参数。

对于安卓来说存在 webkit for webview 和 chromium for webview。

webkit 是一个开源项目,前身是 khtml 和 kjs,专注于网页内容的展示,做了一流的页面渲染引擎,他不是浏览器而且也不想成为浏览器,这个项目包含两个部分,第一个部分是 WebCore, 其中包括对 html,css 很多 w3c 规范的实现,第二部分就是狭义上的 webkit 主要是各个平台的移植并提供相应的接口。也就是 webview 和类似于 webview,这样的接口提供操作和显示网页的能力。

目前使用 WK 的主流浏览器或者 webview 包括 chrome,safari, 安卓平台以及众多的移动浏览器。

chromium 是基于 webkit 之上的一个浏览器项目,由谷歌来发起,这个项目发展的还是比较迅速的,他对新特性的支持还是比较好的,比如 webgl,css3,h5 等等,在性能方向也非常不错 chrome 一般选择稳定的 chromium 作为基础。


Webkit for WebviewChromium from Webview备注
版本Android4.4 以下Android4.4 以上--
JS 解释器WebCore JavaScriptV8--
H5278434--
远程调试不支持支持Android4.4 及以上支持
内存占用相差 20-30M
WebAudio不支持支持Android 5.0 及以上支持
WebGL不支持支持Android 5.0 及以上支持
WebRTC不支持支持Android 5.0 及以上支持

安卓第三方内核,主要是安卓的版本较多,对 WebView 二次封装产生的,这里主要说下 X5 内核。

他的速度是比较快的相比系统 WebView 的网页打开速度有 30% 的提升,在流量方面使用云端优化技术节省 20% 以上。安全问题可在 24 小时内修复。更稳定经过亿级用户的使用考研,CRASHE(崩溃) 率低于 0.15%。没有系统内核的碎片化问题,更小的兼容性问题,支持夜间模式,适屏排版,字体设置等浏览器增强功能。在 H5 和 ES6 上有更完整的支持,集成了强大的视频播放器,支持视频格式远多于系统的 WebView,视频和文件的格式支持 X5 内核多于系统内核,自带防劫持。

一般 webview 选型,IOS 建议使用 WKWebView, 安卓建议使用 X5。

WebView 性能优化

当 App 首次打开时,默认是不初始化浏览器内核的,当创建 WebView 实例的时候,才会启动浏览器内核,打开事件需要 70-700 毫秒,并创建 webview 的基础框架。

可以使用全局的 Webview 对延迟的毫秒进行优化,就是在客户端启动的时候,就初始化一个全局的 WebView 待用,当用户访问 Webview 的时候直接使用这个 WebView 加载对应网页。

这样会减少首次打开 WebView 的时间,缺点是会有一些额外的内存消耗。

导航栏可以预加载,以前是在 webview 加载完成之后进行初始化,可以改为和 webview 并行一起加载。

对于登录来说 H5 页面上接口每次查询 Cookie 中是否有登录态,无登录态 H5 跳转统一登录页,App 登录成功写入 Cookie。可以改为 Cookie 统一在 Webview 中设置 cookie。也就是初始化 Webview 的时候判断是否登录,如果登录了就打开 H5 页面,如果没登录就自动跳转登录页面。

webview 加载页面的 url 尽量前置,不要放在最后,可以和业务逻辑并行处理,总而言之减少页面的白屏时间,让用户最快的看到页面。

提升滚动条的使用体验,原本是使用系统自带的滚动条的进度值,可以自己模拟滚动条的加载过程,让用户感觉页面加载变快了。也就是初始快速的加载到 60% 以上,给用户感觉加载很快的感觉。其实真实速度并没有变...

js-sdk 优化,也就是 oc 和 js 通信的一个方式。一般 jssdk 有三种方式实现,第一种就是常见的 scheme 的方式,就是我们在 h5 页面里面定义一些特殊的链接,拿到这个 scheme 之后原生拦截。然后把需要的回调函数和参数进行拦截,但这样有个问题,url 一般是 256 个字符,有长度的限制不能无限的传递。

第二种是 iframe 的形式,通过后台启一个页面进行拦截取 iframe 的变更拿到 js 的方法给 oc 实现通信,iframe 是依赖 jssdk.js 文件的,需要 sdk 文件作为桥梁实现通信目的。

第三种是 webkit 的方式,他是一种直接调用的方式,无需依赖任何的 sdk 文件。

浏览器缓存策略

缓存机制优势适用场景Android 开关IOS 开关
浏览器缓存机制HTTP 协议层支持静态文件的缓存浏览器负责浏览器负责
Web Storage较大的存储空间,使用简单临时,简单数据的缓存,浏览器上的 LocalStorage、SessionStoragewebSettings.setDomStorageEnabled(true)默认开启无关闭
Web SQL Database存储,管理复杂结构数据建议用 IndexDB 替代webSettings.setDatabaseEnabled(true)默认开启无关闭
Application Cache方便构建离线 App离线 App, 静态文件缓存webSettings.setAppCacheEnabled(true)默认开启无关闭
IndexDB存储任何类型数据,使用简单,支持索引结构,关系复杂的数据存储webSettings.setJavaScriptEnabled(true)默认开启无关闭

H5 离线化的实现方式

全局离线包,包含公共的资源,可供多个应用共同使用。

还可以有私有化的离线包,只可以被某个应用单独使用。

离线包的工作原理:

![img](data:image/svg+xml;utf8,)

首先会加载一个全局的包就是一些基础的文件,加载之后会把包释放放在内存里,接着会做一个检测,查看本地是否安装,如果已经安装就释放到内存,如果没有安装就触发离线包的下载,就是我们做好的包放在服务器中,然后从服务器获取过来,在下载之前会进行一个本地和线上版本的对比,版本不一致的话就会下载最新的包,如果一致就取本地的就可以了。

最终这个包会解压释放在内存里面,当 webview 在加载 url 的时候会直接从内存里面读取,如果能读取到就加载内存中的页面数据进行展示,假设读取不到也就是说本地没有这个业务就会使用线上的 url 地址让页面加载就可以了。因为我们一般不会把所有的业务都做成离线化的形式,假设 webview 查询的到就用离线化,查询不到就用垫底的线上 url 展示。无论本地离线包加载失败还是没有这个离线包,都使用线上 url 来垫底。

离线包的下载一包情况下如果用户处于移动网络状态下,不会在后台下载离线包,如果当前用户点击 app,离线包没有下载好,用户就要等待离线包下载好才能用。可以采取 wifi 静默下载的方案。

从服务器请求的离线包信息存储到本地数据库的过程中,离线包信息包括离线包的下载地址,离线包版本号,加密签名信息等,安装离线包其实就是将离线包从下载目录拷贝到手机安装目录。

一些大厂的离线包方案比如美团的 LsLoader 通用移动端 WebApp 离线化方案,腾讯的 Alloykit 手 Q 离线包,阿里的极致 Hybrid 航旅离线包再加速。原理基本上都是一致的,细节上可以做些参考。

混合开发介绍

1.RN React Native 是基于 React 语法的, 希望实现的是一套代码可以在各个端使用。他的优势很明显,代码是可以共享的无论是 IOS 还是安卓还是 H5, 性能方面几乎也与 Native 相同。并且提供了非常流畅的动画,因为他在渲染之前代码就已经转换为了原生视图。

调试时无需每次代码变更都编译打包,可即时查看更小效果,极大提高了开发人力。

支持热更新,不需要每次发版都发布应用到商店,发版时间可以自由控制,安卓和 ios 同时发版。

img

一共分成四层实现,最下面是 native 的原生层也就是 OC 和 Java,在这之上是 UI 渲染器,图片处理,网络通信,和一些工具库,再向上是 C++: JSCore,Bridge 也就是 js 的运行环境和 js 和 native 的桥接。最上面才是 js 层也就是 js 的一些组件。

RN 的 jsx 文件通过 JSBridge 会针对不同平台打包成不同的格式,比如 IOS 的. m 文件,安卓的. xml 文件,以及 H5 的. html 文件。

为什么会有 RN 其实是因为应用商店发版的问题,每一次发版都需要审核,可能审核不通过,而且安卓可能要发布多个商店,还有两端研发不同步的问题,也就是安卓和 ios 相同的业务需要开发两遍。

如果你公司的技术是 React 全家桶,那还是建议选用 RN 的。

小程序

小程序的愿景是触手可及,用户扫一扫或者搜索下就可以打开应用,不需要安装太多应用。

向程序相比 App,开发门槛更低,优于 H5 接近 Native 的体验,可以使用相机,位置,网络,存储等丰富的原生能力。支持顶部下拉,搜索,扫码等入口,简单方便,用完即走,不需要像 App 那样下载,直接打开支持热更新。

img

小程序出现的行业背景,对于 App 大厂来说需要流量变现,比如微信,他是没办法变现的,所以可以使用小程序生态将第三方引入进来,形成了一个小型的应用市场。对于企业应用来说,移动流量枯竭,获客比较困难,可以降低获客成本和开发成本,业务上提供更多的试错机会。

平台类产品如果输出给商家端,相比多个 app 的方式,比较推荐使用小程序。

Flutter

号称编写一次可以部署到各个终端,web, android,ios,mac,linux,windows,fuchsia os。

底层使用 Skia 图形引擎,图形性能媲美原生应用,界面更像一个全屏应用程序或 2D 游戏,速度比较快,使用本机 ARM 二进制文件,做到提前编译,不需要 JVM,也就是 java 虚拟机。

img

底层实现

img

flutter 在 2017 年 5 月份出现,生态不够丰富,学习曲线相对较高,但是他的性能较好,如果考虑性能,团队人员足够的话建议选择 fluttr。

CDN

CDN 是内容分发网络,利用每一台最靠近用户的服务器,更快更可靠的将文件发送给用户。以加快访问速度。

CDN 的有点很明显,因为会给用户指派较近,较顺畅的服务器节点,所以速度会比较快,服务器放在不同地点,减少了互联的流量,也降低了快带成本,当某个服务器故障时,自动调用临近地区的服务器。

回源是指浏览器访问 CDN 上静态文件时,文件缓存过期,直接穿透 CDN 而访问源站机器的行为。这是 CDN 的一个策略。

CDN 的缓存分为三级,浏览器本地缓存也就是 header 头配置的缓存,CDN 边缘节点缓存,CDN 源站缓,一般是三级,也可能业务比较少就采用两级缓存,浏览器缓存和 CDN 源站缓存。

缓存时间设置过短的话,CDN 边缘节点缓存经常失效,导致频繁回源,增大了源站负载,访问速度也会变慢,缓存时间设置的过长,文件更新慢,用户本地缓存不能及时更新,所以一般是结合业务情况而定。

一般不同资源类型缓存时间设置不用,html 一般 3 分钟左右,js,css 可以 10 分钟,一天,一个月,看变更情况。

现在一般我们文件的命名都会以 hash 串的形式,如果文件有变更生成的文件名就会有变更,否则还是之前的名字,这样我们缓存的时间就可以设置的长一些。

CDN 可以灰度发布,也就是在部分地区部分运营商优先发布静态资源,验证通过后再进行全量发布。具体实施可以从域名方面下手,设置特殊 VIP 解析到要灰度的城市或者运行商。也可以调整源站机器,给灰度的城市或者运营商配置单独的源站机器。

一般在活动期间比如说大促,需要增加机房宽带,增加运营商流量,增大 CDN 缓存时间等等。

DNS

DNS 是将网站域名和地址互相映射的一个分布式数据库,我们访问一个网站首先会通过 DNS 将域名匹配为 ip 地址,然后再通过 ip 地址去访问对应的服务器。

客户端里面有一个 http dns, 客户端直接访问 http 的接口,可以获取业务在域名访问系统中配置的最优 ip,基于容灾的考虑,app 内是需要保留使用运营商 DNS 解析方式的,客户端再获取到 ip 后会直接向 ip 中发送业务请求。

比如一个 http 请求,在 header 中会指定 host 字段,向 ip 发送标准的 http 请求就可以了,总的来说采用 http-dns 来解析域名能绕过三四级运营商接续域名出现的一些问题,在 http-dns 返回的正确 ip 之后是直接使用 ip 去发送 http 请求的,只需要关注通信内容的安全就可以了。

安卓系统可以采用 okhttp 模块,他支持 http2,http2 可以在一个链接上一次性发送多个请求,支持 gzip,也支持响应缓存避免网络重复请求,如果服务器配置了多个 ip 地址,当第一个 ip 链接失败的时候,okhttp 会自动尝试下一个 ip 地址。

ios 没有现成的模块,我们可以在 app 启动时,缓存所有可能要用到的域名 ip 比如接口,网关,同时异步处理,客户端无需得到缓存结果。如果 cache 中有此域名的圆环,直接返回缓存的 ip,如果缓存中没有此域名,则重新向 httpdns server 进行申请然后缓存下来。

H5 的做法一般是设置多个域名,因为浏览器对并发数是有限制的,一个域名一般最大连接数是 6,所以我们可以将用户访问的一些 api 接口作为一个域名,页面中的样式和资源可以设置成一个域名,图片也可以单独设置成一个域名,甚至多个域名,来打破浏览器的这种限制。

http 优化

http 的优化主要就是减少请求数,这可能是我们日常工作中经常遇到的,也是大家耳熟能详的。

图片可以使用雪碧图,dataurl, webfont。

可以考虑将业务中的 js 或者 css 合并,不要切割的太小。如果不想合并成一个可以使用 Combo 的方式让服务去返回,可以在 url 上通过参数的形式告诉服务加载那些资源。

接口也可以合并,不要拆分的太细,可以让服务去合并,不经常变化的接口和资源也可以存储在 LocalSrorage,有变化就更新,没有变化就从本地取。

有些时候我们的某个页面会出现问题,或者打开白屏,但是接口没有问题,页面也没有问题,资源也是可以访问通的,这个时候可能就是 cookie 太大了,已经超出了原本可控的范围,我们都知道 cookie 是会随着页面间的跳转携带的,这就肯能导致页面无法访问,这种问题不常见,但实际工作中确实会遇到。

可以在页面中设置 cookie 白名单,意思就是定期检查我们的 cookie,如果是需要的就保留,不需要的就删除,定期整理。cookie 控制可以减小页面间传输的大小,也可以对 cookie 进行有效的管理。

服务器缓存配置

当一个文件被浏览器加载的时候我们实际上是不知道这个文件是否是过期的,所以浏览器和服务器之间存在一种约定,通过 header 头的配置,确定文件是否过期。

一般在响应头中包含一个 expires 的头信息,他的值为日期 + 时间,表示在此时间之后,响应过期,如果数值为 0,表示资源已经过期。

当然如果响应头中包含 Cache-Control, 设置了 max-age 或者 s-max-age 指令,那么就会忽略 expires,而取 Cache-Control。

Cache-Control 通过制定的指令来实现缓存机制,缓存指令是单向的,这意味着在请求中设置的指令不一定被包含在响应中。他的语法比较简单,Cache-Control: max-age=秒设置缓存存储的最大周期,超过这个时间缓存被认为过期。

ETag 是资源版本的标识符,可以让缓存更搞笑,并节省带宽,因为如果内容没有改变,Web 服务器不需要发送完整的响应,如果发生了改变,使用 ETag 有助于防止资源的同时更新相互覆盖。

ETag 类似于指纹,也可能被某些服务器用于跟踪,比较 ETag 能快速确定资源是否变更,但也可能被跟踪服务器永久存留。

ETag: "5c6cccc123-1d45"
复制代码

Last-Modified 是一个响应收不,其中资源包含源头服务器认定的资源做出修改的日期和时间。他常被用作一个验证器来判断接收到的或者存储的资源是否一致。由于精度比 ETag 要低,所以这是一个备用机制,包含有 if-Modified-Since 或 If-Unmodified-Since 首部的条件请求会使用这个字段。

Date 是通用的首部,其中包含了报文创建的日期和时间。

Gzip 压缩

可以对文本进行压缩,一般是 html,css,js,对于非文本不会压缩,比如说图片资源,压缩比率可以达到 50%-70%

本地测试开启 https

这个内容不应该写在这,但是实在不知道写在哪了。

https 这里就不过多介绍了,毕竟不是这篇该有的内容。

浏览器目前基本上已经默认开启了 https,所以为了 SEO 我们也建议使用 https,而且 https 也更加安全。

如果是对外的网站我们需要和经销商购买 ssl 证书,可以在 gogetssl,ssls.com,sslmate.com 中去购买,当然这些证书是有时效的。

如果本地测试的话,也可以在本地安装一个测试证书,我们可以通过 mkcert 来实现。首先需要安装它。

brew install mkcert
复制代码

安装根证书

mkcert ---install
复制代码

生成本地签名,给 123.com

mkcert 123.com
复制代码

这样就生成了一对证书,我们可以将这对证书配置在 nginx 里面,具体配置方法可参考我之前写的 nginx 文章。

http2

http2 是 http 的第二版,简称 h2 或 h2c,它采用二进制传输数据,多路复用,允许通过一个链接发起多个请求,所以一般使用 h2 雪碧图就没什么用了,他超出了浏览器限制最大连接数的局限,对 header 头进行压缩从而降低传输体积,支持服务端推送 (server push),可以从服务端将数据推送给客户端。

开启 HTTP2 可以降低服务器压力,提升网站访问速度,而且可以更好的保护网站安全因为他是强制使用 https 的。

开启 http2 其实也很简单,我们需要重新编译 nginx,并且开启 http_ssl_module 和 http_v2_module

cd nginx-xxx
./configure --with-http_ssl_module --with-http_v2_module
make && make install
复制代码

同样这里可以参照之前我写的 nginx 文章,其实就是在 listen 443 端口后添加 http2 标识。

server {
    listen 443 ssl http2;
}
复制代码

前端的研发流程

首先是技术选型,包括页面渲染技术和混合式开发技术,然后是项目的初始化,包括 React,Vue,Angular,依赖模块引入,一般会存在一个私有的 NPM,接着开始本地开发,方便前端调试和看到效果,项目联调,产品和设计师对效果进行确认,最后项目整体部署上线。

项目开始之前前后端会指定一些数据接口,有了接口文档前后端就可以并行开发,前端开发页面和交互,后端开发业务逻辑。都开发完后前后端开始进行联调,最后发布上线。

自动化测试

UI 自动化,上手比较简单,不过稳定性较差,常用的工具有 appium,他是一个开源的工具用于自动化 ios 手机,安卓手机还有 windows 桌面的一个测试工具,robot framework 是基于 python 可扩展的关键字测试框架用于端到端,验收测试以及测试驱动开发,可用于测试分布式异构应用程序包括可以验证涉及多种技术的接口,selenium 用于 web 应用程序测试工具可以直接运行在浏览器上,可以和用户的真正操作是一样的,支持 ie, 火狐,谷歌,欧朋等常用浏览器,主要用来测试浏览器的兼容性。airtest 支持自动化的脚本录制一键回收,轻而易举就能实现自动化测试流程,还是比较常用的。

接口自动化,使用稳定,性价比非常高,工具有 java + restassured,是 java 实现的,轻量级的可以通过编写代码向客户端发送请求并且验证返回结果。python + requests 主要对 pthon 接口进行测试, JMeter 用于对软件做压力测试,HttpRunner 只需要一份脚本就可以实现自动化测试性能测试,线上监控,持续集成等多种测试的需求。

单元测试,性价比极高,一般由开发完成,但是有一些单元测试框架,Junit5,pytest, unittest。

作者:隐冬

链接:https://juejin.cn/post/6911512163249029134

来源:掘金

最后

欢迎关注【前端瓶子君】✿✿ヽ (°▽°) ノ✿

欢迎关注「前端瓶子君」,回复「算法」,加入前端算法源码编程群,每日一刷(工作日),每题瓶子君都会很认真的解答哟!

回复「交流」,吹吹水、聊聊技术、吐吐槽!

回复「阅读」,每日刷刷高质量好文!

如果这篇文章对你有帮助,「在看」是最大的支持

》》面试官也在看的算法资料《《

“在看和转发” 就是最大的支持