Apa itu rendering sisi server dan apakah saya membutuhkannya?

Halo, Habr!



Di tahun baru, mari kita mulai percakapan kita dengan Anda dengan artikel seed tentang rendering sisi server. Jika Anda tertarik, publikasi terbaru tentang Nuxt.js dan penerbitan lebih lanjut tentang arah ini dimungkinkan.



Dengan munculnya kerangka kerja dan pustaka JavaScript modern, yang terutama ditujukan untuk membuat halaman web interaktif dan aplikasi satu halaman, seluruh proses menampilkan halaman kepada pengguna telah banyak berubah.



Sebelum munculnya aplikasi yang sepenuhnya dibuat oleh JS di browser, HTML disajikan kepada klien sebagai tanggapan atas panggilan HTTP. Ini dapat dilakukan dengan mengembalikan file HTML statis dengan konten, atau dengan memproses respons menggunakan bahasa sisi server (PHP, Python, atau Java), dan dengan cara yang lebih dinamis.



Solusi ini memungkinkan Anda membuat situs responsif yang berjalan jauh lebih cepat daripada situs respons permintaan standar, karena solusi ini menghilangkan waktu yang dihabiskan oleh permintaan "dalam perjalanan".



Respons khas dari server terhadap permintaan ke situs yang ditulis dalam React akan terlihat seperti ini:



<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <link rel="shortcut icon" href="/favicon.ico">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/app.js"></script>
  </body>
</html>
      
      





Dengan memilih respon ini, browser kita juga akan memilih "paket" yang app.js



berisi aplikasi kita, dan setelah satu atau dua detik, ini akan menampilkan seluruh halaman.



Pada titik ini, Anda sudah dapat menggunakan pemeriksa HTML bawaan browser untuk melihat semua HTML yang dirender. Namun, melihat kode sumber, kami tidak akan melihat apa pun selain HTML di atas.



Mengapa ini menjadi masalah?



Meskipun perilaku ini tidak akan menjadi masalah bagi sebagian besar pengguna kami atau saat mengembangkan aplikasi, ini mungkin menjadi tidak diinginkan jika:



  • -, ,
  • , ,


Jika, dari sudut pandang demografis, audiens target Anda termasuk dalam salah satu grup ini, maka bekerja dengan situs akan merepotkan - khususnya, pengguna harus menunggu lama, menatap tanda "Memuat ..." (atau lebih buruk lagi, di layar kosong).



"Oke, tapi secara demografis, target audiens saya jelas bukan salah satu dari grup ini, jadi haruskah saya khawatir?"



Ada dua hal lain yang perlu dipertimbangkan saat bekerja dengan aplikasi yang dirender klien: mesin telusur dan keberadaan media sosial .



Saat ini, dari semua mesin pencari, hanya Google yang memiliki kemampuan untuk menampilkan situs dan memperhitungkan JS-nya sebelum menampilkan halaman. Selain itu, meskipun Google dapat menampilkan halaman indeks situs Anda, diketahui bahwa mungkin ada masalah saat menavigasi situs yang memiliki router.



Ini berarti bahwa akan sangat sulit bagi situs Anda untuk naik ke puncak hasil mesin pencari manapun selain Google.



Masalah yang sama dapat dilihat di jejaring sosial, misalnya, di Facebook - jika tautan ke situs Anda dibagikan, baik nama maupun gambar pratinjau tidak akan ditampilkan dengan benar.



Bagaimana mengatasi masalah ini



Ada beberapa cara untuk mengatasinya.



J - Cobalah untuk membuat semua halaman utama situs Anda tetap statis



Saat situs platform dibuat, di mana pengguna harus masuk dengan nama penggunanya, dan tanpa masuk ke sistem, konten tidak diberikan kepada pengunjung, Anda dapat mencoba meninggalkan halaman publik statis (ditulis dalam HTML) situs Anda, khususnya, indeks, "tentang kami", "kontak "Dan jangan gunakan JS saat menampilkannya .



Karena konten Anda dibatasi oleh persyaratan login, itu tidak akan diindeks oleh mesin pencari dan tidak dapat dibagikan di media sosial.



B - Buat bagian dari aplikasi Anda sebagai halaman HTML selama pembuatan



Anda dapat menambahkan pustaka seperti react-snapshot ke proyek Anda ; mereka digunakan untuk membuat salinan HTML dari halaman aplikasi Anda dan menyimpannya dalam direktori khusus. Direktori ini kemudian digunakan bersama dengan paket JS. Dengan demikian, HTML akan disajikan dari server bersama dengan responsnya, dan situs Anda juga akan dilihat oleh pengguna yang menonaktifkan JavaScript, serta oleh mesin telusur, dll.



Biasanya, mengonfigurasi react-snapshot tidak sulit: cukup tambahkan pustaka ke proyek Anda dan ubah skrip build sebagai berikut:



"build": "webpack && react-snapshot --build-dir static"





Kerugian dari solusi ini adalah: semua konten yang ingin kami hasilkan harus tersedia pada waktu pembuatan - kami tidak dapat mengakses API apa pun untuk mendapatkannya, kami juga tidak dapat membuat konten sebelumnya yang bergantung pada data yang disediakan oleh pengguna (misalnya dari URL).



C - Buat aplikasi JS yang menggunakan rendering server



Salah satu nilai jual terbesar dari aplikasi JS generasi saat ini adalah bahwa mereka dapat dijalankan di klien (browser) dan server. Hal ini memungkinkan pembuatan HTML untuk halaman yang lebih dinamis, yang kontennya belum diketahui pada waktu pembuatan. Aplikasi ini sering disebut sebagai "isomorphic" atau "universal".



Dua solusi rendering sisi server yang paling populer untuk React adalah:





Buat implementasi SSR Anda sendiri



Penting: jika Anda akan mencoba membuat implementasi SSR Anda sendiri untuk aplikasi React sendiri, Anda perlu menyediakan node backend untuk server Anda. Anda tidak akan dapat menerapkan solusi ini ke host statis seperti yang Anda lakukan dengan halaman github.



Hal pertama yang perlu kita lakukan adalah membuat aplikasi, sama seperti aplikasi React lainnya.



Mari buat titik masuk:



// index.js
import React from 'react';
import { render } from 'react-dom';
import App from './App.js';render(<App />, document.getElementById('root'));
      
      







Dan komponen-aplikasi (App):



// App.js
import React from 'react';const App = () => {
  return (
    <div>
      Welcome to SSR powered React application!
    </div>
  );
}
      
      





Dan juga "pembungkus" untuk memuat aplikasi kita:



// index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <div id="root"></div>
    <script src="/bundle.js"></script>
  </body>
</html>
      
      





Seperti yang Anda lihat, aplikasinya cukup sederhana. Pada artikel ini, kita tidak akan membahas semua langkah yang diperlukan untuk menghasilkan perakitan webpack + babel yang benar.

Jika Anda meluncurkan aplikasi dalam keadaannya saat ini, pesan selamat datang akan muncul di layar. Melihat kode sumber, Anda akan melihat konten file index.html



, tetapi pesan selamat datang tidak akan ada. Untuk mengatasi masalah ini, mari tambahkan rendering server. Pertama, mari tambahkan 3 paket:



yarn add express pug babel-node --save-dev
      
      





Express adalah server web yang kuat untuk node, pug adalah mesin template yang dapat digunakan dengan express, dan babel-node adalah pembungkus untuk node yang menyediakan transpilasi on-the-fly.



Pertama, mari salin file kita index.html



dan simpan sebagai index.pug



:



// index.pug
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <div id="root">!{app}</div>
    <script src="bundle.js"></script>
  </body>
</html>
      
      





Seperti yang Anda lihat, file tersebut tidak banyak berubah, kecuali yang sekarang telah dimasukkan ke dalam HTML !{app}



. Ini adalah variabel pug



yang nantinya akan diganti dengan HTML yang sebenarnya.



Mari buat server kita:



// server.jsimport React from 'react';
import { renderToString } from 'react-dom/server';
import express from 'express';
import path from 'path';import App from './src/App';const app = express();
app.set('view engine', 'pug');
app.use('/', express.static(path.join(__dirname, 'dist')));app.get('*', (req, res) => {
  const html = renderToString(
    <App />
  );  res.render(path.join(__dirname, 'src/index.pug'), {
    app: html
  });
});app.listen(3000, () => console.log('listening on port 3000'));
      
      





Mari kita analisis file ini secara berurutan.



import { renderToString } from 'react-dom/server';
      
      





Pustaka react-dom berisi ekspor bernama terpisah renderToString



yang berfungsi seperti yang kita ketahui render



, tetapi tidak merender DOM, tetapi HTML sebagai string.



const app = express();
app.set('view engine', 'pug');
app.use('/', express.static(path.join(__dirname, 'dist')));
      
      







Kami membuat instance server ekspres baru dan memberitahukannya bahwa kami akan menggunakan mesin templating pug



. Dalam kasus ini, kita dapat bertahan dengan membaca file seperti biasa dan melakukan operasi "temukan dan ganti", tetapi pendekatan ini benar-benar tidak efektif dan dapat menyebabkan masalah karena beberapa akses ke sistem file, atau masalah dengan cache.



Di baris terakhir, kami memberi tahu express untuk mencari file di direktori dist



, dan jika permintaan (misalnya /bundle.js



) cocok dengan file yang ada di direktori ini, maka kembalikan.



app.get('*', (req, res) => {
});
      
      







Sekarang kami memberi tahu express untuk menambahkan penangan ke setiap URL yang tidak cocok - termasuk file yang tidak ada index.html



(seperti yang Anda ingat, kami menamainya index.pug



, dan tidak ada di direktori dist



).



const html = renderToString(
  <App />
);
      
      





Dengan bantuan renderToString



kami menampilkan aplikasi kami. Kode terlihat persis seperti titik masuk, tetapi kecocokan seperti itu bersifat opsional.



res.render(path.join(__dirname, 'src/index.pug'), {
  app: html
});
      
      





Sekarang setelah kita merender HTML, kita memberi tahu express untuk merender file sebagai tanggapan index.pug



dan mengganti variabel app



dengan HTML yang kita terima.



app.listen(3000, () => console.log('listening on port 3000'));
      
      





Terakhir, kami mengaktifkan server untuk memulai dan mengkonfigurasinya untuk mendengarkan pada port 3000.

Sekarang kami hanya perlu menambahkan skrip yang diperlukan ke package.json



:



"scripts": {
  "server": "babel-node server.js"
}
      
      





Sekarang, dengan menelepon yarn run server



, kita harus menerima konfirmasi bahwa server memang sedang berjalan. Buka browser di localhost : 3000, di mana, sekali lagi, kita akan melihat aplikasi kita. Jika kita melihat kode sumber pada tahap ini, kita melihat:



<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <div id="root">div data-reactroot="">Welcome to SSR powered React application!</div></div>
    <script src="bundle.js"></script>
  </body>
</html>
      
      





Jika semuanya terlihat seperti ini, itu berarti rendering server berfungsi seperti yang diharapkan, dan Anda dapat mulai memperluas aplikasi Anda!



Mengapa kita masih membutuhkan bundle.js?



Dalam kasus aplikasi yang sangat sederhana, yang dibahas di sini, tidak perlu menyertakan bundle.js - tanpa file ini, aplikasi kita akan tetap berfungsi. Namun dalam kasus aplikasi nyata, Anda masih perlu menyertakan file ini.



Ini akan memungkinkan browser yang dapat menangani JavaScript untuk mengambil alih pekerjaan dan kemudian berinteraksi dengan halaman Anda yang sudah ada di sisi klien, dan browser yang tidak tahu cara mengurai JS akan membuka halaman dengan HTML yang diinginkan yang dikembalikan server.



Hal-hal untuk diingat



Terlepas dari kenyataan bahwa rendering server terlihat cukup mudah, saat mengembangkan aplikasi, Anda perlu memperhatikan beberapa topik yang sekilas tidak terlalu jelas:



  • , , . , , HTML, this.state



    ,
  • componentDidMount



    — , , . , , . , ( res.render



    ) , . -
  • react (. @reach/router react-router) , URL, . !



All Articles