Uporaba spletnih komponent z Next (ali katerim koli ogrodjem SSR)

V moji Prejšnja objava pogledali smo Shoelace, ki je knjižnica komponent s polno zbirko komponent UX, ki so lepe, dostopne in – morda nepričakovano – zgrajene z Spletne komponente. To pomeni, da jih je mogoče uporabljati s katerim koli ogrodjem JavaScript. Medtem ko interoperabilnost Reactove spletne komponente trenutno ni idealna, obstajajo rešitve.

Toda ena resna pomanjkljivost spletnih komponent je njihovo trenutno pomanjkanje podpore za upodabljanje na strani strežnika (SSR). V delu je nekaj, kar se imenuje Declarative Shadow DOM (DSD), vendar je trenutna podpora zanj precej minimalna in dejansko zahteva nakup vašega spletnega strežnika za oddajanje posebnih oznak za DSD. Trenutno poteka delo za Naprej.js ki se jih veselim videti. Toda za to objavo si bomo ogledali, kako upravljati spletne komponente iz katerega koli ogrodja SSR, kot je Next.js, danes.

Na koncu bomo opravili precej ročnega dela in nekoliko škodujejo zagonski zmogljivosti naše strani v procesu. Nato bomo preučili, kako zmanjšati te stroške delovanja. A da ne bo pomote: ta rešitev ni brez kompromisov, zato ne pričakujte drugače. Vedno izmerite in profilirajte.

Težava

Preden se poglobimo, si vzemimo trenutek in dejansko razložimo težavo. Zakaj spletne komponente ne delujejo dobro z upodabljanjem na strani strežnika?

Aplikacijska ogrodja, kot je Next.js, vzamejo kodo React in jo zaženejo prek API-ja, da jo v bistvu "stringificirajo", kar pomeni, da spremeni vaše komponente v navaden HTML. Tako bo drevo komponente React upodobljeno na strežniku, ki gosti spletno aplikacijo, in ta HTML bo poslan skupaj s preostalim dokumentom HTML spletne aplikacije v brskalnik vašega uporabnika. Poleg tega HTML je nekaj oznake, ki naložijo React, skupaj s kodo za vse vaše komponente React. Ko jih brskalnik obdela oznak, bo React ponovno upodobil drevo komponent in stvari uskladil s HTML-jem SSR, ki je bil poslan. Na tej točki se bodo vsi učinki začeli izvajati, upravljalniki dogodkov se bodo povezali in stanje bo dejansko ... vsebovalo stanje. Na tej točki postane spletna aplikacija interaktivno. Pokliče se postopek ponovne obdelave vašega drevesa komponent na odjemalcu in ožičenja vsega hidracijo.

Torej, kaj ima to opraviti s spletnimi komponentami? No, ko nekaj upodabljaš, reci isto Shoelace komponento, ki smo jo obiskali prejšnjič:


   General 
   Custom 
   Advanced 
   Disabled 

  This is the general tab panel.
  This is the custom tab panel.
  This is the advanced tab panel.
  This is a disabled tab panel.

…Reagirajte (ali iskreno kaj JavaScript framework) bo videl te oznake in jih preprosto posredoval. React (ali Svelte ali Solid) niso odgovorni za spreminjanje teh oznak v lepo oblikovane zavihke. Koda za to je skrita znotraj katere koli kode, ki definira te spletne komponente. V našem primeru je ta koda v knjižnici Shoelace, vendar je koda lahko kjer koli. Kar je pomembno je ko se koda izvaja.

Običajno bo koda, ki registrira te spletne komponente, potegnjena v običajno kodo vaše aplikacije prek JavaScripta import. To pomeni, da se bo ta koda znašla v vašem svežnju JavaScript in se izvajala med hidracijo, kar pomeni, da med tem, ko bo vaš uporabnik prvič videl HTML SSR, in hidracijo, ti zavihki (ali katera koli spletna komponenta) ne bodo upodobili pravilne vsebine . Potem, ko pride do hidracije, se bo prikazala ustrezna vsebina, kar bo verjetno povzročilo premikanje vsebine okoli teh spletnih komponent in prilagajanje pravilno oblikovani vsebini. To je znano kot a blisk neslogovne vsebine, ali FOUC. Teoretično bi lahko med vse to vstavili oznako oznake za ujemanje s končnim izhodom, vendar je to v praksi skoraj nemogoče, zlasti za knjižnico komponent tretjih oseb, kot je Shoelace.

Premikanje naše registracijske kode spletne komponente

Težava je torej v tem, da se koda, ki spletnim komponentam omogoči, da naredijo, kar morajo, dejansko ne deluje, dokler ne pride do hidracije. Za to objavo si bomo ogledali izvajanje te kode prej; pravzaprav takoj. Ogledali si bomo združevanje naše kode spletne komponente po meri in ročno dodajanje skripta neposredno v naš dokument zato se zažene takoj in blokira preostanek dokumenta, dokler se ne zažene. To je običajno grozna stvar. Bistvo upodabljanja na strani strežnika je, da ne blokirajte obdelavo naše strani, dokler naš JavaScript ni obdelan. Toda ko je končano, to pomeni, da bodo spletne komponente registrirane in bodo takoj in sinhrono oddajale pravo vsebino, ker dokument prvotno upodablja naš HTML iz strežnika.

V našem primeru smo samo želimo zagnati našo registracijsko kodo spletne komponente v blokirnem skriptu. Ta koda ni ogromna in poskušali bomo občutno zmanjšati udarce v zmogljivosti z dodajanjem nekaj glav predpomnilnika za pomoč pri naslednjih obiskih. To ni popolna rešitev. Ko uporabnik prvič brska po vaši strani, bo vedno blokiran, medtem ko se ta skriptna datoteka nalaga. Naslednji obiski se bodo lepo shranili v predpomnilnik, toda ta kompromis morda ne izvedljivo za vas — e-trgovina, kdo? Kakorkoli že, profilirajte, izmerite in se pravilno odločite za svojo aplikacijo. Poleg tega je v prihodnosti povsem možno, da bo Next.js v celoti podpiral DSD in spletne komponente.

Prvi koraki

Vsa koda, ki si jo bomo ogledali, je notri ta repo GitHub in tukaj razporejen z Vercelom. Spletna aplikacija upodablja nekatere komponente Shoelace skupaj z besedilom, ki spremeni barvo in vsebino ob hidraciji. Morali bi videti, kako se besedilo spremeni v »Hydrated«, pri čemer se komponente Shoelace že pravilno upodabljajo.

Koda spletne komponente za povezovanje po meri

Naš prvi korak je ustvariti en sam modul JavaScript, ki uvozi vse definicije naših spletnih komponent. Za komponente Shoelace, ki jih uporabljam, je moja koda videti takole:

import { setDefaultAnimation } from "@shoelace-style/shoelace/dist/utilities/animation-registry";

import "@shoelace-style/shoelace/dist/components/tab/tab.js";
import "@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js";
import "@shoelace-style/shoelace/dist/components/tab-group/tab-group.js";

import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";

setDefaultAnimation("dialog.show", {
  keyframes: [
    { opacity: 0, transform: "translate3d(0px, -20px, 0px)" },
    { opacity: 1, transform: "translate3d(0px, 0px, 0px)" },
  ],
  options: { duration: 250, easing: "cubic-bezier(0.785, 0.135, 0.150, 0.860)" },
});
setDefaultAnimation("dialog.hide", {
  keyframes: [
    { opacity: 1, transform: "translate3d(0px, 0px, 0px)" },
    { opacity: 0, transform: "translate3d(0px, 20px, 0px)" },
  ],
  options: { duration: 250, easing: "cubic-bezier(0.785, 0.135, 0.150, 0.860)" },
});

Naloži definicije za in komponente in preglasi nekatere privzete animacije za pogovorno okno. Dovolj preprosto. Ampak zanimiv del tukaj je vnos te kode v našo aplikacijo. mi ne more preprosto import ta modul. Če bi to storili, bi se povezal v naše običajne svežnje JavaScript in bi deloval med hidracijo. To bi povzročilo FOUC, ki se mu poskušamo izogniti.

Medtem ko ima Next.js številne kaveljčke webpack za združevanje stvari po meri, bom uporabil Živi namesto tega. Najprej ga namestite z npm i vite in nato ustvarite a vite.config.js mapa. Moj izgleda takole:

import { defineConfig } from "vite";
import path from "path";

export default defineConfig({
  build: {
    outDir: path.join(__dirname, "./shoelace-dir"),
    lib: {
      name: "shoelace",
      entry: "./src/shoelace-bundle.js",
      formats: ["umd"],
      fileName: () => "shoelace-bundle.js",
    },
    rollupOptions: {
      output: {
        entryFileNames: `[name]-[hash].js`,
      },
    },
  },
});

To bo ustvarilo datoteko svežnja z našimi definicijami spletne komponente v shoelace-dir mapo. Prestavimo ga na public mapo, tako da jo bo stregel Next.js. Prav tako bi morali spremljati natančno ime datoteke, z zgoščeno vrednostjo na koncu. Tukaj je skript Node, ki premakne datoteko in zapiše modul JavaScript, ki izvozi preprosto konstanto z imenom datoteke svežnja (to bo kmalu prišlo prav):

const fs = require("fs");
const path = require("path");

const shoelaceOutputPath = path.join(process.cwd(), "shoelace-dir");
const publicShoelacePath = path.join(process.cwd(), "public", "shoelace");

const files = fs.readdirSync(shoelaceOutputPath);

const shoelaceBundleFile = files.find(name => /^shoelace-bundle/.test(name));

fs.rmSync(publicShoelacePath, { force: true, recursive: true });

fs.mkdirSync(publicShoelacePath, { recursive: true });
fs.renameSync(path.join(shoelaceOutputPath, shoelaceBundleFile), path.join(publicShoelacePath, shoelaceBundleFile));
fs.rmSync(shoelaceOutputPath, { force: true, recursive: true });

fs.writeFileSync(path.join(process.cwd(), "util", "shoelace-bundle-info.js"), `export const shoelacePath = "/shoelace/${shoelaceBundleFile}";`);

Tukaj je spremljevalni skript npm:

"bundle-shoelace": "vite build && node util/process-shoelace-bundle",

To bi moralo delovati. Zame, util/shoelace-bundle-info.js zdaj obstaja in izgleda takole:

export const shoelacePath = "/shoelace/shoelace-bundle-a6f19317.js";

Nalaganje skripta

Pojdimo v Next.js _document.js in povlecite ime naše datoteke paketa spletne komponente:

import { shoelacePath } from "../util/shoelace-bundle-info";

Nato ročno upodabljamo a označite v . Tukaj je moj celoten _document.js datoteka izgleda tako:

import { Html, Head, Main, NextScript } from "next/document";
import { shoelacePath } from "../util/shoelace-bundle-info";

export default function Document() {
  return (
    
      
        
      
      
        
); }

In to bi moralo delovati! Naša registracija Shoelace se bo naložila v blokirni skript in bo na voljo takoj, ko bo naša stran obdelala začetni HTML.

Izboljšanje učinkovitosti

Lahko bi pustili stvari takšne, kot so, vendar dodajmo predpomnjenje za naš sveženj Shoelace. Naročili bomo Next.js, naj omogoči predpomnjenje teh svežnjev Shoelace z dodajanjem naslednjega vnosa v našo konfiguracijsko datoteko Next.js:

async headers() {
  return [
    {
      source: "/shoelace/shoelace-bundle-:hash.js",
      headers: [
        {
          key: "Cache-Control",
          value: "public,max-age=31536000,immutable",
        },
      ],
    },
  ];
}

Zdaj, ob naslednjih brskanjih po našem spletnem mestu, vidimo, da se sveženj Shoelace lepo predpomni!

Plošča DevTools Sources je odprta in prikazuje naložen sveženj Shoelace.
Uporaba spletnih komponent z Next (ali katerim koli ogrodjem SSR)

Če se naš sveženj Shoelace kdaj spremeni, se bo spremenilo ime datoteke (prek :hash del iz zgornje lastnosti vira), bo brskalnik ugotovil, da te datoteke nima predpomnjene, in jo bo preprosto zahteval svežo iz omrežja.

Zavijanje

To se je morda zdelo kot veliko ročnega dela; in bilo je. Na žalost spletne komponente ne ponujajo boljše podpore za upodabljanje na strani strežnika.

Ne smemo pa pozabiti na prednosti, ki jih ponujajo: lepo je, da lahko uporabljamo kakovostne komponente UX, ki niso vezane na določen okvir. Prav lepo je, da lahko eksperimentirate s popolnoma novimi okviri, kot je npr Masivna, ne da bi morali poiskati (ali vdreti skupaj) nekakšen zavihek, modal, samodokončanje ali katero koli komponento.

Časovni žig:

Več od Triki CSS