На прошлой неделе у меня была простая задача: Добавить новую схему Recipe JSON LD на нашу платформу.
Если вы не знаете, что такое JSON LD Schema, не волнуйтесь, это очень просто. JSON LD расшифровывается как "Объектная нотация JavaScript для связанных данных", и это просто способ описать, дать некоторый контекст о содержании страниц вашего веб-сайта. Эта схема помогает некоторым объектам, например поисковым системам, понять содержание страницы. Возможно, вы публикуете новостную статью, как это делает New York Times, как и другие издатели, но HTML структура вашего сайта и других сайтов отличается (надеюсь). Это затрудняет для поисковых систем получение и понимание всех частей контента, таких как заголовок, подзаголовок, изображения, видео, дата публикации и т. д., и это может привести к неправильному пониманию. (Google называет это структурированными данными и помогает Google понять содержание вашего сайта, которое можно использовать для отображения расширенных фрагментов в результатах поиска).
Узнайте больше о JSON LD и о том, как это помогает вашим страницам лучше выглядеть в Результатах Google.
Вот пример кода:
<script type="application/ld+json"> { "@context": "http://schema.org", "@type": "NewsArticle", "mainEntityOfPage": { "@type": "WebPage", "@id": "https://google.com/article" }, "headline": "Article headline", "image": [ "https://example.com/photos/1x1/photo.jpg", "https://example.com/photos/4x3/photo.jpg", "https://example.com/photos/16x9/photo.jpg" ], "datePublished": "2015-02-05T08:00:00+08:00", "dateModified": "2015-02-05T09:20:00+08:00", "author": { "@type": "Person", "name": "John Doe" }, "publisher": { "@type": "Organization", "name": "Google", "logo": { "@type": "ImageObject", "url": "https://google.com/logo.jpg" } }, "description": "A most wonderful article" } </script>
Почему функциональное программирование хорошо подходит для построения схемы?
Если вы посмотрите на приведенный выше пример, тип схемы — NewsArticle, а поле author — это объект типа Person с информацией об авторе. article и поле издатель с типом Организация и информацией о компании, в данном примере Google.
Теперь, если вы посмотрите на другой тип схемы, например, Обзор, вы увидите, что есть некоторые общие поля (взгляните на поля автора и издателя):
<script type="application/ld+json"> { "@context": "http://schema.org", "@type": "Review", "url": "http://www.localreviews.com/restaurants/1/2/3/daves-steak-house.html", "author": { "@type": "Person", "name": "Lisa Kennedy", "sameAs": "https://plus.google.com/114108465800532712602" }, "publisher": { "@type": "Organization", "name": "Denver Post", "sameAs": "http://www.denverpost.com" }, "datePublished": "2014-03-13T20:00", "description": "Great old fashioned steaks but the salads are sub par.", "inLanguage": "en", "reviewRating": { "@type": "Rating", "worstRating": 1, "bestRating": 4, "ratingValue": 3.5 } } </script>
Данные в разделах Автор и Издатель не совпадают, но если вы говорите об одном и том же веб-сайте, то данные одинаковы.
Мы можем создать функцию, которая обрабатывает автора, а другую — для издателя, примерно так:
<script type="application/ld+json"> { "@context": "http://schema.org", "@type": "Review", "url": data.url, "author": getAuthorSchema(data), "publisher": getPublisherSchema(data), "datePublished": data.publishedDate, "description": data.description, "reviewRating": getReviewRatingSchema(data) } </script>
Но я все еще недоволен результатом, потому что у нас есть две проблемы:
- Мы всегда должны вызывать функции и передавать один и тот же параметр.
- У нас есть общие поля для нескольких схем, такие как @context, он по-прежнему будет http://schema.org, то же самое для URL, даты публикации и описания.
Хорошим подходом для этого может быть композиция функций.
Что такое функциональная композиция

Очень просто, вы составляете функциональную базу из нескольких функций. Первая функция возвращает какие-то данные, передает их второй функции в качестве параметра, затем вторая функция возвращает какие-то данные, и они передаются третьей функции в качестве параметра, что-то вроде A -> B -> C. Идея в этом случае заключается в том, что если мы удалим B, все продолжит работать, A -> C означает, что мы всегда должны предоставлять схему и возвращать новую схему.
Функция A вызывается первой, и ей нужна схема. Мы можем начать со схемы, уникальной для схемы проверки, например @type.
const data = { url: 'https://giulianok.com', description: 'Best website!' }; const schema = { "@type": "Review", }; A(schema, data);
Теперь давайте создадим необходимые нам функции:
- reviewSchema: создает уникальные данные для схемы обзора.
- injectAuthor: вставляет поле автора в базу схемы на основе предоставленных данных.
- injectPublisher: вставляет поле издателя в базу схемы на основе предоставленных данных.
- injectReviewRating: вставляет поле reviewRating в базу схемы на основе предоставленных данных.
обзорСхема
const reviewSchema = ({ schema, data }) => { return { data, schema: { ...schema, // Extending the previous schema "@context": "http://schema.org", "@type": "Review", "url": data.url, "datePublished": data.publishedDate, "description": data.description } } }
injectАвтор
const injectAuthor = ({ schema, data }) => { return { data, schema: { ...schema, // Extending the previous schema "author": { "@type": "Person", "name": data.author.name, "sameAs": data.author.url } } } }
injectPublisher
const injectPublisher = ({ schema, data }) => { return { data, schema: { ...schema, // Extending the previous schema "publisher": { "@type": "Organization", "name": data.organization.name, "sameAs": data.organization.url } } } }
injectReviewRating
const injectReviewRating = ({ schema, data }) => { return { data, schema: { ...schema, // Extending the previous schema "reviewRating": { "@type": "Rating", "worstRating": data.review.rating.worst, "bestRating": data.review.rating.best, "ratingValue": data.review.rating.value } } } }
Посмотрите, у них одинаковая структура, они получают в качестве параметра объект со свойствами data и schema и возвращают другой объект с такой же структурой. Единственное отличие заключается в свойстве схемы, а это схема + несколько новых полей.
Этот способ получения и возврата объектов помогает построить схему в любом порядке, и результат будет одинаковым, например:
const data = { url: 'https://giulianok.com', description: 'Best website!', // and more fields... }; const schema = { "@type": "Review", }; injectReviewRating(injectPublisher(injectAuthor(reviewSchema(schema, data)))); // or injectAuthor(injectPublisher(injectReviewRating(reviewSchema(schema, data)))); // etc..
Сделайте так, чтобы было легко читать
Единственное, что вам нужно вызвать функцию, а затем передать другую функцию, еще и еще. Вы можете структурировать следующим образом:
injectAuthor( injectPublisher( injectReviewRating( reviewSchema(schema, data) ) ) )
Но его по-прежнему трудно читать, понимать и поддерживать. Вместо этого вы можете использовать lodash flow:
const createReviewSchema = flow( reviewSchema, injectAuthor, injectPublisher, injectReviewRating ); createReviewSchema(schema, data);
А как насчет других схем?
Ну, вы можете переиспользовать множество функций, в случае со статьей вы все еще можете использовать injectAuthor и injectPublisher, вот в чем идея:
const createArticleSchema = flow( articleSchema, // New function injectAuthor, injectPublisher ); createArticleSchema(schema, data);
Вывод
Композицию функций может быть немного сложно понять поначалу, поскольку возвращаемое значение одной функции передается в качестве параметра другой функции. Lodash Flow может помочь организовать код и поддерживать его.
Первоначально опубликовано на giulianok.com 28 июля 2018 г.