كيفية: تم تحديث AngularJS 1.5+ باستخدام ES6 و Webpack و Mocha و SASS والمكونات

هناك العديد من الأسباب التي قد ترغب في مواصلة العمل مع AngularJS 1.x ، لذلك سأفترض ببساطة أن لديك أسبابك.

Angular! == AngularJS "يشير هذا الموقع وجميع محتوياته إلى AngularJS (الإصدار 1.x) ، إذا كنت تبحث عن أحدث Angular ، فيرجى زيارة angular.io" - angularjs.org

بالنسبة للمشروعات الجديدة ، أوصي باستخدام React لأن هذا هو الزخم في تطوير الواجهة الأمامية.

لقد صنعت ريبو GitHub يمكنك شوكة / استنساخ لبدء مشروعك الخاص:

jsdoc_output // حيث يتم إنشاء المستندات
node_modules // أين تذهب أشياء البائع الخاص بك
.gitignore
حدد mocha-webpack.opts // تهيئة webpack مختلفة للاختبار
package.json
README.md
webpack.config.base.js
webpack.config.js // يمتد التكوين الأساسي
webpack.config.test.js // يمتد التكوين الأساسي
عامة
| index-bundle.js // webpack إنشاء حزمة
| index.html و
| index.js // webpack
|
\ --- superAwesomeComponent
        componentStylez.sass
        componentTemplate.html
        fancyJsModule.js
        theComponent.js
        theComponent.spec.js
        theComponentController.js

ولدت باستخدام شجرة / أ / و على النوافذ

دعونا نتحقق من index.html


  <فائقة رهيبة مكون
    بعض المدخلات = "93"
    some-output = "IndexCtrl.fancyValue = value">
  
  <فائقة رهيبة مكون
    بعض المدخلات = "2"
    some-output = "IndexCtrl.fancyValue = IndexCtrl.fancyValue + IndexCtrl.addValue">
  
  <ص>
    متغير على وحدة التحكم أعلى المكونات: {{IndexCtrl.fancyValue}}
  

<الذيل>
  
لم يتم اتخاذ أي إجراء حتى الآن

يمكنك أن ترى هنا ، أن لدينا اثنين من الأزرار عنصرين رهيبة مكون. هذه هي مكونات الزاوي 1.5.

1.5 الزاوي المكونات

مكونات الزاوي 1.5 هي مجرد توجيهات ذات قيم افتراضية أفضل. إنها عناصر دائمًا ، وهناك "وحدة تحكم مثل $ ctrl" الافتراضية ، ولديها نطاق عزل. معظم ما تعلمته من المكونات ، تعلمت هنا https://toddmotto.com/exploring-the-angular-1-5-component-method/

المكونات لها ارتباطان ، بعض المدخلات وبعض المخرجات.

هذه المكونات مفيدة لأنها تتيح لنا تغليف مجموعة من وظائف العرض والتحكم. دعنا ننظر إلى ملف المكون

استيراد قالب من "./componentTemplate.html"
استيراد المكوناتستايلز من './componentStylez.sass'
استيراد {ComponentController} من './theComponentController.js'
روابط const = {
  someInput: '<' ،
  بعض الإخراج: '&'
}
تصدير const theComponent = {
  تحكم: مكون ،
  قالب،
  الارتباطات
}

لاحظ كيف يمكن إعادة استخدام كل عنصر من عناصر التحكم. يمكن أن تكون وحدة التحكم خاصة بهذا المكون ، أو يمكن أن تكون وحدة تحكم تستخدم في مكان آخر.

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

تستفيد وحدة التحكم من ميزات ES6 العادية ، ولن أتطرق إلى كيفية عملها ، لكن لاحظ فقط بنية الفصل المستخدمة ، ونقص النطاق $. والنتيجة هي وحدة تحكم غير مناسبة للإطار ، ناقصًا الاستفادة من حدث دورة حياة المكون ($ onInit)

استيراد fancyFunction من './fancyJsModule.js'
/ **
 * يوفر معالجات للالمكون
 * /
فئة مكون التحكم
  / **
   * تعلن أن روابط الإدخال غير محددة
   *urnurn {undefined} غير محدد
   * /
  البناء () {
    console.log ('ارتباطات الإدخال غير محددة!' ، this.someInput)
  }
  / **
   * المكالمات someOutput مع قيمة someInput وضعت في fancyFunction
   *urnurn {undefined} غير محدد
   * /
  doSuperThings () {
    console.log ("القيام بالأشياء الفائقة")
    this.someOutput ({value: fancyFunction (this.someInput، 3)})
  }
  / **
   * تعلن أن روابط المدخلات محددة
   *urnurn {undefined} غير محدد
   * /
  $ onInit () {
    console.log (يتم تعريف روابط الإدخال! ، this.someInput)
  }
}
تصدير {ComponentController}

StandardJS التنسيق

الفرق الواضح هو عدم وجود فواصل منقوطة. أنا شخصياً أعتقد أن هذا يوفر رمزًا أكثر نظافة ، ويتم حل المشكلات المتعلقة باستخدام الفاصلة المنقوطة بدقة باستخدام linter / formatter من StandardJS ، مما سيمنعك من مواجهة مشكلات غريبة هناك.

Webpack (والتي يمكن أن تكون مربكة)

لاحظ كيف يتعين علينا فقط استيراد index.bundle.js في index.html. هذا لأننا نستخدم Webpack ، الذي يجمع جميع أصولنا في ملف واحد. يتضمن ذلك قوالبنا و javascript و css وأي شيء يمكن أن تتخيله فيه.

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

يمكن العثور على دليل على هذا التعقيد في حقيقة أن لدينا سببًا لملفات 3 webpack.config * .js. يوفر المرء قاعدة ، والثاني هو استيعاب إعداد الاختبار الخاص بنا ، والثالث هو تقسيم الشفرة إلى أجزاء البائع (وهو ما لا نريد القيام به في إعداد الاختبار لدينا للقيام بتفاعلات غريبة مع CommonsChunkPlugin).

var path = requ ('path')
var webpack = require ('webpack')
module.exports = {
  الإدخال: {
    'index': path.join (__ dirname، / / public/index.js ')
  }،
  انتاج: {
    اسم الملف: '[name] -bundle.js' ،
    path: path.join (__ dirname، / public / ')،
    devtoolLineToLine: صحيح ،
    pathinfo: صحيح ،
    sourceMapFilename: '[name] .js.map' ،
    publicPath: path.join (__ dirname، / / src / main / webapp / ')
  }،
  وحدة: {
    اللودرات: [
      {test: /\.js$/، loader: 'babel-loader'، exclude: / node_modules /}،
      {test: /\.css$/، loader: 'style-loader! css-loader'}،
      {test: /\.sass$/ ، اللوادر: ['style-loader' ، 'css-loader' ، 'sass-loader']} ،
      {test: /\.html$/، loader: 'raw-loader'}،
      // مضمنة base64 عناوين URL للصور <= 8k ، عناوين URL المباشرة لبقية
      {test: /\.(png|jpg)$/، loader: 'url-loader؟ limit = 8192'}،
      // يساعد في تحميل المغلق bootstrap.
      {test: /\.woff(\؟v=\d+\.\d+\.\d+)؟$/،
        loader: 'url؟ limit = 10000 & minetype = application / font-woff'} ،
      {test: /\.woff2$/،
        loader: 'url؟ limit = 10000 & minetype = application / font-woff'} ،
      {test: /\.ttf(\؟v=\d+\.\d+\.\d+)؟$/،
        لودر: 'url؟ limit = 10000 & minetype = application / octet-stream'} ،
      {test: /\.eot(\؟v=\d+\.\d+\.\d+)؟$/،
        محمل: 'ملف'} ،
      {test: /\.svg(\؟v=\d+\.\d+\.\d+)؟$/،
        أداة التحميل: 'url؟ limit = 10000 & minetype = image / svg + xml'}
    ]
  }،
  الإضافات: [
    حزمة الويب الجديدة. HotModuleReplacementPlugin ()
  ]،
  devServer: {
    publicPath: '/' ،
    contentBase: path.join (__ dirname، / / public ')،
    ضغط: صحيح
  }،
  devtool: "eval"
}

لن أشرح كل شيء هنا ، لأن هذا هو ما تمثله مستندات webpack (هذا الرابط مخصص لـ Webpack 1 على الرغم من أننا نستخدم Webpack 2. مستندات Webpack 2 شاملة تمامًا في عدم اكتمالها ، ولكن انظر عمليات الترحيل كابل بيانات).

لإعطاء نظرة عامة ، يجب عليك تحديد:

  • أين يبدأ التطبيق الخاص بك
  • أين تذهب الحزمة
  • كيف ستستورد الأشياء بطريقة سحرية
  • ما المكونات الإضافية التي تستخدمها
  • إعداد webpack-dev-server الخاص بك
  • كيف يتم إعداد خرائط المصدر الخاصة بك.

ماذا؟ الإضافات؟ خرائط المصدر؟ لماذا أحتاج إلى خادم آخر؟

الإضافات

نحن هنا نستخدم فقط المكون الإضافي لـ HotModuleReplacement (HMR). إنها تتيح لمتصفحنا إعادة التحميل تلقائيًا عند تغيير الملف. هذا يزيل بطريقة سحرية خطوة واحدة من التكرار العادي للكتابة ، حفظ ، اختبار.

هناك أطنان من المكونات الإضافية الأخرى (واحد لم أتمكن من تجربته على هذا النحو: https://github.com/owen-it/ng-loader)

فيما يلي قائمة بمكونات Webpack الشائعة: https://github.com/webpack-contrib/awesome-webpack (لماذا تفعل Webpack أشياء كثيرة؟)

خرائط المصدر

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

إذا نظرنا إلى الوراء على theComponent.js ، فإن هذا يحتوي على معظم Webpack الخاص بنا

استيراد قالب من "./componentTemplate.html"
استيراد المكوناتستايلز من './componentStylez.sass'
استيراد {ComponentController} من './theComponentController.js'
روابط const = {
  someInput: '<' ،
  بعض الإخراج: '&'
}
تصدير const theComponent = {
  تحكم: مكون ،
  قالب،
  الارتباطات
}

لاحظ كيف نستورد HTML و SASS و ES6 هنا. ويتم ذلك من خلال رافعاتنا. يعتمد المُحمل المستخدم على اسم الملف.

Webpack ديف خادم

يعد Webpack-dev-server شيءًا رائعًا ، بغض النظر عما إذا كان لديك خلفية حقيقية أم لا. وهو يدعم HMR ، وهو خادم ملفات ثابت مما يجعل تطورك سريعًا. بالإضافة إلى ذلك ، سيؤدي استخدام خادم webpack-dev-server إلى إجبارك على فصل الواجهة الأمامية والخلفية.

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

في هذا الإعداد ، يتم تشغيل webpack-dev-server ، جنبًا إلى جنب مع كل ما هو مطلوب آخر لتطوير الواجهة الأمامية ، بواسطة أمر تشغيل واحد npm run dev ، كما هو محدد في package.json

{
  "name": "modern-angularjs-starter" ،
  "الإصدار": "0.0.1" ،
  "الوصف": "المشروع الأساسي" ،
  "الرئيسي": "index.js" ،
  "البرامج النصية": {
    "dev": "متزامن - قتل الآخرين \" webpack-dev-server - المضيف 0.0.0.0 \ "\" npm تشغيل مستندات \ ""،
    "docs_gen": "jsdoc -r -d jsdoc_output / public /"،
    "docs_watch": "watch \" npm تشغيل docs_gen \ "public" ،
    "docs_serve": "يتم عرض مستندات echo على المنفذ 8082! && live-server -q --port = 8082 - no-browser jsdoc_output /"،
    "docs": "بالتزامن - قتل الآخرين \" npm تشغيل docs_serve \ "\" npm run docs_watch \ ""،
    "postinstall": "bower install" ،
    "webpack": "webpack" ،
    "اختبار": "mocha-webpack public / ** / *. spec.js"
  }،
  "devDependencies": {/ * مخفية للمسافة * /}
  "التبعيات": {/ * مخفية للمساحة * /}
}

لاحظ استخدام متزامن (https://www.npmjs.com/package/concurrently)

هذا يتيح لنا تشغيل 2 أوامر حظر بالتوازي.

لاحظ أن هناك أيضًا أوامر اختبار وتوثيق. تنشئ أوامر الوثائق صفحات JSDoc ثم تستضيف بعد ذلك على خادم صغير يقوم التحديث التلقائي (على غرار HMR) بالمتصفح عندما يكون هناك تغيير. وبهذه الطريقة يمكنك مشاهدة تحديث مستنداتك أثناء كتابتها إذا قمت بالحفظ كثيرًا.

لم يتم إظهاره في هذا المشروع ، إلا أن تحديد الأنواع في JSDoc هو وسيلة جيدة لتحديد عقود البيانات بين الواجهة الأمامية / الخلفية. بدلاً من ذلك ، يمكنك فقط استخدام typescript (هناك رافعات لذلك).

اختبار الوحدة: (لأنه يستحق الجهد)

يعد الاختبار باستخدام ES6 + AngularJS + Webpack عملية صعبة للغاية. كل من هذه الأسباب مضاعفات. بالنسبة لاختبار الوحدات ، انتهى بي الأمر بالاستقرار في وحدات صغيرة جدًا ، واختبار وحدات التحكم في AngularJS كوظائف في العقدة. الكرمة مشهور إلى حد كبير ، ولكن في رأيي الاختبارات ليست في الحقيقة اختبارات وحدة. سيكون من المفيد الحصول على كليهما.

وبالتالي ، لدينا mocha-webpack. يتيح لنا ذلك استخدام الواردات في اختباراتنا ، دون تحديد نقطة دخول لكل منها.

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

يعد هذا مفيدًا بشكل خاص لكتابة الاختبارات حيث يكون الاستهزاء بالأشياء الموجودة داخل اختبار الوحدة النمطية أمرًا ضروريًا في بعض الأحيان قبل التنفيذ. - حقن محمل
/ * eslint-disable * /
استيراد تشاي من "تشاي"
استيراد sinon من "sinon"
const theControllerInjector = require ('inject-loader! ./ theComponentController.js')
دع {نتوقع ، ينبغي ، تأكيد} = تشاي
صف ('superAwesomeComponent' ، دالة () {
  اسمحوا كعب
  اسمح للمكون بالتحكم
  اسمحوا تحكم
قبل كل (وظيفة setupComponent () {
    كعب = sinon.stub (). إرجاع (1)
    theComponentController = theControllerInjector ({
      // الوحدة بسيطة حقا ، لذلك ليس من الضروري حقا أن يسخر منها
      // في تطبيق حقيقي ، قد يكون الأمر أكثر تعقيدًا (أي شيء يُجري مكالمات API)
      './fancyJsModule.js': كعب الروتين
    }). ComponentController
    تحكم = جديد theComponentController ()
    controller.someOutput = sinon.stub ()
    controller.someInput = 1
  })
  صف ('doSuperThings' ، الوظيفة () {
    it ('calls fancyFunction'، function () {
      controller.doSuperThings ()
      تأكيد (stub.calledOnce)
    })
  })
})

لاستخدام محقن الحاقن ، نستخدم بناء جملة محمل الطلب + webpack القديم لأنه لا يوجد التحقق من اسم ملف البدل الذي يمكننا القيام به للاستيراد (لا نريد تمرير جميع ملفات js إلى محقن الحاقن طوال الوقت) . توفر لنا عملية إعادة هذه الوظيفة وظيفة يمكننا الاتصال بها باستخدام كائن يصرخ على عمليات الاستيراد المختلفة:

theComponentController = theControllerInjector ({
  './fancyJsModule.js': كعب الروتين
}). ComponentController

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

نستخدم chai كمكتبة تأكيد لدينا ، sinon للسخرية / التجسس ، وموكا لإجراء الاختبارات.

لا يحاول هذا الاختبار أن يكون مثالًا جيدًا لما يجب اختباره ، إنه ببساطة يوضح كيفية إعداد الاختبار باستخدام ES6 + Webpack + Mocha + Angular.

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

سو ...

توفر هذه البنية طريقة لتحديث واجهة AngularJS دون قفزة الإطار. واحدة من أكبر فوائد هذا النهج هو أنه يستخلص الكثير من التعليمات البرمجية الخاصة بـ AngularJS.

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

لا يزال لدى AngularJS قدر لا بأس به من الحياة ، لكن لا يوجد جدال حول تجاوزه للوراء. ES6 / 7 ، ومع ذلك ، لا تزال في ارتفاع (http://vanilla-js.com).

تحيا AngularJS!

بالمناسبة ، تحقق من التشدقات السابقة على JS https://medium.com/@narthur157/let-s-talk-about-javascript-bdb0bdf57fae