IT/互联网/科技
手机百度前端工程化之路
2023-08-13 12:08

手机百度前端工程化之路

PS:年前参与一个速度优化项目,其中一个优化方式就是减少cookie,减少请求头中的cookie,在慢速网络的速度提升有明显提高!

ok百度代码规范,继续探索……

细粒度缓存

上面的版本号解决方案,是将md5值存在一个cookie,一个md5值32位,即使使用一半也16位,加上cookie的key,怎么也要20个字节以上,我们能不能利用20~100个字节,尽量缓存更多的缓存文件版本号信息呢?

于是我们开始了版本号细化的工作。

梳理可以缓存的静态资源,将文件分为:基础层、通用层和业务逻辑层,缓存的主要是基础层和通用层的代码

指定cookie的value值格式,为了缓存更多的版本号信息,我们不再使用md5做版本号信息,而是规定了下面的格式:jA-V_cB-V,即jA和cB代表缓存的文件名,保持两位(j代表js,c代表css,t代表前端js模板文件,);V代表版本号,保持一位,版本号是36进制的手机百度前端工程化之路,当版本号要超过一位时,从0开始重新记录;按照每周上线一次的情况,cookie时间是一周,36个版本号可以够我们用的

将需要缓存的文件统一放在一个路径下管理

这样做了之后,就是用脚本做缓存文件自动更新版本号了,开始想到的是通过svn hook的方式,当有新的ci时,计算md5值,写入一个版本号config文件。上线时比较线上config和svn中的config,如果不一样就升版本号。但是每次ci都做一次的方式又多此一举、略显蛋疼,最终的方案是在上线脚本中做了一些工作,没有使用svn hook:

对缓存文件路径下的文件做md5hao123,生成一张map

去线上拉取最新的版本号config文件,跟第一步生成的map做比较,不一样则版本升高

多维度缓存

上面的解决方案还是不够完美,总感觉存的东西还是少,所以又做了一个 多维度cookie版本方案 。

我们把cookie看成可以两个维度来存储: 域名 和 路径 。

举例

域名下百度代码规范,有三个产品:新闻、视频和小说,分别放在三个path:

那么新闻、视频和小说,各自有各自的通用代码,比如:通用样式,通用js组件。这样我们在设置cookie的时候指定相应的path,则可以实现多维度缓存

开启缓存

为了实现的缓存,我们增加了类来处理cookie,下发缓存代码,将FIS扩展smarty的 标签进行了修改,增加了属性,即下面代码就可以将页面开启缓存:

1
2
3

<%html localstorage="true"%>
//something~
<%/html%>

模块化

为了解决重复代码的问题,我们开始结合FIS来做模块化,像seajs、这些CMD、AMD框架,是后加载的,即用什么就拉取什么,属于异步模块。js为了实现异步模块,而大量的代码在处理模块依赖关系。在移动上,我们不希望这样,我们希望在后端维护模块的依赖关系,当我require一个模块的时候,会按照依赖关系,依次输出。

我写了一个Bdbox的AMD规范的模块化基础库,然后在FIS编译时,包裹AMD的define外层,并且可以生成一张加载资源表,当使用、和标签内使用require这些smarty扩展标签时,会通过php来动态维护模块依赖。

关于FIS的模块化和静态资源管理,厂子FIS开发团队同学有一篇文章《如何高效地管理网站静态资源》

模块化举例

现在页面要引入 moduleA 模块,而 moduleA 依赖于 moduleB 和 moduleC ,moduleB 和 moduleC 又有自己的依赖模块,如果不先输出 moduleB 和 moduleC 的依赖模块,直接执行 moduleA 的 define 函数会报错的,因为 moduleA 模块依赖的moduleB 和 moduleC 还没有达到 ready 的状态。

有时候甚至更加复杂的依赖关系:

这时候通过《如何高效地管理网站静态资源》文章提到的,FIS编译后会得到的模块依赖关系表:map.json,来做动态模块依赖管理。

通过修改fis编译脚本,将模块依赖文件内容放到map.json中,当使用smarty扩展语法标签的时,php会自动读取map.json,然后将依赖解析出来,提前将moduleA依赖的模块都在其 code>define 之前引入,所以下面的两种代码写法:

1
2
3
4
5

<%require name="common:bdbox/moduleA"%>`
<%*或者*%>
<%script%>
    var moduleA = require('common:bdbox/moduleA');
<%/script%>

实际输出的html代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<script>
    define('common:bdbox/moduleB', function(){
        //A依赖模块B
    });
    define('common:bdbox/moduleC', function(){
        //A依赖模块C
    });
    define('common:bdbox/moduleA', function(){
        //模块A
        var C = require('common:bdbox/moduleC');
        var B = require('common:bdbox/moduleA');
    });
    var moduleA = require('common:bdbox/moduleA');
script>

对于不是模块的js或者css文件,如果使用了,则主动使用来读取内容。

Q & A为啥不用FIS自己的modjs,而自己重复造轮子?为什么命名不是标准的AMD规范?静态资源引入模式

上面所有的关于静态资源管理的解决方案,都是围绕 一刀切 的方案在做优化,而没有利用http本身的cache,实际上:在3G、wifi甚至4G的环境中,http cache的方案,在易用性和兼容性上面要比+inline内嵌静态资源的方式要好。

而且从手机百度真实的用户网络类型统计来说,3G+wifi已经达到75%以上,如果能结合wise团队提供的ip测速库和公司的CDN服务,会有一种更好的解决方案,进一步来说,如果可以根据网络类型和用户真实网络速度,自由选择在+inline和CDN方案之间切换就更好了。于是我们做到了!一种新的渲染方式出现了:CDN+combo。

再说这种渲染方式之前,先梳理下上面提到的一些名词:

好,继续那模块化说的moduleA模块依赖moduleB和moduleC来说,经过 tag模式 ,会输出下面的html:

1
2
3
4
5
6

<script src="http://xxx/bdbox/moduleC.js">script>
<script src="http://xxx/bdbox/moduleB.js">script>
<script src="http://xxx/bdbox/moduleA.js">script>
<script>
    var moduleA = require('common:bdbox/moduleA');
script>

这样模块化的代码经常成了网页的瓶颈,因为模块化存在,造成了更多的外链!下面我们需要一个CDN+combo服务,来合并http请求。

因为smarty的扩展语法,结合之前生成的map.json,我们实现了模块化依赖关系后端自动处理依赖,然后选择最合理的输出顺序。这时候我们不是直接输出对应的tag或者inline内容,而是将它合并到一个combo服务对应的URL,统一输出!

1

<script src="cdn-combo-server?file=bdbox/moduleC,bdbox/moduleB,bdbox/moduleA">script>

渲染模式智能切换

如何根据用户网络环境智能切换渲染方式呢?我继续改造了smarty的标签,添加属性,通过wise测速库和手机百度客户端传给我们的网络类型,选择不同的方式:

1
2
3
4
5
6
7
8
9

<%if ($slow_network || $nettype=='2G') && $support_localstorage %>
    <%html rendermode="inline" localstorage="true"%>
<%elseif $fast_network%>
    <%html rendermode="combo"%>
<%else%>
    <%html rendermode="inline"%>
<%/if%>
//……
<%/html%>

拆分父子模板

上面的方案,我们如果逐个页面去写代码,改方案,想想就蛋疼,所以我们拆分了父子模板,从框架本身来分,一个module对应一个父模板,其他子模板使用smarty的extends标签实现继承关系。

经过模板拆分后,子模板专注于做业务,父模板专注于做解决方案,而且也方便了抽样和统计。

其他总结

试想一下,如果2015年,用户都用上了4G,那么我们需要将父模板的改成 ="combo" 就可以全部切到 CDN+combo 的渲染方式上,这得减少了多少工作量啊:)

【本文来源于互联网转载,如侵犯您的权益或不适传播,请邮件通知我们删除】

发表评论
0评