苦逼前端

单页面web应用避免白屏

Javascript2017-03-11 12:51

ajax的兴起给web开发带来了新思路,彻底划清了前后端开发的界线,一些优秀单页面SPA也应运而生,然而事情的发展总是有两面性的,单页面应用同样也会有些缺陷,如不利于搜索引擎抓取,首页白屏等,本篇将着重对第二个问题进行分析、解决。

单页面应用的交互方式决定了页面打开伊始是没有数据的,当然也可以进行优化,像本博客采用的方案,后面会说到。首次打开大部分的单页面应用都会发现,做的差一点的会是先白屏,好一些的会给loading提示,但这也没有多大的意义,页面本身虽然加载完了,由于数据接口的滞后导致了页面仍然不可用。有没有什么解决办法呢,比如让首屏依赖的数据「提前到达」。

我们想到了<script>标签的时序性,如果在单页面程序的<script>标签前插入一个<script>标签,而这个标签指向了首屏数据接口,后台按照jsonp的约定方式返回,是不是就可以「同步」的取回该数据了?

像这样:

<script>
    function jsonp_callback(jsonp_data){
        window.jsonp_data = jsonp_data;
    };
</script>
<!-- 下面是用于取数据的标签 xxx接口返回 'jsonp_callback({a: 1, b: 2})' -->
<script src="xxx"></script>
<!-- 使用数据 -->
<script>
    console.log(jsonp_data)
    //{a: 1, b: 2}
    </script>

虽然这样做从代码层面来讲并不完美,引入了两个全局变量,但确实把首页白屏的问题解决掉了。然而有时候这还是不够的,比如这个首页数据的接口可能需要一些参数,如果是简单的常量我们可以直接拼在url后面,但如果需要动态获取,该怎么处理呢,比如拿当前页面的url?动态创建script标签然后append到head,这显然是不行的,因为这样创建的标签会最后执行。

我们又想到了document.write的阻塞性,通过该方法动态的写入script标签,这个问题似乎就解决了:

<script>
    function jsonp_callback(jsonp_data){
        window.jsonp_data = jsonp_data;
    };
    //xxx接口返回 'jsonp_callback({a: 1, b: 2})' 
    document.write('<script src="xxx"><\/script>');
</script>
<script>
    console.log(jsonp_data);
    //{a:1, b: 2}
</script>

document.write的使用一定要小心,他可能会带来很多意料之外的结果:
比如把console.log(jsonp_data)放进第一个script标签的末尾,能正确的拿到数据吗?
显然是不能的,因为此时虽然document.write会阻塞代码继续往下执行,直到把内容write完毕,但由于script标签的时序性,需要远程加载内容的script标签并不会立即执行,它会等到它上面的所有script标签依次执行完毕后才会执行。

再比如,上面的document.write直接输出接口内容,再把console.log(jsonp_data)放到它后面,可以正确的拿到数据吗:

<script>
    function jsonp_callback(jsonp_data){
        window.jsonp_data = jsonp_data;
    };
    document.write('<script>jsonp_callback({a: 1, b: 2})<\/script>');
    console.log(jsonp_data);
</script>

答案是可以的,因为document.write里面的内容会立即执行,或许这么说有些牵强,等到找到具体资料再补充吧。

该解决方案基本不需要服务端过多的支持,只需要把接口做成支持jsonp的方式即可。
上面还提到了本博客的解决方案:pushState+后端渲染首屏,大致的结构是这样的,用户初次访问会通过后台直接渲染,后续的跳转用pushState+ajax实现。

这样做的好处是用户访问的任何页面都可以直接到达,无需等待ajax加载。
坏处也很明显:后台需要把用户所有可能触达的页面全部在后台准备一份,等待用户的「初次访问」,还需要把所有数据做成接口,等待用户在前端跳转时发起的ajax请求。对后端来说开销确实很大,不过本博客采用了相同的前后端模板,只需要在后端分配好路由即可,所有数据一式两份,一份输出到页面,一份做成ajax接口。

或许这就是node的方便之处吧,它「更懂得前端需要什么」

评论(2)
  • 116.90.80.*: 挽...3年6个月前
  • luckymore: zai挽...3年6个月前
还可输入200个字