Начнем с создания нового приложения Next.js:
$ yarn create next-app — typescript
В корне проекта Next.js на TypeScript будут созданы файлы next-env.d.ts
и tsconfig.json
. Файл next-env.d.ts
делает так, чтобы типы Next.js были подобраны компилятором TypeScript. Его не нужно удалять или редактировать.
Файл tsconfig.json
— это пользовательская конфигурация компилятора TypeScript. Если понадобятся дополнительные типы, нужно создать *.d.ts
в корне и сослаться на него в массиве include
файла tsconfig.json
.
Более подробную информацию можно найти в официальной документации.
Добавление файлов favicon
Наведем порядок и добавим подписи к проекту. Например, удалим ненужные компоненты и добавим иконку приложения. favicon.io — эффективный инструмент для создания файлов иконок веб-приложений.
public/
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
└── site.webmanifest
Файлы иконок должны быть помещены в общедоступную папку. Также нужно добавить свойства в site.webmanifest
, чтобы дополнить его и привести к стандартам прогрессивного веб-приложения.
- ./public/site.webmanifest:
{
"name": "Next.js 100 - TypeScript",
"app_name": "Next.js 100 - TypeScript",
"short_name": "Next.js 100 - TypeScript",
"description": "Build a PWA to achieve 100% in Google Lighthouse and Next.js Analytics",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/favicon-32x32.png",
"sizes": "32x32",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/favicon-16x16.png",
"sizes": "16x16",
"type": "image/png",
"purpose": "any maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"start_url": "/"
}
- ./pages/_document.tsx:
import { Head, Html, Main, NextScript } from 'next/document'
const Document = () => (
<Html>
<Head>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link
href="/favicon-16x16.png"
rel="icon"
type="image/png"
sizes="16x16"
/>
<link
href="/favicon-32x32.png"
rel="icon"
type="image/png"
sizes="32x32"
/>
<link rel="apple-touch-icon" href="/apple-touch-icon.png"></link>
<link rel="manifest" href="/site.webmanifest" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
export default Document
Когда файлы будут готовы, добавьте метатеги в элемент <head>
файла _document.tsx
.
manifest.json и site.webmanifest
Согласно w3.org, разработчики могут использовать различные расширения, такие как manifest.json
и site.webmanifest
. Однако зарегистрированным в системе IANA расширением файла для manifest
является .webmanifest
. Мы будем использовать site.webmanifest
в этом репозитории.

Производственная сборка — это оптимизированная версия приложения. Сгенерированный код в .next компилируется и минифицируется. Обычно производственная версия выкладывается с целью оптимизации производительности.

Продолжим, чтобы окрасить все оставшиеся показатели отчета в зеленый цвет.
Добавляем метатеги title и description
Исходя из отчета Lighthouse, добавление элементов title
и description
может улучшить как уровень доступности, так и показатели SEO.


В Next.js мы можем разместить метатег description
в файле _document.tsx
. Однако элемент <title>
нужно добавить на каждую страницу.
- ./pages/_document.tsx:
import { Head, Html, Main, NextScript } from 'next/document'
const Document = () => (
<Html>
<Head>
+ <meta
+ name="description"
+ content="Build a PWA to achieve 100% in Google Lighthouse and Next.js Analytics"
+ />
+
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
- ./pages/index.tsx:
import type { NextPage } from 'next'
+ import Head from 'next/head'
const Index: NextPage = () => {
return (
<div>
+ <Head>
+ <title>Next.js 100 - TypeScript</title>
+ </Head>
+
<main>
<h1>Next.js 100 - TypeScript</h1>
</main>
Добавление атрибута lang к элементу HTML
Начиная с версии 10, Next.js поддерживает интернационализированную маршрутизацию. Next.js будет автоматически добавлять атрибут lang
в зависимости от языка пользователя.
- ./next.config.js:
const nextConfig = {
reactStrictMode: true,
+ i18n: {
+ locales: ['en', 'nl'],
+ defaultLocale: 'en',
+ localeDetection: false,
+ },
})
module.exports = nextConfig
Добавим конфигурацию i18n в next.config.js
, чтобы указать поддерживаемые языки. Мы увидим, что автоматически определяемый атрибут lang
добавлен к элементу HTML.

Теперь в отчете от Lighthouse все четыре показателя окрашены в зеленый цвет (100). Тем не менее на кружке PWA все еще видим минус.
Прогрессивное веб-приложение (PWA)
Довольно удобно превратить простое приложение Next.js в прогрессивное веб-приложение, добавив пакет next-pwa
:
$ yarn add next-pwa
Импортируйте пакет next-pwa
в next.config.js
и добавьте конфигурации. Желательно, чтобы service worker не функционировал во время процесса разработки.
- ./next.config.js:
/** @type {import('next').NextConfig} */
+ const withPWA = require('next-pwa')
+ const runtimeCaching = require('next-pwa/cache')
+
const nextConfig = withPWA({
reactStrictMode: true,
i18n: {
locales: ['en', 'nl'],
defaultLocale: 'en',
localeDetection: false,
},
+ pwa: {
+ disable: process.env.NODE_ENV !== 'production',
+ dest: 'public',
+ runtimeCaching,
+ buildExcludes: [/middleware-manifest.json$/],
+ },
})
module.exports = nextConfig
Для настройки PWA также необходимы метатеги theme-color
и referrer
.
- ./pages/_document.tsx:
import { Head, Html, Main, NextScript } from 'next/document'
const Document = () => (
<Html>
<Head>
<meta
name="description"
content="Build a PWA to achieve 100% in Google Lighthouse and Next.js Analytics"
/>
+ <meta name="theme-color" content="#333333" />
+ <meta name="referrer" content={'strict-origin'} />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
manifest.json и проблема предварительного кэширования
После развертывания в Vercel мы столкнемся с проблемой предварительного кэширования. Поэтому в следующей сборке PWA кэш времени выполнения добавлен, а файл middleware-manifest.json
исключен.
После выполнения вышеуказанных манипуляций, магическим образом в кружке PWA в отчете от Lighthouse появляется зеленая галочка!

Проверка безопасности
Несмотря на то, что мы достигли 100% в отчете от Lighthouse, этого недостаточно. Надо также подумать и о безопасности, поэтому проверим соответствующий показатель.

По данным webpagetest.org, оценка безопасности находится в группе E. Это означает, что нам предстоит решить ряд проблем.



Добавляем заголовки безопасности
Мы можем исправить эти проблемы, добавив заголовки безопасности в next.config.js
:
/** @type {import('next').NextConfig} */
const withPWA = require('next-pwa')
const runtimeCaching = require('next-pwa/cache')
+ const securityHeaders = () => [
+ {
+ key: 'X-Content-Type-Options',
+ value: 'nosniff',
+ },
+ {
+ key: 'X-Frame-Options',
+ value: 'SAMEORIGIN',
+ },
+ {
+ key: 'X-XSS-Protection',
+ value: '1; mode=block',
+ },
+ ]
+
const nextConfig = withPWA({
reactStrictMode: true,
i18n: {
locales: ['en', 'nl'],
defaultLocale: 'en',
localeDetection: false,
},
pwa: {
disable: process.env.NODE_ENV !== 'production',
dest: 'public',
runtimeCaching,
buildExcludes: [/middleware-manifest.json$/],
},
+ async headers() {
+ return [
+ {
+ source: '/:path*',
+ headers: securityHeaders(),
+ },
+ ]
+ },
})
module.exports = nextConfig
Добавляем политику безопасности контента
Для обеспечения политики безопасности контента мы должны добавить vitals.vercel-insights.com для Vercel Analytics:
/** @type {import('next').NextConfig} */
const withPWA = require('next-pwa')
const runtimeCaching = require('next-pwa/cache')
+ const ContentSecurityPolicy = `
+ default-src 'self';
+ script-src 'self';
+ connect-src 'self' vitals.vercel-insights.com;
+ style-src 'self';
+ font-src 'self';
+ `
+
const securityHeaders = () => [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
+ {
+ key: 'Content-Security-Policy',
+ value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
+ },
]
Пройдем еще одну проверку на webpagetest.org для получения лучшей оценки.

Наконец-то мы получили группу А+ по уровню безопасности.
Vercel Analytics
Google Lighthouse — почти стандартный инструмент для проверки основных показателей сайта. Но эта оценка предназначена для одной точки данных. Чтобы оценить веб-показатели с точки зрения реального опыта, нужно воспользоваться Vercel analytics. Этот инструмент позволяет измерять производительность сайта путем сбора метрик при посещении пользователями его страниц.

Настройка Vercel Analytics в Vercel довольно проста, мы просто включаем его на вкладке Analytics целевого проекта.

Поскольку Vercel Analytics собирает показатели производительности по мере посещения пользователями сайта, нужно зайти на страницу несколько раз, прежде чем данные появятся на дашборде.
Вывод
В этой статье мы проследили весь путь создания прогрессивного веб-приложения с Next.js и получения 100-процентного результата от Google Lighthouse, Next.js Analytics и уровня безопасности A+ на webpagetest.org. Надеюсь, этот материал послужит трамплином для ваших потрясающих проектов!
Исходный код можно найти в этом репозитории.
Читайте также:
- Nexus — новый визуализатор дерева компонентов для Next.js
- Установка Next.js с использованием клиентского сервера Express и TypeScript
- Получение общих данных в Next.js одним запросом
Читайте нас в Telegram, VK и Дзен
Перевод статьи Rocky Li: How to Set Up Next.js with TypeScript to Get a 100% Score in Google Lighthouse and Vercel Analytics