Skip to content

Instantly share code, notes, and snippets.

@luckyadam
Last active August 29, 2015 14:22
Show Gist options
  • Select an option

  • Save luckyadam/2d280413bb0ad7fe94a4 to your computer and use it in GitHub Desktop.

Select an option

Save luckyadam/2d280413bb0ad7fe94a4 to your computer and use it in GitHub Desktop.
静态资源前置加载系统
静态资源前置加载系统
===
> 性能优化一直是前端工程师探索的课题,而且已经有了很多的经验和手段,但是目前这些优化都是在做一些极致优化,比如优化降低白屏时间几十几百毫秒,性能提升百分之多少,这样的效果从数据上看起来不错,但是用户能感知到的真正提升却很小。如何让用户真正感受到加载的“快”,是本套方案探索的主题。
## 概述
为了达到让用户使用时感知到快的目的,我们考虑是否能在某些入口形式的页面主动去加载次级页面的静态资源,以节省用户在浏览这些次级页的白屏时间。这看起来有些trick,但是用户在浏览页面的时候,对于页面来说大部分时间是空闲的,为什么我们不能利用这一部分时间来做些额外的事情呢?
比如京东的618活动,京东首页是有入口的,而这种活动页面的图片一般会比较多,比较大,用户直接进入的话加载时间肯定会非常久。如果我们在京东首页异步去加载这些资源,利用浏览器缓存缓存到本地的话,用户再去浏览活动页的话加载时间必然会少很多,相当于我们将一部分加载时间进行前置,但由于异步加载的方式,首页的白屏时间并不会减少,用户感知不到资源加载被前置,所以用户感知到的实际加载速度会快很多。
但是,在某一页面,我们去额外加载哪些页面的静态资源,又是一个问题。
比较不错的解决方案是,我们基于用户最有可能的页面访问路径来做,比如我们根据统计数据知道80%的用户会是A->B->C->D这一访问路径,那么我们可以自动地为这些页面打上标记需要额外异步去加载哪些静态资源,配合资源加载器,能够实现自动化加载。
但是,目前这类统计数据缺失,我们没有办法根据数据来分析出一条访问路径来。所以,在系统初期,我们考虑采用人工配置的方式,并且页面间独立,每个页面只用关心自己需要额外加载的静态资源(或称为依赖)。在之后我们会完善页面点击数据的统计,分析出用户最有可能的页面访问路径,以此来自动化规划页面所依赖的静态资源。而人工配置的方式还是有一定好处的,我们可以利用它来作为辅助或强制的手段。
## 设计
![系统设计图](http://ww2.sinaimg.cn/large/49320207gw1esz1ajra5kj215q0m2jwa.jpg)
### 依赖资源列表
每个页面都需要知道自己额外加载的静态资源,所以我们相当于需要知道当前页面的依赖,页面额外加载资源的JSON设计
```
{
'deps': {
'url1': {
'css': [
'http://static.paipai.com/a.css'
],
'js': [
'http://static.paipai.com/a.js'
],
'img': [
'http://static.paipai.com/a.png'
]
},
'url2': {
'css': [
'http://static.paipai.com/b.css'
],
'js': [
'http://static.paipai.com/b.js'
],
'img': [
'http://static.paipai.com/b.png'
]
}
}
}
```
获得**资源依赖列表**方式我们考虑采用异步的方式,就是我们额外向获取依赖资源列表的服务发请求去拿到当前页面的的依赖资源列表。这样做是为了降低本系统和业务逻辑之间的耦合性,只需要在页面外挂资源加载的脚本。
### 资源加载器
在知道当前页面的静态资源依赖信息后,我们需要一个资源加载器来加载各种资源,资源加载器部分会包括加载JS、CSS、图片3种资源的加载器,它会根据依赖资源列表异步去加载所有资源,类似如下
```javascript
// 加载js
var script = document.createElement('script');
script.src = src;
script.onload = function () {
};
document.getElementsByTagName('head')[0].appendChild(script);
// 加载css
var link = document.ceateElement('link');
link.href = href;
document.getElementsByTagName('head')[0].appendChild(link);
// 加载图片
var img = new Image();
img.src = src;
img.onload = function () {
};
```
### 获取依赖资源列表服务
我们需要一个服务来获取传入页面所依赖的静态资源列表,这个服务很简单,它接受页面Url作为参数,然后返回上面所设计的JSON数据。
这是一个部署在线上的服务,需要有承载高并发请求的能力,所以为了提高系统的稳定性与部署服务的成本,我们排除了数据库的的选项,考虑读取配置文件的方式来提供内容。
这个服务将接收**资源配置管理平台**推送的配置文件,在接受到来自页面获取依赖资源的请求时读取配置文件,将对应内容返回回来。
而作为一个线上服务,它必然是部署在服务集群上的,所以配置文件同步会是一个问题,我们需要有对应的脚本将配置文件发送到集群的每一台机器上去。
### 资源配置管理平台
上面介绍到,我们的资源依赖初期将依靠人工配置的,所以我们需要一个资源配置管理的后台系统来满足我们的需求。同时资源配置管理平台也是与获取依赖资源列表服务对接的平台,它将依赖获取服务**推送配置文件**。
这是一个部署在内网环境的服务,所以我们可以考虑采用一些我们感兴趣的技术来自由发挥,我们的技术选型是后端采用**NodeJs+MongoDB**来搭建我们的服务,前端页面采用**ReactJs**来构建页面。
资源配置管理平台将包含以下功能:
- 产品线
- 账号体系
- 页面依赖展示
- 页面包含静态资源展示
- 页面依赖配置
- 页面包含静态资源配置
- 操作历史
- 统计数据展示
- 自动抓取页面包含的静态资源
**资源前置加载系统**应该是与产品线业务无关的,它应该能适用于不同的产品线,所以它应该是类似于平台的存在。基于这样的考虑,我们在资源配置管理平台中提供了产品线管理的功能,不同的产品线之间相互独立,生成的配置文件也将区分开。
资源依赖是基于页面Url的,在系统中可以配置每个页面Url对应依赖页面Url,以及每个Url所包含的静态资源,这样依赖关系就可以确立下来。用户在配置管理平台中配置完后,选择发布,会将**当前产品线**的配置文件通过脚本推送到线上的依赖获取服务中。
同时系统将基于账号体系建立用户操作历史,以及URL对应的操作历史记录,以方便追查问题和回退历史版本。
让用户自己去配置每个页面有哪些资源还是非常麻烦,在后续计划中我们会在配置管理平台中提供一个**爬虫**自动去抓取页面Url对应的静态资源,存入数据库中,这样用户只需要配置某一页面所依赖的其他页面的Url,而不再需要配置页面的静态资源了。
## 统计
统计是检验成果的唯一标准,我们这套方案的收益目前还存在于理论上,我们需要统计数据来支撑我们的方案。
目前我们主要统计页面上依赖资源的加载时间,加载时间会根据``deps``中的url进行区分,计算每个url的静态资源的加载时间,来统计为每个次级页面的加载节省的时间,统计数据会上报到资源配置管理平台中,以可视化的图表展现出来。
除此之外,我们会提供自动化点击统计的方案,收集页面区域的点击统计,分析数据,得出用户在当前页面最有可能去的页面
## 静态资源缓存系统
目前的方案,只是利用了浏览器的缓存机制,但是如果是304协商缓存,我们还是多一次和服务器协商缓存的消耗,为了进一步提升,我们考虑利用localStorage来设计一套静态资源缓存系统,在大多数情况下,不发起网络交互,来提升我们的加载性能。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment