Salah satu masalah yang harus Anda selesaikan saat menulis aplikasi rendering Sisi Server adalah bekerja dengan tag meta yang harus dimiliki setiap halaman, yang membantu mengindeksnya dengan mesin pencari.
Mulai dari google, solusi pertama yang akan Anda
tuju adalah kemungkinan besar
React Helmet .
Salah satu kelebihan perpustakaan adalah dapat dianggap isomorfis dalam beberapa cara dan dapat digunakan dengan sempurna baik di sisi klien maupun di sisi server.
class Page extends Component { render() { return ( <div> <Helmet> <title>Turbo Todo</title> <meta name="theme-color" content="#008f68" /> </Helmet> {/* ... */} </div> ); } }
Di server, router akan terlihat seperti ini:
app.get('/*', (req, res) => { const html = renderToString(<App />); const helmet = Helmet.renderStatic(); res.send(` <!doctype html> <html ${helmet.htmlAttributes.toString()}> <head> ${helmet.title.toString()} ${helmet.meta.toString()} </head> <body ${helmet.bodyAttributes.toString()}> <div id="app">${html}</div> </body> </html> `); });
Kedua cuplikan ini sepenuhnya benar dan efisien, tetapi ada satu TETAPI, kode di atas untuk server sepenuhnya sinkron dan karena itu benar-benar aman, tetapi jika menjadi asinkron, ia akan menyembunyikan bug yang sulit di-debug sendiri:
app.get('/*', async (req, res) => {
Masalahnya di sini terutama di perpustakaan React Helmet itu sendiri dan, khususnya, di mana ia mengumpulkan semua tag di dalam Pohon React dan menempatkannya pada kenyataannya menjadi variabel global, dan karena kode tersebut telah menjadi tidak sinkron, kode tersebut dapat mencampur permintaan yang diproses secara simultan dari pengguna yang berbeda.
Kabar baiknya di sini adalah bahwa garpu dibuat atas dasar perpustakaan ini dan sekarang lebih baik untuk memberikan preferensi ke perpustakaan
bereaksi-helm-async . Paradigma utama di dalamnya adalah bahwa dalam kasus ini, konteks helm reaksi akan diisolasi dalam kerangka permintaan tunggal dengan merangkum aplikasi Pohon React di HelmetProvider:
import { Helmet, HelmetProvider } from 'react-helmet-async'; app.get('/*', async (req, res) => {
Ini bisa selesai, tetapi mungkin Anda akan melangkah lebih jauh dalam upaya untuk memeras kinerja maksimum dan meningkatkan beberapa metrik SEO. Misalnya, Anda dapat meningkatkan metrik Time To First Byte (TTFB) - ketika server dapat mengirim tata letak halaman dengan potongan saat dihitung, alih-alih menunggu hingga dihitung sepenuhnya. Untuk melakukan ini, Anda akan mulai melihat ke arah menggunakan
renderToNodeStream alih-alih
renderToString .
Di sini kita dihadapkan dengan masalah kecil. Untuk mendapatkan semua tag meta yang dibutuhkan halaman, kita harus melalui seluruh pohon reaksi aplikasi, tetapi masalahnya adalah bahwa tag meta harus dikirim sebelum saat kita mulai streaming konten menggunakan renderToNodeStream. Bahkan, kita perlu menghitung React Tree dua kali dan terlihat seperti ini:
app.get('/*', async (req, res) => { const helmetContext = {}; let app = ( <HelmetProvider context={helmetContext}> <App/> </HelmetProvider> ); // do a first pass render so that react-helmet-async // can see what meta tags to render ReactDOMServer.renderToString(app); const { helmet } = helmetContext; response.write(` <html> <head> ${helmet.title.toString()} ${helmet.meta.toString()} </head> <body> `); const stream = ReactDOMServer.renderToNodeStream(app); stream.pipe(response, { end: false }); stream.on('end', () => response.end('</body></html>')); });
Dengan pendekatan seperti itu, kebutuhan untuk optimasi seperti itu pada prinsipnya menjadi pertanyaan besar dan tidak mungkin kita akan meningkatkan metrik TTFB yang ingin kita capai.
Di sini kita bisa memainkan sedikit optimasi dan ada beberapa opsi
- alih-alih penggunaan renderToString renderToStaticMarkup, yang mungkin akan membantu sampai batas tertentu memenangkan waktu
- alih-alih menggunakan penyaji yang ditawarkan oleh reaksi dari kotak, buat versi cahaya Anda sendiri dari bagian melalui pohon reaksi, misalnya, berdasarkan perpustakaan reaksi-tree-walker , atau menolak untuk membuat pohon sepenuhnya dan hanya melihat pada tingkat pertama pohon, tidak memperhatikan komponen yang disematkan, jadi katakan rendering dangkal
- pertimbangkan sistem caching yang kadang-kadang mungkin melewatkan jalan pertama melalui pohon reaksi
Tetapi bagaimanapun juga, segala sesuatu yang digambarkan terdengar terlalu canggih dan, pada prinsipnya, meragukan perlombaan ini untuk efisiensi, ketika beberapa arsitektur kompleks yang abnormal dibangun dalam beberapa milidetik.
Bagi saya, dalam hal ini, bagi mereka yang terbiasa dengan cara mengekstrak data untuk rendering untuk RSK (dan jika seseorang tidak tahu, maka ini adalah
artikel yang sangat baik tentang topik ini), kami akan membantu dengan cara yang sama mengekstraksi meta tag untuk halaman.
Konsep umum adalah bahwa kita memiliki file konfigurasi untuk router - ini adalah struktur JS biasa, yang merupakan array objek, masing-masing berisi beberapa bidang tipe
komponen ,
path . Berdasarkan url permintaan, kami menemukan router dan komponen yang terkait dengannya dari file konfigurasi. Untuk komponen-komponen ini, kami mendefinisikan sekumpulan metode statis seperti
loadData dan, misalnya,
buat Tag Tag untuk meta tag kami.
Dengan demikian, komponen halaman itu sendiri akan menjadi seperti ini:
class ProductPage extends React.Component { static createMetatags(store, request){ const item = selectItem(store, request.params.product_id); return [] .concat({property: 'og:description', content: item.desc}) .concat({property: 'og:title', content: item.title}) } static loadData(store, request){
Kami telah mendefinisikan metode createMetatags statis yang membuat set meta tag yang diperlukan. Dengan mengingat hal ini, kode pada server akan menjadi seperti ini:
app.get('/*', async (req, res) => { const store = createStore(); const matchedRoutes = matchRoutes(routes, request.path);
Yaitu Sekarang kita tidak perlu merender pohon Bereaksi dua kali - kita dapat segera mengekstraksi semua yang kita butuhkan untuk bekerja dari aplikasi isomorfik, dengan analogi dengan ekstraksi data untuk rute.