до«Добавить каркасный экран в проект vue»В этой статье представлена концепция каркасного экрана и его применение в проектах Vue. В этой статье рассказывается, как ускорить рендеринг скелетного экрана в браузере.
Время рендеринга скелетного экрана
Давайте сначала посмотрим на временную шкалу и откроем панель производительности в Chrome Devtool:
Нетрудно обнаружить, что после загрузки HTML браузеру все еще нужно дождаться загрузки стиля (index.css) перед рендерингом скелетного экрана. Это связано с тем, что браузеру нужны DOM и CSSOM для построения дерева рендеринга, поэтому и HTML, и CSS являются ресурсами, блокирующими рендеринг. Это имеет смысл в большинстве сценариев, в конце концов, следует избегать того, чтобы пользователь мог видеть мерцание контента до и после загрузки стиля (FOUC).
Но стили, требуемые каркасным экраном, уже встроены в HTML, а CSS для внешнего рендеринга контента, очевидно, не должен блокировать рендеринг скелетного экрана. Есть ли способ изменить эту функцию?
Переместить ссылку в тело
Первое, что приходит на ум, это<link>от<head>перейти к<body>, спецификация HTML позволяет это:
A
<link>tag can occur either in the head element or in the body element (or both), depending on whether it has a link type that is body-ok. For example, the stylesheet link type is body-ok, and therefore a<link rel="stylesheet">is permitted in the body.
Таким образом, CSS будет блокировать только последующий контент, а каркасный экран не будет затронут.
<head>
<style>Skeleton CSS</style>
</head>
<body>
<div>Skeleton DOM</div>
<link rel='stylesheet' href='index.css'>
<div id='app'>...</div>
</body>
Но после тестирования в Chrome вы обнаружите, что CSS по-прежнему блокирует рендеринг в ветке Chrome по этой проблеме.Обсуждать, вы можете видеть, что, поскольку нет строгой спецификации стратегии рендеринга для этой ситуации, в разных браузерах появляются разные показатели:
- Chrome по-прежнему блокирует рендеринг.Webpagetest
- IE работает как положено и просто блокирует последующий контент.Webpagetest
- Firefox вообще не блокирует рендеринг, если
<head>блокировка произошла в<link>. Это приведет к FOUC для последующего контента.Webpagetest. нуждаться в<link>После добавления пустого<script> </script>Для достижения эффекта блокировки последующего рендеринга контента.
В этом длительном обсуждении разработчики попытались достичьСледующий эффект:
- любой появляется в
<link>Последующее содержимое DOM не будет добавлено в дерево визуализации до тех пор, пока не будет загружена таблица стилей, что блокирует последующую визуализацию. - для
<link>Увеличиватьasyncсвойства, такие как<script>изdefer/async, не блокирует рендеринг и применяется сразу после загрузки. - вставлен JS
<link>будет загружаться асинхронно.
Таким образом, разработчики смогут настроить порядок объявления браузера, отобразить содержимое страницы как можно быстрее. Разработчики Jake предложили использовать с HTTP2Сцены:
<body>
<!-- HTTP/2 push this resource, or inline it, whichever's faster -->
<link rel="stylesheet" href="/site-header.css">
<header></header>
<link rel="stylesheet" href="/article.css">
<main></main>
<link rel="stylesheet" href="/site-footer.css">
<footer></footer>
</body>
Но эта функция до сих пор не реализована в Chrome, и приходится обращаться к другим методам.
Асинхронно загружать таблицы стилей
loadCSSПредусмотрено два способа асинхронной загрузки таблиц стилей:
<link ref='preload'>- обеспечить глобальный
loadCSSметод, динамически загружающий указанную таблицу стилей. Мы будем использовать первый метод, который также рекомендуется loadCSS.
<link ref='preload'>Пусть браузер только запросит загрузку таблицы стилей, но не применит стиль, когда это будет сделано, и он не будет блокировать рендеринг в браузере. Если вы хотите применить стили после завершения загрузки, вы можетеonloadИзменено в функции обратного вызоваrelценностьstylesheet, применяется как обычная блокирующая таблица стилей. Кроме того, посколькуподдержка браузерапроблема с loadCSS
при условииpolyfill(используя атрибут media) и перейти на более раннюю версию, если JS не поддерживается. Полный пример выглядит следующим образом:
<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>
Мы применим этот подход в проекте Vue, в котором используются каркасные экраны.
Применить в проекте Vue
Хотя асинхронно загруженная таблица стилей не будет блокировать рендеринг экрана скелета, когда содержимое внешнего интерфейса заменяет содержимое экрана скелета, необходимо убедиться, что таблица стилей была загружена в это время, в противном случае действительно значимый содержимое страницы будет отображаться FOUC. Поскольку таблицы стилей и JS загружаются в непредсказуемом порядке, мы должны учитывать порядок их загрузки.
Главная идея
Во-первых, вы должны убедиться, что экземпляр Vue выполняется после загрузки асинхронной таблицы стилей.устанавливать, если на данный момент стиль не доработан, ставим метод монтирования в глобал, и вызываем его после загрузки стиля:
app = new App();
window.mountApp = () => {
app.$mount('#app');
};
if (window.STYLE_READY) {
window.mountApp();
}
затем используйте<link ref='preload'>, когда загрузка завершена, если обнаружено, что существует глобальнаяmountApp, выполнить монтирование:
<link rel='preload' href='index.css' as='style' onload='this.onload=null;this.rel='stylesheet';window.STYLE_READY=1;window.mountApp&&window.mountApp();'>
С конкретными идеями давайте рассмотрим проблемы, с которыми можно столкнуться при применении в конкретных проектах. Если вас не волнуют подробности, вы можете перейти в раздел «Конечный эффект».
Использовать с HTMLWebpackPlugin
При создании SPA обычно используетсяHTMLWebpackPlugin, этот плагин генерирует окончательный HTML-код на основе шаблона, переданного разработчиком, когда мы включаемinjectопция автоматически вставляется<link>а также<script>. Для реализации изложенных выше идей требуются некоторые модификации.
Во-первых, в шаблон нам нужно добавить JS и CSS<link ref='preload'>:
<head>
<% for (var jsFilePath of htmlWebpackPlugin.files.js) { %>
<link rel="preload" href="<%= jsFilePath %>" as="script">
<% } %>
<% for (var cssFilePath of htmlWebpackPlugin.files.css) { %>
<link rel="preload" href="<%= cssFilePath %>" as="style" onload="this.onload=null;this.rel='stylesheet';window.STYLE_READY=1;window.mountApp&&window.mountApp();">
<noscript><link rel="stylesheet" href="<%= cssFilePath %>"></noscript>
<% } %>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>
</head>
Затем, поскольку карта не вставлена<link>, мы можем написать простой подключаемый модуль Webpack, который слушает HTMLWebpackPlugin.мероприятие, чтобы отфильтровать CSS. Таким образом, плагин не будет автоматически вставлен<link>сейчас:
module.exports = class OmmitCSSPlugin {
constructor() {}
apply(compiler) {
compiler.plugin('compilation', (compilation) => {
compilation.plugin(
'html-webpack-plugin-alter-asset-tags',
(args, cb) => {
args.head = args.head.filter((link) => link.attributes.rel !== 'stylesheet');
cb(null, args);
}
);
});
}
}
окончательный эффект
После использования этого метода снова посмотрите на временную шкалу и обнаружите, что время рендеринга скелетного экрана увеличилось:
Наконец, вы можете обратиться кДЕМО-адреса такжеадрес гитхаба.
Кроме того, вLavasтакже встроенныйэта функция, добро пожаловать в использование Lavas для быстрого создания приложений PWA.