читал некоторое время назадПрактика обновления PWA от Ele.meОдна статья, много пользы. вИспользуйте Vue для предварительного рендеринга скелетного экрана во время сборки.Этот раздел предлагает разработчикам новые идеи по сокращению времени белого экрана и улучшению взаимодействия с пользователем. Эта статья будет опираться на эту идею и попытается добавить экран-скелет в проект Vue.
Что такое каркасный экран?
Представлено в GoogleОриентированный на пользователяИз четырех показателей производительности страницы FP/FCP (рендеринг первого экрана) могут быть наиболее знакомы разработчикам. Картинка ниже взята изоригинальный:
Что касается рендеринга первого экрана как можно быстрее и сокращения времени белого экрана, методы оптимизации, которые я могу придумать, примерно следующие:
- оптимизацияКритический путь рендеринга, чтобы свести к минимуму блокировку рендеринга JavaScript и CSS. Общие практики включают использование
async/defer
Разрешить браузеру загружать JavaScript, не блокируя синтаксический анализ HTML, встраивать стили ключевых частей страницы в HTML и т. д. - Кэширование с помощью сервис-воркеров AppShell, чтобы ускорить последующие посещения.
- Используйте HTTP/2 Server Push, чтобы помочь браузерам обнаруживать статические ресурсы как можно раньше и сократить количество запросов.Говоря о HTTP/2 Server PushЭта статья знакомит с практикой Ele.me в этом отношении, отправляя запросы API вместо статических ресурсов.
Скелетный экран в полной мере использует первые два пункта. Картинка ниже взята из оригиналаСтремитесь к перцептивному опытуодин период. Как видно из Skeleton Screen на рисунке, до того, как страница будет полностью отрисована, пользователь увидит простой стиль, изображающий общий фрейм текущей страницы, и воспринимает, что страница загружается постепенно. заменены, и опыт хороший.
Скелетный экран можно рассматривать как простой ключевой путь рендеринга.Поскольку это всего лишь общий фрейм страницы, стиль не слишком сложен, а встроенный код в HTML очень мал. После кэширования HTML-страницы, содержащей скелетный экран, с помощью Service Worker, быстрее получить отображение из кэша.
Реализовать идеи
обратитесь к исходному текстуИспользуйте Vue для предварительного рендеринга каркасных экранов во время сборки.Идея, представленная в разделе, я также рассматриваю экран-скелет как компонент маршрутизации, использую функцию пререндеринга Vue при построении, вставляю фрагмент HTML результата рендеринга компонента экрана-скелета в точку монтирования шаблона HTML-страницы. и вставьте стиль в заголовок метки. Таким образом, когда внешний рендеринг будет завершен, Vue будет использоватьклиентская смесь, замените содержимое каркасного экрана в точке монтирования реальным содержимым страницы.
Имея в виду приведенные выше идеи, давайте посмотрим, как добавить экран-скелет в простое приложение Vue.
Реализация
Для этого я разработал плагин webpack:vue-skeleton-webpack-plugin. Далее будут представлены некоторые детали реализации из следующих трех аспектов:
- Используйте Vue для предварительного рендеринга скелетного экрана
- Вставьте результат рендеринга скелетного экрана в HTML-шаблон.
- Вставьте каждый маршрут экрана скелета в режиме разработки
Используйте Vue для предварительного рендеринга скелетного экрана
Мы используем Vueпредварительный рендерингФункция рендеринга скелетных компонентов экрана, незнакомые студенты могут сначала прочитать официальные документыОсновное использованиеодин период.
Во-первых, вам нужно создать входной файл, который использует только скелетный компонент экрана:
// src/entry-skeleton.js
import Skeleton from './Skeleton.vue';
// 创建一个骨架屏 Vue 实例
export default new Vue({
components: {
Skeleton
},
template: '<skeleton />'
});
Затем создайте объект конфигурации веб-пакета для рендеринга на стороне сервера, указав только что созданный файл записи в качестве записи зависимости записи:
// webpack.skeleton.conf.js
{
target: 'node', // 区别默认的 'web'
entry: resolve('./src/entry-skeleton.js'), // 多页传入对象
output: {
libraryTarget: 'commonjs2'
},
externals: nodeExternals({
whitelist: /\.css$/
}),
plugins: []
}
Здесь показаны только одностраничные приложения.В многостраничных приложениях укажите запись как объект, содержащий запись каждой страницы. Примеры объектов конфигурации webpack на нескольких страницах см.Многостраничные тестовые примеры для плагиновилиШаблон лавы MPA.
Затем мы передаем этот объект конфигурации веб-пакета в плагин скелетного экрана через параметры.
// webpack.dev.conf.js
plugins: [
new SkeletonWebpackPlugin({ // 我们编写的插件
webpackConfig: require('./webpack.skeleton.conf')
})
]
Когда плагин скелетного экрана запущен, он будет использовать веб-пакет для компиляции входящего объекта конфигурации и получения файла пакета каркасного экрана. Затем просто создайте средство визуализации с содержимым этого файла пакета и вызовитеrenderToString()
метод для получения результата рендеринга HTML в виде строки. Так как нам не нужно сохранять файловые продукты процесса на жестком диске, используем in-memory файловую системуmemory-fsВот и все.
// vue-skeleton-webpack-plugin/src/ssr.js
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer;
// 从内存文件系统中读取 bundle 文件
let bundle = mfs.readFileSync(outputPath, 'utf-8');
// 创建 renderer
let renderer = createBundleRenderer(bundle);
// 渲染得到 HTML
renderer.renderToString({}, (err, skeletonHtml) => {});
По умолчанию содержимое стиля, на которое ссылаются модули веб-пакета, встроено в пакет JavaScript. Официальный плагин ExtractTextPluginВозможно разделение стилей. Мы также используем этот плагин для вывода содержимого стиля скелета экрана в отдельный файл CSS. Подробнее об использовании плагинов см.официальная документацияилиШаблоны Vue на основе webpack.
// vue-skeleton-webpack-plugin/src/ssr.js
// 加入 ExtractTextPlugin 插件到 webpack 配置对象插件列表中
serverWebpackConfig.plugins.push(new ExtractTextPlugin({
filename: outputCssBasename // 样式文件名
}));
На данный момент мы получили содержимое HTML и стилей результата рендеринга скелетного экрана, а затем нам нужно позаботиться о том, как внедрить результат в шаблон HTML-страницы.
Внедрить результаты рендеринга
Проект шаблона Vue webpack использует HTML Webpack PluginСоздание HTML-файлов. обратитесь к плагинуОписание события, мы выбираем для мониторингаhtml-webpack-plugin-before-html-processing
Событие, в функции обратного вызова события плагин передаст ожидающий в данный момент HTML-контент для дальнейшего изменения.
Мы знаем, что окончательный результат рендеринга скелетного компонента экрана включает в себя HTML и стиль, стиль можно вставить непосредственно в тег head, а HTML нужно вставить в точку монтирования. Пользователи плагина могут установить это расположение точки монтирования через параметры, которые будут использоваться по умолчанию.<div id="app">
.
Вроде бы все идет хорошо, но в многостраничных приложениях все немного сложнее. В многостраничных проектах обычно вводятся несколько плагинов HTML Webpack, например, в нашемШаблон лавы MPAиспользуется вМногостраничный плагин WebpackВот именно, что приводит кhtml-webpack-plugin-before-html-processing
Событие запускается несколько раз.
В многостраничном приложении объект конфигурации веб-пакета, который мы передаем плагину каркасного экрана, содержит несколько записей:
// webpack.skeleton.conf.js
entry: {
page1: resolve('./src/pages/page1/entry-skeleton.js'),
page2: resolve('./src/pages/page2/entry-skeleton.js')
}
Это означает, что каждый разhtml-webpack-plugin-before-html-processing
Когда событие инициируется, подключаемый модуль каркасного экрана должен идентифицировать файл ввода, обрабатываемый в данный момент, выполнить webpack для компиляции файла ввода каркасного экрана, соответствующего текущей странице, и отобразить соответствующий компонент каркасного экрана. Процесс поиска текущего обрабатываемого входного файла выглядит следующим образом:
// vue-skeleton-webpack-plugin/src/index.js
// 当前页面使用的所有 chunks
let usedChunks = htmlPluginData.plugin.options.chunks;
let entryKey;
// chunks 和所有入口文件的交集就是当前待处理的入口文件
if (Array.isArray(usedChunks)) {
entryKey = Object.keys(skeletonEntries);
entryKey = entryKey.filter(v => usedChunks.indexOf(v) > -1)[0];
}
// 设置当前的 webpack 配置对象的入口文件和结果输出文件
webpackConfig.entry = skeletonEntries[entryKey];
webpackConfig.output.filename = `skeleton-${entryKey}.js`;
// 使用配置对象进行服务端渲染
ssr(webpackConfig).then(({skeletonHtml, skeletonCss}) => {
// 注入骨架屏 HTML 和 CSS 到页面 HTML 中
});
На данный момент мы завершили рендеринг и вставку скелетного экрана, и есть небольшая проблема в разработке, требующая внимания.
Вставьте маршрут в режиме разработки
Как упоминалось ранее, поскольку Vue будет использоватьклиентская смесь, содержимое экрана скелета будет заменено после завершения внешнего рендеринга, так как же легко просматривать и отлаживать его во время разработки?
Использование инструментов разработки браузера для установки точек останова является одним из способов, но если правила маршрутизации, соответствующие компонентам экрана-скелета, можно вставить в файл маршрута в режиме разработки, чтобы можно было получить доступ к экранам-скелетам каждой страницы, как и к другим компонентам маршрута, это даст возможность разработки и отладки станет более удобным. Работа по вставке кода правила в файл маршрутизации будетзагрузчик плагиновзавершено в. Если вы не знакомы с загрузчиком webpack, вы можете обратиться кофициальная документация.
Нам нужно вставить в файл маршрутизации два типа кода: код, который вводит компонент каркасного экрана, и соответствующий объект правила маршрутизации. Что касается точки вставки кода, относительно просто ввести код компонента, просто поместив его в начало файла, а правила маршрутизации необходимо вставить в массив объектов маршрутизации.В настоящее время я использую простое сопоставление строк, чтобы найти начальную позицию этого массива. Например, в следующем примере вам нужно передать загрузчикуroutes: [
чтобы определить, куда вставить маршрут.
// router.js
import Skeleton from '@/pages/Skeleton.vue'
routes: [
{ // 插入骨架屏路由
path: '/skeleton',
name: 'skeleton',
component: Skeleton
}
// ...其余路由规则
]
В многостраничном приложении каркас экрана, соответствующий каждой странице, должен вставлять код, и пользователь может настроить шаблон для импорта инструкции компонента каркаса экрана и правил маршрутизации через заполнитель. Загрузчик будет использовать эти шаблоны во время выполнения, заменив заполнители реальными экранными именами каркаса. В следующем примере предположим, что у нас естьPage1.skeleton.vue
а такжеPage2.skeleton.vue
Эти два скелетных экрана можно использовать в режиме разработки через/skeleton-page1
а также/skeleton-page2
Получите доступ к этим двум маршрутам скелетного экрана. Дополнительные описания параметров см.здесь.
// webpack.dev.conf.js
module: {
rules: [] // 其他规则
.concat(SkeletonWebpackPlugin.loader({
resource: resolve('src/router.js'), // 目标路由文件
options: {
entry: ['page1', 'page2'],
importTemplate: 'import [nameCap] from \'@/pages/[name]/[nameCap].skeleton.vue\';',
routePathTemplate: '/skeleton-[name]'
}
}))
}
Конкретные примеры применения на нескольких страницах см.Многостраничный тестовый примерилиШаблон лавы MPA.
Суммировать
благодарныйПрактика обновления PWA от Ele.meВ этой статье предлагается решение этой проблемы. Конечно, статья содержит гораздо больше, чем скелетный экран, я считаю, что каждый читатель получит много пользы.
плагинЕсли у вас возникнут какие-либо проблемы во время использования, вы можете спросить ISSUE.
Байду LavasЭто решение PWA на основе Vue, которое помогает разработчикам легко создавать сайты PWA. Многие шаблоны также используют этот плагин, и вы можете попробовать его и оставить комментарии.