Menggunakan Komponen Web Dengan Berikutnya (atau Kerangka Kerja SSR Apa Pun)

Dalam saya sebelumnya pasca kami melihat Tali Sepatu, yang merupakan pustaka komponen dengan rangkaian lengkap komponen UX yang indah, dapat diakses, dan — mungkin secara tidak terduga — dibuat dengan Komponen Web. Ini berarti mereka dapat digunakan dengan kerangka JavaScript apa pun. Sementara interoperabilitas Komponen Web React, saat ini, kurang ideal, ada solusi.

Tetapi satu kelemahan serius dari Komponen Web adalah kurangnya dukungan untuk rendering sisi server (SSR). Ada sesuatu yang disebut Declarative Shadow DOM (DSD) sedang dikerjakan, tetapi dukungan saat ini untuk itu cukup minim, dan itu sebenarnya membutuhkan persetujuan dari server web Anda untuk memancarkan markup khusus untuk DSD. Saat ini ada pekerjaan yang sedang dilakukan untuk Next.js yang saya nantikan untuk dilihat. Tetapi untuk posting ini, kita akan melihat cara mengelola Komponen Web dari kerangka SSR apa pun, seperti Next.js, hari ini.

Kami akhirnya akan melakukan pekerjaan manual yang tidak sepele, dan sedikit merusak kinerja startup halaman kami dalam prosesnya. Kami kemudian akan melihat bagaimana meminimalkan biaya kinerja ini. Tapi jangan salah: solusi ini bukan tanpa pengorbanan, jadi jangan berharap sebaliknya. Selalu ukur dan profil.

Masalahnya

Sebelum kita menyelam, mari kita luangkan waktu sejenak dan benar-benar menjelaskan masalahnya. Mengapa Komponen Web tidak berfungsi dengan baik dengan rendering sisi server?

Kerangka kerja aplikasi seperti Next.js mengambil kode React dan menjalankannya melalui API untuk “merangkai”nya, yang berarti mengubah komponen Anda menjadi HTML biasa. Jadi, pohon komponen React akan dirender di server yang menghosting aplikasi web, dan HTML itu akan dikirim bersama dokumen HTML aplikasi web lainnya ke browser pengguna Anda. Bersamaan dengan HTML ini ada beberapa tag yang memuat React, bersama dengan kode untuk semua komponen React Anda. Saat browser memproses ini tag, React akan merender ulang pohon komponen, dan mencocokkan semuanya dengan HTML SSR yang dikirimkan. Pada titik ini, semua efek akan mulai berjalan, event handler akan terhubung, dan status akan benar-benar… berisi status. Pada titik inilah aplikasi web menjadi interaktif. Proses memproses ulang pohon komponen Anda di klien, dan menghubungkan semuanya disebut hidrasi.

Jadi, apa hubungannya dengan Komponen Web? Nah, ketika Anda membuat sesuatu, ucapkan Tali Sepatu yang sama komponen yang kami kunjungi terakhir kali:


   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.

… Bereaksi (atau jujur Apa pun kerangka kerja JavaScript) akan melihat tag tersebut dan hanya meneruskannya. React (atau Svelte, atau Solid) tidak bertanggung jawab untuk mengubah tag tersebut menjadi tab yang diformat dengan baik. Kode untuk itu tersimpan di dalam kode apa pun yang Anda miliki yang mendefinisikan Komponen Web tersebut. Dalam kasus kami, kode itu ada di perpustakaan Tali Sepatu, tetapi kodenya bisa di mana saja. Yang penting adalah saat kode berjalan.

Biasanya, kode yang mendaftarkan Komponen Web ini akan ditarik ke dalam kode normal aplikasi Anda melalui JavaScript import. Itu berarti kode ini akan berakhir di bundel JavaScript Anda dan dijalankan selama hidrasi yang berarti bahwa, antara pengguna Anda pertama kali melihat HTML SSR dan hidrasi terjadi, tab ini (atau Komponen Web apa pun dalam hal ini) tidak akan membuat konten yang benar . Kemudian, ketika hidrasi terjadi, konten yang tepat akan ditampilkan, kemungkinan menyebabkan konten di sekitar Komponen Web ini bergerak dan sesuai dengan konten yang diformat dengan benar. Ini dikenal sebagai kilasan konten tanpa gaya, atau FOUC. Secara teori, Anda bisa menempelkan markup di antara semua itu tag agar sesuai dengan hasil akhir, tetapi ini hampir tidak mungkin dalam praktiknya, terutama untuk pustaka komponen pihak ketiga seperti Tali Sepatu.

Memindahkan kode registrasi Komponen Web kami

Jadi masalahnya adalah kode untuk membuat Komponen Web melakukan apa yang perlu mereka lakukan tidak akan benar-benar berjalan sampai terjadi hidrasi. Untuk posting ini, kita akan melihat menjalankan kode itu lebih cepat; segera, sebenarnya. Kami akan melihat bundling kustom kode Komponen Web kami, dan secara manual menambahkan skrip langsung ke dokumen kami jadi itu segera berjalan, dan memblokir sisa dokumen sampai selesai. Ini biasanya hal yang mengerikan untuk dilakukan. Inti dari rendering sisi server adalah untuk tidak memblokir halaman kami dari pemrosesan hingga JavaScript kami telah diproses. Tetapi setelah selesai, itu berarti bahwa, karena dokumen pada awalnya merender HTML kita dari server, Komponen Web akan didaftarkan dan akan segera dan serentak memancarkan konten yang tepat.

Dalam kasus kami, kami hanya ingin menjalankan kode pendaftaran Komponen Web kami dalam skrip pemblokiran. Kode ini tidak terlalu besar, dan kami akan berupaya mengurangi kinerja yang dicapai secara signifikan dengan menambahkan beberapa header cache untuk membantu kunjungan berikutnya. Ini bukan solusi sempurna. Pertama kali pengguna menelusuri halaman Anda akan selalu memblokir saat file skrip itu dimuat. Kunjungan berikutnya akan disimpan dengan baik, tetapi pengorbanan ini mungkin tidak layak untuk Anda — e-commerce, siapa saja? Pokoknya, buat profil, ukur, dan buat keputusan yang tepat untuk aplikasi Anda. Selain itu, di masa depan sangat mungkin Next.js akan sepenuhnya mendukung DSD dan Komponen Web.

Memulai

Semua kode yang akan kita lihat ada di repo GitHub ini dan ditempatkan di sini dengan Vercel. Aplikasi web merender beberapa komponen Tali Sepatu bersama dengan teks yang mengubah warna dan konten saat terhidrasi. Anda seharusnya dapat melihat teks berubah menjadi "Terhidrasi," dengan komponen Tali Sepatu sudah dirender dengan benar.

Kode Komponen Web bundling khusus

Langkah pertama kami adalah membuat modul JavaScript tunggal yang mengimpor semua definisi Komponen Web kami. Untuk komponen Tali Sepatu yang saya gunakan, kode saya terlihat seperti ini:

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

Ini memuat definisi untuk dan komponen, dan menimpa beberapa animasi default untuk dialog. Cukup sederhana. Tetapi bagian yang menarik di sini adalah memasukkan kode ini ke dalam aplikasi kita. Kita tidak bisa hanya import modul ini. Jika kita melakukannya, itu akan dibundel ke dalam bundel JavaScript normal kita dan berjalan selama hidrasi. Ini akan menyebabkan FOUC yang kami coba hindari.

Sementara Next.js memang memiliki sejumlah kait webpack ke hal-hal bundel khusus, saya akan menggunakan Tinggal alih-alih. Pertama, instal dengan npm i vite lalu buat file vite.config.js mengajukan. Milik saya terlihat seperti ini:

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

Ini akan membangun file bundel dengan definisi Komponen Web kami di shoelace-dir map. Mari kita pindahkan ke public folder sehingga Next.js akan menyajikannya. Dan kita juga harus melacak nama file yang tepat, dengan hash di ujungnya. Berikut adalah skrip Node yang memindahkan file dan menulis modul JavaScript yang mengekspor konstanta sederhana dengan nama file bundel (ini akan segera berguna):

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

Berikut skrip npm pendamping:

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

Itu harus bekerja. Untuk saya, util/shoelace-bundle-info.js sekarang ada, dan terlihat seperti ini:

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

Memuat skrip

Mari kita masuk ke Next.js _document.js file dan tarik nama file bundel Komponen Web kami:

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

Kemudian kami secara manual membuat tandai di . Inilah keseluruhan saya _document.js file terlihat seperti:

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

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

Dan itu harus berhasil! Pendaftaran Tali Sepatu kami akan dimuat dalam skrip pemblokiran dan segera tersedia saat halaman kami memproses HTML awal.

Meningkatkan kinerja

Kita bisa membiarkan hal-hal apa adanya tapi mari kita tambahkan caching untuk bundel Tali Sepatu kita. Kami akan memberi tahu Next.js untuk membuat bundel Tali Sepatu ini dapat disimpan dalam cache dengan menambahkan entri berikut ke file konfigurasi Next.js kami:

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

Sekarang, pada penelusuran berikutnya ke situs kami, kami melihat bundel Tali Sepatu di-caching dengan baik!

Panel Sumber DevTools terbuka dan menampilkan bundel Tali Sepatu yang dimuat.
Menggunakan Komponen Web Dengan Berikutnya (atau Kerangka Kerja SSR Apa Pun)

Jika bundel Tali Sepatu kami pernah berubah, nama file akan berubah (melalui :hash bagian dari properti sumber di atas), browser akan menemukan bahwa file tersebut tidak memiliki cache, dan hanya akan memintanya segar dari jaringan.

Membungkus

Ini mungkin tampak seperti banyak pekerjaan manual; dan itu adalah. Sangat disayangkan Komponen Web tidak menawarkan dukungan out-of-the-box yang lebih baik untuk rendering sisi server.

Tetapi kita tidak boleh melupakan manfaat yang mereka berikan: senang dapat menggunakan komponen UX berkualitas yang tidak terikat pada kerangka kerja tertentu. Senang juga bisa bereksperimen dengan kerangka kerja baru, seperti Padat, tanpa perlu menemukan (atau meretas bersama) semacam tab, modal, pelengkapan otomatis, atau komponen apa pun.

Stempel Waktu:

Lebih dari Trik CSS