ما هو إطار العمل؟ ولماذا لا نبدأ من الصفر
درس 1 من 6
1ما هو إطار العمل؟ ولماذا لا نبدأ من الصفر
الصورة الكبيرةفي الأقسام السابقة بنيت كل شيء بيدك: صفحة تعيد رسمها بـ render()، خادمًا تقرأ فيه الطلب وتفحص المسار بسلسلة if، وقاعدة بيانات تفتح لها الجلسة وتغلقها. لاحظت أن هذه الأعمال نفسها تتكرّر في كل مشروع، بغضّ النظر عن فكرته. إطار العمل هو هذا الجزء المتكرّر مكتوبًا مرة واحدة، مختبرًا، ومشتركًا بين ملايين المطوّرين. أنت تضيف فقط ما يجعل تطبيقك مختلفًا.
ماذا تشاهد في الرسم: ما يتكرّر في كل تطبيق ينتقل إلى الإطار، ويبقى لك ما يخصّ تطبيقك: قائمة المقهى، وقواعد الطلب، وشكل الصفحة.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
تخيّل أنك تبني بيتًا. تستطيع أن تصنع كل طوبة بيدك وتصبّ الأساس وتمدّ أسلاك الكهرباء وأنابيب الماء وحدك، وسيأخذ هذا سنوات. أو تبدأ من هيكل خرساني جاهز: الأعمدة والسقف ونقاط الكهرباء والماء في أماكنها، وأنت تختار عدد الغرف والألوان والأثاث. هذا الهيكل هو إطار العمل. ولأنه جاهز، له قواعد: لا تستطيع أن تضع غرفة مكان عمود. أمّا المكتبة فهي مثل صندوق عدّة تستعيره: مطرقة ومثقاب تستخدمهما حين تريد وكيف تريد. الفرق في كلمة واحدة: أنت تمسك العدّة، لكن الهيكل هو الذي يحدّد أين تعمل.
الإطار يقلب اتجاه التحكّم (Inversion of Control، أو «مبدأ هوليوود»: لا تتصل بنا، نحن نتصل بك). الإطار يملك الحلقة الرئيسية ودورة الحياة: يستقبل الحدث (طلب HTTP، ضغطة زر)، يختار الدالة المسجّلة له، يمرّر لها المدخلات بعد تحليلها، ثم يتولّى الرد أو الرسم والأخطاء. أنت تكتب نقاط امتداد: دوال مسارات، مكوّنات، وسائط (middleware)، نماذج تحقّق. المكتبة على العكس: دوال تناديها أنت ويعود التحكّم إليك (json.dumps، fetch). الحدّ ليس حادًا دائمًا: React يسمّي نفسه مكتبة لكنه ينادي مكوّناتك مثل الإطار، وNext.js إطار مبني فوق React.
قبل أن تبدأ
🖼️الواجهات الأمامية
بنيت قائمة مهام وأعدت رسمها بـ render() يدويًا.
📨الخادم وREST
رأيت في درس REST نفس المهمة مرة بـ http.server ومرة بـ Flask.
🐍دوال بايثون
تعرف أن الدالة قيمة تستطيع حفظها في قاموس وتمريرها.
الفكرة الجوهرية
1المشكلة: كود تعيد كتابته في كل مشروع
كل تطبيق ويب، مهما كانت فكرته، يحتاج نفس الأعمال: يستقبل الطلب، يعرف أي مسار طُلب، يقرأ البيانات المرسلة، يتحقّق منها، يرد بالرمز الصحيح، ويتعامل مع الأخطاء. هذا خادم مقهى مكتوب بيد، بلا أي إطار. شغّله ولاحظ كم سطرًا لا علاقة له بالقهوة:
# a server by hand: every new route is one more "elif", in every project you build
orders = []
def handle(method, path, body=None):
if method == "GET" and path == "/menu":
return 200, ["latte", "mocha", "v60"]
elif method == "POST" and path == "/orders":
if not isinstance(body, dict) or "item" not in body:
return 400, {"error": "item is required"}
orders.append(body["item"])
return 201, {"id": len(orders), "item": body["item"]}
elif method == "GET" and path.startswith("/orders/"):
number = path.split("/")[-1]
if not number.isdigit() or not 1 <= int(number) <= len(orders):
return 404, {"error": "no such order"}
return 200, {"id": int(number), "item": orders[int(number) - 1]}
return 404, {"error": "not found"}
print(handle("GET", "/menu"))
print(handle("POST", "/orders", {"item": "latte"}))
print(handle("POST", "/orders", {}))
print(handle("GET", "/orders/1"))
print(handle("GET", "/orders/9"))يعمل، لكن تخيّل خمسين مسارًا: سلسلة elif طويلة، وتحقّق مكرّر في كل فرع، وفحص يدوي للأرقام. والأسوأ أنك ستكتب نفس الهيكل في مشروعك القادم، ومن بعده.
2الحل: إطار صغير تبنيه بنفسك
لنأخذ الجزء المتكرّر ونضعه في مكان واحد. هذا «إطار» كامل في خمسة عشر سطرًا: يحفظ دوالك في قاموس حسب الفعل والمسار، وحين يصل طلب يختار الدالة ويناديها، ويحوّل أي خطأ تحقّق إلى رد 400. علامة @app.route هي نفسها التي رأيتها في Flask: تقول للإطار «سجّل هذه الدالة لهذا المسار».
class TinyFramework:
def __init__(self):
self.routes = {}
def route(self, method, path):
def register(fn):
self.routes[(method, path)] = fn # remember your function, do not call it yet
return fn
return register
def handle(self, method, path, body=None):
fn = self.routes.get((method, path))
if fn is None:
return 404, {"error": "not found"}
try:
return 200, (fn(body) if body is not None else fn()) # the framework calls you
except ValueError as error:
return 400, {"error": str(error)}
# ---- everything below is your app; everything above is the framework ----
app = TinyFramework()
@app.route("GET", "/menu")
def menu():
return ["latte", "mocha", "v60"]
@app.route("POST", "/orders")
def create_order(body):
if "item" not in body:
raise ValueError("item is required")
return {"item": body["item"], "status": "brewing"}
print(app.handle("GET", "/menu"))
print(app.handle("POST", "/orders", {"item": "mocha"}))
print(app.handle("POST", "/orders", {}))
print(app.handle("GET", "/tea"))انظر لما تغيّر: دوال تطبيقك صارت قصيرة وتتكلّم عن القهوة فقط، ولا تعرف شيئًا عن رموز 404 و400. لاحظ أيضًا أنك لم تنادِ menu() بنفسك أبدًا: الإطار هو الذي ناداها حين وصل الطلب. Flask وFastAPI وExpress تعمل بنفس الفكرة بالضبط، لكن بآلاف الأسطر المختبرة بدل خمسة عشر.
3مكتبة أم إطار؟ من ينادي من
الكلمتان تختلطان كثيرًا، والفرق الحقيقي في سؤال واحد: من ينادي من؟ المكتبة تناديها أنت متى شئت ثم يعود التحكّم إليك، مثل json.dumps أو math.sqrt. الإطار يمسك التحكّم وينادي كودك في اللحظة التي يقرّرها هو.
| المكتبة | الإطار | |
|---|---|---|
| من يتحكّم؟ | أنت: تنادي دالة وتأخذ النتيجة | الإطار: ينادي دوالك عند الحدث |
| شكل الكود | كودك هو البرنامج، والمكتبة قطعة داخله | الإطار هو البرنامج، وكودك قطع داخله |
| القواعد | قليلة: استخدمها كما تشاء | كثيرة: أسماء ملفات ومجلدات وطريقة كتابة |
| أمثلة | requests، pandas، lodash، json | Express، FastAPI، Django، Next.js |
وبعض الأدوات في المنتصف: React يسمّي نفسه «مكتبة» لأنه يهتم بالواجهة فقط، لكنه ينادي مكوّناتك مثل الإطار، لذلك يسمّيه الناس إطارًا في الحديث اليومي. أمّا Next.js فإطار كامل مبني فوق React.
4ماذا تكسب وماذا تدفع
ما تكسبه: سرعة (التوجيه والتحقّق والأخطاء جاهزة)، أمان أكثر (الإطار يسدّ ثغرات معروفة كنت ستنساها)، هيكل مشترك (أي مطوّر يعرف الإطار يفهم مشروعك في يوم)، توثيق ومجتمع يجيب أسئلتك، وفرص عمل تطلب الإطار بالاسم.
ما تدفعه: وقت لتعلّم قواعده، «سحر» يحدث دون أن تراه فيصعب عليك فهم الخطأ أحيانًا، تحديثات تغيّر طريقة الكتابة بين نسخة وأخرى، وصعوبة الانتقال لإطار آخر بعد أن يكبر المشروع.
5متى لا تحتاج إطارًا؟
الإطار أداة، لا شرط. لا تحتاجه في سكربت يرتّب ملفات أو يحسب تقريرًا، ولا في صفحة تعريفية ثابتة تكفيها HTML وCSS، ولا حين تتعلّم فكرة جديدة وتريد أن ترى كل خطوة بعينك (ولهذا بدأت أنت بلا إطار). تحتاجه حين يكبر التطبيق، أو يعمل عليه أكثر من شخص، أو يستقبل مستخدمين حقيقيين يجب حمايتهم.
مثال محلول
ما كتبته بيدك، ومن سيكتبه بدلًا منك. هذه أعمال قمت بها في الأقسام السابقة، والإطار الذي يتولّاها في هذا القسم:
| العمل | أين كتبته بيدك | من يتولّاه هنا |
|---|---|---|
| قراءة الطلب واختيار المسار | درس REST بـ http.server | Express وFastAPI |
| التحقّق من البيانات المرسلة | سلاسل if في كل مسار | Pydantic داخل FastAPI |
| توثيق الواجهة البرمجية | لم تكتبه أصلًا | صفحة /docs في FastAPI تلقائيًا |
| إعادة رسم القائمة عند كل تغيير | render() في درس DOM | React |
| صفحات يبنيها الخادم | قوالب Jinja في درس SSR | Next.js |
لاحظ أن كل سطر في الجدول شيء فهمته قبل أن يُؤتمت لك. هذا هو الترتيب الصحيح: تفهم الفكرة بيدك، ثم تسلّمها للإطار وأنت تعرف ماذا يفعل.
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| الإطار يغنيك عن تعلّم اللغة. | الإطار مكتوب باللغة نفسها، وكل خطأ فيه خطأ لغة. من يعرف JavaScript جيدًا يتعلّم React في أسابيع. |
| استخدام إطار ليس «برمجة حقيقية». | كل الشركات تقريبًا تبني بأطر. المهارة الحقيقية أن تعرف ماذا يفعل الإطار تحت الغطاء، وهذا ما فعلته حين بنيت كل شيء بيدك أولًا. |
| الإطار الأشهر هو الأفضل لكل مشروع. | الأشهر يعني مجتمعًا أكبر ووظائف أكثر، لكن سكربتًا صغيرًا لا يحتاج إطارًا، وفريقًا يكتب بايثون لن يختار إطار JavaScript. |
اختبر فهمك
ما الفرق الجوهري بين المكتبة والإطار؟
السؤال هو من يمسك التحكّم. مع المكتبة تبقى أنت المتحكّم، ومع الإطار يقرّر هو متى تعمل دوالك.
في الإطار الصغير الذي بنيته، من نادى الدالة menu()؟
@app.route سجّلها فقط، وhandle اختارها من القاموس ونادتها حين وصل GET /menu.
أي حالة لا تحتاج فيها إطارًا غالبًا؟
السكربت الصغير لا يستقبل طلبات ولا يرسم واجهة، فلا يوجد عمل متكرّر يحتاج هيكلًا جاهزًا.
بطاقات مراجعة سريعة
@app.route، لينادي الإطار الدالة لاحقًا.تحقق من فهمك
اشرح لصديق غير تقني ما هو إطار العمل في جملتين.
هيكل جاهز للبرنامج فيه كل الأجزاء التي تتكرّر في أي تطبيق. أنا أضيف فقط ما يخصّ تطبيقي، والهيكل يشغّله في الوقت المناسب.
لماذا تعلّمت بناء الخادم والواجهة بيدك قبل الأطر؟
لأفهم ما يؤتمته الإطار، فلا يصير «سحرًا»، وأستطيع فهم أخطائه وتغيير إطاري لاحقًا دون أن أبدأ من الصفر.
اذكر شيئًا تكسبه وشيئًا تدفعه حين تستخدم إطارًا.
تكسب السرعة والأمان والهيكل المشترك، وتدفع وقت تعلّم قواعده وصعوبة الخروج منه لاحقًا.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.