Veebikomponentide kasutamine koos Nextiga (või mis tahes SSR-raamistikuga)

Minu eelmine postitus vaatasime Shoelace'i, mis on komponentide kogu koos täieliku UX-i komponentide komplektiga, mis on ilusad, juurdepääsetavad ja – võib-olla ootamatult – ehitatud Veebikomponendid. See tähendab, et neid saab kasutada mis tahes JavaScripti raamistikuga. Kuigi Reacti veebikomponendi koostalitlusvõime ei ole praegu ideaalne, on lahendusi.

Kuid üks veebikomponentide tõsine puudus on nende praegune serveripoolse renderduse (SSR) toe puudumine. Töös on midagi, mida nimetatakse deklaratiivseks vari-DOM-iks (DSD), kuid praegune tugi sellele on üsna minimaalne ja see nõuab DSD jaoks spetsiaalse märgistuse väljastamiseks teie veebiserveri sisseostu. Praegu tehakse tööd Järgmine.js mida ootan pikisilmi. Kuid selle postituse jaoks vaatame, kuidas hallata veebikomponente mis tahes SSR-i raamistikust, nagu Next.js, täna.

Teeme lõpuks mittetriviaalse hulga käsitsitööd ja veidi kahjustades protsessi käigus meie lehe käivitusjõudlust. Seejärel vaatame, kuidas neid jõudluskulusid minimeerida. Kuid ärge tehke viga: see lahendus ei ole kompromissideta, nii et ärge oodake teisiti. Alati mõõta ja profileerida.

Probleem

Enne kui sukeldume, võtame veidi aega ja selgitame probleemi. Miks ei tööta veebikomponendid serveripoolse renderdamisega hästi?

Rakendusraamistikud, nagu Next.js, võtavad Reacti koodi ja käitavad seda API kaudu, et seda sisuliselt "stringifitseerida", mis tähendab, et see muudab teie komponendid lihtsaks HTML-iks. Seega renderdatakse Reacti komponendi puu veebirakendust hostivas serveris ja see HTML saadetakse koos ülejäänud veebirakenduse HTML-dokumendiga teie kasutaja brauserisse. Selle HTML-i kõrval on mõned sildid, mis laadivad Reacti, koos kõigi teie Reacti komponentide koodiga. Kui brauser neid töötleb sildid, renderdab React komponentipuu uuesti ja sobitab asjad alla saadetud SSR-i HTML-iga. Sel hetkel hakkavad kõik efektid töötama, sündmuste käitlejad ühendavad juhtmestiku ja olek sisaldab tegelikult olekut. Just sel hetkel muutub veebirakendus interaktiivne. Kutsutakse välja teie komponentide puu uuesti töötlemine kliendis ja kõik juhtmestikud hüdratsioon.

Niisiis, mis on sellel pistmist veebikomponentidega? Noh, kui te midagi renderdate, öelge sama Kingapael komponent, mida külastasime viimane aeg:


   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.

…Reageerige (või ausalt mistahes JavaScripti raamistik) näeb neid silte ja edastab need lihtsalt edasi. React (või Svelte või Solid) ei vastuta nende siltide kenasti vormindatud vahekaartideks muutmise eest. Selle kood on peidetud mis tahes koodi sisse, mis need veebikomponendid määratleb. Meie puhul on see kood kingapaela teegis, kuid kood võib olla kõikjal. Tähtis on kui kood töötab.

Tavaliselt tõmmatakse neid veebikomponente registreeriv kood JavaScripti kaudu teie rakenduse tavalisse koodi import. See tähendab, et see kood kerkib teie JavaScripti kimpu ja käivitatakse hüdratatsiooni ajal, mis tähendab, et SSR-i HTML-i esmakordse nägemise ja hüdratatsiooni toimumise vahelisel ajal ei renderda need vahekaardid (või mis tahes veebikomponent) õiget sisu. . Seejärel, kui hüdratsioon toimub, kuvatakse õige sisu, mis tõenäoliselt põhjustab nende veebikomponentide ümber oleva sisu liikumise ja sobitub õigesti vormindatud sisuga. Seda tuntakse kui a stiilita sisu välkvõi FOUC. Teoreetiliselt võite nende kõigi vahele lisada märgistuse sildid, mis sobiksid valmis väljundiga, kuid see on praktikas täiesti võimatu, eriti kolmanda osapoole komponentide teegi puhul, nagu Shoelace.

Veebikomponendi registreerimiskoodi teisaldamine

Seega on probleem selles, et kood, mis paneb veebikomponendid tegema seda, mida nad peavad tegema, ei tööta tegelikult enne, kui hüdratatsioon toimub. Selle postituse jaoks vaatame selle koodi varem käivitamise võimalust; kohe, tegelikult. Vaatleme oma veebikomponendi koodi kohandatud komplekteerimist ja skripti käsitsi lisamist otse meie dokumendile. nii et see käivitub kohe ja blokeerib ülejäänud dokumendi seni, kuni see töötab. See on tavaliselt kohutav asi. Serveripoolse renderdamise mõte seisneb selles mitte blokeerige meie lehe töötlemine, kuni meie JavaScript on töödeldud. Kuid kui see on tehtud, tähendab see, et kuna dokument renderdab algselt meie HTML-i serverist, siis veebikomponendid registreeritakse ja väljastavad kohe ja sünkroonselt õiget sisu.

Meie puhul oleme lihtsalt soovivad käivitada meie veebikomponendi registreerimiskoodi blokeerivas skriptis. See kood ei ole tohutu ja püüame jõudlust oluliselt vähendada, lisades mõned vahemälu päised, mis aitavad järgmistel külastustel. See pole ideaalne lahendus. Kui kasutaja esimest korda teie lehte sirvib, blokeeritakse see skriptifaili laadimise ajal alati. Järgnevad külastused salvestatakse ilusti vahemällu, kuid see kompromiss ei pruugi olla teie jaoks teostatav — e-kaubandus, keegi? Igatahes profiilige, mõõtke ja tehke oma rakenduse jaoks õige otsus. Lisaks on tulevikus täiesti võimalik, et Next.js toetab täielikult DSD-d ja veebikomponente.

Alustamine

Kogu kood, mida me vaatame, on sees see GitHubi repo ja siin koos Verceliga. Veebirakendus renderdab mõned kingapaela komponendid koos tekstiga, mis muudab hüdratatsiooni ajal värvi ja sisu. Peaksite nägema, et tekst muutub olekuks „Hydrated”, kusjuures kingapaela komponendid renderdatakse juba korralikult.

Kohandatud komplekteerimise veebikomponendi kood

Meie esimene samm on luua üks JavaScripti moodul, mis impordib kõik meie veebikomponentide määratlused. Kasutatavate kingapaela komponentide puhul näeb minu kood välja järgmine:

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)" },
});

See laadib definitsioonid ja komponendid ja alistab mõned dialoogi vaikeanimatsioonid. Piisavalt lihtne. Kuid huvitav osa siin on selle koodi lisamine meie rakendusse. Meie ei saa lihtsalt import see moodul. Kui me seda teeksime, koondataks see meie tavalistesse JavaScripti kimpudesse ja töötaks hüdratsiooni ajal. See põhjustaks FOUC-i, mida püüame vältida.

Kuigi Next.js-il on kohandatud asjade komplekteerimiseks mitmeid veebipaketi konkse, kasutan ma neid Elab selle asemel. Esiteks installige see koos npm i vite ja seejärel loo a vite.config.js faili. Minu oma näeb välja selline:

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`,
      },
    },
  },
});

See loob meie veebikomponentide määratlustega pakettfaili shoelace-dir kausta. Liigutame selle üle public kausta, et Next.js seda teenindaks. Samuti peaksime jälgima faili täpset nime, mille lõpus on räsi. Siin on sõlme skript, mis liigutab faili ja kirjutab JavaScripti mooduli, mis ekspordib lihtsa konstandi koos komplektifaili nimega (see on peagi kasulik):

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}";`);

Siin on npm-i kaasskript:

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

See peaks töötama. Minule, util/shoelace-bundle-info.js on praegu olemas ja näeb välja selline:

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

Skripti laadimine

Läheme edasi faili Next.js _document.js faili ja tõmmake sisse meie veebikomponendi komplekti faili nimi:

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

Seejärel renderdame käsitsi a märgendis . Siin on minu kogu _document.js fail näeb välja selline:

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

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

Ja see peaks töötama! Meie kingapaela registreerimine laaditakse blokeerivas skriptis ja on kohe saadaval, kui meie leht töötleb esialgset HTML-i.

Toimivuse parandamine

Võiksime jätta asjad nii, nagu nad on, kuid lisame oma kingapaela komplekti vahemällu. Anname rakendusele Next.js käsu muuta need Shoelace'i komplektid vahemällu salvestatavaks, lisades meie Next.js konfiguratsioonifaili järgmise kirje:

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

Nüüd, meie saidi järgmistel sirvimisel näeme, et Shoelace'i kimp on kenasti vahemällu salvestatud!

DevToolsi allikate paneel on avatud ja kuvab laaditud kingapaela komplekti.
Veebikomponentide kasutamine koos Nextiga (või mis tahes SSR-raamistikuga)

Kui meie kingapaela komplekt kunagi muutub, muutub faili nimi (läbi :hash osa ülaltoodud lähteatribuudist), leiab brauser, et tal pole seda faili vahemällu salvestatud, ja taotleb seda lihtsalt võrgust värskena.

Pakke kuni

See võis tunduda suure käsitsitööna; ja oligi. On kahetsusväärne, et veebikomponendid ei paku serveripoolsele renderdamisele paremat kohest tuge.

Kuid me ei tohiks unustada nende pakutavaid eeliseid: on tore, et saame kasutada kvaliteetseid UX-i komponente, mis ei ole seotud konkreetse raamistikuga. Tore on katsetada uhiuute raamistikega, näiteks Tahke, ilma et oleks vaja leida (või kokku häkkida) mingit vahekaarti, modaalset, automaatset täitmise või mis tahes komponenti.

Ajatempel:

Veel alates CSSi trikid