AngularJS模块化和依赖注入

时间:2020-01-09 10:34:01  来源:igfitidea点击:

AngularJS带有内置的依赖项注入机制。我们可以将应用程序划分为AngularJS可以相互注入的多种不同类型的组件。模块化应用程序可以更轻松地重用,配置和测试应用程序中的组件。

AngularJS包含以下核心类型的对象和组件:

  • Value
  • Factory
  • Service
  • Provider
  • Constant

可以使用AngularJS依赖项注入机制将这些核心类型相互注入。在本文的其余部分中,我将解释如何定义这些组件并将它们相互注入。

Value

AngularJS中的值是一个简单的对象。它可以是数字,字符串或者JavaScript对象。值通常用作注入工厂,服务或者控制器的配置。

值必须属于AngularJS模块。以下是向AngularJS模块添加值的三个示例:

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.value("stringValue", "abc");

myModule.value("objectValue", { val1 : 123, val2 : "abc"} );

这些值是使用模块上的" value()"函数定义的。第一个参数是值的名称,第二个参数是值本身。工厂,服务和控制器现在可以通过名称来引用这些值。

注入值

只需通过添加与该名称同名的参数(将值定义时传递给value()函数的第一个参数)添加到AngularJS控制器函数中即可。这是一个例子:

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.controller("MyController", function($scope, numberValue) {

    console.log(numberValue);

});

请注意,控制器功能的第二个参数与该值的名称相同。

Factory

Factory是一个创建值的函数。当服务,控制器等需要从工厂注入的值时,工厂将按需创建值。一旦创建,该值可用于需要注入的所有服务,控制器等。因此,工厂与值的不同之处在于它可以使用工厂函数来创建它返回的对象。我们还可以将值注入工厂以供创建对象时使用。我们不能通过值来做到这一点。

这是一个在模块上定义工厂的示例,以及一个将工厂创建的值注入的控制器:

var myModule = angular.module("myModule", []);

myModule.factory("myFactory", function() {
    return "a value";
});

myModule.controller("MyController", function($scope, myFactory) {

    console.log(myFactory);

});

如我们所见,它与定义和注入值对象非常相似。请记住,注入的不是工厂功能,而是工厂功能产生的值。

将值注入工厂Factory

我们可以将值注入工厂。就像将值注入控制器一样。这是一个例子:

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.factory("myFactory", function(numberValue) {
    return "a value: " + numberValue;
});

在此示例中,注入值用于创建由工厂功能创建的对象。

服务

AngularJS中的服务是一个单例JavaScript对象,其中包含一组函数。这些功能包含服务执行其工作所需的任何逻辑。

AngularJS服务是使用模块上的service()函数创建的。这是一个例子:

function MyService() {
    this.doIt = function() {
        console.log("done");
    }
}

var myModule = angular.module("myModule", []);

myModule.service("myService", MyService);

如我们所见,服务的定义与工厂和值有所不同。首先,服务被定义为一个单独的命名函数。那是因为AngularJS中的服务是使用new关键字创建的。因此,AngularJS将在内部执行此操作:

var theService = new MyService();

除了将服务定义为内部具有函数的函数外,还可以将它们添加到AngularJS并与AngularJS一起使用,就像使用值或者函数一样。我们将服务注入到控制器中,如下所示:

function MyService() {
    this.doIt = function() {
        console.log("done");
    }
}

var myModule = angular.module("myModule", []);

myModule.service("myService", MyService);

myModule.controller("MyController", function($scope, myService) {

    myService.doIt();

});

将值注入服务

我们可以将值注入到服务中,就像可以将值注入到控制器中,或者将服务注入到控制器等中一样。这是一个示例:

var myModule = angular.module("myModule", []);

myModule.value  ("myValue"  , "12345");

function MyService(myValue) {
    this.doIt = function() {
        console.log("done: " + myValue;
    }
}

myModule.service("myService", MyService);

注意," MyService"函数的参数的命名方式与在模块上注册的值相同。因此,该值将在创建时注入到服务中。

提供者

AngularJS中的提供者是我们可以创建的最灵活的工厂形式。我们可以像使用服务或者工厂一样向模块注册提供程序,只是使用provider()函数。这是一个AngularJS提供程序示例:

var myModule = angular.module("myModule", []);

myModule.provider("mySecondService", function() {
    var provider = {};

    provider.$get = function() {
        var service = {};

        service.doService = function() {
            console.log("mySecondService: Service Done!");
        }

        return service;
    }

    return provider;
});

如我们所见,provider()函数有两个参数。第一个参数是提供程序创建的服务/对象的名称。在这种情况下,名称为" mySecondService"。第二个参数是创建提供程序的函数。注意:提供程序本身就是工厂,因此目前没有从提供程序创建任何实际的服务或者对象。仅定义创建提供程序的功能。

当我们查看创建提供程序的函数时,可以看到该提供程序是一个JavaScript对象。

JavaScript提供程序对象包含单个$ get()函数。这是提供程序的工厂功能。换句话说,$ get()函数创建提供者创建的任何内容(服务,值等)。在上面的示例中,提供程序创建了一个服务对象,其中包含一个称为doService()的单一服务函数(标准JavaScript函数)。

为了将提供程序的产品注入到控制器中,请像指定服务一样指定对提供程序的依赖性。注入控制器的是提供者创建的产品,而不是提供者本身。这是一个AngularJS提供程序注入示例:

myModule.controller("MyController", function($scope, mySecondService) {

    $scope.whenButtonClicked = function() {
        mySecondService.doIt();
    }

});

如我们所见,提供程序的名称在控制器功能中用作参数。由提供者的$ get()函数创建的对象将被注入到该参数中。

配置提供者

在模块的配置阶段,可以通过调用提供程序的功能来进一步配置提供程序。这是一个例子:

var myModule = angular.module("myModule", []);

myModule.provider("mySecondService", function() {
    var provider = {};
    var config   = { configParam : "default" };

    provider.doConfig = function(configParam) {
        config.configParam = configParam;
    }

    provider.$get = function() {
        var service = {};

        service.doService = function() {
            console.log("mySecondService: " + config.configParam);
        }

        return service;
    }

    return provider;
});

myModule.config( function( mySecondServiceProvider ) {
    mySecondServiceProvider.doConfig("new config param");
});

myModule.controller("MyController", function($scope, mySecondService) {

    $scope.whenButtonClicked = function() {
        mySecondService.doIt();
    }

});

请注意,提供程序对象现在如何具有一个名为doConfig()的添加功能。此功能可用于在提供程序上设置配置参数。

还要注意对" myModule.config()"函数的调用。 config函数将一个函数作为参数。此功能可以配置模块。传递给config()的函数采用一个名为mySecondServiceProvider的参数。该名称与提供程序在后缀" Provider"中注册的名称相同。后缀告诉AngularJS注入提供程序本身,而不是注入提供程序创建的对象。在传递给config()函数的函数内部,调用了mySecondServiceProvider.doConfig()函数,该函数在提供程序上设置config参数。

在示例的后面定义的控制器仅取决于提供者(而不是提供者本身)创建的对象。为此,可以使用名为" mySecondService"的参数,该参数是向服务提供者注册的名称。如我们所见,该服务是在$ scope.whenButtonClicked()函数内部使用的。

常量

在关于提供程序的上一节中,我们看到了如何通过module.config()函数配置提供程序。不幸的是,我们不能将值注入到module.config()函数中。相反,我们可以注入常量。

AngularJS中的常量是使用module.constants()函数定义的。这是一个AngularJS常量示例:

myModule.constant("configValue", "constant config value");

现在可以像下面这样将这个常量注入到module.config()函数中:

myservices.config( function( mySecondServiceProvider, configValue ) {
    mySecondServiceProvider.doConfig(configValue);
});

如我们所见,参数configValue匹配常量的名称,该常量也是configValue。因此,常数值将被注入该参数。然后,将常量值作为参数传递给mySecondServiceProvider提供程序上的doConfig()函数。

模块之间的依赖关系

如我们所见,值,工厂和服务已添加到AngularJS模块。一个模块可以使用另一模块的值,工厂和服务。为此,模块需要声明对模块的依赖关系,该依赖关系包含要使用的值,工厂和服务。这是一个例子:

var myUtilModule = angular.module("myUtilModule", []);

myUtilModule.value  ("myValue"  , "12345");

var myOtherModule = angular.module("myOtherModule", ['myUtilModule']);

myOtherModule.controller("MyController", function($scope, myValue) {

});

注意第二个模块(myOtherModule)如何在传递给angular.module()函数的第二个参数(数组内部)中列出第一个模块的名称(myUtilModule)。这告诉AngularJS,在myUtilModule内部定义的所有值,工厂和服务也应该在myOtherModule模块内部可用。换句话说," myOtherModule"依赖于" myUtilModule"。

其次,请注意MyController控制器函数现在如何声明一个名为myValue的参数。该值将从" myUtilModule"模块上注册的值中提供。

AngularJS中的缩小安全依赖注入

当我们缩小JavaScript时,JavaScript缩小器会将本地变量和参数的名称替换为较短的名称。但是,AngularJS使用控制器函数,工厂,服务和提供程序的参数名称来决定向其工厂函数中注入什么。如果名称更改,AngularJS将无法注入正确的对象。

为了使AngularJS代码缩小安全,我们需要提供要作为字符串注入的对象名称。将这些字符串与需要注入值的函数一起包装在数组中。这是一个AngularJS缩小安全依赖项注入示例:

var myapp = angular.module("myapp", ['myservices']);

myapp.controller("AController", ['$scope', function(p1) {
    p1.myvar = "the value";
}]);

这个例子将$ scope对象注入到控制器函数的p1参数中。

注意控制器功能是如何注册的。与其直接将控制器函数传递给angular.controller函数,不如传递一个数组。该数组包含要注入控制器功能的值的名称,以及控制器功能本身。控制器功能始终是此数组中的最后一个值。如果需要注入多个值,则值名称会在数组的开头列出,并按顺序将它们注入到函数中。这是一个缩小安全的多值示例:

var myapp = angular.module("myapp", ['myservices']);

myapp.controller("AController", ['$scope', '$http', function(p1, p2) {
    p1.myvar = "the value";
    p2.get("/myservice.json");
}]);

本示例将$ scope对象插入到p1参数中,并将$ http服务注入到控制器函数的p2参数中。

现在,控制器功能的参数名称不再重要。 AngularJS将使用数组开头的字符串来确定要向控制器函数中注入的内容。

可以将相同的机制用于工厂,服务和提供者以提供最小化的安全依赖项注入。这是一个缩小安全的工厂,服务和提供者的示例:

var myutil = angular.module("myutil", []);

myutil.value("safeValue", "a safe value");

myutil.factory("safeFactory", ['safeValue', function(p1) {
    return { value : p1 };
}]);

function MySafeService(p1){
    this.doIt = function() {
        return "MySafeService.doIt() called: " + p1.value;
    }
}
myutil.service("safeService", ['safeFactory', MySafeService]);

myutil.provider("safeService2", function() {
    var provider = {};

    provider.$get = ['safeService', function(p1) {
        var service = {};

        service.doService = function() {
            console.log("safeService from provider: " + p1.doIt());
        }

        return service;
    }];

    return provider;
});

myapp.controller("AController", ['$scope', 'safeService2', function(p1, p2) {
    p1.myvar = "the value";
    p2.doService();
}]);

请特别注意提供者的声明。注意,如何在提供者工厂函数中未指定依赖项,而是在提供者工厂函数内部返回的提供者的$ get()函数中进行指定。实际上,使用具有依赖项和函数实现名称的数组,而不是仅使用$ get()函数。除此之外,指定提供程序的依赖性与提供程序的工厂,服务和控制器功能相同。