Почему Guess.js?
Предзагрузка страниц — это особая функциональность или скрипт браузера. В режиме активации такой скрипт поручает браузеру загрузить ресурсы, которые может посетить пользователь, до фактического запроса нужного контента. Эти ресурсы загружаются и кэшируются для дальнейшего использования без выполнения явного запроса от пользователя. Данный процесс называется предварительной выборкой.
Механизм предзагрузки страниц повышает производительность, обеспечивая моментальную загрузку спрогнозированного контента. Однако он также может загрузить содержимое, невостребованное пользователем. Вот почему так важна “правильная” предзагрузка страниц, качественное выполнение которой улучшает пользовательский опыт.
Guess.js — это библиотека JavaScript и инструмент для автоматизации процесса прогнозируемой предварительной выборки.
Знакомство с Guess.js
Библиотека Guess.js создана в апреле 2018 года. Текущая версия — 0.4.22, и она находится еще в стадии альфа-версии. Guess.js выполняет предзагрузку страниц на основе данных, полученных такими аналитическими технологиями, как Google Аналитика и моделями МО.
Прогностическая аналитика данных применяется в нескольких случаях.
- Прогнозирование следующей страницы (или страниц), которые с большей вероятностью посетит пользователь и предварительная выборка этих страниц.
— Страница: Предварительное отображение или предвыборка страницы, которую пользователь может открыть следующей.
— Бандлы: Предварительная выборка бандлов (англ. bundle), связанных сN
количеством первых страниц. - Прогнозирование последующего фрагмента контента (статьи, продукта или видео), который может посмотреть пользователь.
- Прогнозирование типов виджетов, с которыми чаще всего взаимодействует пользователь, например игры.
Guess.js поддерживает такие статические сайты, как Gatsby, Next.js, Nuxt.js и многое другое. В статье мы рассмотрим принцип работы Guess.js и для этой цели воспользуемся приложением Angular.
Анализ работы Guess.js в приложении Angular
Исследование качества работы Guess.js проходит в рабочей среде Angular и состоит из 7 этапов. На первых трех мы создаем проект Angular с ленивой загрузкой для каждого маршрута, а на последующих четырех настраиваем и выполняем прогнозируемую предварительную выборку с помощью Guess.js.
- Создание проекта Angular.
- Настройка 3-х маршрутов:
'/'
,'/one'
и'/two'
. - Запуск приложения для просмотра загруженных бандлов.
- Создание файла прогностической аналитики
routes.json
. - Расширение Angular CLI для конфигурации Webpack.
- Конфигурация Guess.js в приложении Angular.
- Выполнение прогнозируемой предварительной выборки с помощью Guess.js.
1. Создание проекта Angular
Глобально устанавливаем Angular CLI:
% npm i -g @angular/cli
Проверяем версию:
% ng version
Angular CLI: 14.2.1
Установив ng
, создаем новый проект:
% ng new angular-guess
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS
2. Настройка 3-х маршрутов: ‘/’, ‘/one’ и ‘/two’
Настраиваем 3 маршрута ('/'
, '/one'
и '/two'
) и изменяем для них файлы приложения:
На данном скриншоте вновь добавленные файлы выделены зеленым цветом, а измененные — желтым.
Маршрут index
, /
Модуль index
определен в src/app/index/index.module.ts
:
import { NgModule } from '@angular/core';
import { IndexComponent } from './index.component';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [IndexComponent],
imports: [
RouterModule.forChild([
{
path: '',
component: IndexComponent
}
])
]
})
export class IndexModule {}
Он определяет дочерний маршрут RouterModule
(строки 8–13 в коде) с IndexComponent
(строка 11).
IndexComponent
определен в src/app/index/index.component.ts
:
import { Component } from '@angular/core';
@Component({
selector: 'app-index',
template: 'Home1'
})
export class IndexComponent {}
Он определяет селектор как 'app-index'
(строка 4). Шаблон — просто текст 'Home1'
(строка 5).
Маршрут /one
Модуль one
определен в src/app/one/one.module.ts
:
import { NgModule } from '@angular/core';
import { OneComponent } from './one.component';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [OneComponent],
imports: [
RouterModule.forChild([
{
path: '',
component: OneComponent
}
])
]
})
export class OneModule {}
Он определяет дочерний маршрут RouterModule
(строки 8–13) с OneComponent
(строка 11).
OneComponent
определен в src/app/one/one.component.ts
:
import { Component } from '@angular/core';
@Component({
selector: 'app-one',
template: 'One'
})
export class OneComponent {}
Он определяет селектор как 'app-one'
(строка 4). Шаблон — просто текст 'One'
(строка 5).
Маршрут /two
Модуль two
определен в src/app/two/two.module.ts
:
import { NgModule } from '@angular/core';
import { TwoComponent } from './two.component';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [TwoComponent],
imports: [
RouterModule.forChild([
{
path: '',
component: TwoComponent
}
])
]
})
export class TwoModule {}
Он определяет дочерний маршрут RouterModule
(строки 8–13) с TwoComponent
(строка 11).
TwoComponent
определен в src/app/two/two.component.ts
:
import { Component } from '@angular/core';
@Component({
selector: 'app-two',
template: 'Two'
})
export class TwoComponent {}
Он определяет селектор как 'app-two'
(строка 4). Шаблон — просто текст 'Two'
(строка 5).
Файлы приложения
src/app/app-routing.module.ts
определяет маршруты приложения:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./index/index.module').then(m => m.IndexModule)
},
{
path: 'one',
loadChildren: () => import('./one/one.module').then(m => m.OneModule)
},
{
path: 'two',
loadChildren: () => import('./two/two.module').then(m => m.TwoModule)
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Маршруты определены в следующих строках: строки 5–9 — ''
(маршрут index
); строки 10–13 — '/one'
; строки 14–17 — '/two'
. Все они являются маршрутами с ленивой загрузкой (строки 8, 12 и 16).
src/app/app.component.html
определяет UI приложения, как показано ниже:
<a routerLink="">Home</a>
<a routerLink="one">One</a>
<a routerLink="two">Two</a>
<br>
<router-outlet></router-outlet>
В верхней части указаны 3 ссылки: Home
(строка 1), One
(строка 2) и Two
(строка 3).
Строка 5 указывает место переноса строки.
Строка 7 — это router-outlet
, плейсхолдер для компонента выбранного маршрута.
src/app/app.component.css
определяет CSS приложения:
a {
margin-right: 5px;
}
Он просто добавляет интервал в 5 пикселей между ссылками.
3. Запуск приложения для просмотра загруженных бандлов
Запускаем приложение: ng serve
.
Это базовое приложение, отображающее содержимое выбранного маршрута.
Из-за применения ленивой загрузки каждая страница отправляет запрос к соответствующим бандлам JavaScript. Для просмотра сетевых запросов используется браузер Firefox.
Чистим кэш перед выполнением:
Мы работаем с Firefox, поскольку в его сетевом инспекторе есть столбец transferred
. Он показывает количество фактически переданных байтов для загрузки ресурса или объясняет в сообщении, почему ресурс не был передан, возможно, кэширован cached
.
Скриншот индексной страницы:
На нем отчетливо видно, что загружен только один бандл: src_app_index_index_module_ts.js
.
4. Создание файла прогностической аналитики routes.json
Как правило, в реальном приложении задействуются инструменты отслеживания и анализа или модели МО. В данном случае для простоты мы создадим файл прогностической аналитики routes.json
:
{
"/": {
"/one": 80,
"/two": 20
},
"/one": {
"/": 90,
"/two": 10
},
"/two": {
"/": 90,
"/one": 10
}
}
Строка 2 подразумевает, что текущий сеанс — это '/'
. В 80 сеансах пользователи посетили '/one'
(строка 3), а в 20 сеансах — '/two'
(строка 4).
Строка 6 указывает, что текущий сеанс — '/one'
. В 90 сеансах пользователи посетили '/'
(строка 7), а в 10 сеансах — '/two'
(строка 8).
В строке 10 текущим сеансом значится '/two'
. В 90 сеансах пользователи посетили '/'
(строка 11), а в 10 сеансах — '/one'
(строка 12).
5.Расширение Angular CLI для конфигурации Webpack
Angular 5 предоставлял команду ng eject
, извлекающую базовую конфигурацию Webpack для кастомизации. В Angular 6 ей на смену пришли так называемые задачи-компоновщики builders
, выполняющие кастомизацию:
% npm i -D @angular-builders/custom-webpack
@angular-builders/custom-webpack
является частью devDependencies
в package.json
:
"devDependencies": {
"@angular-builders/custom-webpack": "^14.0.1"
}
В angular.json
изменяем значение builder
с @angular-devkit/build-angular:browser
на @angular-builders/custom-webpack:browser
(строка 14 в нижепредставленном коде) и добавляем customWebpackConfig
в options
(строки 16–18):
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-guess": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extend.webpack.config.js"
},
"outputPath": "dist/angular-guess",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
...
},
"defaultConfiguration": "production"
},
...
}
}
}
}
Теперь extend.webpack.config.js
можно применить для настройки Guess.js в Webpack.
6. Конфигурация Guess.js в приложении Angular
Устанавливаем пакеты Guess.js следующей командой:
% npm i -D guess-webpack guess-parser
Пакет guess-webpack
содержит плагин Guess.js Webpack, а пакет guess-parser
— набор парсеров для статического анализа приложений.
Оба пакета используются в extend.webpack.config.js
:
const { GuessPlugin } = require('guess-webpack');
const { parseRoutes } = require('guess-parser');
module.exports = {
plugins: [
new GuessPlugin({
// В качестве альтернативы вы можете предоставить ID представления Google Аналитики
// GA: 'XXXXXX',
reportProvider() {
return Promise.resolve(JSON.parse(require('fs').readFileSync('./routes.json')));
},
runtime: {
delegate: false
},
routeProvider() {
return parseRoutes('.');
}
})
]
};
Строки 9–11: reportProvider
возвращает аналитические данные, на основе которых GuessPlugin
создает модель МО. В упрощенном случае он считывает статический файл routes.json
. Чаще всего для указания ID представления Google Аналитики применяется GA
. Guess.js получает данные из аккаунта Google Аналитики и автоматически формирует отчет.
Строки 12–14: runtime
задает конфигурацию времени выполнения, где delegate
в значении false
. Это позволяет Guess.js обрабатывать предварительную выборку бандлов.
Строки 15–17: routeProvider
делегирует свой вызов parseRoutes
, который возвращает установленное соотношение между маршрутами и блоками кода JavaScript (англ. chunck).
7. Выполнение прогнозируемой предварительной выборки с помощью Guess.js
Мы настроили Guess.js в приложении Angular. Прогнозируемая предварительная выборка работает только в среде продакшн.
Создаем продакшн-сборку с помощью команды:
% npm run build
Переходим в директорию dist
для запуска продакшн-сборки:
% cd dist/angular-guess && serve -s .
Переходим в браузер Firefox и чистим кэш.
- Посещаем индексную страницу:
Бандл index
(в розовой рамке) 159.40d7a09f1a145796.js
загружен. Бандл one
(в красной рамке) 248.e52910797ae356c5.js
предварительно загружен, при этом в столбце Initiator
(инициатор) указано other
.
На основании файла прогностической аналитики routes.json
пользователь, находясь на http://localhost:3000
, с большей вероятностью посетит /one
, чем /two
:
"/": {
"/one": 80,
"/two": 20
}
Следовательно, http://localhost:3000/
загружает бандлы не для /two
, а для /
и /one
.
- Нажимаем ссылку
One
для перехода на/one
:
Предварительно загруженный бандл 248.e52910797ae356c5.js
(в зеленой рамке) загружен как cached
.
На основании файла прогностической аналитики routes.json
пользователь, находясь на http://localhost:3000/one
, с большей вероятностью посетит /
, чем /two
:
"/one": {
"/": 90,
"/two": 10
}
Бандл для /
уже загружен, и нет необходимости предварительно загружать бандл для /two
.
- Переходим по ссылке
Two
на/two
:
Бандл 628.a486e5775634c464.js
(оранжевой рамке) загружен.
На основании файла прогностической аналитики routes.json
пользователь, находясь на http://localhost:3000/two
, с большей вероятностью посетит /
, чем /one
:
"/two": {
"/": 90,
"/one": 10
}
Бандл для /
уже загружен, и нет необходимости предварительно загружать бандл для /one
.
На этом этапе все бандлы загружены.
Guess.js грамотно загружает или выполняет предзагрузку. С видео вышеописанного процесса можно ознакомиться по ссылке.
Репозиторий с полным вариантом кода представлен по этой ссылке.
Заключение
Guess.js — это библиотека JavaScript и инструмент для автоматизации процесса прогнозируемой предварительной выборки. Мы изучили ее работу в приложении Angular и убедились, что она отлично справляется с поставленной задачей. Надеемся, что вскоре эта инновационная технология будет официально представлена.
Читайте также:
- Как оптимизировать приложения на Angular
- ReactJS, Angular5 и Vue.js — какой фреймворк выбрать в 2018 году?
- ANGULAR v7: подсказки командной строки, виртуальная прокрутка, перетаскивание и др.
Читайте нас в Telegram, VK и Дзен
Перевод статьи Jennifer Fu: Evaluating Guess.js in Angular Applications