在手机上打开页面时,常常会由于网络很差致使须要较长的加载时间,若是这段时间内只是显示一个“白板”,用户体验很是很差。一般的解决方案就是完整打开页面前给用户展现一个加载动画,让用户可以看到页面还活着呢。本觉得是个很简单的活,真作起来才发现【有很多学问】!css
一、不加载资源文件
顺序:加载、解析、渲染、绘制html
二、加载CSS
加载外部的CSS文件会阻止渲染,不论link的标签放在什么位置上。
顺序:加载、解析、加载(阻塞了渲染)、渲染、绘制浏览器
三、加载JS
a、在body前加载外部的JS文件会阻止渲染
顺序:加载、解析、渲染(?)、加载(阻塞)、解析(JS执行完多了个解析)、渲染、绘制网络
b、在body后加载外部的JS文件不会阻止渲染
顺序:加载、解析、渲染、绘制、加载、解析、渲染、绘制app
四、在<script>标签中动态建立link标签加载CSS
加载CSS中断了页面的渲染和绘制框架
五、在<script>标签中经过setTimeout函数动态建立link标签加载CSS
加载CSS没有中断。我理解这是由于JS是单线程的,放在timeout里建立的link去排队,浏览器就先无论它了。异步
先定好目标:尽快让用户看到变化,不要让用户觉得页面已经不响应,再逐步加载内容。
最快的方式就是作一个空的页面,不加载任何外部资源(包括:CSS和JS)。页面上方加载动画的CSS定义和页面元素,提供异步加载页面元素、CSS和JS文件的JS。经过JS加载各种资源成功后关闭动画效果,清除没必要要的内容。async
这样就来个新问题,如何实现动态加载问题?函数
外部文件的动态加载问题不少文章都深刻分析过了,简单说,就是用异步加载,可是要考虑到各个JS文件的依赖关系问题。综合比较了一下,决定requirejs实现动态加载。由于最近一直用angular,因此下面的代码是require.js+angular。工具
HTML
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta content="width=device-width,user-scalable=no,initial-scale=1.0" name="viewport"> <base href='/'> <title>loading</title> <style id='loadingStyle' type="text/css"> /*省略了,这里是loading元素转圈的样式,为了快应该压缩了再放里面*/ </style> </head> <body> <div id="content" ng-app="app" ng-controller="ctrl"> <ul class='list-group'> <li class='list-group-item' ng-repeat="d in data"><i ng-bind="d"></i></li> </ul> </div> <div class="loading"><div class='loading-indicator'><i></i></div></div> <script src="static/js/require.js" defer async data-main="test/loading/loader.js?_=11"></script> </body> </html>
loader.js
由于angular不是AMD,因此用shim引用成angular全局变量
require.config({ paths: { "angular": "/static/js/angular.min", }, shim: { "angular": { exports: "angular" }, }, deps: ['/test/loading/app.js?_=10'] });
app.js
若是真的要动态添加样式,建议先得到数据,把数据展示出来,在加载样式,这样就能让用户今早看到变化。
define(["angular"], function(angular) { 'use strict'; angular.module('app', []).controller('ctrl', ['$scope', '$timeout', function($scope, $timeout) { $scope.data = []; // 模拟长时间得到数据 $timeout(function() { for (var i = 0; i < 100; i++) { $scope.data.push('data:' + i); } // 模拟长时间得到样式 $timeout(function() { var link, head; link = document.createElement('link'); link.href = "/test/loading/app.css?_=" + (new Date()).getTime(); link.rel = 'stylesheet'; link.onload = function() { var eleLoading, eleStyle; eleLoading = document.querySelector('.loading'); eleLoading.parentNode.removeChild(eleLoading); eleStyle = document.querySelector('#loadingStyle'); eleStyle.parentNode.removeChild(eleStyle); }; head = document.querySelector('head'); head.appendChild(link); }, 2000); }, 2000); }]); });
app.css
#content{color:red;}
能够考虑页面的布局也动态加载,这样用户能够先看见页面的框架,而后再获取数据填到框架中。可是尚未想到成熟的解决方案。