Μετάβαση στο περιεχόμενο

Το συμβόλαιο απόδοσης

Αυτή είναι η μοναδική επίσημη λίστα απαιτήσεων που πρέπει να ικανοποιεί το <head> κάθε frontend όταν αποδίδει δεδομένα SEO Rankbeam. Είναι η πηγή αλήθειας για:

  • τις δοκιμές μονάδας μορφής renderer του core (tests/Unit/Services/RenderingContractTest.php) — το γρήγορο σκέλος χωρίς framework που καλύπτει το CI του πακέτου·
  • τις εφαρμογές αναφοράς ανά stack στο rankbeam-examples (Blade, Inertia + Vue / React / Svelte, Livewire), των οποίων οι δοκιμές browser και SSR επαληθεύουν τις ίδιες απαιτήσεις σε πραγματικό DOM·
  • τους οδηγούς framework (Blade, Inertia και JSON, Livewire), οι οποίοι δεν πρέπει ποτέ να τεκμηριώνουν προσέγγιση που το παραβιάζει.

Αν ένα stack δεν μπορεί να ικανοποιήσει έναν όρο, αυτό είναι ελάττωμα ή τεκμηριωμένος περιορισμός — όχι λόγος να αποδυναμωθεί το συμβόλαιο. Το επίπεδο δεδομένων (SEOResolver → αμετάβλητο SEODataTagRenderer) είναι ανεξάρτητο από framework· μόνο το πώς τα επιλυμένα δεδομένα φτάνουν στο DOM, επιβιώνουν από την πλοήγηση πελάτη και παραμένουν ορατά στους ανιχνευτές διαφέρει ανά stack, και αυτό ακριβώς ορίζει το συμβόλαιο.

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


1. Τιμές — τι περιέχει ένα συμμορφούμενο <head>

Τίτλος, περιγραφή, κανονική διεύθυνση URL

  • Ακριβώς ένα <title>, με τον επιλυμένο τίτλο — ποτέ διπλό επίθημα (ο resolver προσθέτει το seo.title_suffix μία φορά, προστατεύοντας από τίτλο που ήδη τελειώνει με αυτό).
  • Μία meta description, μόνο όταν επιλύθηκε περιγραφή (χωρίς κενή ετικέτα).
  • Ένα <link rel="canonical">.

Robots

  • Εκπέμπεται <meta name="robots"> μόνο όταν η οδηγία διαφέρει από την προεπιλογή ιστοτόπου. Ένα περιττό index,follow είναι θόρυβος, και η απουσία του είναι ακριβώς ό,τι αντιμετωπίζει ο ανιχνευτής ως index,follow. Η σύγκριση αγνοεί τα κενά (index, followindex,follow)· μια διαφορετική οδηγία εκπέμπεται αυτούσια. Το seo.robots.emit_default = true επιβάλλει την ετικέτα.
  • Υποστηρίζονται ντετερμινιστικές προηγμένες οδηγίες: noindex, nofollow, noarchive, nosnippet, max-snippet, max-image-preview, max-video-preview, notranslate, unavailable_after. Είναι επιλυμένες τιμές συμβολοσειρών· η προτεραιότητά τους είναι η αλυσίδα resolver (καθολικά → διαδρομή → μοντέλο → ρητά). Ίδιες είσοδοι ⇒ ίδια έξοδος.

Open Graph

  • og:title, og:description, og:type, og:url, og:site_name, og:locale.
  • article:* (published_time, modified_time, author, section, tag) μόνο όταν og:type === 'article' και η τιμή είναι πραγματική — ποτέ επινοημένη, ποτέ σε σελίδα που δεν είναι άρθρο.
  • og:image με og:image:width / og:image:height / og:image:alt και og:image:type όταν είναι γνωστά. Πολλές εικόνες ομαδοποιούνται — κάθε og:image ακολουθείται αμέσως από τις δικές της ιδιότητες διαστάσεων/alt/type.

Twitter Cards

  • twitter:card, twitter:title, twitter:description, twitter:image και twitter:image:alt (όταν είναι γνωστό το alt της εικόνας).
  • Τα twitter:site και twitter:creator είναι προαιρετικά και ανεξάρτητα — το ένα μπορεί να υπάρχει χωρίς το άλλο, και κανένα δεν επινοείται από το άλλο.

hreflang και γλώσσα

  • Το hreflang έχει εγγενή διαδρομή επίλυσης μέσω του hook getSEOAlternates() ενός μοντέλου.
  • Οι εναλλακτικές hreflang, όταν υπάρχουν, είναι απόλυτες, κανονικοποιημένες, μοναδικές ανά γλώσσα και αμοιβαίες όπου τα δεδομένα είναι πλήρη. Το x-default μόνο όταν έχει ρυθμιστεί.
  • Το og:locale:alternate αντικατοπτρίζει μόνο γλώσσες που έχουν πραγματική κοινωνική παραλλαγή (αντιστοίχιση en-USen_US· σύγκριση στην αντιστοιχισμένη μορφή, χωρίς απαίτηση κυριολεκτικής ισότητας).
  • Ισοτιμία <html lang> με την επιλυμένη γλώσσα (ο όρος ανήκει στο συμβόλαιο, παρότι η εφαρμογή εκπέμπει το στοιχείο <html>).

JSON-LD ανά σελίδα

  • Αναλύσιμο και ασφαλές έναντι </script> (το payload κωδικοποιείται με JSON_HEX_TAG ώστε καμία τιμή να μην μπορεί να κλείσει πρόωρα το στοιχείο script — προστασία από αποθηκευμένο XSS).
  • Είναι αποδεκτά τόσο πολλά τμήματα <script> όσο και ένα συνδυασμένο @graph.
  • Σταθερό @id χρησιμοποιείται μόνο όπου οι οντότητες συνδέονται πραγματικά (Organization ↔ WebSite ↔ WebPage)· σταθερό @id δεν είναι υποχρεωτικό σε αυτόνομους κόμβους.

2. Κανονικοποίηση και αμετάβλητοι κανόνες

  • Απόλυτες URL http(s) για canonical, og:url, og:image, twitter:image. Καμία κενή ετικέτα ή ετικέτα null δεν φτάνει ποτέ στο DOM.
  • Τα canonical και og:url ΠΡΕΠΕΙ να επιλύονται στην ίδια κανονικοποιημένη URL. Η διαφωνία είναι ΑΥΣΤΗΡΗ αποτυχία, όχι προειδοποίηση.
  • Η πολιτική κανονικοποίησης της canonical είναι συνεπής σε όλο το σύστημα: σχήμα / host / θύρα / πεζά-κεφαλαία διαδρομής / επιτρεπόμενες παράμετροι query / τελική κάθετος αντιμετωπίζονται με τον ίδιο τρόπο κάθε φορά. Οι ευρετηριάσιμες σελίδες αναφέρονται στον εαυτό τους· μια σελίδα noindex δεν κληρονομεί τη στρατηγική canonical άλλης σελίδας.
  • Η διαφυγή εξαρτάται από τον προορισμό: χαρακτηριστικό HTML, κείμενο και JSON χρησιμοποιούν το καθένα τον σωστό κωδικοποιητή. Οι έλεγχοι συγκρίνουν αποκωδικοποιημένες σημασιολογικές τιμές, όχι byte.
  • Η ισοτιμία μεταξύ renderer είναι σημασιολογική, όχι byte προς byte. render() (HTML) ≡ toArray()toInertiaHead() μετά την κανονικοποίηση — οι τρεις αναπαραστάσεις δικαιολογημένα διαφέρουν σε σειρά και μορφή ετικετών. Οι κανόνες μοναδικών και επαναλαμβανόμενων ιδιοτήτων είναι ρητοί (ένα og:title· πολλά article:tag).
  • Ιδιοκτησία ετικετών: ο renderer πελάτη αντικαθιστά ετικέτες του πακέτου (με κλειδιά, δείτε §4) χωρίς να διαγράφει άσχετες ετικέτες της εφαρμογής.

3. Συμπεριφορά — πλοήγηση στην πλευρά πελάτη

Μετά από κάθε επίσκεψη Inertia ή wire:navigate του Livewire:

  • υπάρχει ακριβώς ένα από κάθε μοναδικό στοιχείο (<title>, περιγραφή, canonical, κάθε og:*/twitter:*), κανένα παρωχημένο·
  • το JSON-LD δεν συσσωρεύεται — το schema προηγούμενης σελίδας αφαιρείται, δεν προστίθεται από πάνω (το Livewire αντιμετωπίζει το <script> ως μη αφαιρούμενο asset, οπότε τα script schema φέρουν data-seo-schema και αναγνωριστικό ανά URL, και αυτά της προηγούμενης σελίδας αφαιρούνται στο livewire:navigated — δείτε τον οδηγό Livewire)·
  • η πλοήγηση από σελίδα πλούσια σε μεταδεδομένα σε λιτή σελίδα αφαιρεί τις επιπλέον ετικέτες (η λιτή σελίδα δεν κρατά description/og/schema της πλούσιας)·
  • μηδενικές προειδοποιήσεις hydration, και τα μεταδεδομένα είναι σημασιολογικά ίδια πριν και μετά το hydration.

4. head-keys του Inertia (ιδιοκτησία ετικετών)

Το toInertiaHead() προσθέτει σταθερό head-key σε κάθε εγγραφή meta/link. Το Inertia αφαιρεί διπλότυπα στοιχεία head με βάση αυτό το χαρακτηριστικό: μια ετικέτα <Head> σελίδας με ίδιο head-key με ετικέτα layout την αντικαθιστά, αντί να προσθέσει διπλότυπο.

  • Βασικό κλειδί = name ?? property για meta, rel για συνδέσμους.
  • Οι επαναλαμβανόμενες ετικέτες διαφοροποιούνται ώστε καθεμία να κρατά μοναδικό κλειδί: article:tagarticle:tag, article:tag:1, …· hreflang → alternate:en-US, alternate:fr-FR.

Συνδέστε το στα πρότυπα ως :head-keyόχι ως :key του Vue (που είναι το άσχετο κλειδί συμφιλίωσης v-for και δεν επηρεάζει την αφαίρεση διπλότυπων head του Inertia).


5. Ορατότητα ανιχνευτών (ρητές λειτουργίες)

  • Το SSR / prerender ΠΡΕΠΕΙ να εκπέμπει όλο το συμβόλαιο στο ακατέργαστο HTML της HTTP απόκρισης — αυτό δοκιμάζεται χωριστά από το DOM μετά το hydration (με ανενεργό JS).
  • Το CSR μόνο δεν μπορεί να ισχυρίζεται συμμόρφωση για ανιχνευτές. Το προεπιλεγμένο Inertia (χωρίς SSR) εισάγει τα μεταδεδομένα στην πλευρά πελάτη: το αρχικό HTML που ανακτά ένας ανιχνευτής δεν έχει μεταδεδομένα SEO. Αυτό τεκμηριώνεται, δεν κρύβεται — τα ορατά σε ανιχνευτές μεταδεδομένα απαιτούν Inertia SSR ή prerendering (και το JSON-LD για ανιχνευτές πρέπει να αποδίδεται στον διακομιστή).

6. Εκτός πεδίου / μη στόχοι

  • Ζητήματα εφαρμογής, όχι renderer: charset, viewport, favicons. (Σημείωση: το <meta charset> πρέπει να προηγείται κάθε μεταδεδομένου εκτός ASCII, οπότε η εφαρμογή καθορίζει τη σειρά αυτών των στοιχείων head.)
  • Η δοκιμή e2e ελέγχει μόνο την παραγόμενη έξοδο. Δεν επιβεβαιώνει ευρετηρίαση Google, επιλογή κανονικής διεύθυνσης URL, επιλεξιμότητα εμπλουτισμένων αποτελεσμάτων ή κατάταξη· και δεν επιβεβαιώνει MIME/διαθεσιμότητα απομακρυσμένων εικόνων. Αυτά ανήκουν σε προαιρετικές δοκιμές ενσωμάτωσης/HTTP, ποτέ στον πίνακα δοκιμών browser.

7. Κατάσταση συμμόρφωσης

Τι αποδεικνύει κάθε όρο σήμερα. Μονάδας = RenderingContractTest (core, CI πακέτου). Browser/SSR = rankbeam-examples (προγραμματισμένος πίνακας δοκιμών). Εφαρμογή = ευθύνη της εφαρμογής υποδοχής. Προγραμματισμένο = στόχος στο συμβόλαιο, αλλά τα δεδομένα δεν μοντελοποιούνται ακόμη από το SEOData, οπότε ο renderer εκπέμπει το ασφαλές υποσύνολο.

ΌροςΚατάσταση
Ακριβώς ένα επιλυμένο <title>, χωρίς διπλό επίθημαΜονάδας + Browser
Meta description μόνο όταν υπάρχειΜονάδας + Browser
Ένα <link rel="canonical">, ποτέ κενόΜονάδας + Browser
Robots μόνο όταν διαφέρει από την προεπιλογή· αυτούσιο· διακόπτης emit_defaultΜονάδας + Browser
Προηγμένες οδηγίες robots μέσω προτεραιότητας resolverΜονάδας (resolver)
og:title/description/type/url/site_name/locale· γλώσσα en-USen_USΜονάδας + Browser
article:* μόνο όταν og:type=article και είναι πραγματικόΜονάδας + Browser
og:image παρόν και απόλυτοΜονάδας + Browser
og:image:width/height/alt, og:image:type, ομαδοποίηση πολλών εικόνωνΠρογραμματισμένο — το SEOData έχει μία συμβολοσειρά ogImage· δεν μοντελοποιούνται ακόμη διαστάσεις/alt/type. Ο renderer εκπέμπει ένα απόλυτο og:image.
twitter:card/title/description/image· site/creator ανεξάρτηταΜονάδας + Browser
twitter:image:altΠρογραμματισμένο — δεν μοντελοποιείται ακόμη πεδίο alt εικόνας.
hreflang απόλυτο, μοναδικό ανά γλώσσαΜονάδας + Browser
Αμοιβαιότητα hreflang, x-default όταν ρυθμίζεταιBrowser (εξαρτάται από τα δεδομένα)
og:locale:alternate αντικατοπτρίζει πραγματικές κοινωνικές παραλλαγέςΠρογραμματισμένο — δεν μοντελοποιείται ακόμη αντιστοίχιση κοινωνικών παραλλαγών ανά γλώσσα.
Ισοτιμία <html lang>Εφαρμογή (+ το ελέγχει ο Browser)
JSON-LD αναλύσιμο και ασφαλές έναντι </script>Μονάδας + Browser
Πολλά script Ή @graph· σταθερό @id όπου συνδέονται οντότητεςΜονάδας (γράφος merchant) + Browser
Απόλυτες URL· χωρίς κενές ετικέτες/τιμές nullΜονάδας + Browser
canonicalog:url (αυστηρή αποτυχία αν διαφέρουν)Μονάδας + Browser
Συνεπής κανονικοποίηση canonical· αυτοαναφορά· απομόνωση noindexBrowser
Διαφυγή ανά προορισμό· αποκωδικοποιημένη σημασιολογική ισοτιμίαΜονάδας
Σημασιολογική ισοτιμία μεταξύ renderer (render()toArray()toInertiaHead())Μονάδας
Σταθερό head-key Inertia και διαφοροποίηση επαναλαμβανόμενωνΜονάδας + Browser
Πλοήγηση πελάτη: ένα μοναδικό στοιχείο, κανένα παρωχημένο, χωρίς συσσώρευση JSON-LD, αφαίρεσηBrowser — ο renderer παρέχει τα hooks data-seo-schema που χρειάζεται ο καθαρισμός
Μηδενικές προειδοποιήσεις hydration· ισοτιμία πριν/μετάBrowser
SSR εκπέμπει όλο το συμβόλαιο στο ακατέργαστο HTML· CSR μόνο τεκμηριώνεται ως μη συμμορφούμενοBrowser + τεκμηρίωση

Οι προγραμματισμένοι όροι είναι συνειδητά, τεκμηριωμένα κενά — το συμβόλαιο είναι ο διαρκής στόχος και πρόκειται για προσθετικές, συμβατές προς τα πίσω επεκτάσεις για μελλοντική εργασία (απαιτούν νέα πεδία / στήλες SEOData, έκδοση SemVer-minor). Ο renderer εκπέμπει σήμερα το ασφαλές υποσύνολο· δεν επινοεί ποτέ τιμή που δεν έχει.

Το rankbeam/laravel-seo διανέμεται με άδεια MIT.