Χρήση στοιχείων Web με το Next (ή οποιοδήποτε πλαίσιο SSR)

Σε μου το προηγούμενο άρθρο εξετάσαμε το Shoelace, το οποίο είναι μια βιβλιοθήκη στοιχείων με μια πλήρη σειρά στοιχείων UX που είναι όμορφα, προσβάσιμα και — ίσως απροσδόκητα — κατασκευασμένα με Στοιχεία Ιστού. Αυτό σημαίνει ότι μπορούν να χρησιμοποιηθούν με οποιοδήποτε πλαίσιο JavaScript. Ενώ η διαλειτουργικότητα του Web Component του React είναι, προς το παρόν, λιγότερο από ιδανική, υπάρχουν λύσεις.

Αλλά ένα σοβαρό μειονέκτημα των στοιχείων Web είναι η τρέχουσα έλλειψη υποστήριξης για απόδοση από την πλευρά του διακομιστή (SSR). Υπάρχει κάτι που ονομάζεται Declarative Shadow DOM (DSD) στα σκαριά, αλλά η τρέχουσα υποστήριξη για αυτό είναι αρκετά ελάχιστη και στην πραγματικότητα απαιτεί buy-in από τον web server σας για να εκπέμπει ειδική σήμανση για το DSD. Αυτή τη στιγμή γίνεται δουλειά Next.js που ανυπομονώ να δω. Αλλά για αυτήν την ανάρτηση, θα εξετάσουμε τον τρόπο διαχείρισης των στοιχείων Web από οποιοδήποτε πλαίσιο SSR, όπως το Next.js, σήμερα.

Θα τελειώσουμε κάνοντας μια μη τετριμμένη χειρωνακτική εργασία και ελαφρώς βλάπτοντας την απόδοση εκκίνησης της σελίδας μας στη διαδικασία. Στη συνέχεια, θα εξετάσουμε πώς να ελαχιστοποιήσουμε αυτό το κόστος απόδοσης. Αλλά μην κάνετε λάθος: αυτή η λύση δεν είναι χωρίς συμβιβασμούς, οπότε μην περιμένετε διαφορετικά. Πάντα μέτρο και προφίλ.

Το πρόβλημα

Πριν βουτήξουμε, ας αφιερώσουμε μια στιγμή και ας εξηγήσουμε πραγματικά το πρόβλημα. Γιατί τα στοιχεία Web δεν λειτουργούν καλά με την απόδοση από την πλευρά του διακομιστή;

Τα πλαίσια εφαρμογών όπως το Next.js λαμβάνουν τον κώδικα του React και τον εκτελούν μέσω ενός API για να τον "περιορίσουν" ουσιαστικά, που σημαίνει ότι μετατρέπει τα στοιχεία σας σε απλό HTML. Έτσι, το δέντρο στοιχείων React θα αποδοθεί στον διακομιστή που φιλοξενεί την εφαρμογή Ιστού και αυτό το HTML θα σταλεί μαζί με το υπόλοιπο έγγραφο HTML της εφαρμογής Ιστού στο πρόγραμμα περιήγησης του χρήστη σας. Μαζί με αυτό το HTML είναι μερικά ετικέτες που φορτώνουν το React, μαζί με τον κώδικα για όλα τα στοιχεία του React. Όταν ένα πρόγραμμα περιήγησης τα επεξεργάζεται ετικέτες, το React θα αποδώσει ξανά το δέντρο συστατικών και θα αντιστοιχίσει τα πράγματα με το SSR'd HTML που στάλθηκε. Σε αυτό το σημείο, όλα τα εφέ θα αρχίσουν να εκτελούνται, οι χειριστές συμβάντων θα συνδεθούν και η κατάσταση θα περιέχει πραγματικά κατάσταση. Σε αυτό το σημείο γίνεται η εφαρμογή Ιστού διαδραστικό. Καλείται η διαδικασία επανεπεξεργασίας του δέντρου συστατικού σας στον πελάτη και καλωδίωσης όλων ενυδάτωση.

Λοιπόν, τι σχέση έχει αυτό με τα στοιχεία Web; Λοιπόν, όταν αποδίδεις κάτι, πες το ίδιο Κορδόνι στοιχείο που επισκεφτήκαμε τελευταία φορά:


   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.

…Αντιδράστε (ή ειλικρινά κάθε JavaScript framework) θα δει αυτές τις ετικέτες και θα τις μεταβιβάσει απλώς. Το React (ή το Svelte ή το Solid) δεν είναι υπεύθυνο για τη μετατροπή αυτών των ετικετών σε καρτέλες με ωραία μορφοποίηση. Ο κώδικας για αυτό είναι κρυμμένος μέσα σε οποιονδήποτε κώδικα έχετε που ορίζει αυτά τα στοιχεία Web. Στην περίπτωσή μας, αυτός ο κωδικός βρίσκεται στη βιβλιοθήκη Shoelace, αλλά ο κωδικός μπορεί να βρίσκεται οπουδήποτε. Αυτό που είναι σημαντικό είναι όταν εκτελείται ο κώδικας.

Κανονικά, ο κωδικός που καταχωρεί αυτά τα στοιχεία Ιστού θα τραβηχτεί στον κανονικό κώδικα της εφαρμογής σας μέσω JavaScript import. Αυτό σημαίνει ότι αυτός ο κώδικας θα λήξει στη δέσμη JavaScript και θα εκτελεστεί κατά την ενυδάτωση, πράγμα που σημαίνει ότι, όταν ο χρήστης θα δει για πρώτη φορά το HTML SSR και θα συμβεί ενυδάτωση, αυτές οι καρτέλες (ή οποιοδήποτε στοιχείο Web για αυτό το θέμα) δεν θα αποδώσουν το σωστό περιεχόμενο . Στη συνέχεια, όταν συμβεί ενυδάτωση, θα εμφανιστεί το κατάλληλο περιεχόμενο, προκαλώντας πιθανότατα το περιεχόμενο γύρω από αυτά τα στοιχεία Ιστού να μετακινηθεί και να ταιριάζει στο σωστά μορφοποιημένο περιεχόμενο. Αυτό είναι γνωστό ως α φλας χωρίς στιλ, ή FOUC. Θεωρητικά, θα μπορούσατε να κολλήσετε σήμανση ανάμεσα σε όλα αυτά ετικέτες για να ταιριάζουν με το τελικό αποτέλεσμα, αλλά αυτό είναι εντελώς αδύνατο στην πράξη, ειδικά για μια βιβλιοθήκη εξαρτημάτων τρίτου κατασκευαστή όπως το Shoelace.

Μετακίνηση του κωδικού εγγραφής του στοιχείου Web

Επομένως, το πρόβλημα είναι ότι ο κώδικας για να κάνει τα Web Components να κάνουν ό,τι πρέπει να κάνουν δεν θα εκτελείται πραγματικά μέχρι να συμβεί ενυδάτωση. Για αυτήν την ανάρτηση, θα εξετάσουμε την εκτέλεση αυτού του κώδικα νωρίτερα. αμέσως μάλιστα. Θα εξετάσουμε την προσαρμοσμένη ομαδοποίηση του κώδικα του στοιχείου Ιστού μας και την μη αυτόματη προσθήκη ενός σεναρίου απευθείας στο έγγραφό μας έτσι εκτελείται αμέσως και μπλοκάρει το υπόλοιπο έγγραφο μέχρι να γίνει. Αυτό είναι συνήθως ένα τρομερό πράγμα. Το όλο νόημα της απόδοσης από την πλευρά του διακομιστή είναι να δεν αποκλείουμε την επεξεργασία της σελίδας μας μέχρι να ολοκληρωθεί η επεξεργασία του JavaScript. Αλλά μόλις ολοκληρωθεί, αυτό σημαίνει ότι, καθώς το έγγραφο αποδίδει αρχικά την HTML μας από τον διακομιστή, τα στοιχεία Web θα καταχωρηθούν και θα εκπέμπουν αμέσως και συγχρονισμένα το σωστό περιεχόμενο.

Στην περίπτωσή μας, είμαστε μόλις ψάχνουμε να εκτελέσουμε τον κωδικό εγγραφής του στοιχείου Web σε μια δέσμη ενεργειών αποκλεισμού. Αυτός ο κώδικας δεν είναι τεράστιος και θα προσπαθήσουμε να μειώσουμε σημαντικά το χτύπημα απόδοσης προσθέτοντας μερικές κεφαλίδες προσωρινής μνήμης για να βοηθήσουμε με τις επόμενες επισκέψεις. Αυτή δεν είναι η τέλεια λύση. Η πρώτη φορά που ένας χρήστης περιηγείται στη σελίδα σας θα αποκλειστεί πάντα κατά τη φόρτωση αυτού του αρχείου σεναρίου. Οι επόμενες επισκέψεις θα αποθηκευτούν καλά στην προσωρινή μνήμη, αλλά αυτή η ανταλλαγή ίσως όχι είναι εφικτό για εσάς — ηλεκτρονικό εμπόριο, κανείς; Τέλος πάντων, προφίλ, μέτρηση και λήψη της σωστής απόφασης για την εφαρμογή σας. Επιπλέον, στο μέλλον είναι απολύτως δυνατό το Next.js να υποστηρίζει πλήρως DSD και Web Components.

Ξεκινώντας

Όλος ο κώδικας που θα εξετάσουμε είναι μέσα αυτό το repo GitHub και αναπτύχθηκε εδώ με τη Vercel. Η εφαρμογή Ιστού αποδίδει ορισμένα στοιχεία κορδονιών μαζί με κείμενο που αλλάζει χρώμα και περιεχόμενο κατά την ενυδάτωση. Θα πρέπει να μπορείτε να δείτε το κείμενο να αλλάζει σε "Ενυδατωμένο", με τα στοιχεία Κορδόνι παπουτσιών να έχουν ήδη αποδοθεί σωστά.

Προσαρμοσμένη ομαδοποίηση κώδικα στοιχείου Web

Το πρώτο μας βήμα είναι να δημιουργήσουμε μια ενιαία λειτουργική μονάδα JavaScript που εισάγει όλους τους ορισμούς μας για το στοιχείο Web. Για τα στοιχεία κορδόνι παπουτσιών που χρησιμοποιώ, ο κώδικάς μου μοιάζει με αυτό:

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 αυτή η ενότητα. Αν το κάναμε αυτό, θα ομαδοποιούνταν στα κανονικά πακέτα JavaScript και θα λειτουργούσαν κατά την ενυδάτωση. Αυτό θα προκαλούσε το FOUC που προσπαθούμε να αποφύγουμε.

Ενώ το Next.js έχει μια σειρά από άγκιστρα webpack για προσαρμοσμένη δέσμη στοιχείων, θα χρησιμοποιήσω Ζει αντι αυτου. Πρώτα, εγκαταστήστε το με 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`,
      },
    },
  },
});

Αυτό θα δημιουργήσει ένα αρχείο δέσμης με τους ορισμούς μας για το στοιχείο Web στο shoelace-dir ντοσιέ. Ας το μεταφέρουμε στο public φάκελο ώστε το Next.js να τον εξυπηρετήσει. Και θα πρέπει επίσης να παρακολουθούμε το ακριβές όνομα του αρχείου, με τον κατακερματισμό στο τέλος του. Ακολουθεί ένα σενάριο Node που μετακινεί το αρχείο και γράφει μια λειτουργική μονάδα JavaScript που εξάγει μια απλή σταθερά με το όνομα του αρχείου δέσμης (αυτό θα σας φανεί χρήσιμο σύντομα):

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 αρχείο και τραβήξτε το όνομα του αρχείου δέσμης στοιχείων Web:

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.

Βελτίωση της απόδοσης

Θα μπορούσαμε να αφήσουμε τα πράγματα ως έχουν, αλλά ας προσθέσουμε προσωρινή αποθήκευση για το πακέτο κορδονιών παπουτσιών. Θα πούμε στο Next.js να κάνει αυτά τα πακέτα κορδονιών με δυνατότητα προσωρινής αποθήκευσης προσθέτοντας την ακόλουθη καταχώρηση στο αρχείο διαμόρφωσης Next.js:

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

Τώρα, σε επόμενες περιηγήσεις στον ιστότοπό μας, βλέπουμε το πακέτο κορδόνια παπουτσιών να αποθηκεύεται όμορφα στην προσωρινή μνήμη!

Ο πίνακας DevTools Sources ανοίγει και εμφανίζει το φορτωμένο πακέτο κορδονιών.
Χρήση στοιχείων Web με το Next (ή οποιοδήποτε πλαίσιο SSR)

Εάν αλλάξει ποτέ το πακέτο κορδονιών παπουτσιών, το όνομα του αρχείου θα αλλάξει (μέσω του :hash τμήμα από την παραπάνω ιδιότητα προέλευσης), το πρόγραμμα περιήγησης θα διαπιστώσει ότι δεν έχει αποθηκευμένο αυτό το αρχείο και απλώς θα το ζητήσει νέο από το δίκτυο.

Ολοκληρώνοντας

Αυτό μπορεί να φαινόταν σαν πολλή χειρωνακτική εργασία. και ήταν. Είναι λυπηρό που τα στοιχεία Ιστού δεν προσφέρουν καλύτερη υποστήριξη εκτός συσκευασίας για απόδοση από την πλευρά του διακομιστή.

Αλλά δεν πρέπει να ξεχνάμε τα πλεονεκτήματα που παρέχουν: είναι ωραίο να μπορείτε να χρησιμοποιείτε ποιοτικά στοιχεία UX που δεν συνδέονται με ένα συγκεκριμένο πλαίσιο. Είναι πολύ ωραίο να μπορείς να πειραματίζεσαι με ολοκαίνουργια πλαίσια, όπως π.χ Στερεά, χωρίς να χρειάζεται να βρείτε (ή να χακάρετε μαζί) κάποιου είδους καρτέλα, modal, αυτόματη συμπλήρωση ή οποιοδήποτε άλλο στοιχείο.

Σφραγίδα ώρας:

Περισσότερα από Κόλπα CSS