得物AppH5秒开优化实战

导读一开始我们的H5页面秒开率只有30%左右,现在我们的H5页面秒开率达到了 75%。这中间巨大的差异究竟有哪些黑科技在里面?我们为什么要做H5页面的秒开优化?我们的秒开指标是如何统计的?客户端和H5是怎么配合做到 1+1>2的?监控是如何发现H5页面可优化项的?我们又通过监控发现了哪些可优化的问题呢?

H5秒开优化是一个老生常谈的问题,本文将逐步介绍如何通过客户端 + H5 的优化手段(1+1>2)把秒开从 30% 提升到 75% ?后续接口预请求、客户端预渲染以及预加载2.0上线后还会再次助力指标提升。

为什么要做优化?

Global Web Performance Matters for ecommerce 的报告中指出:

  • 47%的用户更在乎网页在2秒内是否完成加载。
  • 52%的在线用户认为网页打开速度影响到他们对网站的忠实度。
  • 每慢1秒造成页面 PV 降低11%,用户满意度也随之降低降低16%。
  • 近半数移动用户因为在10秒内仍未打开页面从而放弃。

整体系统架构图:

首先讲一下得物用来衡量秒开的指标 FMP,那为什么不选择 FCP 或者 LCP 呢?FCP 只有要渲染就会触发,LCP 兼容性不佳,得物希望站在用户的角度来衡量秒开这件事情,用户从点击打开一个WebView到首屏内容完整的呈现出来的时间点就是得物定义的FMP触发时机。

指标清楚了之后,再来看一下完整的 FMP 包含哪些耗时。

接下来将分为两大部分进行介绍,客户端优化部分和H5 优化部分。

通过 HTML 预加载、HTML 预请求、离线包、接口预请求、链接保活、预渲染等手段提升页面首屏打开速度,其中预加载、预请求、离线包分别可提升10%左右的秒开。

通过配置由客户端提前下载好HTML主文档,当用户访问时直接使用已经下载好的HTML文档,以此减少HTML网络请求时间,从而提升网页打开速度。

前人栽树后人乘凉,得物App有很多的资源位,banner、金刚位、中通位等,这些位置显示什么内容,早就已经是智能推荐算法产出的了,那么就可以直接指定这些资源位进行预加载。

页面被预加载之后,总不能一直不更新吧?那么什么时候更新页面的缓存呢?

  • 预加载的页面存放于内存中,关闭App缓存就会被清除
  • 通过配置过期时间人为控制最大缓存时间
  • 在页面进入后发起异步线程去更新HTML文档。

被现实打脸:

但是在后面的灰度过程中被现实狠狠的教育了一顿,发现有些SSR的页面会涉及到状态的变更,比如说:领劵场景。这些状态都是经过SSR服务渲染好的,用户在进入页面时还没有领劵,这个时候去更新HTML文档,实属更新了个寂寞,在用户领劵之后关闭页面再次进入,发现页面中的状态仍是让用户领劵,点击领劵又告诉人家你已经领过了。

改进措施

  1. H5 页面在打开时针对状态可能会发生变更的组件,再次请求接口获取最新的状态数据。
  2. 客户端由进入页面就更新HTML文档修改为:关闭WebView时更新HTML文档。

至此问题也解决了,工程师的任务结束了吗?如果你认为功能做上去就算结束,那么此时此刻请你一定要转变思维,想一想我们的目标是什么?我们的目标是「提升秒开」,预加载只是一种提升秒开的手段,但是在功能做上去之后并不知道这个功能带来了多少秒开的收益,因此在把功能开发完成上线之后,就要开始关注上线之后的结果,来看看预加载的性能表现如何。从下图可以看到,预加载开启状态下可提升10%以上的秒开率。

  1. 预加载的页面基本上都是 SSR 服务的页面,预加载无形中造成了大量的请求,此时得物的SSR服务扛不住这么大的请求量;
  2. 即使SSR 服务扛得住,也会对后端整个服务链路造成压力。

(1)SSR服务扩容

要解决服务器压力问题,很自然就会想到增加机器,于是我们对SSR机器数量做了一次扩容,将机器数量提升了一倍,这个时候继续尝试扩大预加载的用户数量,但是仍然无法抗住这么大的QPS,而且此时还引发了第二个问题,算法部门的服务器发出了告警,于是放量计划又一次遇到了阻碍。

(2)破局者 CDN

利用CDN 服务器的缓存能力既可以减轻 SSR 服务器的压力又可以减少后端服务链路的压力,这么好的东西为什么不用呢?这里留个悬念,后面将H5优化部分会详细介绍。

(3)客户端配合改造

支持针对CDN域名进行全部开放预加载能力,针对非CDN域名保持原有放量比例。

在这个过程中还分析了页面的流量占比,发现开屏广告来源的页面流量占比也很高,那么是不是可以把开屏广告的HTML文档内容也给预加载下来呢?

开屏页面预加载策略

  1. 对预加载列表进行去重,开屏广告列表中可能会存在重复的页面,他们的背景图和生效时间是不同的;
  2. 增加了生效时间相关配置,开屏广告列表中存在于未来某段时间才会展示的页面;
  3. 添加黑白名单控制,开屏广告列表中可能会有第三方合作页面,他们不希望预加载统计会造成PV时不准确。

既然可以提前下载好HTML,那是不是可以更进一步,提前把页面内的资源加载好,这样在打开一个页面的时候可以减少大部分的网络请求从而更快速的把内容呈现给用户。这里还需要考虑如何跟下面讲到的离线包进行协作。

在WebView初始化同时,去请求HTML主文档,等待HTML文档下载完成 且 WebView初始成功后渲染,减少用户等待时间,客户端请求成功后,WebView加载本地 HTML,并保存以供下次使用。预请求HTML开启状态下可提升8%左右的秒开。

预请求 VS 预加载

本质上HTML预加载和HTML预请求的区别就是下载HTML文档的时机不同, 预加载是在App启动后用户无任何操作的情况下就会去下载,但是预请求只会在用户单击打开H5页面的时候才会去下载。如果用户是第二次打开某个H5页面,此时发现本地有已经下载好的HTML且尚未过期就会直接使用,这个时候的行为表现就跟预加载的功能是一致的了。

上线之后发现预请求只提升了2%左右的秒开,经过分析发现问题:

  1. 缓存有效时间太短,页面过期时间只配置了10分钟,也就是说在10分钟之后用户就要重新去下载一次,那能不能把缓存时间延长呢?
  2. H5页面是没有自更新能力,无法支持配置更长的缓存时间,跟预加载HTML问题一致。

在本地用低端机对整个秒开耗时链路进行了分析,为什么要用低端机分析呢?低端机有个好处,天然的加上了慢放功能,可以最大程度发现问题。

从图中可以看出h5 页面加载之前 耗时 分布在 activityStart() 函数,该函数 包含了 onCreateView ,其中耗时最长是 布局填充 inflate(),因为 WebView 对象是提前创建好的,直接从对象池中取出的,所以耗时主要在 初始化过程,WebView 自身的初始化 WebViewChromiumFactoryProvider. startYourEngines (耗时 87 us,不到 1 ms),耗时还有 WebView 的一些其他初始化,jockey 的初始化 等等。

而秒开的计算是包含了 View 初始化到 WebVIew url 加载 的耗时,从而发现了优化点,可以将Webview loadUrl 前置,h5 页面加载 与 原生布局填充并行执行。在 onCreateView 时,创建 FrameLayout 进行返回,执行 WebView loadUrl 之后,主线程开始 对布局进行 inflate,布局加载成功后,将其 addView 到 FrameLayout 中,减少了 loadUrl 的阻塞时长。中高端机型有 15ms 左右优化,低端机型有 30 ~ 50 ms 优化 效果。

预请求HTML时机是在进入到 native 页面中,这个时间点距离用户单击事件已经过去100ms,那么是否可以将下载HTML的时机提前呢?经过一番探索,最终选择在路由阶段进行拦截,既可以统一收口而且距离用户点击的时间间隔可以忽略不计。通过这种方式将下载HTML时机提前了平均80ms+。

此时的流程变成了下面这样。

可能有的同学会问了,为什么不在用户点击的时候去下载呢?从用户点击到路由肯定还是有耗时的。

  1. 代码层面不好维护,如果在点击时就需要侵入到业务层面,入口千千万,很难进行维护管理;
  2. 从点击到路由这部分耗时在线下进行了性能测试,几乎可以忽略不计。

在上述问题解决后,将缓存时间修改为1天,发现预请求HTML开启状态下可提升8%左右的秒开,已经和预加载的效果相差不大了。

通过提前将H5页面内所需的css、js等资源聚合在一个压缩包内,由客户端在App启动后进行下载解压缩,在后续访问H5页面时,匹配是否有本地离线资源,从而加速页面访问速度。

资源拦截这块安卓这边实现比较简单,WebView支持 shouldInterceptRequest, 可以在该方法内检测是否需要进行资源拦截,需要的话返回 WebResourceResponse 对象,不需要直接返回 null。

但是在iOS 这边遇到了一些困难,调研了以下方案:

NSURLProtocol 拦截方式,使用WKBrowsingContextController和registerSchemeForCustomProtocol。通过反射的方式拿到了私有的 class/selector。通过把注册把 http 和 https 请求交给 NSURLProtocol 处理。通过这种方式确实可以拦截请求,但是发现post请求的body会出现丢失的问题。而且NSURLProtocol一经注册就是全局开启。我们希望他只会拦截接入了离线包的页面,但是没有办法控制他,他会拦截所有页面的请求,包括第三方合作页面,显然这是无法接受的。

在iOS 11及以上系统中, 拥有了加载自定义资源的API:WKURLSchemeHandler。

可以修改当前页面url为自定义 scheme 协议,比如:https://fast.dewu.com 修改为 duapp://fast.dewu.com 然后在客户端内注册该 scheme,前端配合修改页面内所有的资源请求未自适应协议,如:src=\”//fast.dewu.com/xxx\” 就可以实现拦截。但是在测试过程中发现,接口为了安全起见只允许白名单内的域名发起跨域请求,且无法配置多个域名,导致该方案无法继续进行。

仍然是使用 WKURLSchemeHandler 然后通过 hook WKWebview 的 handlesURLScheme 方法来支持 http 和 https 请求的代理。通过这种方式虽然可以拦截请求了,但是遇到了以下问题:

(1)body丢失问题

不过在 iOS 11.3 之后对这种情况做了修复处理,只有 blob 类型的数据会丢失。需要由JS来代理 fetch 和 XMLHttpRequest 的行为,在请求发起时将 body 内容通过 JSBridge 告知 native,并将请求交给客户端进行发起,客户端在请求完成后 callback js 方法。

(2)Cookie 丢失、无法使用问题

通过代理 document.cookie 赋值和取值动作,交由客户端来进行管理,但是这里需要额外注意一点,需要做好跨域校验,防止恶意页面对cookie进行修改。

至此功能开发完成上线,先来一组线上收益数据,安卓开启离线情况有有10%左右的收益,但是iOS开启离线的反而秒开率更低。经过修复处理后iOS也可提升10%以上的秒开。

安卓和iOS实现差异

经过分析对比发现,安卓的拦截动作比较轻,可以判断是否需要拦截,不需要拦截可以交给WebView自己去请求。

但是iOS这边一旦页面开启拦截后,页面中所有的http和https请求都会被拦截掉,由客户端发起请求进行响应,无法将请求交还给WebView自己去发起。

iOS 缓存问题修复

页面中的资源经过客户端请求代理后原本第二次打开WebView本身会使用缓存的内存,现在缓存也失效了,于是只能在客户端内实现了一套缓存机制。

    1. 根据 http 协议来判定哪些资源可以被缓存以及缓存的时长
    2. 添加自定义的控制策略,仅允许部分类型的资源进行缓存

从下图可以看到离线包的下载错误率在 6% 左右浮动,这么高的下载错误率肯定是无法接受的, 经过一系列优化手段,把离线包下载错误率从6%左右浮动下降至 0.3%左右浮动。

先来看下优化前的流程图和问题点。

通过埋点发现大量 unknown host 、网络请求失败、网络连接断开的情况。分析代码发现下载未做队列控制,会同时并发下载多个离线包,从而导致多个下载任务争抢资源的情况。针对发现的问题点做出了以下优化:

    1. 下载失败添加重试机制,并可动态配置重试次数用于缓解网络请求失败、网络连接断开的问题。
    2. 添加下载任务队列管理功能,可动态配置并发下载数量,用于缓解不同下载任务争抢资源的问题。
    3. 针对弱网和无网络情况延迟到网络良好时下载。
    4. 离线包下载支持 httpdns,用于解决域名无法解析的情况。

下面是优化之后的流程图:

针对离线资源是直接存储在磁盘上的,每次访问都会有磁盘IO耗时,经过在低端机器上做测试发现这个耗时会在 0 – 10ms 之间进行波动,后面会把内存合理的利用起来,通过设置内存上限,文件数量上限,甚至是文件类型,并通过 LRU 策略进行内存文件的淘汰更新。

通过客户端发起H5页面首屏接口请求,远比等待客户端页面初始化、下载HTML、JS下载执行的时机更提前,从而节省用户的首屏等待时间。在本地测试过程中发现接口预请求可提前100+ms,用户也就可以更快的看到内容。

客户端会在App启动后获取配置,保存支持预请求的页面地址及对应的接口信息,在用户打开WebView时,会并行发起对应预请求的接口,并保存结果。当JS执行开始获取首屏数据时,会先询问客户端是否已经存有对应的响应数据,如果此时已经拿到数据则无需发起请求,否则 js 也会发起接口请求并开启竞速模式。以下是整体流程图:

那么客户端怎么知道这个页面需要请求什么接口呢?以及接口的参数是什么呢?那自然少不了配置平台,它支持以下功能:

    1. 配置需要预加载的页面 url并对应一个需要请求的 api url 以及参数
    2. 配置审核功能,避免错误配置发布上线

首先即使是在 SSR 的情况下,首屏内容中仍然可能有部分组件是骨架直出的,需要等待页面渲染执行时才会去请求数据,另外还有一部分页面是SPA的。针对这两种情况都能做到很好的补充。

开启后DNS 90分位耗时从80ms降至0ms,TCP建连90分位耗时从65ms分位耗时降为0,DNS平均耗时从55ms降为4.3ms,TCP建连平均耗时从30ms降为2.5ms。

通过上图可以看到一个网络请求在经过DNS解析耗时、TCP建连耗时、SSL建连耗时阶段之后才能把请求发出去,那么是否可以节省这段时间的耗时呢?

客户端常用的网络请求框架如OkHttp等,都能完整支持http1.1与HTTP2的功能,也就支持连接复用。了解了这个连接复用机制优势,那就可以利用起来,比如在APP开屏等待的时候,就预先建立关键域名的连接,这样进入相应页面后可以更快的获取到网络请求结果,给予用户更好体验。在网络环境偏差的情况下,这种预连接理论上会有更好的效果。

可以通过对域名链接提前发起一个HEAD请求从而建立链接,网络框架会自动将连接放入连接池。并在默认无操作5分钟后进行释放,在五分钟内重复执行上述动作即可一直保持链接。

另外这里需要注意下连接池的数量问题,如果连接池的数据太小,但是域名比较多的话,通过预建连保持的链接很容易就会被释放掉,这就需要通过域名收敛或者调大连接池的数量来进行优化。

那预建连会不会增加服务器的压力呢?这个肯定是会的,首先会针对预建连功能本身就行灰度策略,在HTML页面通过CDN托管后,直接针对 cdn 域名进行全量开启,从而不用担心 cdn 域名扛不住压力。

来看一下线上效果,通过下图可以看到在开启后DNS 90分位耗时从80ms降至0ms,TCP建连90分位耗时从65ms分位耗时降为0,DNS平均耗时从55ms降为4.3ms,TCP建连平均耗时从30ms降为2.5ms。

客户端提前通过WebView将页面渲染好,等待用户访问时,可直接展示。从而达到瞬开效果。但是这种功能肯定不能对所有的页面进行开放,而且存在一定的弊端。

    1. 会额外消耗客户端资源,需要在主线程空闲时执行,并需要控制预渲染的页面数量。
    2. 如果页面一进入就会下红包雨,这种页面是不适合做预渲染的,需要进行规避。

下图【开学季】是业务上已经进行预渲染的H5页面,可以看到在打开【开学季】页面时,页面已经渲染完毕,丝毫没有等待过程。

后面计划把这种能力放大到通用的webview上,针对大促以及PV量高的页面进行开放。

SSR服务端渲染(英语:server side render)一般情况下,一个H5页面的数据渲染完全由客户端来完成,先通过AJAX请求到页面数据并把相应的数据填充到模板,形成完整的页面来呈现给用户。而服务端渲染把数据的请求和模板的填充放在了服务端,并把渲染的完整的页面返回给客户端。

SSR对于秒开有平均15%的提升,既然是服务端渲染,就会对服务器造成压力,尤其是在预加载HTML功能开启后,那得物是如何解决的呢?

    1. 接口缓存:node服务向ctx中注入redis实例,业务方在服务端渲染的逻辑中处理接口相关的缓存,这其中涉及:配置文件下发、ab接口。
    2. 静态页面缓存:由于页面完成是无接互,并且所有用户展示都是一样的,由renderToHtml生成静态html资源,写入缓存。
    3. 无用户状态页面缓存:此类页面大多数情况下展示内容都是一样的,服务端请求的数据也都是一致的,服务端处理的时候根据是否有用户登陆状态判断是否需要缓存。
    4. 千人千面接口内容由SSR修改为CSR,并显示骨架图,由于千人千面内容都是由算法接口提供,且算法接口本身响应较慢,因此通过这种方式可以减少服务端响应耗时,且能更快速的给用户展示内容。

通过这么多优化手段仍然无法满足预加载的需求,并且通过分析发现网络阶段耗时较长,最终还是搬出了CDN这个大杀器,一直没上CDN的原因有很多,主要有以下几方面:

(1)得物的页面是千人千面的每个人看到的内容都不同

通过上述优化4即可解决,将原本SSR渲染的内容修改CSR,由于已经上了CDN了,后续计划将这部分内容再次修改回SSR,这样用户可以更快的看到商品而不是骨架,然后通过 CSR 的方式来更新内容。

(2)页面状态变更,无法及时更新缓存

这个问题在上述客户端预加载优化部分已经有解决方案了,可以通过在页面打开后针对有需要的组件再次请求接口刷新数据以确保数据的准确性,但是这部分工作量也是比较大的,梳理出来的需要刷新状态的组件就有30+,而且之后开发的组件都需要考虑状态更新的事情。

(3)HTML模板内容变更无法及时更新

引起模板内容变更的地方有两处,第一个场景就是在搭建器场景下,运营可以动态修改模板内容导致页面结构变更(低频次),第二个场景是项目发版后模板内容需要更新(高频)。

这个问题可以通过在感知到内容变更时自动调用CDN服务商的刷新缓存接口来更新CDN缓存内容。

通过puppeteer将SPA页面渲染出来并将HTML文档进行保存,配合上述页面刷新策略,并将HTML通过CDN进行托管,让你的 SPA 页面 像 SSR 一样丝滑。

主要实现方案是通过基于webpack的插件prerender-spa-plugin,并配置需要预渲染的路由,这样经过打包之后就会产出对应路由的页面。方案本身是通用的,但是每接入一个页面都需要人工check。

众所周知 css 加载会阻塞HTML渲染,最终将首屏公共css从118kb缩减至38kb,下图通过 chrome 模拟弱网环境下的SSR页面加载时序图。从图中可以看出 styles.fb201fce.chunk.css 下载耗时 18s,阻塞了页面的渲染,HTML 主文档耗时 2.38s 就已经下载完成了,但是实际渲染时间却是在 20s 之后。

优化思路也比较单间,将首屏所需要的css 文件通过内联方式内嵌到HTML中,由SSR服务一并返回,并对css文件进行拆分,按需加载。

思路有了,接下来就看怎么去实现了,最初尝试了MiniCssExtractPlugin 插件他可以把css分成单独的文件,并且每一个js会对应生成一个css文件,但是他需要建立在webpack5之上,然而项目中使用的next版本是9.5,于是就想着升级到最新版next12,升级后发现,在构建中其他包各种报错,发现有些包并不支持最新的next12,在尝试一天的修复之后,仍未解决,且升级到最新版不确定是否会引发其他稳定性问题,暂且搁置寻找其他方法。

经过不懈的努力,通过阅读 next 源码发现了端倪,发现在打包时将所有的公共css通过 splitChunks 进行分组,由于项目中组件都是动态引入的,这里直接在 next.config.js 中修改webpack打包参数,将 splitChunks.cacheGroups.styles 配置删除,使用默认的 chunks: async 配置,即可实现按需引入。

(1)避免图片src为空

虽然src属性为空字符串,但浏览器仍然会向服务器发起一个HTTP请求,尤其是在SSR服务器压力扛不住的情况下,因此这里需要特别注意一下。

(2)图片压缩以及格式选择

WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。

通过向图片服务器传递参数选择合适的分辨率。

(1)打包优化

  • 页面组件拆分,优先加载首屏内容所需资源
  • webpack splitchunks 有效拆分公共依赖,提高缓存利用率
  • 组件按需加载
  • Tree Shaking 缩减代码体积

(2)非关键js、css延迟加载

  • defer、async、动态加载js
  • iOS 设备延迟加载 js

(3)媒体资源加载优化

  • 图片、视频懒加载
  • 资源压缩、通过向图片服务器传递参数选择合适的分辨率

(4)其他资源优化

  • 数据埋点上报延迟发送,不阻塞 onload 事件触发
  • 自定义字体优化,使用 fontmin 生成精简的字体包

(5)页面渲染优化

  • 页面渲染时间优化
    • SSR页面首屏 css 内联(Critial CSS)
  • 合理使用 Layers
  • 布局抖动优化:提前定好宽高
  • 减少重排重绘操作

(6)代码层面优化

  • 耗时任务分割
    • 通过 Web Worker 减少主线程耗时
  • 通过 RAF 回调,在线程空闲时执行代码逻辑
  • 避免 css 嵌套过深

为了帮助开发者更好地衡量和改进前端页面性能,W3C性能小组引入了 Performance API ,其中Navigation Timing API 实现了自动、精准的页面性能打点。得物前端性能监控指标也都是从 Performance API 中获取数据进行上报统计分析的。

SDK 数据采集完毕后,会上报到 阿里云 sls 日志平台,随后通过 flink 实时消费清洗数据后落库至 clickhouse 中,平台后端通过读取 clickhouse 数据并做各种聚合处理后使用。

做优化之前首先要建立监控指标,互联网称之为抓手,没有监控指标的情况下,任你怎么优化,都不知道优化的效果怎么样,更不知道下一步该做什么?以及还有哪些问题没解决。因此优化之前指标先行,当然一定要指标的准确性。

指标大盘主要包含以下功能:

    1. 可快速查看某段时间内的版本、设备厂商、设备名、设备系统版本以及网络占比情况,还可以根据这些字段进行筛选排查。
    2. 中间区域展示了比较关注的整体以及活动页面的客户端耗时和H5秒开耗时的情况。
    3. 底部区域展示了各业务域的秒开耗时情况。
    4. 这里同时展示了平均耗时90分位耗时,平均耗时有个弊端就是很容易被平均,大家应该都有被平均的经历。90分位耗时这里简单讲解一下:意思就是有90%的访问耗时是低于90分位耗时的,以此类推 50 分位就是有50%的访问耗时是低于50分位的,分位值是将所有的耗时数据从小到大排序后得出的。

在正常情况下,完成上述的优化措施后用户基本是可以秒开 H5 页面的了。但异常情况总是会有的,用户的网络环境和系统环境千差万别,甚至 WebView 也可能发生内部崩溃。当发生问题时,用户看到的可能就直接只是一个白屏页面了,所以进一步的优化手段就是需要去检测是否发生白屏以及相应的应对措施。

检测白屏最直观的方案就是对 WebView 进行截图,遍历截图的像素点的颜色值,如果非纯色的颜色点超过一定的阈值,就可以认为不是白屏。首先获取包含 WebView 视图的 Bitmap 对象,然后把截图缩小到指定的分辨率大小如:100*auto,遍历检测图片的像素点,当非纯色的像素点大于 5% 的时候就可以认为是非白屏的情况,但是还有很多列外的情况,我们通过图片识别技术对截图进行分析,可以很好的感知当前是否白屏、是不是在loading、是不是特殊页面等。

白屏是一个重要的指标,我们针对整体白屏率快速拉升、新增白屏页面发出告警通知,便于开发人员及时介入开始排查问题。

主要通过 CDN 未覆盖监控、http请求监控、网络监控(加载失败、耗时异常、传输大小异常)、图片监控(未压缩、分辨率异常)等监控手段发现页面中的潜在问题,同时还提供了问题分析能力,在问题分析页面输入页面url地址即可帮助您发现问题并给出修改建议。

CDN的重要性不言而喻,它可以加速资源访问速度,从而提升用户体验,我们通过对线上埋点数据分析,找出CDN未覆盖的资源列表,从而推动各业务同学优化。

为什么要监控HTTP请求呢?我们先来看一下HTTPS相对于HTTP新增的特点:

  1. 内容加密:采用混合加密技术,中间者无法直接查看明文内容
  2. 验证身份:通过证书认证客户端访问的是自己的服务器
  3. 保护数据完整性:防止传输的内容被中间人冒充或者篡改

那么HTTP就容易被中间人查看到内容,甚至被篡改,既然如此为了我们服务的安全性就需要对现有的http协议统一进行升级改造,那就需要监控去发现。

某些页面秒开率低,那就要分析一下原因,是不是这个页面的接口响应比较慢呢,还是说页面本身有请求比较大的资源?如果发生网络请求失败的情况也要第一时间感知,不能被动等待用户反馈。

包含图片未压缩、图片分辨率异常、图片传输大小大于 300kb 异常、动图资源传输大小大于 1M 异常功能。

上面列出来一堆功能,对于业务的同学可能比较烦恼,我一个页面具体有哪些问题呢?你不能让我去上面的功能里面一个个看,哪个异常是我负责的页面的吧?这个功能本身就行将现有的功能利用起来,通过一个页面path进行聚合分析。

H5异常一直是使用 sentry 进行监控的,但是sentry系统因缺乏同PV、DAU数据的关联性,因此无法衡量产品异常发生后所带来的严重程度。在业务域关联上的缺失导致异常问题无法根据业务域进行划分。用户行为日志也尚未与Native 端侧打通,在问题分析时容易遇到上下文不全的瓶颈。还有一个问题是sentry会有限流措施,当qps较高时会丢弃一部分异常数据。

由于sentry已经可以帮助我们进行一定的问题排查分析能力,我们不打算做sentry同样的功能,而是做sentry不支持的部分,针对上述问题我们设计了以下功能:

  • 异常问题指标衡量
    • 增加异常率、页面异常率、影响用户率趋势
    • 增加问题多维度(系统版本、APP版本、H5发布版本、网络等)下的分布占比、业务域划分
  • 异常问题聚合能力增强(聚合问题能力增强)
    • 异常列表支持最新新增、Top PV、异常次数、影响用户数排序
    • 区分三方sdk异常、接口异常等不同异常类型划分

虽然目前秒开率已经做到了75%以上,但是同时我们还有一个重要的指标,90分位耗时,致力于提升末尾用户H5页面使用体验,在90分位优化完成后,可能会考虑继续深入优化95分位耗时。

至此我们系统的讲解了背景以及从指标建立到秒开优化上线的全过程,全文分成了三个部分,客户端、H5、以及监控。如果阅读本文对您有所收获,麻烦您动一动发财的小手点个赞吧!如果阅读完还意犹未尽或者有什么问题和想法欢迎留言区评论交流。

最后奉上整体优化脑图:

*文/徐铭

欢迎关注「得物技术」微信公众号,每周一三五晚18:30更新技术干货要是觉得文章对你有帮助的话,欢迎评论转发点赞~

类似Visio的Web版绘图工具:JsPlumb

以下jsPlumb介绍基于jsPlubm1.3.3版本,并且基础库是用jQuery1.3.x或以上,与其他版本基础库或jsPlumb的比较或后期版本升级,本文档不做介绍。

Jsplumb是Jquery的一个插件,它能够让你用动态的或静态的链接来连接html界面上行的元素,并且从1.1.0版本开始,提供用鼠标拖动来链接。目前该插件支持三个javascript库,有Jquery、MooToos、Yui3,jsplumb代码是开源的,并且是麻省理工学院许可,由google进行代码托管。

官方示例:http://morrisonpitt.com/jsPlumb/html/demo.html

代码地址:http://code.google.com/p/jsplumb/

Jsplumb介绍地址:http://jsplumb.tumblr.com/

JsPlumb允许您使用SVG、Canvas 或者 VML链接屏幕上的元素,这些取决于您使用的浏览器的能力。

浏览器的兼容性

jsPlumb 1.3.3 已经在以下浏览器测试:

IE 6 on Windows XP

IE 7 on Windows XP

IE 8 on Windows XP

Firefox 3.5.8 on Windows XP

IE 9 on Windows 7

Chrome 12 on Windows 7

Firefox 3.5.8 on Windows 7

Firefox 3.6.3 on Ubuntu 10.04

Chrome on Ubuntu 10.04

Safari 4 on Mac Tiger

Safari 4 on Windows Vista

Safari 5.0.5 on Windows 7

Opera 10.54 on Windows XP

使用jsPlumb需要到如的类库文件,是根据您使用的javascript类库的不同而定,目前提供以下版本。

jQuery

jquery的1.3.x或更高版本

jquery ui的1.7.x或1.8.x(如果您需要支持拖放)

MooTools

MooTools核心库版本1.2.4或更高版本(jsPlumb已在1.2.4和1.3.3版本上测试)

MooTools的Drag.Move的1.2.4.4或更高版本

YUI3

YUI3.3.X ,目前jsPlumb只在YUI3.3.0上测试,YUI3其他版本可能也可以正常工作

jsPlumb可以使用SVG,HTML5的画布元素或VML去呈现需要显示的对象,现代大部分浏览器都支持Canvas 和 SVG,但是IE9以下都不支持这些,默认情况下,jsPlumb都使用canvas去渲染,但如果在使用IE9以下的情况下,jsPlumb将使用vml去渲染,您可以通过下列方式调整渲染模式:

jsPlumb是将所有链接的东西放在一起,所以在jsPlumb中,核心是链接对象,jsPlumb本身可以分成四个组成部分

锚(Anchor):一个位置,放置端点的地方,相对于一个元素的来源,您不需要自己硬编码来创建它,jsPlumb提供给您各种功能,您只需要按照您的需要创建它就可以了。它没有可视化的显示,只是一个逻辑位置,可以使用锚的id来引用它,jsPlumb支持这样做,并且您可以使用坐标来表示[x,y,x方向,y方向]

端点(Endpoint ):链接的一端的可视化表示,您可以创建并可以链接他们;您可以让他们支持拖拽,或者您可以直接使用jsPlumb.connect()在创建链接时直接创建它们。

连接器(Connector):链接两个元素的线,页面的可视化表示,jsPlumb有三种默认类型:Bezier曲线,直线,和流程图链接器,您不用去处理连接器,当您需要使用它们时,您只需要定义它们即可。

覆盖物(Overlay):一个UI组件,是用来是用来装饰连接器,例如标签、箭头等。

锚的概念是指:定义一个链接线能够链接的点,jsPlumb有9个默认的锚点位置,您可以使用它们去链接元素,具体有:

这些位置在jsplumb底层代码中都是以阵列语法表示的,[X,Y,DX,DY],其中X,Y是在区间[0,1]指定锚的位置,DX和DY是在区间[-1,1]指定曲线的事件锚的方向坐标,例如[0,0.5,-1,0]定义了一个“LeftCenter”连接器的曲线,从锚点单向向左的曲线. 同样,[0.5,0,0,-1],定义一个“CenterTop”锚连接器所产生的向上的曲线。

这些都是可以在若干地点之一定位的锚点,当你每次移动一个元素时,会自动选择一个最合适的位置,没有特殊的语法来创建一个DynamicAnchor,你只需要提供一个独立的锚位置,例如数组:

默认的动态锚:

jsPlumb提供一个动态的锚,定名为“AutoDefault” 默认位置有:TopCenter,RightMiddle,BottomCenter和LeftMiddle

动态锚和可拖动的连接是可以进行互操作的,当你开始拖动连接或释放它时,jsPlumb会锁定一个动态的锚的位置, 您可以在界面上看到链接锚点的切换变化。

在我们讨论连接器、终点和覆盖物之前,需要提醒的是:你需要定义一个连接器,端点或覆盖,您必须使用一个“定义”,而不是直接构建一个,这个定义可以是一个字符串,它指定你需要创建的对象。

或者您需要构件的对象的属性名组成的数组,您可以通过其构造函数来创建,例如

也有三个参数的方法,可以让你指定两套参数,jsPlumb会自动为你合并,例如

链接的线,实际上是界面上的各个元素的链接线,jsPlumb有三条连接器实现,一条直线、Bezier曲线和”流程图”连线,默认的连接线是贝塞尔曲线,您可以有选择的设置一个链接器,可以通过设置“connector”来定义一个连接线,或者在添加端点时设置连接线,如果您没有为connector设置一个值,那么他会用他的默认值”Default”

三种链接的定义语法类似

贝塞尔曲线

贝塞尔曲线提供了两个端点之间的立方体路径,它支持一个构造函数参数,

curviness -可选,默认为150,这定义的锚点位于贝塞尔曲线的控制点,以像素为单位的距离,但这并不意味着你的连接器会穿过从你的曲线到这个距离的点,这仅是一个标识而已。

直线:直连绘制直线的两个端点之间。 没有构造函数的参数支持,使用参数endpointStyle定义连接样式或添加端点时定义连接线样式

流程图:这种类型的连接器,绘制一系列垂直或水平段组成的连接 – 经典的流程图​,支持一个单一的构造函数参数

存根 -这是最小长度,以像素为单位,从端点发出的初始存根。 此参数是可选的,默认为30像素

一个端点的UI组件,标志着一个锚的位置,是连接器连接的点,jsPlumb有三个端点实现,点、矩形和图形,你可以指定端点的属性,在jsPlumb.connect时指定属性,或者jsPlumb.Addendpiont时指定,或者设置jsPlumbde target时设定属性,语法可参照connector。

三个可用的端点类型,他们的构造函数参数如下:

点端点(Dot Endpoint)

此端点在屏幕上画一个点。 它支持一个构造函数参数:

半径 -可选,默认为10个像素。 定义点的半径

矩形端点(Rectangle Endpoint)

绘制一个矩形。 支持的构造函数的参数是:

宽度 -可选;默认为20像素。 定义矩形的宽度。

高度 -可选,默认为20个像素。 定义矩形的高度。

图片端点(Image Endpoint):

从一个给定的的URL绘制图像。 此端点支持一个构造函数的参数:

SRC -必需的。 指定要使用的图像的URL。

覆盖界面上的链接元素,如标签或箭头等,jsPlumb有四个默认值:

Arrow:可配置的箭头,放在两个点的连接线上,你可以控制箭头的长度和宽度,

转折点:是一个折回点,方向点(允许值是1和-1,意味着是点的链接方向)

Label:在点的连接器上的可配置的标签

PlainArrow:一个三角形箭头,没有折回点

Diamond:顾名思义,钻石

最后的两个实际上市Arrow的配置的实例,当您调用jsPlumb.connect或jsPlumb.addEndpoint时,您可以指定一个或多个覆盖物。

  1. 这里我们举个例子,默认的箭头,上面加上文字”foo”,如图:

这个链接将有一个箭头标签在连线的一般,并且文字标签在连线的四分之一处,可以注意下id属性,你可以用它来删除标签或做其他操作。

  1. 下面的实例,添加两个覆盖物,请注意,我们在这个例子中使用的参数“connectorOverlays,”而不是上面例子中的“overlays”,这是因为总有一天overlays会用来作为终结点的覆盖物样式:

这个链接将有一个10*30的的箭头和含有文字foo的标签,位置在连接线的开头,另外,需要注意每一个覆盖物的id。

  1. PlainArrow

这仅仅是一个特殊的Arrow的实例,内部jsPlumb给”foldback”硬编码为1,意思是一个平面的箭头尾部边缘,Arror的所有构造函数适用于PlainArrow.

  1. Diamond

这也是一个特殊的Arrow实例,其中jsPlumb内部给foldback硬编码值为2,意味着箭头转变为Diamond,Arrow的所有构造函数适用于Diamond.

  1. Lable

提供了一个文本的标签来装饰连接器,可用的构造函数参数有:

Lable:要显示的文字,你也可用提供一个函数放这儿,用来替代文本,他是做为连接的参数返回,应该返回一个string类型的字符串;

cssClass:一个可选的用于lable的样式类,这是目前首选的”lableStyle”参数,样式有:

Location:做为 “Arrow” 标签应该显示的位置,比例从0到1,包括1。

  1. 显示/隐藏 覆盖

你可用使用setVisible来控制Arrow的显示或隐藏,或者showOverlay(ID)方法,或者hideOverlay(ID)方法,请注意这里的参数id,是我们在设定各个元素时指定的,它可用用来找到那个元素。简单的实例:

并且,连接器connection也有很方便的方法用来替代上面的方法

如图:

连接器connection也提供删除覆盖物的方法,如图:

最简单的方式来设置你的pluming的显示外观等等,是重写这些元素的默认值,如果你不这么做,你得每次都手动的去提供这些值;每一个connect和addEndpoint方法都有一个关联的默认值,这些默认值存储在jsPlumb.Defaults里面,他是一个javascript对象,这些参数的初始值是:

您可用用类似下面的方法来覆盖默认值

  1. 最简单的链接,你可用这样写

在这个例子中,我们已经创建了“element1的”element2的“连接。 请记住,在jsPlumb中连接是靠两个端点,连接器,和零个或多个覆盖物重叠组成,但是,这个实例的“连接”没有提供这些东西,所以jsPlumb在需要这些的地方都用的是默认值,这些默认值是:

–链接的两个端点,jsPlumb默认使用的是Dot点端点,半径10,填充颜色#456

–链接的两个端点的锚,也是用的是默认值

–设定两个端点是否可被用来做为新的链接点,默认值为false

–链接器的类型和外观,默认值是贝塞尔曲线,宽度8,颜色#456

所以这个链接是:

一个8px的贝塞尔曲线,从一个端点(element1)的bottomCenter移到另一个端点的bottomCenter,而且每一个端点的都是以10px为半径,#456为颜色的Dot点,

  1. 让我们稍微加强一点这个链接,并且告诉jsPlumb我们想要什么,如图:

具体样式…就不介绍了,自己看图。

  1. 重用jsPlumb的链接设置

一种非常常见的情况,比如你想要同一个设置,来创造多个链接,jsPlumb connect有两个参数

如果需要链接支持拖放,你首先需要进行一下设置,每一个需要支持拖放的链接,至少需要一个终结点,这里给一个实例,展示怎样去创建Endpoint

此端点将作为连接的资源,任何来自他的链接都将使用jsPlumb默认的样式作为自己的样式

Tip:一些常用的数据使用三个参数的addEndPoint方法

经常发生的一件事:你有一个端点,他的样式和其他的端点只有一点点区别,大多数都是一样的,这样的情况下,你可以定义个相同的样式,有不同的样式可以单独立出来,如下图,注意下anchor设置,两个元素是不一致的:

现在你有一个源端点,你需要在其他元素上创建一个终结点,或者通知jsPlumb你需要使整个元素成为一个拖拽的目标,让我们看看怎样去创建这样一个端点:

还有另外一个方法,jsPlumb.makeTarget(),这个方法需要两个参数,第一个参数是指需要设定的元素,或者元素列表,第二个参数是指当一个链接建立时你需要的端点,在这个例子中,我们用我们之前就用过的端点,-the gray Rentangle—但是我们需要告诉jsPlumb,元素” aTargetDiv”将会被作为dorp的端点:

在jsPlumb中方法jsPlumb.addEndpoint 和方法jsPlumb.makeTarget,对象endpointOptions里面的参数都是一样可以用的,你可以在端点终结点设置很多东西,这个你可以参见api列表说明。下面的示例显示了指定的箭头在链接拖动到终结点的连接线的一半的地方

下面这个示例展示的是,这个终结点,连接到它的连接线是依赖这个终结点的位置,因为它定义了好几个锚点,连接线会自动找到该终结点的动态锚:

这些拖动选项将通过jsPlumb api库来提供支持,jsPlumb将会提供你需要的一切,如果你需要一些拖动过程中的生命周期事件 ,你需要提供给jsPlumb一个通知方法,如果触发某个事件,它会调用该方法,比若说你需要在拖动开始时:

Drop选项和Drag方式是一样的,用法可参见jquery用法

http://docs.jquery.com/UI/Droppable

jsPlumb借用了jquery里面drag/drop的范围概念,范围的概念是指那些可拖放的元素只能够被拖放到那些有范围的元素里面,在jsPlumb中你可以在创建EndPoint时创建一个范围”Scope”,下面给出一个例子:

如果你不提供一个”Scope”,jsPlumb将使用一个默认的范围,在这个方法中,这个值是可用的:

如果你因为某些原因想改变它的值,你可以这样做:

如果你想,你也可以这样,提供一个drap和drop的范围值:

定义端点、覆盖物(覆盖物现在更倾向于用cssClass来替代)、连接线的样式,这些样式是由paintStyle作为jsPlumb connect、jsPlumb.makeTarget、jsPlumb.addEndPoint方法的参数来定义的,根据你调用的不同方法,参数名称会不一样,具体不同如下面所列:

连接线样式

jsPlumb.connect:

jsPlumb.addEndpoint:

注意上面的样式参数,下面我们对它进行讨论

终结点样式

像上面的定义我们可以有下列方法:

  1. 添加终结点时定义:
  1. 作为jsPlumb.connect连接时的参数
  1. 或作为jsPlumb.connect连接对象样式数组中的一个
  1. 或者在jsPlumb.makeTarget方法中

在上面的示例中,我们讲”e1”拖放到作为目标的”e2”中,并且定义了终结点的样式。

覆盖物样式

最完美的设置覆盖物样式的方式是使用创建overlay时的构造函数来定义

样式参数列表

这是设置paintStyle的完整参数列表

但是请注意,fillStyle参数在connector中会被忽略,strokeStyle在EndOptions中会被忽略,此外,如果你使用jsPlumb.connect创建链接,并且为指定任何EndOption样式,EndOptions中的fillStyle样式会被指定为连接线的strokeStyle。

fillStyle, strokeStyle and outlineColor可以使用任何有效的CSS3语法

fillStyle:定义EndPoint的颜色,例如rgba(100,100,100,50), \”blue\”, \”#456\”, \”#993355\”, rgb(34, 56, 78)

strokeStyle:连接器的颜色

lineWidth:连接线的宽度,是个Int值

outlineWidth:连接器或端点的轮廓宽度,int值

outlineColor:连接器或端点的颜色

dashstyle、The VML specstroke-dasharray、stroke-dashoffset、stroke-dashoffset、stroke-linejoin

只适用于VML或SVG或VML和SVG,这里不做介绍

当鼠标悬浮在这些元素上时 ,连接线和终结点都支持鼠标悬浮样式。他们的样式需要被精确的指定,指定的方式和我们在上面讨论的方式一致,悬浮样式也一样继承了基本样式,这是因为当鼠标移上去的时候,您通常会只希望更改颜色,或者轮廓颜色,所以你只需要指定需要改变的值即可,这样避免了你需要定义重复的样式,鼠标悬浮样式的参数名只是需要在正常样式前加入”hover”即可,下面给个例子:

在这个示例中,我们指定两个链接线和终结点的鼠标悬浮样式,这地方的两个连接线和终结点的悬浮样式是一样的,我们也可以使用复数形式使两个端点或两条连接线的悬浮样式不一样,如下图:

上面示例中两个终结点的鼠标悬浮样式是不一样的。通过下面的两种方式也可以实现, jsPlumb.addEndpoint和jsPlumb.makeTarget

(这里我们指定了端点的样式和所有从这个端点链接的线的样式)

(makeTarget也同样支持paintStyle样式)

但是我们得注意,makeTarget不支持连接线的参数,它只支持终结点。所有的链接中的连接线的参数是当你在用链接方法时在源端点设置的。

Canvas 和SVG 都支持渐变,VML不支持,jsPlumb用它自己的语法来定义渐变,来适用于SVG 和Canvas 之间不同的语法。

有两种类型的渐变可用:

(Linear) 线条渐变:彩色线条都在同一个方向

(radial) 半径的,光线似的渐变:有彩色圆圈组成,有这个圈到那个圈的样式。

由于他们的基本样式不同,jsPlumb仅为连接线提供了Linear(第一种)渐变,但为端点提供了两种方式。

线条渐变

要在链接中使用渐变,你需要提供一个渐变的对象,例如:

这里我们有个从win2到win3的连接线,宽度15px;颜色是从绿色到红色的渐变。

注意这地方的渐变对象和stops列表,渐变对象里面的颜色梯度可以是任意数值,每中颜色梯度包含两个值[position,color],position值得范围必须在0到1之间,数值类型是decimal,表示该种颜色开始的位置,颜色的有效值和strokeStyle是一样的。

Stops列表可以包含任意个数,下面的例子表示颜色从红-蓝-绿-蓝-红

如果你使用VML,jsPlumb将会忽略你的渐变指令,所以最好的做法是,你需要单独指定strokeStyle ,因为默认情况下,如果不支持gradient时,jsPlumb就会返回使用使用gradient的基本样式strokeStyle ,如果你没提供strokeStyle ,则默认都是黑色的, 所以最好还是提供strokeStyle 样式,例如:

端点渐变

端点的渐变样式语法和连接线的语法是一样的,你指定端点的渐变样式要么放在endPoint的对象设置里面,要么放在连接线定义时,不同端点的定义数组里面,并且,该信息适用于您正在创建的需要拖拽的端点定义。

看下面示例:

只有Dot和Rectangle 类型的端点支持,图片端点时不支持的,当然在VML中也不支持。

Dot示例:

显示一个端点,颜色渐变从最外端开始,端点的渐变需要的数据显然会比线条渐变多,在一个线条的渐变中,我们只需要从一个点移动到下个点即可,但是在端点的渐变中,我们需要从一个圈移动到另一个圈,默认情况下,jsPlumb会使用一个相同半径的圈。

并且offset和innerRadius也可以使用百分数来替代。

jsPlumb在它创建的每个组件时都会附加一些类,这些类名都是公开的,并且如果你需要的话你可以将他们重写,具体如下:

你可以运用这些样式类来使你的界面达到最佳效果。

jsPlumb提供动画的方法,它是将你使用的基础库中的动画引擎进行封装,并且提供一个回调函数,以供jsPlumb在每一步进行跟踪,你可以自己自己定义这个回调函数,非常方便:

回调函数参数解释:

el:元素id,或者元素的对象

properties:动画的属性,例如持续事件等

options:动画选项,例如回调函数等。

jsPlumb提供了一个完全公用的方法,getConections—来找到关于当前管理的链接,在用这个方法之前,你需要了解jsPlumb中scope概念,

  1. 检索单个范围的链接
  • 获取jsPlumb默认的“scope”中的链接
  • 获取“scope”为myScope中的链接
  1. 更进一步的过滤

getConnection方法提供含js对象的方法来筛选,可有如下三个对象:

scope:您要检索的链接所属范围(域)

source:返回的链接限制为那些有此源id的链接

target: 返回的链接限制为那些有此目标端点id的链接

提供的这三个参数,可以作为对象,也可作为字符串来传递

(返回的是在这两个scope中的连接)

返回值是:

下面列出不同的获取示例:

  1. 获取所有链接
  1. 获取默认scope的链接
  1. 获取指定scope的链接
  1. 获取指定的多个scope的链接,返回
  1. 返回指定source的链接
  1. 返回指定target的链接
  1. 返回指定source和指定target的链接
  1. 三个参数并用

jsPlumb支持绑定到不同的事件 ,有连接线、端点、覆盖物和jsPlumb本身的事件 ,如果需要绑定时间,你需要使用一个方法:jsPlumb.bing(object,event,callback),但是这除了overlay的事件,因为overlay的事件可以直接在overlay定义时直接绑定overlay的事件监听,

jsPlumbConnection—当设定一个连接时会通知,这个新连接对象将会作为回调函数参数传递

jsPlumbConnectionDetached—当一个链接被取消时通知,这个被取消的链接对象会被作为回调函数的参数传递

Click—获取链接的点击事件,回调函数将传递点击的链接和鼠标事件

Dbclick—双击,其他同上

Endpointclick—当端点点击事件时通知,端点对象和鼠标事件是回调函数的参数

endPointDbClick—双击,其他同上

Click—当一个链接被点击时触发,回调函数传递的参数是鼠标事件和connection对象

Dbclick—双击,其他同上

Mouseenter—当鼠标进入链接的path触发,回调函数传递的也是鼠标事件和connection对象

mouseExit—当鼠标从connection的path离开时触发,其他同上

Click– 端点被点击时触发,回调函数传递的参数有鼠标事件和端点对象

Dbclick—双击事件时触发,其他同上

mouseEnter—鼠标进入端点时触发,回调函数传递的也是鼠标事件和端点对象

mouseExit—当鼠标从端点中离开时触发,回调函数传递的参数也是鼠标事件和端点对象

Overlay的事件监听注册有点不同,你将他们作为overlay的构造函数参数,这是因为你从来不会对overlay有真正的动作,下面的示例看看怎么样注册一个overlay的事件:

jsPlumb.connect({

source:\”el1\”,

target:\”el2\”,

overlays:[

[ \”Label\”, {

events:{

click:function(labelOverlay, originalEvent) { alert(\”you clicked on the label overlay for this connection :\” + labelOverlay.connection) }

}

}]

]

});

jsPlumb.connect示例、拖拽的connection示例参见文档http://jsplumb.org/doc/usage.html

1. 分离win5和其他元素的所有链接

2. 隐藏win5的所有链接

3. 隐藏win5链接的所有链接和终结点

4. 显示所有win5的链接

5. 显示win5的所有链接和终点,请注意这个示例中的两个参数,如果它决定这个链接中的其他端点也是不可见的,jsplumb也不会使这个链接显现。

  1. 切换win5的链接线的显示或隐藏
  1. 重画win5的所有链接
  1. 重画win5,win6,win11的所有链接
  1. 重画所有链接
  1. 取消所有链接
  1. 删除从win1出发到给定终结点中间的所有链接和终结点
  1. 删除和win1链接的所有端点和之间的链接
  1. 删除jsplumb实例管理的端点,删除他们之间的链接,这是和jsplumb.reset()方法相同的地方,但它不会清除时间监听列表。
  1. 删除指定的端点和它的连接线
  1. 删除所有的端点、端点、并且清除事件监听列表,返回jsplumb实例的初始化形态
  1. 设置端点不可拖动,不管怎样发出指令
  1. 设置两个端点不可以拖动:
  1. 设置默认情况下是否可拖动元素,默认情况下是可以拖动的
  1. 设置win1可拖动
  1. 设置win1和win2都可拖动
  1. 设置所有包含class=”windows”的元素可拖动

(jQuery下,其他js库下面不列出)

目前jQuery-jsplumb-1.3.3-all.js包含了下面的js库集合:

jsPlumb-1.3.3.js

jsPlumb-defaults-1.3.3.js

jsPlumb-renderers-canvas-1.3.3.js

jsPlumb-renderers-svg-1.3.3.js

jsPlumb-renderers-vml-1.3.3.js

jquery.jsPlumb-1.3.3.js

jsBezier-0.2-min.js

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。