所以我有一个基于Ionic 3的PWA,不久我就意识到在2G / 3G /次优连接上,第一次加载体验很糟糕:基于 first meaningful paint 的大约10秒在Chrome Dev Tools中进行PWA灯塔审核测试。
我确实根据最佳实践等对所有页面和所有常规优化(产品构建等)使用了延迟加载,并将其范围缩小到7s,这仍然不理想。
要想走低一点,我评估了服务器端渲染,但是从ionic 3开始,它尚不可用;(而且我不得不尝试其他方法。
这是我在下面的(实验)中尝试过的事情,我想知道这种方法可能会导致哪些问题 ,因为我自己找不到任何问题。 / p>
以下是Ionic 3网络应用如何在默认index.html中加载其核心javascript资源的方法:
<body>
<!-- Ionic's root component and where the app will load -->
<ion-app></ion-app>
<!-- The polyfills js is generated during the build process -->
<script src="build/polyfills.js"></script>
<!-- The vendor js is generated during the build process
It contains all of the dependencies in node_modules -->
<script src="build/vendor.js"></script>
<!-- The main bundle js is generated during the build process -->
<script src="build/main.js"></script>
</body>
这是css的加载方式:
<head>
...
<link href="build/main.css" rel="stylesheet">
</head>
我在这里指出的关键是main.js是一种立即调用的函数表达式,它在加载的那一刻就立即执行-导致Ionic App自举等。
因此,考虑新用户的初始(首次加载)页面始终是轻型的“简介”页面(带有徽标,一些文本和确认按钮的简单页面(将来可能会成为最终用户许可协议))决定在index.html中创建一个静态html + style +一些js的页面,并延迟main.js的执行,直到用户单击确认按钮,但与此同时,我希望尽快下载main.js和main.css。 。因此,我为index.html想到了以下代码(见下文)基本思想:
索引来源:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic App</title>
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4e8ef7">
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- cordova.js required for cordova apps (remove if not needed) -->
<script src="cordova.js"></script>
<!-- un-comment this code to enable service worker
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>-->
<div id="linksContainer"></div>
</head>
<body>
<!-- Ionic's root component and where the app will load -->
<ion-app>
</ion-app>
<!-- preload magic here -->
<div id="introScreenContainer">
<progress id="loadingProgressBar" value="0" max="100"></progress>
<p class="introTitle">Welcome to my App</p>
<div id="introTextContainer">
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</div>
<button id="swearButton" onclick="loadApp()">i solemnly swear that I am up to no good!</button>
<style>
html, body {
margin: 0;
padding: 0;
}
#loadingProgressBar {
position: absolute;
top: 0px;
width: 100%;
display: block;
}
#introTextContainer {
display: flex;
align-items: center;
height: 100%;
margin-top: 20%;
}
.introTitle {
text-align: center;
}
#swearButton {
position: absolute;
bottom: 4px;
background-color: #488aff;
width: 100%;
border: none;
border-radius: 4px;
color: white;
padding: 12px 32px;
text-align: center;
font-size: 16px;
visibility: hidden;
}
</style>
<script>
// cache dom elements:
var linksContainer = document.getElementById("linksContainer");
var introScreenContainer = document.getElementById('introScreenContainer');
var loadingProgressBar = document.getElementById('loadingProgressBar');
var swearButton = document.getElementById("swearButton");
// loading counter counts scripts that has to be loaded before Ionic App can start:
var loadingCounter = 0;
// this gets called after each core script loads:
function bumpCounter() {
loadingCounter++;
if (loadingCounter === 1) {
loadingProgressBar.setAttribute("value", 25);
}
if (loadingCounter === 2) {
loadingProgressBar.setAttribute("value", 60);
}
if (loadingCounter === 3) {
loadingProgressBar.setAttribute("value", 85);
}
if (loadingCounter === 4) {
loadingProgressBar.setAttribute("value", 100);
swearButton.style.visibility = "visible";
console.log("we can proceed to load");
}
}
function loadApp() {
var mainScript = document.createElement('script')
mainScript.src = "build/main.js";
document.body.appendChild(mainScript);
introScreenContainer.remove();
cssLink.rel = "stylesheet";
}
// feature detect preload capability with link tag:
var supportsPreload = (function () { try { return document.createElement("link").relList.supports('preload'); } catch (e) { return false; } }());
// if preload via link tag is supported we add tags and start downloading of the js and css resources:
if (supportsPreload) {
var mainLink = document.createElement('link');
mainLink.rel = "preload";
mainLink.href = "build/main.js";
mainLink.as = "script";
mainLink.onload = bumpCounter();
linksContainer.appendChild(mainLink);
var cssLink = document.createElement('link');
cssLink.rel = "preload";
cssLink.href = "build/main.css";
cssLink.as = "style";
cssLink.onload = bumpCounter();
linksContainer.appendChild(cssLink);
} else {
// if not supported, then we just proceed as is:
var cssLink = document.createElement('link');
cssLink.rel = "stylesheet";
cssLink.href = "build/main.css";
cssLink.as = "style";
cssLink.onload = bumpCounter();
linksContainer.appendChild(cssLink);
bumpCounter();
}
</script>
</div>
<!-- The polyfills js is generated during the build process -->
<script defer src="build/polyfills.js" onload="bumpCounter()"></script>
<script defer src="build/vendor.js" onload="bumpCounter()"></script>
<!-- my own polyjouices / dependencies -->
<noscript>Javascript needs to be enabled to use this app!</noscript>
</body>
</html>
小更新: