AngularJS แนวทางปฏิบัติที่ดีที่สุดสำหรับการประกาศโมดูล?


115

ฉันมีโมดูล Angular จำนวนมากที่ประกาศในแอปของฉัน เดิมทีฉันเริ่มประกาศโดยใช้ไวยากรณ์ "ล่ามโซ่" ดังนี้:

angular.module('mymodule', [])
    .controller('myctrl', ['dep1', function(dep1){ ... }])
    .service('myservice', ['dep2', function(dep2){ ... }])
    ... // more here

แต่ฉันตัดสินใจว่ามันไม่ง่ายที่จะอ่านดังนั้นฉันจึงเริ่มประกาศโดยใช้ตัวแปรโมดูลเช่นนี้:

var mod = angular.module('mymodule', []);

mod.controller('myctrl', ['dep1', function(dep1){ ... }]);

mod.service('myservice', ['dep2', function(dep2){ ... }]);
...

ไวยากรณ์ที่สองดูเหมือนจะอ่านได้ง่ายกว่าสำหรับฉัน แต่ข้อร้องเรียนเดียวของฉันคือไวยากรณ์นี้ทำให้modตัวแปรอยู่ในขอบเขตทั่วโลก ถ้าฉันเคยมีชื่อตัวแปรอื่นmodมันจะถูกแทนที่ด้วยตัวแปรถัดไป (และปัญหาอื่น ๆ ที่เกี่ยวข้องกับตัวแปรส่วนกลาง)

คำถามของฉันคือวิธีนี้เป็นวิธีที่ดีที่สุดหรือไม่? หรือจะดีกว่าถ้าทำแบบนี้:

(function(){
    var mod = angular.module('mymod', []);
    mod.controller('myctrl', ['dep1', function(dep1){ ... }]);
    mod.service('myservice', ['dep2', function(dep2){ ... }]);
    ...
})();

หรือมันมีความสำคัญเพียงพอที่จะดูแล? เพียงแค่อยากรู้ว่า "แนวทางปฏิบัติที่ดีที่สุด" คืออะไรสำหรับการประกาศโมดูล ขอบคุณล่วงหน้า.


3
ฉันสงสัยเหมือนกันเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุดเชิงมุม
Dalorzo

คำตอบ:


118

วิธีที่ 'ดีที่สุด' ในการประกาศโมดูล

เนื่องจากเชิงมุมอยู่ในขอบเขตส่วนกลางและโมดูลจะถูกบันทึกลงในตัวแปรคุณจึงสามารถเข้าถึงโมดูลผ่านangular.module('mymod'):

// one file
// NOTE: the immediately invoked function expression 
// is used to exemplify different files and is not required
(function(){
   // declaring the module in one file / anonymous function
   // (only pass a second parameter THIS ONE TIME as a redecleration creates bugs
   // which are very hard to dedect)
   angular.module('mymod', []);
})();


// another file and/or another anonymous function
(function(){   
 // using the function form of use-strict...
 "use strict";
  // accessing the module in another. 
  // this can be done by calling angular.module without the []-brackets
  angular.module('mymod')
    .controller('myctrl', ['dep1', function(dep1){
      //..
    }])

  // appending another service/controller/filter etc to the same module-call inside the same file
    .service('myservice', ['dep2', function(dep2){ 
    //... 
    }]);

  // you can of course use angular.module('mymod') here as well
  angular.module('mymod').controller('anothermyctrl', ['dep1', function(dep1){
      //..
  }])
})();

ไม่จำเป็นต้องมีตัวแปรส่วนกลางอื่น ๆ

แน่นอนว่าทั้งหมดขึ้นอยู่กับความชอบ แต่ฉันคิดว่านี่เป็นแนวทางปฏิบัติที่ดีที่สุดเช่นเดียวกับ

  1. คุณไม่ต้องสร้างมลพิษให้กับขอบเขตทั่วโลก
  2. คุณสามารถเข้าถึงโมดูลของคุณได้ทุกที่และจัดเรียงโมดูลและฟังก์ชันลงในไฟล์ต่างๆได้ตามต้องการ
  3. คุณสามารถใช้รูปแบบฟังก์ชันของ "ใช้เข้มงวด";
  4. ลำดับการโหลดไฟล์ไม่สำคัญเท่า

ตัวเลือกสำหรับการจัดเรียงโมดูลและไฟล์ของคุณ

วิธีการประกาศและเข้าถึงโมดูลนี้ทำให้คุณมีความยืดหยุ่นมาก คุณสามารถจัดเรียงโมดูลผ่านประเภทฟังก์ชัน (เช่นที่อธิบายไว้ในคำตอบอื่น) หรือตามเส้นทางเช่น:

/******** sorting by route **********/    
angular.module('home')...
angular.module('another-route')...
angular.module('shared')...

วิธีที่คุณจัดเรียงในท้ายที่สุดเป็นเรื่องของรสนิยมส่วนตัวและขนาดและประเภทของโครงการ โดยส่วนตัวแล้วฉันชอบจัดกลุ่มไฟล์ทั้งหมดของโมดูลภายในโฟลเดอร์เดียวกัน (เรียงลำดับลงในโฟลเดอร์ย่อยของคำสั่งตัวควบคุมบริการและตัวกรอง) รวมถึงไฟล์ทดสอบที่แตกต่างกันทั้งหมดเนื่องจากทำให้โมดูลของคุณสามารถใช้ซ้ำได้มากขึ้น ดังนั้นในโครงการขนาดกลางฉันจึงจบลงด้วยโมดูลฐานซึ่งรวมถึงเส้นทางพื้นฐานทั้งหมดและตัวควบคุมบริการคำสั่งและโมดูลย่อยที่ซับซ้อนมากหรือน้อยเมื่อฉันคิดว่ามันอาจเป็นประโยชน์สำหรับโครงการอื่น ๆ เช่นกันเช่น :

/******** modularizing feature-sets **********/
/controllers
/directives
/filters
/services
/my-map-sub-module
/my-map-sub-module/controllers
/my-map-sub-module/services
app.js
...

angular.module('app', [
  'app.directives',
  'app.filters',
  'app.controllers',
  'app.services',
  'myMapSubModule'
]);

angular.module('myMapSubModule',[
   'myMapSubModule.controllers',
   'myMapSubModule.services',
   // only if they are specific to the module
   'myMapSubModule.directives',
   'myMapSubModule.filters'
]);

สำหรับโครงการใหญ่ ๆ บางครั้งฉันจะจัดกลุ่มโมดูลตามเส้นทางตามที่อธิบายไว้ข้างต้นหรือตามเส้นทางหลักที่เลือกไว้หรือแม้แต่การรวมกันของเส้นทางและส่วนประกอบที่เลือกบางส่วน แต่มันขึ้นอยู่กับจริงๆ

แก้ไข: เพียงเพราะมันเกี่ยวข้องกันและเมื่อเร็ว ๆ นี้ฉันพบอีกครั้ง: ดูแลให้ดีว่าคุณสร้างโมดูลเพียงครั้งเดียว (โดยการเพิ่มพารามิเตอร์ที่สองให้กับ angular.module-function) สิ่งนี้จะทำให้แอปพลิเคชันของคุณยุ่งและตรวจจับได้ยากมาก

2015 แก้ไขในโมดูลการเรียงลำดับ: หนึ่งปีครึ่งของประสบการณ์เชิงมุมในภายหลังฉันสามารถเพิ่มได้ว่าประโยชน์จากการใช้โมดูลที่มีชื่อแตกต่างกันภายในแอปของคุณนั้นค่อนข้าง จำกัด เนื่องจาก AMD ยังทำงานได้ไม่ดีกับ Angular และบริการคำสั่งและตัวกรอง มีอยู่ทั่วโลกในบริบทเชิงมุมอยู่ดี ( ดังตัวอย่างที่นี่ ) ยังคงมีประโยชน์เชิงความหมายและเชิงโครงสร้างอยู่และอาจเป็นประโยชน์ในการรวม / ไม่รวมโมดูลที่มีโค้ดบรรทัดเดียวที่แสดงความคิดเห็นเข้าหรือออก

นอกจากนี้ยังแทบไม่สมเหตุสมผลเลยที่จะแยกโมดูลย่อยตามประเภท (เช่น 'myMapSubModule.controllers') เนื่องจากมักจะพึ่งพาซึ่งกันและกัน


7
คุณไม่จำเป็นต้องใช้ IIFE (ทันทีเรียกใช้ฟังก์ชันนิพจน์) หรือที่เรียกว่าฟังก์ชันการเรียกใช้งานตัวเองแบบไม่ระบุชื่อ
บวก -

1
คุณถูก. คุณจะต้องใช้ก็ต่อเมื่อคุณต้องการใช้รูปแบบฟังก์ชันของ "ใช้เข้มงวด" ไม่เจ็บเลย
hugo der hungrige

1
ในกรณีส่วนใหญ่คุณสามารถใส่ไว้'use strict';ในส่วนประกอบของคุณได้เช่นกัน module.controller(function () { 'use strict'; ... });
Jackson

ฉันชอบการใช้งาน แต่ฉันไม่สนุกกับการผูกมัดด้วยดังนั้นฉันจึงผสมผสานสิ่งนี้กับสิ่งที่ Beterraba กำลังทำอยู่
Mirko

1
เมื่อใช้ AMD โมดูลเดียวชื่อแอปก็เพียงพอแล้ว เราสามารถแยกโมดูล AMD ได้โดยการลบคำสั่งที่ต้องการ และเราไม่จำเป็นต้องลงทะเบียนตัวควบคุมและบริการอีกต่อไป
James

28

ฉันชอบคำแนะนำเชิงมุมของ Johnpapa และนี่คือกฎบางส่วนที่เกี่ยวข้องกับคำถามนี้:

กฎ: ฟังก์ชันที่ตั้งชื่อและไม่ระบุตัวตน

หลีกเลี่ยงการใช้ฟังก์ชันที่ไม่ระบุชื่อ:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', function() { })

ใช้ฟังก์ชันที่มีชื่อแทน:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', Dashboard);

function Dashboard() { }

ดังที่ผู้เขียนกล่าวว่า: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

กฎ: กำหนด 1 องค์ประกอบต่อไฟล์

หลีกเลี่ยงหลายองค์ประกอบในไฟล์เดียว:

angular
  .module('app', ['ngRoute'])
  .controller('SomeController', SomeController)
  .factory('someFactory', someFactory);

function SomeController() { }

function someFactory() { }

Intead ใช้ไฟล์เดียวเพื่อกำหนดโมดูล:

// app.module.js
angular
  .module('app', ['ngRoute']);

ไฟล์เดียวใช้โมดูลเพื่อกำหนดส่วนประกอบ

// someController.js
angular
  .module('app')
  .controller('SomeController', SomeController);

function SomeController() { }

และไฟล์อื่นเพื่อกำหนดองค์ประกอบอื่น

// someFactory.js
angular
  .module('app')
  .factory('someFactory', someFactory);

function someFactory() { }

แน่นอนว่ายังมีกฎอื่น ๆ อีกมากมายสำหรับโมดูลตัวควบคุมและบริการที่ค่อนข้างมีประโยชน์และควรค่าแก่การอ่าน

และขอบคุณสำหรับความคิดเห็นของ ya_dimon โค้ดด้านบนควรถูกรวมไว้ใน IIFE เช่น:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);

คำตอบที่ดีและการเชื่อมโยงที่ดี
Ellesedil

หากฉันมีคอนโทรลเลอร์ที่แตกต่างกันในไฟล์จาวาสคริปต์ที่แตกต่างกันจะไม่ต้องโหลดไฟล์จำนวนมากขึ้นเช่นเซิร์ฟเวอร์ที่เข้าชมมากขึ้น?
Vignesh Subramanian

มันค่อนข้างง่ายที่จะรวม / อัปลักษณ์ / เปลี่ยนชื่อด้วยอึกหรือฮึดฮัดขอบมืดและโดยส่วนตัวแล้วฉันชอบอึก
aqingsao

1
คุณลืมเพิ่มว่าส่วนย่อยทั้งหมดนี้ควรอยู่ใน IIFE มิฉะนั้นคุณจะมีฟังก์ชันเช่น "someFactory ()" ทั่วโลก มีโอกาสเกิดการชนกันของชื่อ (และคุณไม่ต้องการ IIFE ใน es6)
ya_dimon

12

ฉันเพิ่งมีปริศนานี้เช่นกัน ฉันได้เริ่มต้นเช่นเดียวกับคุณที่ใช้ไวยากรณ์ที่ถูกล่ามโซ่ แต่ในระยะยาวมันจะไม่สะดวกกับโครงการขนาดใหญ่ โดยปกติฉันจะสร้างโมดูลตัวควบคุมโมดูลบริการและอื่น ๆ ในไฟล์แยกต่างหากและฉีดเข้าไปในโมดูลแอปพลิเคชันหลักของฉันที่พบในไฟล์อื่น ตัวอย่างเช่น:

// My Controllers File
angular.module('my-controllers',[])
    .controller('oneCtrl',[...])
    .controller('twoCtrl',[...]);

// My Services File
angular.module('my-services',[])
    .factory('oneSrc',[...])
    .facotry('twoSrc',[...]);

// My Directives File
angular.module('my-directives',[])
    .directive('oneDrct',[...])
    .directive('twoDrct',[...]);

// My Main Application File
angular.module('my-app',['my-controllers','my-services','my-directives',...]);

แต่ไฟล์เหล่านี้แต่ละไฟล์มีขนาดใหญ่ขึ้นเมื่อโปรเจ็กต์เติบโตขึ้น ดังนั้นฉันจึงตัดสินใจแยกมันออกเป็นไฟล์แยกกันตามคอนโทรลเลอร์หรือบริการแต่ละตัว ฉันพบว่าการใช้angular.module('mod-name').โดยไม่มีอาร์เรย์การฉีดคือสิ่งที่คุณต้องการเพื่อให้สิ่งนี้ได้ผล การประกาศตัวแปรส่วนกลางในไฟล์เดียวและคาดหวังว่าจะพร้อมใช้งานในอีกไฟล์หนึ่งไม่ได้ผลหรืออาจได้ผลลัพธ์ที่ไม่คาดคิด

ดังนั้นในระยะสั้นแอปพลิเคชันของฉันจึงมีลักษณะดังนี้:

// Main Controller File
angular.module('my-controllers',[]);

// Controller One File
angular.module('my-controllers').controller('oneCtrl',[...]);

//Controller Two File
angular.module('my-controllers').controller('twoCtrl',[...]);

ฉันทำสิ่งนี้กับไฟล์บริการเช่นกันไม่จำเป็นต้องเปลี่ยนไฟล์โมดูลแอปพลิเคชันหลักคุณยังคงฉีดโมดูลเดิมลงในนั้น


1
อะไรคือจุดสำคัญของการสร้างโมดูลแยกต่างหากสำหรับบริการ / คำสั่ง / คอนโทรลเลอร์?
Filip Sobczak

2
ในโครงการขนาดใหญ่สิ่งต่างๆอาจหาได้ยากเมื่อตัวควบคุม / ตัวกรอง / คำสั่ง / บริการทั้งหมดสอดประสานเข้าด้วยกัน นี่เป็นเพียงวิธีหนึ่งในการจัดเก็บของให้เป็นระเบียบ
meconroy

1
@FilipSobczak เขาไม่ได้สร้างโมดูลแยกต่างหากสำหรับบริการ / คำสั่ง / ตัวควบคุม แต่เขาได้สร้างโมดูลเพียงครั้งเดียวโดยใช้angular.module('my-controllers',[]);(โปรดทราบว่าเขาระบุ [] เพียงครั้งเดียวสำหรับการประกาศ) เขาใช้สิ่งนี้ซ้ำในไฟล์อื่น ๆ การแยกไฟล์ทำให้ง่ายต่อการดูแลรักษาโปรเจ็กต์โดยเฉพาะไฟล์ขนาดใหญ่
Devner

8

วิธีปฏิบัติอีกอย่างหนึ่งคือการบรรจุตัวควบคุมคำสั่ง ฯลฯ ในโมดูลของตนเองและฉีดโมดูลเหล่านั้นลงใน "หลัก" ของคุณ:

angular.module('app.controllers', [])
  .controller('controller1', ['$scope', function (scope) {
    scope.name = "USER!";
  }]);

angular.module('app.directives', [])
  .directive('myDirective', [function () {
    return {
      restrict: 'A',
      template: '<div>my directive!</div>'
    }
  }]);

angular.module('app', [
  'app.controllers',
  'app.directives'
]);

ไม่มีอะไรเหลืออยู่ในขอบเขตทั่วโลก

http://plnkr.co/edit/EtzzPRyxWT1MkhK7KcLo?p=preview


เหตุใดคุณจึงใช้app.controllersinsted controllersเป็นชื่อโมดูลมีข้อดีหรือไม่? ฉันเป็นมือใหม่ใน Angularjs
sijo vijayan

4

ฉันชอบแบ่งไฟล์และโมดูลของฉัน

สิ่งนี้:

app.js

var myApp = angular.module('myApp', ['myApp.controllers', 'myApp.directives', 'myApp.services']);

myApp.config(['$routeProvider', function($routeProvider) {
    /* routes configs */
    $routeProvider.when(/*...*/);
}]);

directives.js

var myDirectives = angular.module('myApp.directives', []);

myDirectives.directive( /* ... */ );

service.js

var myServices = angular.module('myApp.services', []);

myServices.factory( /* ... */ );

ฉันไม่ใช่แฟนตัวยงของ "สไตล์ที่ถูกล่ามโซ่" ดังนั้นฉันจึงชอบที่จะเขียนตัวแปรของฉันไว้เสมอ


2
นี่เป็นวิธีที่ฉันเคยทำมา แต่ไฟล์ services.js หรือ controller.js แต่ละไฟล์ทำงานได้อย่างรวดเร็วในโปรเจ็กต์ขนาดใหญ่ในที่สุดคุณจะต้องแยกบริการหรือคอนโทรลเลอร์ออกเป็นไฟล์แยกกัน
meconroy

1
@meconroy เป๊ะ. เมื่อสิ่งนั้นใหญ่ขึ้นเรื่อย ๆ ฉันชอบแบ่งคำสั่งออกเป็นโมดูลเล็ก ๆ แล้วฉีดไปที่โมดูลคำสั่ง "หลัก"
Beterraba

1

ฉันขอแนะนำให้ปฏิบัติตามคู่มือสไตล์ Angularjs
พวกเขาจัดการแนวคิดทั้งหมดตั้งแต่หลักการตั้งชื่อไปจนถึงการแยกส่วนแอปของคุณเป็นต้น

สำหรับ angular 2 คุณสามารถตรวจสอบAngular 2 Style Guide


0

สำหรับฉันการล่ามโซ่เป็นวิธีที่กะทัดรัดที่สุด:

angular.module("mod1",["mod1.submod1"])

 .value("myValues", {
   ...
 })

 .factory("myFactory", function(myValues){
   ...
 })

 .controller("MainCtrl", function($scope){

   // when using "Ctrl as" syntax
   var MC = this;
   MC.data = ...;
 })
 ;

ด้วยวิธีนี้ฉันสามารถย้ายส่วนประกอบระหว่างโมดูลได้อย่างง่ายดายไม่จำเป็นต้องประกาศโมดูลเดียวกันซ้ำสองครั้งไม่ต้องใช้ตัวแปรส่วนกลางใด ๆ

และหากไฟล์ยาวเกินไปวิธีแก้ปัญหาก็ง่าย - แบ่งออกเป็นสองไฟล์โดยแต่ละไฟล์จะประกาศโมดูลของตัวเองที่ด้านบน เพื่อความโปร่งใสมากขึ้นฉันพยายามเก็บโมดูลที่ไม่ซ้ำกันหนึ่งโมดูลต่อไฟล์และตั้งชื่อให้คล้ายกับพา ธ แบบเต็มของไฟล์ ด้วยวิธีนี้ฉันไม่จำเป็นต้องเขียนโมดูลโดยไม่มี[]ซึ่งเป็นจุดเจ็บปวดที่พบบ่อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.