नेक्स्ट (या कोई SSR फ्रेमवर्क) के साथ वेब कंपोनेंट्स का उपयोग करना

मेरे में पिछले पोस्ट हमने शूलेस को देखा, जो एक घटक पुस्तकालय है जिसमें यूएक्स घटकों का एक पूर्ण सूट है जो सुंदर, सुलभ, और - शायद अप्रत्याशित रूप से - के साथ बनाया गया है वेब घटक. इसका मतलब है कि उनका उपयोग किसी भी जावास्क्रिप्ट ढांचे के साथ किया जा सकता है। जबकि रिएक्ट का वेब कंपोनेंट इंटरऑपरेबिलिटी वर्तमान में आदर्श से कम है, उपाय हैं.

लेकिन वेब घटकों की एक गंभीर कमी सर्वर-साइड रेंडरिंग (एसएसआर) के लिए उनके समर्थन की वर्तमान कमी है। कार्यों में डिक्लेरेटिव शैडो डोम (डीएसडी) नाम की कोई चीज है, लेकिन इसके लिए वर्तमान समर्थन बहुत कम है, और डीएसडी के लिए विशेष मार्कअप उत्सर्जित करने के लिए वास्तव में इसे आपके वेब सर्वर से बाय-इन की आवश्यकता होती है। फ़िलहाल इसके लिए काम किया जा रहा है Next.js कि मैं देखने के लिए उत्सुक हूँ। लेकिन इस पोस्ट के लिए, हम देखेंगे कि किसी भी SSR ढांचे से वेब घटकों को कैसे प्रबंधित किया जाए, जैसे Next.js, आज.

हम मैन्युअल काम की एक गैर-तुच्छ राशि को समाप्त कर देंगे, और थोड़ा प्रक्रिया में हमारे पेज के स्टार्टअप प्रदर्शन को नुकसान पहुंचा रहा है। फिर हम देखेंगे कि इन प्रदर्शन लागतों को कैसे कम किया जाए। लेकिन कोई गलती न करें: यह समाधान ट्रेडऑफ़ के बिना नहीं है, इसलिए अन्यथा अपेक्षा न करें। हमेशा मापें और प्रोफाइल करें।

समस्या

इससे पहले कि हम गोता लगाएँ, आइए एक पल लें और वास्तव में समस्या की व्याख्या करें। वेब घटक सर्वर-साइड रेंडरिंग के साथ अच्छी तरह से काम क्यों नहीं करते हैं?

Next.js जैसे एप्लिकेशन फ्रेमवर्क रिएक्ट कोड लेते हैं और इसे एक एपीआई के माध्यम से अनिवार्य रूप से "स्ट्रिंग" करने के लिए चलाते हैं, जिसका अर्थ है कि यह आपके घटकों को सादे HTML में बदल देता है। तो रिएक्ट कंपोनेंट ट्री वेब ऐप को होस्ट करने वाले सर्वर पर रेंडर करेगा, और वह HTML वेब ऐप के बाकी HTML डॉक्यूमेंट के साथ आपके यूजर के ब्राउजर पर भेज दिया जाएगा। इसके साथ ही HTML कुछ हैं आपके सभी रिएक्ट घटकों के लिए कोड के साथ रिएक्ट लोड करने वाले टैग। जब कोई ब्राउज़र इन्हें संसाधित करता है टैग, रिएक्ट घटक ट्री को फिर से प्रस्तुत करेगा, और नीचे भेजे गए SSR'd HTML के साथ चीजों का मिलान करेगा। इस बिंदु पर, सभी प्रभाव चलना शुरू हो जाएंगे, ईवेंट हैंडलर तार-तार हो जाएंगे, और राज्य वास्तव में… राज्य को समाहित करेगा। यह इस बिंदु पर है कि वेब ऐप बन जाता है इंटरैक्टिव. क्लाइंट पर आपके कंपोनेंट ट्री को फिर से प्रोसेस करने और सब कुछ वायर करने की प्रक्रिया कहलाती है जलयोजन.

तो, इसका वेब घटकों से क्या लेना-देना है? ठीक है, जब आप कुछ प्रस्तुत करते हैं, तो वही शूलेस कहें घटक जो हमने देखा पिछली बार:


   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.

…प्रतिक्रिया (या ईमानदारी से .) कोई जावास्क्रिप्ट फ्रेमवर्क) उन टैग्स को देखेगा और बस उन्हें पास कर देगा। रिएक्ट (या स्वेल्टे, या सॉलिड) उन टैग्स को अच्छी तरह से फॉर्मेट किए गए टैब में बदलने के लिए जिम्मेदार नहीं हैं। उसके लिए कोड आपके पास जो भी कोड है, वह उन वेब घटकों को परिभाषित करता है। हमारे मामले में, वह कोड Shoelace लाइब्रेरी में है, लेकिन कोड कहीं भी हो सकता है। महत्वपूर्ण क्या है जब कोड चलता है.

आम तौर पर, इन वेब घटकों को पंजीकृत करने वाला कोड जावास्क्रिप्ट के माध्यम से आपके एप्लिकेशन के सामान्य कोड में खींच लिया जाएगा import. इसका मतलब है कि यह कोड आपके जावास्क्रिप्ट बंडल में आ जाएगा और हाइड्रेशन के दौरान निष्पादित होगा, जिसका अर्थ है कि, आपके उपयोगकर्ता के बीच पहली बार SSR'd HTML और हाइड्रेशन हो रहा है, ये टैब (या उस मामले के लिए कोई वेब घटक) सही सामग्री प्रस्तुत नहीं करेंगे . फिर, जब हाइड्रेशन होता है, तो उचित सामग्री प्रदर्शित होगी, जिससे इन वेब घटकों के आसपास की सामग्री इधर-उधर हो सकती है और ठीक से स्वरूपित सामग्री में फिट हो सकती है। यह एक के रूप में जाना जाता है अनस्टाइल सामग्री का फ्लैश, या एफओयूसी। सिद्धांत रूप में, आप उन सभी के बीच मार्कअप चिपका सकते हैं तैयार आउटपुट से मेल खाने के लिए टैग, लेकिन व्यवहार में यह सब असंभव है, खासकर शूलेस जैसे तीसरे पक्ष के घटक पुस्तकालय के लिए।

हमारे वेब घटक पंजीकरण कोड को स्थानांतरित करना

तो समस्या यह है कि वेब घटकों को वह करने के लिए कोड जो उन्हें करने की ज़रूरत है, वास्तव में हाइड्रेशन होने तक नहीं चलेगा। इस पोस्ट के लिए, हम जल्द ही उस कोड को चलाने पर विचार करेंगे; तुरंत, वास्तव में। हम अपने वेब कंपोनेंट कोड को कस्टम बंडल करते हुए देखेंगे, और मैन्युअल रूप से सीधे हमारे दस्तावेज़ में एक स्क्रिप्ट जोड़ेंगे इसलिए यह तुरंत चलता है, और बाकी दस्तावेज़ को तब तक ब्लॉक करता है जब तक कि वह ऐसा न कर दे। ऐसा करना सामान्य रूप से एक भयानक बात है। सर्वर-साइड रेंडरिंग का संपूर्ण बिंदु है: नहीं हमारे पेज को तब तक प्रोसेस करने से रोकें जब तक कि हमारी जावास्क्रिप्ट प्रोसेस न हो जाए। लेकिन एक बार हो जाने के बाद, इसका मतलब है कि, जैसा कि दस्तावेज़ शुरू में सर्वर से हमारे HTML को प्रस्तुत कर रहा है, वेब घटक पंजीकृत हो जाएंगे और दोनों तुरंत और समकालिक रूप से सही सामग्री का उत्सर्जन करेंगे।

हमारे मामले में, हम केवल हमारे वेब घटक पंजीकरण कोड को एक अवरुद्ध स्क्रिप्ट में चलाने के लिए देख रहे हैं। यह कोड बहुत बड़ा नहीं है, और हम बाद की यात्राओं में सहायता के लिए कुछ कैश हेडर जोड़कर प्रदर्शन हिट को काफी कम करने की कोशिश करेंगे। यह सही समाधान नहीं है। जब कोई उपयोगकर्ता पहली बार आपके पृष्ठ को ब्राउज़ करता है तो वह स्क्रिप्ट फ़ाइल लोड होने पर हमेशा अवरुद्ध हो जाएगा। बाद की विज़िट अच्छी तरह से कैश हो जाएंगी, लेकिन यह ट्रेडऑफ़ शायद नहीं आपके लिए संभव हो - ई-कॉमर्स, कोई भी? वैसे भी, अपने ऐप के लिए प्रोफ़ाइल, माप और सही निर्णय लें। इसके अलावा, भविष्य में यह पूरी तरह से संभव है कि Next.js पूरी तरह से DSD और वेब घटकों का समर्थन करेगा।

आइए शुरू करते हैं

हम जो भी कोड देख रहे हैं वह सभी में है इस GitHub रेपो और Vercel के साथ यहां तैनात किया गया. वेब ऐप कुछ शूलेस घटकों को टेक्स्ट के साथ प्रस्तुत करता है जो हाइड्रेशन पर रंग और सामग्री को बदलता है। आप शूलेस घटकों के साथ पहले से ही ठीक से प्रस्तुत करने के साथ टेक्स्ट को "हाइड्रेटेड" में बदलने में सक्षम होना चाहिए।

कस्टम बंडलिंग वेब घटक कोड

हमारा पहला कदम एक एकल जावास्क्रिप्ट मॉड्यूल बनाना है जो हमारी सभी वेब घटक परिभाषाओं को आयात करता है। मेरे द्वारा उपयोग किए जा रहे शूलेस घटकों के लिए, मेरा कोड इस तरह दिखता है:

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

यह के लिए परिभाषाओं को लोड करता है और घटक, और संवाद के लिए कुछ डिफ़ॉल्ट एनिमेशन को ओवरराइड करता है। काफी सरल। लेकिन यहां दिलचस्प टुकड़ा इस कोड को हमारे आवेदन में प्राप्त कर रहा है। हम नही सकता केवल import यह मॉड्यूल। अगर हमने ऐसा किया, तो यह हमारे सामान्य जावास्क्रिप्ट बंडलों में बंडल हो जाएगा और हाइड्रेशन के दौरान चलेगा। यह उस FOUC का कारण बनेगा जिससे हम बचने का प्रयास कर रहे हैं।

जबकि Next.js में कस्टम बंडल चीजों के लिए कई वेबपैक हुक हैं, मैं इसका उपयोग करूंगा रहता है बजाय। सबसे पहले, इसे स्थापित करें npm i vite और फिर एक बनाएँ vite.config.js फ़ाइल। मेरा ऐसा दिखता है:

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

यह हमारी वेब घटक परिभाषाओं के साथ एक बंडल फ़ाइल का निर्माण करेगा shoelace-dir फ़ोल्डर। आइए इसे आगे ले जाएं public फ़ोल्डर ताकि Next.js इसकी सेवा करेगा। और हमें फ़ाइल के सटीक नाम का भी ध्यान रखना चाहिए, इसके अंत में हैश होना चाहिए। यहां एक नोड स्क्रिप्ट है जो फ़ाइल को स्थानांतरित करती है और एक जावास्क्रिप्ट मॉड्यूल लिखती है जो बंडल फ़ाइल के नाम के साथ एक साधारण स्थिरांक निर्यात करती है (यह जल्द ही काम में आ जाएगी):

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

यहाँ एक साथी npm स्क्रिप्ट है:

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

यह काम करना चाहिए। मेरे लिए, util/shoelace-bundle-info.js अब मौजूद है, और इस तरह दिखता है:

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

स्क्रिप्ट लोड हो रहा है

चलिए Next.js में चलते हैं _document.js फ़ाइल और हमारे वेब घटक बंडल फ़ाइल के नाम पर खींचो:

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

फिर हम मैन्युअल रूप से प्रस्तुत करते हैं a में टैग करें . यहाँ मेरा पूरा क्या है _document.js फ़ाइल की तरह दिखता है:

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

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

और वह काम करना चाहिए! हमारा शूलेस पंजीकरण एक अवरुद्ध स्क्रिप्ट में लोड होगा और तुरंत उपलब्ध होगा क्योंकि हमारा पृष्ठ प्रारंभिक HTML को संसाधित करता है।

प्रदर्शन सुधारना

हम चीजों को वैसे ही छोड़ सकते हैं जैसे वे हैं लेकिन चलिए अपने Shoelace बंडल के लिए कैशिंग जोड़ते हैं। हम Next.js को अपनी Next.js कॉन्फ़िगरेशन फ़ाइल में निम्न प्रविष्टि जोड़कर इन Shoelace बंडलों को कैश करने योग्य बनाने के लिए कहेंगे:

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

अब, हमारी साइट पर बाद में ब्राउज़ करने पर, हम शूलेस बंडल को अच्छी तरह से कैशिंग करते हुए देखते हैं!

DevTools Sources पैनल खुला और लोडेड शूलेस बंडल दिखा रहा है।
नेक्स्ट (या कोई SSR फ्रेमवर्क) के साथ वेब कंपोनेंट्स का उपयोग करना

यदि हमारा शूलेस बंडल कभी बदलता है, तो फ़ाइल का नाम बदल जाएगा (के माध्यम से) :hash उपरोक्त स्रोत संपत्ति से भाग), ब्राउज़र को पता चलेगा कि उसके पास वह फ़ाइल कैश्ड नहीं है, और बस इसे नेटवर्क से नए सिरे से अनुरोध करेगा।

ऊपर लपेटकर

यह बहुत सारे मैनुअल काम की तरह लग सकता है; और वो यह था। यह दुर्भाग्यपूर्ण है कि वेब घटक सर्वर-साइड रेंडरिंग के लिए बेहतर आउट-ऑफ-द-बॉक्स समर्थन प्रदान नहीं करते हैं।

लेकिन हमें उनके द्वारा प्रदान किए जाने वाले लाभों को नहीं भूलना चाहिए: गुणवत्ता वाले यूएक्स घटकों का उपयोग करने में सक्षम होना अच्छा है जो एक विशिष्ट ढांचे से बंधे नहीं हैं। बिल्कुल नए ढांचे के साथ प्रयोग करने में सक्षम होना अच्छा है, जैसे ठोस, किसी प्रकार के टैब, मोडल, स्वतः पूर्ण, या किसी भी घटक को खोजने (या एक साथ हैक करने) की आवश्यकता के बिना।

समय टिकट:

से अधिक सीएसएस ट्रिक्स