- loading:
- 在页面最前面加loading相关的html和css
- 结合html-webpack-plugin插入loading(html+css)
- prerender-spa-plugin(`暂记`)
- 内联CSS(`不考虑缓存策略的话`)
- 骨架屏<br/>
Q: css文件的加载阻塞骨架屏的渲染<BR/>
A:使用preload,伪代码以下:

复制代码
动态polyfill
User-Agent
头,判断其支持的特性,返回合适的polyfill。webpack相关优化
Tree Shaking
:
modules: false
; pachage.json
配置sideEffects
。import { capitalize } from 'lodash-es';
复制代码
import {a} from xx
转换为
import {a} from 'xx/a'
复制代码
// a.js
export function a() {}
// b.js
export function b(){}
// package/index.js
import a from './a'
import b from './b'
export { a, b }
// app.js
import {a} from 'package'
console.log(a)
复制代码
结果treeShaking后:// a.js
export function a() {}
// b.js 再也不导出 function b(){}
function b() {}
// package/index.js 再也不导出 b 模块
import a from './a'
import b from './b'
export { a }
// app.js
import {a} from 'package'
console.log(a)
复制代码
配合 webpack 的 scope hoisting
和 uglify
以后,b 模块的痕迹会被彻底抹杀掉。// b.js
export function b(v) { reutrn v }
console.log(b(1))
复制代码
处理后 b 模块内容变成了:// b.js
console.log(function (v){return v}(1))
复制代码
注意:b文件中保留了console的代码。模块/包
都会被完整的移除。(上面的b文件被移除)shim
或者polyfill
慎用!"sideEffects": [
"*.css",
"src/javascript/base/da.js"
]
复制代码
splitChunks
import()
懒加载
scroll
事件Intersection Observer
获取元素可见性placeholder
(提早占位)闪屏
的现象。三方组件:react-placeholder
、react-hold
(缓存时注意拉取CGI接口的参数处理)
接口动静分离 & Redis缓存
(node层缓存html)
PWA直出优化
(前端缓存html)
Web App Manifest
,Service Worker
,Push API & Notification API
,App Shell & App Skeleton
promise-based
webworker
)load
事件注册//只会对topics/下面的路径进行优化
navigator.serviceWorker.register('/topics/sw.js');
复制代码
生命周期javascript
指定serviceworkerJS文件的位置,加载解析执行;load事件中注册css
将指定的静态资源进行离线缓存html
对旧缓存作删除等处理;接管控制权前端
SW更新机制
java
背景:
SW
没有自动更新的逻辑,它须要在页面加载(一次跳转)以后才会去请求sw.js
解决:因为浏览器判断sw.js
是否更新是经过字节方式,所以修改cacheName
会从新触发install
并缓存资源。此外,在activate
事件中,咱们须要检查cacheName
是否变化,若是变化则表示有了新的缓存资源,原有缓存须要删除。node
SW.js
文件下载,并触发install
事件。SW
还在工做,新的SW
进入waiting
状态(此时两个SW
同时存在,旧的SW
掌管当前页面)。
二次跳转
)才能展现最新的页面。self.skipWaiting()
://caches是全局变量
self.addEventListener('install',e =>{
e.waitUntil(
caches.open(cacheStorageKey)
.then(cache => cache.addAll(cacheList))
.then(() => self.skipWaiting())
)
})
复制代码
waiting
状态,因此在waiting
阶段,采用必定的策略来进行页面的刷新:如弹窗提示用户是否刷新,若刷新则调用wb.messageSW({type: 'SKIP_WAITING'});
触发message事件。【webWorker】main.js中作相关逻辑处理,经过postMessage传递消息控制worker
:self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
// the new v2 Service Worker will immediately kill the old v1 activated Service Worker once the v2 Service Worker installs.
self.skipWaiting();
}
});
复制代码
activate
事件;能够在此处对旧缓存作删除等处理;不重刷,接管控制权:self.clients.claim()
self.addEventListener('activate',function(e){
e.waitUntil(
//获取全部cache名称
caches.keys().then(cacheNames => {
return Promise.all(
// 获取全部不一样于当前版本名称cache下的内容
cacheNames.filter(cacheNames => {
return cacheNames !== cacheStorageKey
}).map(cacheNames => {
return caches.delete(cacheNames)
})
)
}).then(() => {
//直接接管当前页面的权限
return self.clients.claim()
})
)
})
复制代码
注意:react
sw.js
和manifest.json
静态资源的缓存策略:不缓存,必须校验
if('serviceWorker' in navigator) {
fetch('./cas').then(() => {
if(降级) {
//注销掉全部sw
unregister();
}else {
//注册
register();
}
})
}
复制代码
首次启动优化
背景:首次加载时没有资源,因此会走线上,等于没优化
方案:构建时,把整个项目用到的资源输出到一个list,而后inline到sw.js。当sw install,就会把这个list的资源所有请求进行缓存。这样作的结果是,不管用户第一次进入咱们站点的哪一个页面,都会把整个站点全部的资源都加载回来并缓存。 webpack
涉及内容:
- link相关(rel、media)
- defer、async
- 缓存(4种缓存、缓存策略、ServiceWork
)
- 优化网络(H2 Push
、Preload/Prefetch
、域名拆分)
-推送JSON
/json内联
,加速首页渲染
- 浏览器中各资源加载的优先级git
当咱们须要某些网络资源时,加载和执行每每耦合在一块儿,下载完当即执行,而加载过程是阻塞式的,延长了onload时间。所以如何在资源执行前预加载资源,减小等待网络的开销即是咱们要探讨的问题。github
附一张不一样资源浏览器优先级的图示(来源):
async/defer: 无阻塞加载
DOMContentLoaded
事件触发前执行DOMContentLoaded
事件触发前执行,所以最好只包含一个延迟脚本不足:仅限于脚本资源;执行时机不可控或存在执行顺序问题
,用于非关键资源。
使用ajax加载资源:能够实现预加载。
不足:优先级较低,没法对首屏资源提早加载。
Webkit浏览器预测解析:chrome的预加载扫描器html-preload-scanner
经过扫描节点中的 "src"
, "link"
等属性,找到外部链接资源后进行预加载,避免了资源加载的等待时间,一样实现了提早加载以及加载和执行分离。
原始解析作法:
Server Push 图片来源
Link: <https://example.com/other/styles.css>; rel=preload; as=style;
复制代码
仅预加载,不推送:
Link: <https://example.com/other/styles.css>; rel=preload; as=style;nopush
复制代码
目标:减小请求数量和提升页面加载速度。
特色:多页面共享push cache(动态数据json除外
)
适用场景:若是不推送这个资源,浏览器就会请求这个资源。
须要注意:要确保没有发起没必要要的推送,浪费流量。可使用preload标签代替,或者在HTTP头中加nopush
属性。
【若是服务器或者浏览器不支持 HTTP/2
,那么浏览器就会按照 preload 来处理这个头信息,预加载指定的资源文件。】
不足:
Edge和Safari
的支持很差,慎用使用一次
Golomb-compressed sets
算法生成指纹,编码为base64,而后存入Cookiepush cache: 只在会话中存在,一旦会话结束就会被释放
;内存缓存、Service Worker缓存、Disk缓存、Push缓存
)【扩展】
问题1:不只js渲染阻塞,同时js执行后可能获取一些数据(JSON),才能真正渲染完成,如何解决?
- 用于动态资源的提早推送,注意参数需固定,不带随机变量的
![]()
- 服务端渲染(直出同构)
![]()
- 内联JSON(目前有些工程使用此方法传递同步数据)
另外两种较常见的渲染方式图片来源:
fetch
,能够强制浏览器请求资源,同时不阻塞文档 onload
事件。当前页面使用,尽早下载,优先级较高;内存缓存
(没有设置资源的缓存策略时)中。let { relList } = document.createElement('link');
return relList && relList.supports && relList.supports('preload');
复制代码
//link标签
<link rel="preload" as="style" herf="./a.css"/>
<link rel="prefetch" as="script" href="./b.js"/>
//动态建立
let preLink = document.createElement('link');
preLink.rel ='prefetch'; //感受动态建立不适合preload
prelink.as = 'script';
preLink.href = './a.js';
复制代码
不一样值代表资源类型,对应的优先级不一样:style
, script
, image
, media
, document
, font
。 问题: 官方说法:不带 “as”
属性的 preload 的优先级将会等同于异步请求。 测试:没有发请求。
as='style'
和as='script'
预加载,会形成二次下载anonymous
匿名,不带认证信息),一样会形成二次下载preload字体时即便同域也须要带crossOrigin,不然一样会形成二次下载 Requests without credentials use a separate connection
。来源twitter
//head中
<link rel="preload" href="https://abs.twimg.com/k/zh-cn/init.zh-cn.3b38ddbf651139df6007.js" as="script">
//body底部
<script src="https://abs.twimg.com/k/zh-cn/init.zh-cn.3b38ddbf651139df6007.js" async></script>
复制代码
//若支持preload,异步下载完不会当即执行
<link rel="preload" >
//下载完当即应用到DOM树
<link rel="stylesheet" >
//异步下载,只有打印的时候才会应用,不符合则不会应用,所以不会阻塞渲染
<link rel="stylesheet" media='print' >
复制代码
// 1. 支持preload:
//因为preload只是获取样式,不会当即应用,所以使用onload改变link的rel使其当即生效9)
<link rel="preload" href="style.css" as="style" onload="this.onload = null;this.rel ='stylesheet'">
注:设置onload=null主要是由于有些浏览器会在rel改变时再次出发load事件。
// 2. 不支持preload
// 1)获取所有link
let links = document.getElementsByTagName("link");
// 2)缓存每一个link的media
var finalMedia = link.media || "all";
// 3)改变link的rel和media(异步下载但不会应用)
link.rel = "stylesheet";
link.media = "only x";
// 4)若是绑定onload事件(为了启用media)
if( link.addEventListener ){
link.addEventListener( "load", enableStylesheet );
} else if( link.attachEvent ){
link.attachEvent( "onload", enableStylesheet );
}
// 5)为了应对旧的浏览器不支持link的onload事件
setTimeout( enableStylesheet, 3000 );
// 6)enableStylesheet回调,将media恢复,样式当即应用
link.setAttribute( "onload", null );
link.media = finalMedia;
复制代码
适用于对于首页无关的样式:因为preload的资源,可以异步加载样式,所以能够避免在加载首页无关样式时阻塞初始渲染。
对于首页初始渲染中重要的样式:
1)内联 (注意,会将静态资源的缓存策略与页面的缓存策略捆绑
)
2)HTTP/2的serverPush
知道了preload和prefetch的用途,那如何结合项目实践呢?因为webpack目前基本是项目必备,因此首先介绍结合webpack的使用;而后对quiklink进行简单介绍。
PreloadWebpackPlugin
经常使用的配置以下:
new PreloadWebpackPlugin({
//preload or prefetch方式
rel: '',
/*
*即<link as='' />中的as,代表资源类型,不一样的类型决定了不一样的执行优先级
*好比:script的优先级大于style
*/
as: '',
//排除的html页面集合,即只关联要配置的页面
excludeHtmlNames: [],
//所关联页面须要使用preload或prefetch的资源
include: []
})
复制代码
其中include的两种使用:
import()
动态导入的模块。可使用prefetch
方式异步加载模块;```
include: ['vendor', 'index']
```
复制代码
注意事项
HtmlWebpackPlugin
插件使用HtmlWebpackPlugin
后面,由于PreloadWebpackPlugin
须要使用其提供的hook钩子将构造的<link>
插入html中:plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin()
]
复制代码
使用效果 对某个页面中include的资源,最终会在对应页面head中插入link标签:
<link as="script" href="/common.js" rel="preload">
<link as="script" href="/asyncChunk.js" rel="prefetch">
复制代码
当真正使用时,因为已经下载到本地,直接读取执行,性能获得较大的提高。
好处:拆分chunk,减小首屏js体积。
若是工程没有使用HtmlWebpackPlugin,能够对动态导入的资源作以下处理:
import(/* webpackPrefetch: true */)
import(/* webpackPreload: true */)
复制代码
【版本限制】需webpack v4.6.0+ 才支持预取和预加载。本地测试后,发现prefetch可用,preload无效(有成功的烦请告知)。
工做原理 经过获取页面中a
标签的href
,试图更快的加载接下来可能要访问的页面。
IntersectionObserver(交叉观察器): 检测当前视口的links
let target= document.getElementById('a');
io = new IntersectionObserver(
entries => {},
{
threshold: [1] //交叉区域为1时会触发callback
}
);
io.observe(target);
复制代码
【备注】常规的主要是经过getBoundingClientRect()
获取元素在视口中的详细位置,来实现滚动加载以及吸附等功能。
requestIdleCallback:等到浏览器空闲时
【备注】注意其和requestAnimationFrame
的区别
检查当前的网络环境:navigator.connection.effectiveType //4G、2G...
prefetch缓存的待下载的url
小巧的js库,使用了如上4个特性,每个都值得细细品味。
工做流程:
判断当前网络情况,若使用的是2G或者开启了省流模式(data-saver
),则不作处理
data-saver: The user may enable such preference, if made available by the user agent, due to high data transfer costs, slow connection speeds, or other reasons.
题外话:prefetch有点偷流量的意思,我想看什么才消耗对应资源产生的流量,而prefetch擅自为我作主,偷偷下载不少我可能并不须要的资源(在早前流量特贵的时候这么作,估计会被打死...)。
三种下载资源的方式:fetch
、xhr
、<link rel=prefetch href="" />
使用说明
综合来看,PreloadWebpackPlugin
更适合对chunk而非html文件的处理;而quikLink
更适合博客类的网站,或者服务端渲染的页面,这样才能实现"秒开"的预期效果。
【欢迎留言】本文是否对你有帮助,亦或有所
遗漏笔误
等,烦请告知。