zoukankan      html  css  js  c++  java
  • 移动端H5活动页优化方案

    背景

    项目:移动端H5电商项目
    痛点:慢!!!
    初始方案:最基本的图片懒加载,静态资源放到cdn,predns等等已经都做了。但是还是慢,慢在哪?
    显而易见的原因:由于前后端分离,所有的数据都由接口下发,之后根据模板渲染页面。也就是说,我们需要先加载js,等到js加载完毕之后,请求接口,接口返回数据之后,渲染页面,加载图片等等。尽管使用了模块化的加载方式,但是对于要求高的首页和活动页,给用户的感知也不是很好。

    初版解决方案

    最初,由于时间紧迫,基本上都是从客户端作优化处理,基本上可以总结为以下几个方面。

    一、本地缓存

    我们做了本地缓存优化的策略,第一次请求之后就把接口数据缓存到localStorage里面,并且存储当时的时间,设定过期时间,一般设置为5分钟,用户在5分钟内重复打开页面,不会再次请求接口,从localstorage中拿取数据,直接渲染页面。
    后续干脆把模板渲染好的html片段存储了起来,直接拼接,省去了模板计算的时间。
    基本实现方案如下:

    var 
        cache = localStorage.getItem('cache')
        , expires = 5 * 60 * 1000
    ;
    
    // 判断是否过期
    function isOverdue(pastTime, expires) {
        return Date.now() - pasttime >= expires;
    }
    
    if (cache && !isOverdue(cache.time, expires)) {
        // 说明缓存存在,并且没有过期
        // 就正常取cache.data做相应的渲染
    } else {
        // 说明缓存不存在或者已经过期了
        // 重新请求接口
        $.get('a.cn', funciton (res) {
            // do something
            // 把对应的渲染操作处理完成之后,将数据缓存,并记录当前的时间
            localStorage.setItem('cache', {
                data: res,
                time: Date.now()
            })
        })
    } 
    

    然而还是不够,新用户在首次打开时,还是不能秒开页面,并且用户在5分钟之后重新加载之时,仍然会有一定的延迟(由于浏览器会缓存一部分静态资源,此时再打开并不会像用户初次打开一样那么慢)。

    二、进一步缓存静态资源

    在日常开发中,有很多依赖库,常用的fastclick,swipe等等,这些库,没有必要每次都去加载,虽然浏览器会对一些静态资源做缓存,但是却不能完全被我们控制,所以,可以将这些不常发生变化的静态资源缓存起来,同样的,存到localStorage里面。

    需要注意

    这个方案有一个问题,如果是直接加载的script标签,是无法直接拿到它的脚本内容的。

    <script id="script1" src="js/jquery.js"></script>
    
    console.log(document.querySelector('#script1').innerHTML);
    // 此时输出的是undefined,因为innerHTML是获取标签内容,此时script标签里并没有内容。
    
    <script id="script2">
    console.log('2');
    </script>
    
    console.log(document.querySelector('#script2').innerHTML);
    // 此时输出的是console.log('2');
    // 因为innerHTML是获取标签内容,此时script标签里并没有内容。
    

    这当然不是我们想要的,我们需要的是外链js的可执行代码。

    动态添加js的两种方案

    我在前一篇高性能JavaScript读书笔记中提到了两种方案。

    1. 动态脚本元素

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.onload = function () {
        // do something
    }
    script.src = 'jquery.js';
    document.getElementByTagName('heda')[0].appendChild(script);
    

    这种方法可以监控到脚本的完成事件,但是由于也是通过添加一个script标签,并不能拿到我们想要的js代码。

    2. 通过XMLHttPRequest脚本注入

    var xhr = new XMLHttpRequest();
    xhr.open("get", "file1.js", true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                localStorage.setItem('file1', xhr.responseText);
            }
        }
    }
    

    需要特别注意的是,这个方法有跨域的风险,所以,我们需要静态资源服务器的allow-origin设置为*。或者直接加载本域名下的js。

    有同学要问了,既然localStorage这么强大,为什么不把所有的东西都缓存起来呢?
    当然是因为它的大小有限制,在FireFox和chrome中,一般来说,sessionStorage和localStorage大小为10MB,而safari只有5MB。详见这篇文章
    这就限制了我们什么都存的想法。如果图片较小,可以缓存起来。

    第二版解决方案

    在前一版方案里,我们解决了后续加载的速度缓慢问题,在后续的页面打开速度上,基本上可以做到秒开。
    但是这个方案还是有不足,对于新用户的体验不是很好,如果用户在5min的这个时间点上打开,速度还是会有所下降。
    最后还是只能做SSR。
    知乎上有一个问题,为什么现在又流行服务端渲染html?
    服务端渲染有很多好处,对我们此时而言,最大的好处就是页面直出。省去了请求接口这一步操作。并且对于提高用户体验上来说,很有好处。
    如果时间充裕,可以使用node做服务端,但是由于历史原因,我们这个项目迁移起来也比较费时间,所以最后决定使用openresty来做。
    openResty的教程网上很多了,我也不多说,除了官方git,推荐开涛博客学习。

    不论我们是在客户端取接口数据,还是服务端,活动页的数据一般来说都有一定持续时长,也就是说,我们也可以在我们的nginx服务器上做一个缓存,假设有一个用户访问了一个活动,那么在接下来的五分钟之内,其他任何用户(相同权限下)访问到的就是第一个用户访问时缓存好的页面。

    local args = ngx.req.get_uri_args()
    local acId = args['acId']
    local key = 'ac' .. acId
    local expire = 5 * 60 --缓存时间5分钟
    
    local value = dict.getData(key, expire)
    
    if not value then
        local res = ngx.location.capture('/a.json')
    
        if res.status == 200 then
            -- dict是一个用来处理存储和读取逻辑的脚本
            dict.setData(key, res.body)
        else
            ngx.say(dict.getData(key))
        end
    else
        --  ngx.log(4, type(value))
        ngx.say(value)
    end
    

    在dict中,我们可以把接口数据转换成通过模板转换为html片段存储起来。这样,用户在第一次进入我们的页面以后,也不会感觉慢了。

    what's more

    openResty能做的事不仅仅是这些,一些网关上权限的控制等等都可以用它来实现。
    上面两个方案还有一些不足之处,比如首屏可能内容比较多,一次都加载过来,不一定会快。直观的首屏的由服务端渲染,对于不重要的内容,可以通过AJAX来异步加载。
    如何计算活动页这样的页面中,首屏有多大,如何组织代码,哪些部分采用服务端渲染,哪些采用AJAX。这些问题在实际开发中也需要考虑。
    限于时间,此次没有能比较完善的解决这个问题,在日后开发中,还会继续完善活动页优化方案。

  • 相关阅读:
    不务正业系列-浅谈《过气堡垒》,一个RTS玩家的视角
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 40. Combination Sum II
    138. Copy List with Random Pointer
    310. Minimum Height Trees
    4. Median of Two Sorted Arrays
    153. Find Minimum in Rotated Sorted Array
    33. Search in Rotated Sorted Array
    35. Search Insert Position
    278. First Bad Version
  • 原文地址:https://www.cnblogs.com/liuyongjia/p/8728900.html
Copyright © 2011-2022 走看看