Skip to content

ما هو Server Side Rendering ؟ وما الهدف منه ؟

ما هو Server Side Rendering ؟ وما الهدف منه ؟

1 يناير 2019 | 00:00

Server Side Rendering - اختصارا SSR - هي عملية يتم خلالها توليد كود HTML الخاص بتطبيقات جافاسكريبت الحديثة في الخادم عوضا عن المتصفح.

لماذا ؟ وكيف ؟ هذان هما السؤالان اللذان سنجيب عليهما في هذا الدرس :)

لماذا أصبحنا نتكلم عن Server Side Rendering

في تطبيقات الويب التقليدية، حالما يقوم المستخدم بالدخول إليها عن طريق عنوان URL، يتم إرسال طلب Http إلى الخادم حيث التطبيق مستضاف. بعد ذلك يقوم خادم الويب في تلك الإستضافة (مثلا خادم Python ،PHP أو غيرهما) بتوليد كود HTML الخاص بتلك الصفحة بشكل كامل ويقدمه إلى المتصفح ليقوم بعرضه.

هذه العملية تتكرر مع كل صفحة يتم طلب من طرف المستخدم.

المتصفح كان عمله بسيطا وسهلا مقارنة بحاله اليوم :)

ولكن في أيامنا هذه، انتشر ما يعرف بتطبيقات الويب أحادية الصفحة، وأصبح معظم العمل يتم على مستوى المتصفح. هذا الأخير يقوم فقط بطلب البيانات من الخادم الذي يلبي الطلب على شكل واجهة برمجية API يتم استهلاكها في المتصفح بطبيعة الحال وبناء عليها يتم توليد وتقديم كود HTML للمستخدم عن طريق جافاسكريبت.

في مدونة توتومينا تحدثنا مرارا عن عدد من أطر عمل جافاسكريبت التي تساعد المطورين على بناء تطبيقات ويب حديثة بأقل مجهود.

React.js واحد من أشهر هذه الأطر وأكثرها استخداما.

في تطبيق مبني على React.js مثلا، عندما نطلب صفحة من الخادم فإن كل ما نحصل عليه هو مايلي :

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

نلاحظ أنها صفحة تقريبا فارغة، لا تتضمن سوى <div> وأسفل منه ملف جافاسكريبت app.js يتم استدعاؤه.

هكذا تبدو تطبيقات الويب أحادية الصفحة في كل أطر عمل جافاسكريبت الأخر (Vue.js ،Angular إلخ…).

عندما تصل هذه الصفحة إلى المتصفح، يقوم الأخير بواسطة جافاسكريبت الموجود في الملف app.js، بتحديد المكونات (Components) التي يجب توليدها وتركيبها في الصفحة بناء على URL (أو Route) المطلوب. كل هذا العمل يتم في المتصفح (Client) بعد أن تصل إليه الصفحة الفارغة قادمة من الخادم (Server).

الآثار الجانبية

تطبيقات الويب ذات الصفحة الواحدة جميلة وتجعل المستخدم يحسن كأنه في تطبيق مكتبي أصلي، ولكن لكل شيء جميل آثار جانبية لا يجب إهمالها، أو على الأقل يجب أن نكون على دراية بها.

إليكم أهم الآثار الجانبية أو النواقص التي تعاني منها تطبيقات SPA :

لإيجاد حلول لكل هذه المشاكل تم اختراع مصطلح Server Side Rendering، وفي الحقيقة هو كان دائما موجودا ولكن لم يكن أحد يتحدث عنه مثل اليوم لأنه كان الخيار الأصلي والوحيد بالنسبة لمطوري الويب.

تم بناء عدة أطر عمل جافاسكريبت حول أطر عمل الواجهات الأمامية المعروفة لكي تصبح قادرة على العمل في الخوادم وليس في المتصفحات فقط.

تطبيقات الويب أحادية الصفحة التي تستفيد من SSR تعرف باسم Isomorphic applications، أي تطبيقات الويب المتماثلة لأنها تشتغل في بيئتي الويب والعميل معا. (تعرف كذلك باسم Universal Apps).

مثال عملي ل Server Side Rendering

في هذا الدرس سنرى كيفية تقديم تطبيق ويب (تطبيق React.js) من الخادم، ولن نقوم باستخدام أي من أطر العمل التي سبقت الإشارة إليها، بل سنرى كيفية فعل ذلك من الصفر وباستخدام إطار العمل Express فقط.

سنقوم بإعداد مشروع React.js من الصفر لأن مشاريع create-react-app لا تدعم Server side rendering :) لذلك سننشئ المشروع بناء على البنية كما تلاحظون في الصورة أسفله.

shared/App.js

في الملف shared/App.js نجد مكون React عادي :

import React from "react";
 
function App() {
  return (
    <div className="App">
      <h1>React SSR Application</h1>
    </div>
  );
}
 
export default App;

server/index.js

أما الملف server/index.js فيسكون على هيئة كهذه :

import React from "react";
import { renderToString } from "react-dom/server";
 
import express from "express";
 
import App from "../shared/App";
 
const app = express();
 
app.use(express.static("public"));
 
app.get("*", (req, res) => {
  const reactDom = renderToString(<App />);
 
  res.send(htmlTemplate(reactDom));
});
 
app.listen(process.env.PORT || 3000, () => {
  console.log("Server is listening");
});
 
function htmlTemplate(reactDom) {
  return `
  <!DOCTYPE html>
    <head>
      <title>Isomorphic React Application</title>
    </head>
    <body>
      <div id="root">${reactDom}</div>
      <script src="/bundle.js"></script>
    </body>
  </html>
`;
}

سأقوم بشرح ما قد يبدو غير مفهوم في هذا الملف.

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

هنا قمنا باستيراد الوظيفة renderToString من react-dom/server، وتلعب دور render في الخادم. الفرق أنها تقوم بإرجاع ال HTML على شكل String وليس شجرة DOM كما جرت عليه العادة في ناحية المتصفح.

const app = express();
 
app.use(express.static("public"));

السطر الأول هنا يقوم بإنشاء تطبيق إكسبريس، بينما الثاني يخبر Express بأنه سيجد الملفات الساكنة في المجلد public. الملف الساكن الوحيد في مثالنا هو build.js الذي يتم توليده من طرف Webpack.

app.get("*", (req, res) => {
  const reactDom = renderToString(<App />);
 
  res.send(htmlTemplate(reactDom));
});

هنا نقول لإكسبريس أن يقوم بتحويل المكون <App/> إلى كود Html على شكل String وذلك عن طريق الوظيفة renderToString.

بعد ذلك يتم تقديم هذا Html للمتصفح عن طريق res.send.

browser/index.js

الملف الأخير الذي يخصنا في هذا المثال هو browser/index.js:

import React from "react";
import ReactDOM from "react-dom";
 
import App from "../shared/App";
 
ReactDOM.hydrate(<App />, document.getElementById("root"));

نرى بأن هذا الملف بسيط للغاية، فقط قمنا باستيراد المكون <App/> وركبناه في العنصر div#root.

النقطة الوحيدة المثيرة للإنتباه في هذا الملف هي أننا استخدمنا الوظيفة ReactDOM.hydrate عوض ReactDOM.render. وفي الحقيقة الوظيفتان تقومان بنفس الدور، الفرق أن hydrate مصممة خصيصا للحالة التي يتم فيها تقديم كود HTML المكون بواسطة renderToString، يعني حالة Server Side Rendering.

هكذا عندما نقوم بتشغيل المشروع على المتصفح - عن طريق npm run dev - سنلاحظ بأن الشفرة المصدرية للصفحة موجودة بالكامل، لأنه تم توليدها وتقديمها من طرف الخادم.

ستجدون الشفرة المصدرية لهذا المثال كاملة (بما فيها ملف webpack.config.js :) ) على Github في هذا الرابط.

في الختام

كما رأينا، إضافة خاصية Server Side Rendering لتطبيقات الويب ليس بالأمر الصعب جدا ولكن ليس سهلا كذلك :) خاصة إذا كان التطبيق كبيرا وأكثر تعقيدا من مثالنا.

هل SSR ضروري ؟ هذا يعتمد على نوع التطبيق والهدف من ورائه. إذا كان التطبيق مثلا عبارة عن مدونة أو موقع تواصل مفتوح مثل فيسبوك أو تويتر، فمن الضروري أن يتم تقديم الشفرة المصدرية من الخادم. أما إذا كان التطبيق مغلقا، مثلا تطبيق لإدارة شركة أو موارد بشرية، فعندها قد لا يستحق العناء والمجهود اللذان سيبذلان من أجله.

على كل حال، هذا الموضوع مثار نقاش حاد بين المطورين كما ستلاحظون من خلال هذه المناقشة على منصة Reddit.

_سيكون من الرائع لو تشاركوا معنا كذلك آراءكم وتجاربكم حول هذه الفكرة لإغناء موضوعنا أكثر :) _

عيسى محمد علي
عيسى محمد علي
مطور ويب متخصص في الواجهات الأمامية، أحب التدوين وإغناء المحتوى التقني للغة الضاد وهذا كان السبب الرئيسي في إنشائي لمدونة توتومينا.