
عندما يتعلق الأمر بمكتبة Ext JS ، يجب أن تسمع الكثير من الأشياء السلبية من خبراء: ثقيلة ، باهظة الثمن ، عربات التي تجرها الدواب. كقاعدة عامة ، ترتبط معظم المشاكل بعدم القدرة على طهيها. مشروع تم تجميعه بشكل صحيح باستخدام Sencha Cmd مع جميع css ، الصور تزن في الإنتاج في منطقة 1Mb ، والتي يمكن مقارنتها بنفس الزاوية. نعم ، ومواطن الخلل ليست أكثر بكثير ...
يمكن معاملة بنات أفكار Sencha بشكل مختلف ، ولكن حتى خصومها المبدئيين يدركون أنه من الصعب العثور على أفضل حل لبناء مشاريع جادة على الإنترانت.
في رأيي ، الشيء الأكثر قيمة في Ext JS ليس مجموعة من مكونات واجهة المستخدم ، بل هي بنية OOP جيدة. حتى مع الأخذ في الاعتبار التطور السريع لـ JS في السنوات الأخيرة ، فإن العديد من الأشياء الضرورية التي تم تنفيذها في Ext JS قبل 7 سنوات لا تزال مفقودة في الفئات الأصلية (مساحات الأسماء ، mixins ، الخصائص الثابتة ، الاتصال المريح للأساليب الأم). هذا ما دفعني قبل بضع سنوات لتجربة إطلاق دروس Ext JS في الخلفية. حول التجارب المماثلة الأولى ، قمت بالفعل بنشر مشاركات على حبري. تصف هذه المقالة تنفيذًا جديدًا للأفكار القديمة وعددًا من الأفكار الجديدة.
قبل أن نبدأ ، انتبه إلى السؤال: ما رأيك ، وأين يتم تنفيذه ، وماذا يفعل مقتطف الشفرة أدناه؟
Ext.define('Module.message.model.Message', { .... ,async newMessage() { ......... this.fireEvent('newmessage', data); ...... } ... })
يتم تنفيذ هذا الرمز على الخادم ويؤدي إلى حدوث حدث "newmessage" في جميع مثيلات فئة "Module.message.model.Message" على جميع أجهزة العميل المتصلة بالخادم.
لتوضيح إمكانيات استخدام Ext JS من جانب الخادم ، سنقوم بتحليل مشروع دردشة بسيط. لن نقوم بأي تسجيل دخول ، فقط عندما تدخل المستخدم يدخل لقبًا. يمكنك نشر رسائل عامة أو خاصة. يجب أن تعمل الدردشة في الوقت الفعلي. يمكن لأولئك الذين يرغبون على الفور تجربة كل هذا الاقتصاد في الأعمال التجارية.
التثبيت
للبدء ، نحتاج إلى nodejs 9+ و redis-server (من المفترض أنه مثبت بالفعل).
git clone https://github.com/Kolbaskin/extjs-backend-example cd extjs-backend-example npm i
نبدأ الخادم:
node server
في المتصفح ، افتح صفحة
localhost : 3000 / www / auth /
أدخل بعض اللقب واضغط على Enter.
المشروع تجريبي ، لذلك لا يوجد دعم للمتصفحات القديمة (هناك تصميمات ES8) ، استخدم Chrome أو FF الجديد.
الخادم
دعنا نذهب بالترتيب.
كود الخادم (server.js)
كما ترى ، هنا كل شيء أكثر أو أقل مستوى للخادم على السريع. من المهم إدراج فئات Ext JS لخدمة المسارات المقابلة:
app.use('/api/auth', Ext.create('Api.auth.Main')); app.use('/www/auth', Ext.create('Www.login.controller.Login'));
تنفيذ REST API
تقدم فئة Api.auth.Main الطلبات إلى REST API (المحمية / الراحة / المصادقة / Main.js).
Ext.define('Api.auth.Main', { extend: 'Api.Base',
إنشاء صفحة HTML ، باستخدام XTemplate على الخادم
الفئة الثانية ، Www.login.controller.Login ، تبني صفحة html عادية مع نموذج تسجيل دخول (محمي / www / login / controller / Login.js).
Ext.define('Www.login.controller.Login', {
تستخدم القوالب نموذج XTemplate القياسي (المحمي / www / login / view / login.tpl)
<h2>{pageTitle} (date: {[Ext.Date.format(values.date,'dmY')]})</h2> <form method="post"> <input name="name" placeholder="name"> <button type="submit">enter</button> </form>
كل ما هو موضح أعلاه هو مجموعة قياسية تمامًا ، سيقول القارئ الدقيق ، ولهذا لم تكن هناك حاجة لتسييج هذه الحديقة مع نقل Ext JS إلى الخادم. لذلك ، ننتقل إلى الجزء الثاني من المقالة ، والذي سيوضح ما كان الهدف منه جميعًا.
الزبون
لنقم بإنشاء تطبيق Ext JS الخاص بالعميل المعتاد في الدليل الثابت. في هذا المثال ، أنا لا أعتبر عمداً استخدام cmd ، لقد أخذت الموضوع الشامل المدمج والموضوع القياسي. قضايا الجمعية هي موضوع منفصل ، ربما ، سيخصص وظيفة منفصلة.
يبدأ كل شيء مع app.js
إن وجود مقبس ويب هو نقطة حاسمة ، فهو يسمح لك بتنفيذ كل السحر الموصوف أدناه.
تخطيط العناصر على الصفحة موجود في فئة Admin.view.Viewport (static / app / view / Viewport.js). لا يوجد شيء مثير للاهتمام هناك.
يتم تنفيذ العناصر الوظيفية الرئيسية (قائمة المستخدمين وشريط الرسائل ونموذج الإرسال) كوحدات منفصلة.
قائمة المستخدمين
الخوارزمية البسيطة لهذه القائمة هي كما يلي: في لحظة فتح الصفحة ، يتم تحميل المستخدمين الحاليين من الخادم. عند اتصال المستخدمين الجدد ، يقوم الخادم بإنشاء حدث "إضافة" في فئة "Module.users.model.UserModel" ، عند قطع الاتصال ، في نفس الفئة ، يتم رفع حدث "إزالة". الشيء هو أن الحدث يتم تشغيله من جانب الخادم ، ويمكنك تتبعه على العميل.
الآن ، أول شيء أولاً. من جانب العميل ، يخزن بيانات المتجر (static / app / modules / users / store / UsersStore.js)
Ext.define('Module.users.store.UsersStore', { extend: 'Ext.data.Store' ,autoLoad: true ,total: 0 ,constructor() {
هناك نقطتان مثيرتان للاهتمام. أولاً ، في السطر "const data = await this.dataModel. $ Read ()؛" يتم استدعاء طريقة الخادم للنموذج. الآن لا تحتاج إلى استخدام Ajax ، وبروتوكولات الدعم ، وما إلى ذلك ، ما عليك سوى استدعاء طريقة الخادم باعتبارها محلية. في الوقت نفسه ، لا يتم التضحية بالسلامة (المزيد عن ذلك أدناه).
ثانيًا ، يتيح لك البناء القياسي لـ this.dataModel.on (...) تتبع الأحداث التي سيتم إنشاؤها بواسطة الخادم.
النموذج هو جسر بين أجزاء العميل والخادم من التطبيق. إنه مثل ازدواجية الضوء - ينفذ خصائص كل من الواجهة الأمامية والخلفية. دعونا ننظر إلى النموذج بعناية.
Ext.define('Module.users.model.UserModel', { extend: 'Core.data.DataModel' ,testClientMethod() { ... } ,testGlobalMethod() { ... } ,privateServerMethod() { .... } ,async $read(params) {
انتبه إلى التعليقات / * النطاق: الخادم * / و / * النطاق: العميل * / - هذه البنيات هي تصنيفات للخادم تحدد من خلالها نوع الطريقة.
testClientMethod - تعمل هذه الطريقة حصريًا على العميل وهي متاحة فقط من جانب العميل.
testGlobalMethod - تعمل هذه الطريقة على العميل وعلى الخادم وهي متاحة للاستخدام من جانب العميل والخادم.
privateServerMethod - يتم تنفيذ الطريقة على الخادم وهي متاحة للاتصال فقط على الخادم.
يعد $ read هو النوع الأكثر إثارة للاهتمام من الطرق التي يتم تشغيلها على جانب الخادم فقط ، ولكن يمكنك تسميتها على كل من العميل وعلى الخادم. البادئة $ تجعل أي طريقة من جانب الخادم متاحة من جانب العميل.
يمكنك تتبع اتصال العميل وقطع الاتصال باستخدام مقبس الويب. يتم إنشاء مثيل لفئة Base.wsClient لكل اتصال مستخدم (محمي / base / wsClient.js)
Ext.define('Base.wsClient', { extend: 'Core.WsClient'
تحتوي طريقة fireEvent ، على عكس الطريقة القياسية ، على معلمة إضافية ، حيث يتم تمريرها على العميل الذي يجب تشغيل الحدث. من المقبول تمرير معرف عميل واحد ، أو صفيف من المعرّفات ، أو سلسلة "الكل". في الحالة الأخيرة ، سيتم تشغيل الحدث على جميع العملاء المتصلين. خلاف ذلك ، هذا هو معيار FireEvent.
إرسال واستقبال الرسائل
وحدة تحكم النموذج (static / app / admin / modules / Messages / view / FormController.js) مسؤولة عن إرسال الرسائل.
Ext.define('Module.messages.view.FormController', { extend: 'Ext.app.ViewController' ,init(view) { this.view = view;
لا يتم حفظ الرسالة في أي مكان على الخادم ، ويتم ببساطة تشغيل حدث "newmessage". من الأهمية بمكان استدعاء "this.fireEvent ('newmessage'، data.to، msg) ؛" ، حيث يتم تمرير معرفات العميل كمستلمين للرسائل. وبالتالي ، يتم تنفيذ توزيع الرسائل الخاصة (static / app / admin / modules / Messages / model / Model.js).
Ext.define('Module.messages.model.Model', { extend: 'Core.data.DataModel' ,async $newmessage(data) { const msg = { user: data.user, message: data.message } if(data.to && Ext.isArray(data.to) && data.to.length) { this.fireEvent('newmessage', data.to, msg); } else { this.fireEvent('newmessage', 'all', msg); } return true; } })
كما هو الحال مع المستخدمين ، يتم تشغيل بيانات قائمة الرسائل بواسطة المتجر (static / app / admin / modules / Messages / store / MessagesStore.js)
Ext.define('Module.messages.store.MessagesStore', { extend: 'Ext.data.Store', fields: ['user', 'message'], constructor() {
بشكل عام ، هذا هو كل ما هو مثير للاهتمام في هذا المثال.
أسئلة محتملة
إن توفر طرق الخادم على العميل أمر جيد بالطبع ، ولكن ماذا عن الأمان؟ اتضح أن المخترق الشرير يمكنه رؤية رمز الخادم ومحاولة كسر الواجهة الخلفية؟لا ، لن ينجح. أولاً ، تتم إزالة جميع طرق الخادم من رمز الصف عند إرسالها إلى متصفح العميل. لهذا الغرض ، الغرض من نطاق التعليقات / التوجيهات / *: ... * /. ثانيًا ، يتم استبدال رمز الطريقة الأكثر شيوعًا من جانب الخادم من خلال بنية وسيطة تنفذ آلية المكالمة عن بُعد على جانب العميل.
مرة أخرى حول الأمن. إذا كان يمكن استدعاء طرق الخادم على العميل ، اتضح ، هل يمكنني استدعاء أي طريقة من هذا القبيل؟ وإذا كانت هذه طريقة تنظيف قاعدة البيانات؟من العميل ، يمكنك فقط استدعاء طرق لها البادئة $ في اسمهم. لمثل هذه الأساليب ، أنت بنفسك تحدد منطق الشيكات والوصول. مستخدم خارجي ليس لديه حق الوصول إلى طرق الخادم بدون $ ، حتى أنه لن يراها (انظر الإجابة السابقة)
يبدو أن لديك نظامًا مترابطًا يرتبط فيه العميل والخادم ارتباطًا وثيقًا. هل القياس الأفقي ممكن؟النظام ، في الواقع ، يبدو متجانسا ، لكنه ليس كذلك. يمكن للعميل والخادم "العيش" على أجهزة مختلفة. يمكن تشغيل العميل على أي خادم ويب تابع لجهة خارجية (Nginx ، Apache ، إلخ.). يتم حل مشكلة فصل العميل والخادم بسهولة كبيرة عن طريق منشئ المشروع التلقائي (يمكنني كتابة منشور منفصل حول هذا). لتنفيذ آلية مراسلة الخدمة الداخلية ، يستخدم النظام قوائم الانتظار (أي أن Redis مطلوب لذلك). وبالتالي ، يمكن تحجيم جزء الخادم بسهولة أفقيًا بمجرد إضافة آلات جديدة.
باستخدام نهج التطوير المعتاد ، كقاعدة عامة ، توفر الواجهة الخلفية مجموعة معينة من واجهات برمجة التطبيقات التي يمكنك الاتصال بتطبيقات العميل المتنوعة (موقع الويب ، تطبيق الهاتف المحمول). في حالتك ، اتضح أنه فقط العميل المكتوب في Ext JS يمكنه العمل مع الواجهة الخلفية؟على الخادم ، ولا سيما في نماذج الوحدة النمطية ، يتم تنفيذ منطق عمل معين. من أجل توفير الوصول إليها من خلال REST API ، يكفي "غلاف" صغير. يتم تقديم مثال مطابق في الجزء الأول من هذه المقالة.
الاستنتاجات
كما ترون ، من أجل الترميز المريح للتطبيقات المعقدة إلى حد ما ، من الممكن جدًا الحصول على مكتبة واحدة في الواجهة الأمامية والخلفية. هذا له فوائد كبيرة.
تسريع عملية التطوير. يمكن لكل عضو من أعضاء الفريق العمل على الواجهة الأمامية والخلفية. توقف العمل لسبب "أنا في انتظار ظهور واجهة برمجة التطبيقات هذه على الخادم" لم يعد ملائمًا.
كود أقل. يمكن استخدام نفس أقسام التعليمات البرمجية على العميل وعلى الخادم (الشيكات ، التحقق ، إلخ).
الحفاظ على مثل هذا النظام هو أبسط وأرخص بكثير. بدلاً من مبرمجين متنوعين ، سيكون النظام قادرًا على دعم واحد (أو نفس الاثنين لكنهما قابلان للتبادل). وللسبب نفسه ، فإن المخاطر المرتبطة بدوران الفريق أقل أيضًا.
القدرة على إنشاء أنظمة الوقت الحقيقي خارج منطقة الجزاء.استخدام نظام اختبار واحد للواجهة والخلفية.