Ако работите за организация или компания в Обединеното кралство в уеб/дигитална роля, вероятно сте прекарали поне известно време през последната седмица, добавяйки банер към един от вашите сайтове, споменаващ смъртта на кралицата и съболезнованията на вашия работодател.

Ако живеете другаде по света, може би сте работили върху подобен банер, но свързан с Covid, войната на Русия срещу Украйна, осъждащ убийството на Джордж Флойд и/или подкрепящ движението BLM. Ако работите в градски център за много голяма компания, вашият работодател може да има план за реагиране на терористичен акт, който включва изпращане на бързо променящи се съобщения към уебсайта ви. От по-леката страна на нещата може да сте били помолени бързо да добавите банер, за да отпразнувате нещо радостно, като например заслужена печалба от награда.

В предишната ми работа направихме оживена сделка в началото на Covid в добавянето на банери с нулево разгръщане към клиентски сайтове, използвайки Google Tag Manager, за да инжектираме JavaScript маркиране в техните страници, но този подход причинява „изместване на оформлението“ и е бавен. В тази статия ще разгледаме далеч по-добър подход, възможен от съвременни CDN и доставчици като AWS, Cloudflare, Fastly, Akamai и Netlify.

Реагиране на извънредна ситуация по унифициран начин в цял набор от корпоративни уебсайтове

Когато 💩 удари феновете, важно е да се представи единен фронт и да се направи информацията достъпна навсякъде веднага щом е готова, но без допълнителни разходи и риск от човешка грешка, която идва от опитите за координиране на твърде много екипи. Плюс това, вашите собственици на продукти, мениджъри по доставки, разработчици и всички останали вероятно имат по-ценни неща за вършене от каскадни промени на съдържанието и гледане на производствени издания, за да променят текста на банер, който много от вашите клиенти в крайна сметка ще игнорират (той казва, не говоря от личен опит - честно!)

Представете си, че работите в голяма компания и трябва да добавите своето послание за празнуване, опасност или нещастие към цялото имущество или портфолио от различни уебсайтове, включително маркетингов сайт, сайт за електронна търговия, фирмен блог, сайт за работа, интранет , и така нататък. Или работа в група компании, като например как Co-op (моят работодател) има уебсайт за нашия „бизнес с храни/смесени магазини“, „уебсайт за електронна търговия за нашия бизнес с храни“, уебсайт за нашия „бизнес с правни услуги“ , застрахователен бизнес, погребален бизнес, плюс за нашето предложение за членство и т.н. Това са много различни уебсайтове, към които да добавим нашия банер!

Времето би било от съществено значение и в извънредна ситуация или «още една „безпрецедентна“ ситуация няма да има време за объркване, така че не бихме искали отделните продуктови екипи, които притежават всички тези сайтове, да трябва да управляват съдържанието и освободете промените.

Не би било твърде трудно да има едно място, доставящо съдържанието на банера на съобщението (централно управлявана CMS), но е по-трудно да се инжектират HTML, JavaScript и CSS в колекция от различни сайтове наведнъж, без да се изисква куп различни системите да бъдат актуализирани и процесите на внедряване/пускане да бъдат стартирани отделно.

Как може едно глобално съдържание да се добави безпроблемно към множество сайтове?

За демонстрация използвах функция Netlify edge, за да направя примерна крайна точка (източник) на адрес https://wolstenhol.me/api/fake-edge-messages-endpoint, която връща едно от 3 възможни съобщения:

const imagineTheseCameFromACMS = [
  {
    theme: THEMES.emergency,
    text: "Something bad has happened, but all our staff are safe. We are posting hourly updates on our Twitter page.",
    link: "https://twitter.com/philw_",
  },
  {
    theme: THEMES.sombre,
    text: "We are saddened by X and wish Y",
  },
  {
    theme: THEMES.celebratory,
    text: `We won best place to work ${new Date().getFullYear()}!!!11`,
    link: "https://example.com/a-pr-blog-article",
  },
];

Както се казва в кода, представете си, че тази крайна точка идва от CMS като Contentful, Drupal, WordPress и т.н. и се управлява от централен комуникационен екип, който знае да я използва само когато се е случило нещо, което е или много специално (да!), много ужасно ( eeek…), или много важно-в-очите-на-PR (без коментар 💂).

Също така накарах крайната точка да връща хеш на съдържанието на всеки банер, така че да можем уникално да идентифицираме всеки банер. Това ще бъде полезно, когато искаме да направим банерите за отхвърляне и да запомним дали текущият банер е бил отхвърлен или не.

Ето пример за простия JSON отговор, който може да върне:

➜  ~ curl https://wolstenhol.me/api/fake-edge-messages-endpoint
{
  "theme": "celebratory",
  "text": "We won best place to work 2022!!!11",
  "link": "https://example.com/a-pr-blog-article",
  "hash": "0f8a02f8e424c95d9768ecfb8cf5ac5772c6d0ef5706fde819f540375d39d93b"
}

или в по-тъмни времена:

➜  ~ curl https://wolstenhol.me/api/fake-edge-messages-endpoint
{
  "theme": "emergency",
  "text": "Something bad has happened, but all our staff are safe. We are posting updates on our Twitter page.",
  "link": "https://twitter.com/bigcorp",
  "hash": "d98d74b647b5eb02ce1cde89001532df2820758b11c70fa78b53d0e72080aa3e"
}

Но как да превърнете този JSON в HTML, вмъкнат в набор от различни сайтове, всички на различни технологични стекове и свързани към техните собствени CMS, или дори вмъкнати в статично съдържание без изобщо CMS — но всички в един и същ CDN или CDN с подобна функционалност? Влез, ✨ръбът✨…

Какво е „ръбът“? („The Edge“? „The Edge?“ 🤷‍♂️)

Да попитаме Google и Cloudflare:

Edge computing е мрежова философия, насочена към приближаване на изчисленията възможно най-близо до източника на данни, за да се намали забавянето и използването на честотна лента. Казано по-просто, крайното изчисление означава изпълнение на по-малко процеси в облака и преместване на тези процеси на локални места, като например компютър на потребител, IoT устройство или периферен сървър [като CDN]. Довеждането на изчисленията до края на мрежата минимизира количеството комуникация на дълги разстояния, която трябва да се осъществи между клиент и сървър.

В контекста на тази статия edge ще бъде нашият CDN и можем да го програмираме с помощта на инструменти като Cloudflare Workers, Netlify Edge Functions, Lambda@Edge (AWS), Akamai EdgeWorkers или Fastly Compute@Edge и т.н.

Тези инструменти ни позволяват да модифицираме отговора на мрежата, докато преминава през слоя edge/CDN, например модифициране на заглавките или тялото на отговора.

Използвам Cloudflare Workers, за да предложа версия „без JS“ и „без CSS“ на моя личен уебсайт, като начин да проверя как изглежда сайтът без CSS (това ви дава улики дали някой е използвайки семантични HTML елементи или не) и как работи сайтът с блокиран или деактивиран JavaScript (или когато съм написал толкова ужасен JavaScript, че всичко се проваля). Тези работници премахват скриптове или свързани със стила HTML елементи и също така добавят x-robots-tag HTTP хедър, за да попречат на търсачките да индексират тези страници.

Добавяне на банер за глобални съобщения през ръба

Ако мога да премахна CSS и <script> елементи от моя сайт с Cloudflare worker, тогава би трябвало да е лесно да ги използвам и за добавяне в някакво съдържание.

Представете си, че един от уебсайтовете на нашата компания е example.com, запазено име на домейн, което да се използва в документация и примерно съдържание, толкова безопасно, че да си играем с него днес.

Ето обикновено example.com:

И ето example.com, когато се изпълнява през нашия Cloudflare Worker. Ако притежавахме example.com, можехме да поставим работника пред него, така че всеки, който иска example.com, ще види банера. Засега обаче можем да видим банерите, като посетим https://global-banners-from-the-edge.philgw.workers.dev:

Как работи това?

Не притежавам example.com, не знам как се актуализира или на каква технология работи, но добавих банер в горната му част. Ако презаредите страницата, можете да видите другите банери да се появяват произволно, тъй като примерният API връща различно съдържание всеки път.

Бихме могли да направим абсолютно същото нещо за всеки друг сайт, притежаван от нашата компания. Тук съм поставил работника пред един от онези уебсайтове за „най-добрата майка - - създаване на уебсайт“…

...и пред Wikipedia:

Въпросът е, че ако всичките ви сайтове са зад Cloudflare, AWS Cloudfront с Lambda@Edge, Akamai EdgeWorkers, Fastly Compute@Edge, всички хоствани на Netlify или на някоя от другите услуги, предлагащи крайни работници, тогава бихме могли да стартираме един и същ работник на всички тях и един централен източник на банерно съдържание би могъл да актуализира всички тези различни сайтовебез дори да се налага да отваряте CMS или кодовата си база. Това е силата на крайните функции!

Покажи ми кода!

Повечето доставчици на крайни функции предоставят лесен начин за пренаписване на съдържанието на страницата. Това е чудесно за извършване на неща като A/B тестване или банери за бисквитки без силна зависимост от JavaScript. В случая на Cloudflare използваме клас HTMLRewriter.

Ето една опростена версия, в която използваме работник, за да извлечем някои данни и да добавим малко HTML, JavaScript и CSS към страницата, както и да променим Cache-Control заглавката на страницата:

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});
const getData = async () => {
  const response = await fetch(
    "https://wolstenhol.me/api/fake-edge-messages-endpoint"
  );
  const json = await response.json();
  return json;
};
class HeadHandler {
  constructor(data) {
    this.data = data;
    // Please excuse the use of a third-party origin CDN,
    // I wouldn't do this in prod…
    this.styles = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/herald.css" integrity="sha256-zQLpc/AA/o1D8NgVLASianBlbMPs9i4carXMzf/L4mY=" crossorigin="anonymous">`;
    this.scripts = `<script src="https://cdn.jsdelivr.net/npm/[email protected]/herald.js" integrity="sha256-AcoJNZAkXVxpi/5ZW/CXeUadY0z5rEH7h/3OAs5HnTg=" crossorigin="anonymous"></script><script>let key = "${data.hash}"; let savedKey = localStorage.getItem("banner--cta-url"); if(savedKey === key) { document.documentElement.classList.add("banner--hide"); }</script>`;
  }
  element(element) {
    element
      .append(this.styles, { html: true })
      .append(this.scripts, { html: true });
  }
}
class BodyHandler {
  constructor(data) {
    this.data = data;
    this.bannerTemplate = `<announcement-banner data-banner-key="${
      data.hash
    }" data-theme="${data.theme}"> ${
      data.link ? `<a href="/bg${data.link}">` : ""
    } ${data.text} ${
      data.link ? `</a>` : ""
    } <button type="button" data-banner-close>Close</button></announcement-banner>`;
  }
  element(element) {
    element
      .prepend(this.bannerTemplate, { html: true });
  }
}
async function handleRequest() {
  const data = await getData();
  // In real-world usage the below line wouldn't be necessary as
  // we would work with the current request.
  const response = await fetch("https://example.com");
  const transformedResponse = new HTMLRewriter()
    .on("head", new HeadHandler(data))
    .on("body", new BodyHandler(data))
    .transform(response);
  // Don't cache the page so that we can update the banner easily.
  transformedResponse.headers.set("cache-control", "no-store, must-revalidate");
  return transformedRes;
}

Малко за самия банер

Фокусът на тази демонстрация беше възможността за инжектиране на банера във всяка страница, а не самия банер, но ако сте играли с демото, може да забележите няколко неща.

Банерът се появява незабавно заедно с останалото съдържание на страницата и не причинява промяна на оформлението. Това е така, защото HTML кодът за банера пристига заедно с HTML кода на останалата част от страницата. Ако използвахме компонент React/Preact/Vue/Alpine, за да заредим JSON от примерната крайна точка и след това изобразим банер, ще видим промяна на оформлението, тъй като съдържанието на банера ще се появи след като останалата част от страницата е била оформена.

Банерът може да се отхвърли и ако презаредите страницата няколко пъти, няма да видите този банер отново, докато не отхвърлите друг банер. Това е така, защото на всеки банер се дава уникален ключ и уеб компонентът на Zach Leatherman’s ‘herald of the dog’ <announcement-banner> помни последния банер, който е бил отхвърлен, и няма да го покаже отново. Част от JavaScript в <head> на страницата гарантира, че няма трептене на банера, преди да бъде скрит (JavaScript в <head> се изпълнява преди браузърът да изобрази нещо).

Банерът е позициониран в горната част на страницата. Мисля, че това е най-безопасният вариант. Инжектира се веднага след отварящия маркер <body>. Би било изкушаващо да опитате и да го добавите в началото на <main> или веднага след <header>, за да се появи под заглавката на сайта на геройско място, но помислете как ще работи това с генерираното не много семантично маркиране от вашия сайт за бордове за работа или със зависещ от JavaScript SPA, чието цялостно HTML-over-the-wire съдържание е основно <body><div id="root"></div><noscript>Lol, sorry!</noscript></body>.

Банерът има различни теми. Мисля, че това е от съществено значение за една гъвкава система. Ще искате забавна тема за добри новини, червена тема за спешни случаи, когато се случи нещо лошо, и тъжна/мрачна тема за моменти на корпоративен размисъл и съчувствие. Ние управляваме темата чрез атрибут за данни в компонента на банера и някои CSS, които инжектираме в страницата. Можех да работя по-усилено върху този аспект и да използвам по-задълбочен начин за предотвратяване на изтичането на стилове на страници в банера (all: unset или обхватен CSS в уеб компонент), но това не беше фокусът на тази публикация в блога.

Ако трябва да направите леки корекции за различни сайтове, тогава това може да се управлява по няколко начина. Ощипванията на съдържанието могат да се обработват в работния модул, а настройките на стила могат да се управляват чрез коригиране на спецификата на CSS на банера (за да се позволи прилагането на някои специфични за сайта сайтове) или чрез създаване на модифицирани теми, например celebratory--dense за по-компактен празничен банер за използване в уеб приложение, а не в маркетингов сайт.

В заключение

Мисля, че работниците на ръба са СТРАХОТНИ. Можем да пуснем силно кеширани или статични уебсайтове, но да добавим слой динамика отгоре, всички управлявани от нашия CDN доставчик. Можем да оформяме мрежовите заявки, докато преминават през CDN, и да използваме това, за да добавяме съдържание към всеки HTML отговор, независимо от това къде се хоства този сайт или как се поддържа. Дори мъртъв сайт на сървър, до който никой не знае как да получи достъп, все още може да има добавено съдържание към него, стига името на домейна му да е насочено към CDN услугата.

За ситуация, в която множество различни сайтове трябва да показват съдържание от един източник, това е най-ефективното и най-лесното за изпълнение решение, за което мисля, че мога да се сетя, и определено нещо, което с удоволствие бих използвал на практика.

Споделете тази публикация

https://twitter.com/philw_/status/1570680943031234564