مقدمة
AngularJS سهل التطوير ، وله العديد من الميزات وتأثيرات جيدة ، مما يؤدي إلى مزيد من التطبيقات ، وبعض الفخاخ مصحوبة. تسرد هذه المقالة بعض المشكلات الشائعة التي تعرضها AngularJS للمشاكل. دعونا نلقي نظرة معا.
1. هيكل دليل MVC
AngularJs ، ليكون صريحا ، هو إطار MVC. لا يتم تعريف نموذجها بوضوح مثل إطار العمل Backbone.js ، ولكن بنيةه هي على نحو مناسب. عندما تعمل في إطار عمل MVC ، من الشائع تصنيفه حسب نوع الملف:
قوالب/ _login.html _feed.htmlapp/ app.js controllers/ logincontroller.js feedcontroller.js التوجيهات/ foundentrydirective.js الخدمات/ loginservice.js feedservice.js
يبدو أن هذا يبدو أنه هيكل واضح ، ناهيك عن أن القضبان تفعل الشيء نفسه. ومع ذلك ، بمجرد أن يبدأ التطبيق في التوسع ، سيؤدي هذا الهيكل إلى فتح العديد من الدلائل في وقت واحد. سواء كنت تستخدم Sublime أو Visual Studio أو VIM مع Tree Nerd ، فستستثمر الكثير من الوقت في الانزلاق لأعلى ولأسفل في شجرة الدليل.
على عكس تقسيم الملفات على النوع ، بدلاً من ذلك ، يمكننا تقسيم الملفات على الخصائص:
app/ app.js feed/ _feed.html feedcontroller.js feedentrydirective.js feedservice.js login/ _login.html logincontroller.js loginservice.js sharber/ capatalialfilter.js
يجعل بنية الدليل هذه من السهل علينا العثور على جميع الملفات المتعلقة بميزة ما ، وبالتالي تسريع تقدمنا في التطوير. في حين أنه قد يكون من المثير للجدل وضع ملفات .html و .js في مكان واحد ، فإن الوقت المحفوظ أكثر قيمة.
2. الوحدة النمطية
من الشائع جدًا وضع كل شيء تحت الوحدة الرئيسية. بالنسبة للتطبيقات الصغيرة ، لا توجد مشكلة في البداية ، لكنك ستجد قريبًا أن هناك شيئًا ما يخونه.
var app = Angular.Module ('app' ، []) ؛ app.Service ('myservice' ، function () {// service code}) ؛ app.controller ('myctrl' ، function ($ scope ، myservice) {// code code}) ؛بعد ذلك ، تتمثل الإستراتيجية المشتركة في تصنيف الكائنات من نفس النوع.
var services = Angular.Module ('Services' ، []) ؛ Services.Service ('myservice' ، function () {// service code}) ؛ Var Controllers = Angular.Module ('Controllers' ، ['Services']) ؛ controllers.controller ('myctrl' ، function ($ scope ، myService) {// controller code}) ؛ var app = Angular.module ('app' ، ['controllers' ، 'services']) ؛تشبه هذه الطريقة بنية الدليل المذكورة في الجزء الأول أعلاه: ليست جيدة بما فيه الكفاية. وفقًا لنفس الفلسفة ، يمكن تصنيفها حسب الخصائص ، مما يؤدي إلى قابلية التوسع.
var shareServicesModule = Angular.module ('shareServices' ، []) ؛ ServentServices.Service ('NetWorkservice' ، function ($ http) {}) ؛ var loginModule = Angular.module ('login' ، ['sharplesservices']) ؛ loginmodule.service ('loginservice' ، function (networkservice) {}) ؛ loginModule.Controller ('loginctrl' ، function ($ scope ، loginservice) {}) ؛ var app = Angular.module ('app' ، ['sharplesservices' ، 'login']) ؛عندما نطور تطبيقًا كبيرًا ، قد لا يكون كل شيء موجود في صفحة واحدة. إن وضع نفس النوع من الميزات في وحدة واحدة يجعل من السهل إعادة استخدام الوحدات النمطية عبر التطبيقات.
3. حقن التبعية
يعد حقن التبعية أحد أفضل الأنماط في AngularJS ، مما يجعل الاختبار أكثر بساطة ويوافق على الاعتماد على أي كائن محدد. طريقة الحقن من AngularJS مرنة للغاية. أسهل طريقة هي مجرد تمرير اسم التبعية إلى function الوحدة النمطية:
var app = Angular.module ('app' ، []) ؛ App.Controller ('mainctrl' ، function ($ scope ، $ timeout) {$ timeout (function () {console.log ($ scope) ؛} ، 1000) ؛}) ؛ هنا ، من الواضح أن MainCtrl يعتمد على $scope و $timeout .
كل شيء جميل حتى تكون مستعدًا لنشره على الإنتاج والرغبة في تبسيط الكود الخاص بك. إذا كنت تستخدم UglifyJS ، فإن المثال السابق سيصبح هكذا:
var app = Angular.Module ("app" ، []) ؛ app.controller ("mainctrl" ، function (e ، t) {t (function () {console.log (e)} ، 1e3)})كيف يعرف AngularJS من الذي يعتمد عليه MainCtrl؟ يوفر AngularJS حلًا بسيطًا للغاية ، أي تمرير التبعيات إلى صفيف ، والعنصر الأخير من الصفيف هو وظيفة ، ويتم استخدام جميع التبعيات كمعلمات لها.
App.Controller ('mainctrl' ، ['$ scope' ، '$ timeout' ، function ($ scope ، $ timeout) {$ timeout (function () {console.log ($ scope) ؛} ، 1000) ؛}]) ؛القيام بذلك سيجعل الكود مبسطًا ، ويعرف AngularJS كيفية تفسير هذه التبعيات الصريحة:
App.Controller ("MainCtrl" ، ["$ scope" ، "$ timeout" ، function (e ، t) {t (function () {console.log (e)} ، 1e3)}])3.1 التبعية العالمية
يحدث هذا في كثير من الأحيان عند كتابة برامج AngularJS: يتمتع كائن بالاعتماد ، وهذا الكائن يرتبط نفسه بالنطاق العالمي ، مما يعني أن هذه التبعية متوفرة في أي رمز AngularJS ، لكن هذا يدمر نموذج حقن التبعية ويسبب بعض المشكلات ، خاصة أثناء عملية الاختبار.
إن استخدام AngularJS يجعل من السهل تغليف هذه التبعيات العالمية في وحدات ، بحيث يمكن حقنها مثل الوحدات النمطية القياسية AngularJS.
UndersCore.js هي مكتبة رائعة تبسط رمز JavaScript بأسلوب وظيفي ، ويمكنك تحويله إلى وحدة نمطية بالطرق التالية:
var underscore = Angular.Module ('Underscore' ، []) ؛ insroscore.factory ('_' ، function () {return window._ ؛ // يجب أن يتم بالفعل تحميل insroscore على الصفحة}) ؛ var app = Angular.Module ('app' ، ['underscore']) ؛ App.Controller ('mainctrl' ، ['$ scope' ، '_' ، function ($ scope ، _) {init = function () {_.keys ($ scope) ؛} init () ؛}]) ؛يسمح هذا النهج للتطبيق بالاستمرار في التطور بأسلوب حقن التبعية AngularJS ، ويمكنه أيضًا تبديل السطح السفلي خلال مرحلة الاختبار.
قد يبدو هذا تافهًا وغير ضروري ، ولكن إذا كان الكود الخاص بك يستخدم استخدامًا صارمًا (ويجب استخدامه) ، فهذا ضروري.
رابعا. توسيع وحدة التحكم
وحدة التحكم هي اللحم والبطاطس AngularJS ، وإذا لم تكن حذراً ، فستضيف الكثير من المنطق ، خاصة في البداية. يجب ألا تعمل وحدة التحكم أبدًا على تشغيل DOM أو عقد محدد DOM ، وهذا هو المكان الذي نحتاج فيه إلى استخدام التعليمات ونماذج NG. وبالمثل ، يجب أن يوجد منطق العمل في الخدمة ، وليس وحدة التحكم.
يجب أيضًا تخزين البيانات في الخدمة ما لم تكن موجودة بالفعل بنطاق $. الخدمة نفسها هي Singleton وهي موجودة طوال حياة التطبيق ، لكن وحدة التحكم عابرة بين حالات التطبيق. إذا تم حفظ البيانات في وحدة التحكم ، عندما يتم إنشاء مثيل لها مرة أخرى ، فإنها تحتاج إلى إعادة تعيين البيانات من مكان ما. حتى إذا تم تخزين البيانات في LocalStorage ، فإن سرعة البحث هي ترتيب أبطأ من متغيرات JavaScript.
يعمل AngularJS بشكل جيد عند اتباع مبدأ المسؤولية الفردية (SRP). إذا كانت وحدة التحكم هي المنسق بين العرض والنموذج ، فيجب أن يحتوي على أقل قدر ممكن من المنطق ، مما سيسهل أيضًا الاختبار.
5. الخدمة مقابل المصنع
تقريبا كل مطور AngularJS يشعرون بالقلق من هذه الأسماء عندما يكون مبتدئًا ، وهو أمر لا يستحق ذلك حقًا ، لأنهم مجرد سكر نحوي لنفس الشيء تقريبًا!
فيما يلي تعريفاتها في رمز مصدر AngularJS:
Function Factory (name ، factoryfn) {return provider (name ، {$ get: factoryfn}) ؛ } خدمة الوظائف (الاسم ، مُنشئ) {return factory (name ، ['$ enjector' ، function ($ enjector) {return $ enjector.instantiate (constructor) ؛}]) ؛} من الرمز المصدر ، يمكنك أن ترى أن الخدمة ببساطة تستدعي وظيفة factory ، وتدعو الأخير وظيفة provider . في الواقع ، يوفر AngularJS أيضًا تغليف provider إضافي لبعض القيم والثوابت والديكور ، والتي لا تؤدي إلى ارتباك مماثل ، وتوثيقها واضح للغاية.
بما أن الخدمة تستدعي وظيفة factory فقط ، فما الفرق؟ الفكرة موجودة في $injector.instantiate : في هذه الوظيفة ، ينشئ $injector مثيلًا جديدًا في مُنشئ service .
فيما يلي مثال يوضح كيف يفعل service factory نفس الشيء:
var app = Angular.module ('app' ، []) ؛ App.Service ('HelloWorldService' ، function () {this.hello = function () {return "hello world" ؛} ؛}) ؛ App.Factory ('HelloWorLdFactory' ، function () {return {hello: function () {return "hello world" ؛}}}) ؛ عندما يتم حقن helloWorldService أو helloWorldFactory في وحدة التحكم ، فإن لديهم جميعًا طريقة Hello التي تُرجع "Hello World". يتم إنشاء إنشاء مُنشئ service مرة واحدة عند إعلانه ، ويتم تمرير كائن factory في كل مرة يتم حقنها ، ولكن لا يزال هناك مثال واحد فقط factory . جميع providers هم المفردون.
بما أنه يمكنك فعل نفس الشيء ، فلماذا تحتاج إلى نمطين مختلفين؟ بالمقارنة مع service ، يوفر factory مزيدًا من المرونة لأنه يمكنه إرجاع وظائف ، والتي يمكن إنشاؤها بعد ذلك. هذا يلبي مفهوم أنماط المصنع في البرمجة الموجهة للكائنات ، حيث يمكن أن يكون المصنع كائنًا يمكنه إنشاء كائنات أخرى.
app.factory ('hellofactory' ، function () {return function (name) {this.name = name ؛ this.hello = function () {return "hello" + this.name ؛} ؛} ؛} ؛}) ؛ فيما يلي مثال على وحدة التحكم ، باستخدام service factory ، يقوم helloFactory بإرجاع وظيفة تحدد قيمة name عند إنشاء كائن جديد.
App.Controller ('Helloctrl' ، function ($ scope ، helloWorldservice ، helloworldfactory ، hellofactory) {init = function () {helloworldservice.hello () ؛ // 'helloworldfactory.hello () ؛ init () ؛}) ؛عندما تكون مبتدئين ، من الأفضل استخدام الخدمة.
يعد Factory مفيدًا أيضًا عند تصميم فصل مع العديد من الطرق الخاصة:
App.Factory ('privatefactory' ، function () {var privatefunc = function (name) {return name.split (""). revers (). مع هذا المثال ، يمكننا أن نجعل طريقة privateFunc غير قابلة للوصول إلى واجهة برمجة تطبيقات privateFactory العامة. يمكن القيام بهذا النمط في service ، ولكنه أسهل في factory .
6. باتارانج غير مستخدم
Batarang هو البرنامج المساعد Chrome ممتاز لتطوير واختبار تطبيقات AngularJS.
يوفر Batarang القدرة على تصفح النماذج ، مما يمنحنا القدرة على مراقبة كيف يرتبط AngularJs بالنطاق ، وهو أمر مفيد للغاية عند التعامل مع التعليمات وعزل مجموعة من القيم المرتبطة.
يوفر Batarang أيضًا رسمًا بيانيًا للاعتماد ، وهو أمر مفيد إذا تعرضنا لقاعدة التعليمات البرمجية غير المختبرة ، والتي يمكن أن تحدد الخدمات التي يجب أن تركز عليها.
أخيرًا ، يوفر Batarang تحليل الأداء. يمكن استخدام Angular كحزمة ولديه أداء جيد ، ولكنه في بعض الأحيان ليس سلسًا لتطبيق مليء بالإرشادات المخصصة والمنطق المعقد. باستخدام أداة أداء Batarang ، يمكنك مراقبة الوظيفة التي تدير أطول وقت في دورة Digest. يمكن أن تظهر أدوات الأداء أيضًا شجرة ساعة كاملة ، وهي مفيدة عندما يكون لدينا الكثير من المراقبين.
7. الكثير من المراقبين
في النقطة السابقة ، ذكرنا أنه يمكن استخدام AngularJs كحزمة ولديها أداء جيد. نظرًا لأن عمليات فحص البيانات القذرة يجب إكمالها في دورة واحدة من الهضم ، بمجرد أن ينمو عدد المراقبين إلى حوالي عام 2000 ، فإن هذه الدورة ستؤدي إلى مشاكل كبيرة في الأداء. (لا يمكن القول أن الرقم 2000 يسبب انخفاضًا كبيرًا في الأداء ، ولكن هذه قيمة تجريبية جيدة. في إصدار إصدار AngularJS 1.3 ، هناك بالفعل بعض التغييرات التي تسمح بالتحكم الصارم في دورات Digest.)
سيقوم "تعبير وظيفة التنفيذ الفوري (IIFE) التالي" بطباعة عدد جميع المراقبين في الصفحة الحالية. يمكنك ببساطة لصقها في وحدة التحكم ومراقبة النتائج. هذا iife مشتق من إجابة Jared على Stackoverflow:
(function () {var root = $ (document.getElementsByTagName ('body')) ؛ var watchers = [] Angular.Foreach (element.children ()وبهذه الطريقة ، يتم الحصول على عدد المراقبين ، جنبًا إلى جنب مع شجرة المراقبة في قسم أداء باتارانج ، يجب أن ترى أين توجد رمز مكرر ، أو في حالة وجود بيانات ثابتة وأيضًا الساعات الخاصة.
عندما تكون هناك بيانات غير متغيرة وتريد استخدام AngularJs لاستلامها ، يمكنك التفكير في استخدام Bindonce. Bindonce هو توجيه بسيط يتيح لك استخدام قوالب في AngularJS ، لكنها لا تضيف إلى الساعة ، مما يضمن أن عدد الساعات لن ينمو.
8. نطاق محدود من نطاق $
الميراث القائم على النموذج الأولي JavaScript له فرق دقيق عن الميراث القائم على الفصل في موجه نحو الكائن الموجهة نحو الكائن ، والذي لا يمثل عادة مشكلة ، لكن هذه الدقة تتجلى عند استخدام $scope . في AngularJS ، يرث كل $scope $scope ، والذي يسمى $rootScope على أعلى مستوى. ( $scope يختلف إلى حد ما عن التوجيهات التقليدية. لديهم نطاق عمل معين ويرثون فقط الخصائص المعلنة صراحة.)
نظرًا لخصائص ميراث النموذج الأولي ، ليس من المهم مشاركة البيانات بين فئات الوالدين والطفل ، ولكن إذا لم تكن حذراً ، فمن السهل إساءة استخدام ممتلكات $scope .
على سبيل المثال ، نحتاج إلى عرض اسم مستخدم على شريط التنقل ، والذي يتم إدخاله في نموذج تسجيل الدخول. يجب أن تعمل المحاولة التالية:
<div ng-controller = "navctrl"> <span> {{user}}} </span> <div ng-controller = "loginctrl"> <span> {{user}} </span> <input ng-model = "user"> </pect> </viv> </div>ثم السؤال هو ...: تم تعيين نموذج مستخدم NG في إدخال النص. عندما يدخل المستخدم المحتوى ، أي قالب سيتم تحديثه؟ navctrl أو loginctrl ، أو كل شيء؟
إذا اخترت LoginCtrl ، فربما فهمت كيف يعمل ميراث النموذج الأولي.
لا تعمل سلسلة النموذج الأولي عند استرداد الحرفي. إذا تم تحديث NAVCTRL أيضًا في نفس الوقت ، فمن الضروري استرداد سلسلة النموذج الأولي ؛ ولكن إذا كانت القيمة كائنًا ، فسيحدث هذا. (تذكر ، في JavaScript ، الوظائف ، المصفوفات ، والكائنات كلها كائنات)
لذلك من أجل الحصول على السلوك المتوقع ، تحتاج إلى إنشاء كائن في NAVCTRL ، والتي يمكن الرجوع إليها بواسطة LoginCtrl.
<div ng-controller = "navctrl"> <span> {{user.name}}} </span> <div ng-controller = "loginctrl"> <span> {{user.name}} </span> <input ng-model = "user.name" الآن ، نظرًا لأن المستخدم كائن ، ستعمل سلسلة النموذج الأولي ، وسيتم تحديث قالب NAVCTRL و $scope و loginCtrl .
يبدو هذا مثالًا مصطنعًا إلى حد ما ، ولكن يمكن أن تنشأ هذه المشكلة بسهولة عند استخدام تعليمات معينة لإنشاء $scope ، مثل ngRepeat .
9. الاختبار اليدوي
نظرًا لأن TDD قد لا تكون الطريقة التي يفضل بها كل مطور ، فإنهم يقومون باختبار يدوي عندما يتحققون مما إذا كان الرمز يعمل أو يؤثر على شيء آخر.
ليس من المنطقي عدم اختبار تطبيق AngularJS. تم تصميم AngularJS لجعلها قابلة للاختبار من الصفر ، وحقن التبعية و NGMOCK هي دليل على ذلك. طور الفريق الأساسي من AngularJS العديد من الأدوات التي يمكن أن تأخذ الاختبار إلى المستوى التالي.
9.1 المنقلة
اختبار الوحدة هو أساس اختبار العمل ، ولكن بالنظر إلى التعقيد المتزايد للتطبيق ، فإن اختبار التكامل أقرب إلى الموقف الفعلي. لحسن الحظ ، قدم الفريق الأساسي من AngularJS الأدوات اللازمة.
لقد أنشأنا Fortractor ، وهو اختبار شامل لمحاكاة تفاعلات المستخدم ، والتي يمكن أن تساعدك على التحقق من صحة برنامج AngularJS الخاص بك.
يستخدم المنقلة إطار اختبار الياسمين لتحديد الاختبارات. المنقلة لديها واجهة برمجة تطبيقات قوية للغاية لسلوكيات التفاعل في الصفحة المختلفة.
لدينا بعض أدوات الاختبار من طرف إلى طرف ، لكن ميزة المنقلة هي أنها تتفهم كيفية العمل مع رمز AngularJS ، وخاصة في دورة $ Digest.
9.2 الكرمة
بمجرد الانتهاء من كتابة اختبارات التكامل مع المنقلة ، فإن الخطوة التالية هي تنفيذ الاختبارات. في انتظار تنفيذ الاختبارات ، وخاصة اختبارات التكامل ، هو حزن باهت لكل مطور. شعر الفريق الأساسي من AngularJS أيضًا بالأسى الشديد ، لذلك قاموا بتطوير الكرمة.
الكرمة هي اختبار يساعد على إيقاف تشغيل حلقات التغذية الراجعة. يمكن لـ Karma القيام بذلك لأنه يقوم بإجراء اختبارات عند تغيير الملف المحدد. ستقوم Karma أيضًا بإجراء اختبارات على متصفحات متعددة ، ويمكن للأجهزة المختلفة أيضًا الإشارة إلى خادم Karma ، والذي يمكنه تغطية سيناريوهات تطبيق العالم الحقيقي بشكل أفضل.
10. استخدم jQuery
jQuery هي مكتبة رائعة ، مع تطوير موحد من الطحالب ، وأصبحت تقريبًا ضرورة لتطوير الويب الحديث. ومع ذلك ، على الرغم من العديد من الميزات الممتازة لـ jQuery ، فإن فلسفتها لا تتفق مع AngularJS.
AngularJS هو إطار لبناء التطبيقات ، في حين أن JQuery هي مكتبة تبسط "عمليات المستندات HTML ومعالجة الأحداث والرسوم المتحركة و Ajax". هذا هو الفرق الأساسي بين الاثنين. تلتزم AngularJS بنية البرنامج ولا علاقة لها بصفحات HTML.
لفهم أفضل كيفية إنشاء برنامج AngularJS ، يرجى التوقف عن استخدام jQuery. يسمح JQuery للمطورين بالتفكير في مشاكل معايير HTML الحالية ، ولكن كما تقول الوثائق ، "يتيح لك AngularJS توسيع مصطلح HTML في طلبك."
يجب أن تتم عمليات DOM فقط في التعليمات ، لكن هذا لا يعني أنها لا يمكن تغليفها إلا مع jQuery. قبل استخدام jQuery ، يجب أن تفكر دائمًا فيما إذا كانت هذه الوظيفة قد تم توفيرها بالفعل بواسطة AngularJS. من القوي حقًا إنشاء أدوات قوية عندما تعتمد التعليمات على بعضها البعض.
لكن jQuery الرائع حقًا هو ضرورة عندما يأتي اليوم ، لكن تقديمه في المقام الأول هو خطأ شائع.
لخص
AngularJS هو إطار ممتاز يتحسن دائمًا بمساعدة المجتمع. على الرغم من أن AngularJS لا يزال مفهومًا متطورًا ، إلا أنني آمل أن يتمكن الأشخاص من اتفاقية المذكورة أعلاه لتجنب المشكلات التي واجهتها في تطوير تطبيقات AngularJS. آمل أن يكون محتوى هذه المقالة مفيدًا للجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.