AngularJS - 基于路由的外部JS动态加载

22

我正在尝试使用Angular Mobile UI库制作一个简单的单页面移动应用程序,包括多个视图和一个“下一页/上一页”按钮来控制每个视图。

基本的模型如下:

<html>
    <head>
        <link rel="stylesheet" href="mobile-angular-ui/dist/css/mobile-angular-ui-base.min.css">
        <link rel="stylesheet" href="mobile-angular-ui/dist/css/mobile-angular-ui-desktop.min.css">

        <script src="js/angular/angular.min.js"></script>
        <script src="js/angular/angular-route.min.js"></script>
        <script src="mobile-angular-ui/dist/js/mobile-angular-ui.min.js"></script>

        <script src="app/app.js"></script>
        <script src="app/firstController.js"></script>
        <script src="app/secondController.js"></script>
        <script src="app/thirdController.js"></script>

    </head>

    <body ng-app="demo-app">
        <div ng-view></div>

        <div ng-controller="nextBackController" class="navbar navbar-app navbar-absolute-bottom">
            <div class="btn-group justified">
              <a href="#/" class="btn btn-navbar btn-icon-only"><i class="fa fa-home fa-navbar"></i></a>
              <a href="#/second" class="btn btn-navbar btn-icon-only"><i class="fa fa-list fa-navbar"></i></a>
            </div>
        </div>

    </body>


</html>

以下是App.js的内容:

var app = angular.module('demo-app', [
  "ngRoute",
  "mobile-angular-ui"
]);

app.config(function($routeProvider) {
  $routeProvider.when('/', { controller: "firstController",
                             templateUrl: "views/first.html"});
  $routeProvider.when('/', { controller: "secondController",
                             templateUrl: "views/first.html"});
  $routeProvider.when('/', { controller: "thirdController",
                             templateUrl: "views/first.html"});
});

controllers = {};

controllers.nextBackController = function($scope, $rootScope) {
    //Simple controller for the next, back buttons so we just put it in app.js
};

app.controller(controllers);

firstController.js将包含类似以下内容的代码:

controllers.firstController = function($scope) {
    //Do our logic here!!!
};

问题在于,如果您注意到HTML页面顶部,我必须加载所有控制器。这是不可扩展的。我希望每个控制器都在它自己的JS文件中,并且不必静态加载每个控制器,因为用户可能永远不需要该控制器。是否有一种方法在切换路由时动态加载实际的JS文件?或者我可以将脚本标记放在我的“first.html”,“second.html”等页面的顶部。


1
@lan 我的问题是如何在需要时引入控制器的JS文件,而不是将它们全部放在主页面的script标签中。假设我有20个控制器,每个组件一个控制器。这样会导致加载时间非常长。我只想在需要时引入firstController的JS文件。或者Angular会自动搜索JS文件吗? - user3621014
特别是对于移动解决方案,您最好将所有脚本连接起来,压缩它们并仅加载一个JavaScript文件。在大多数情况下,这比加载许多小文件要快。延迟是您的敌人。 - hgoebl
@user3621014 哦,好的,我现在明白了,抱歉误解了。如果您担心加载时间,您可以考虑缩小文件、gzip压缩,甚至捆绑文件,如果这是一个真正的问题的话。 - Ian
看看这个非常酷的示例项目,是mrgamer创建的。不要把它视为所有项目的圣杯,但拥有另一种可能性的观点很酷。在解决方案中,为了开发方便,一切都被分离,但在构建阶段会合并所有内容...真的很酷。可以制作出相当快速和流畅的SPA应用程序。https://github.com/mrgamer/angular-login-example.git - Rohan Büchner
显示剩余2条评论
7个回答

22

如果我理解正确,您需要为每个视图加载特定的脚本?我正在分享这段代码片段,它来自一个使用 ocLazyLoader 插件按需加载模块的个人项目。

var myApp = angular.module("myApp", [
"ui.router", 
"oc.lazyLoad",  
]); 

接下来在你的路由中,你可以相应地加载动态JS/CSS文件,在这个例子中,我正在加载UI Select插件的依赖项。

myApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {

$stateProvider

  // State
    .state('demo', {
        url: "/demo.html",
        templateUrl: "views/demo.html",
        data: {pageTitle: 'demo page title'},
        controller: "GeneralController",
        resolve: {
            deps: ['$ocLazyLoad', function($ocLazyLoad) {
                return $ocLazyLoad.load([{
                    name: 'ui.select',
 // add UI select css / js for this state
                    files: [
                        'css/ui-select/select.min.css', 
                        'js/ui-select/select.min.js'
                    ] 
                }, {
                    name: 'myApp',
                    files: [
                        'js/controllers/GeneralController.js'
                    ] 
                }]);
            }]
        }
    })

1
你救了我!我遇到了一个小问题,折腾了四个多小时,但是你的解决方案对我非常有效。谢谢,伙计! - Rajkumar

7
如果您熟悉Grunt:

https://github.com/ericclemmons/grunt-angular-templates

使用Grunt和上述构建任务创建一个.js文件,其中包含所有视图。在视图目录中包含一个监听所有html文件的观察任务侦听器。每当对任何部分视图进行更改时,都会创建一个“$templateCache”条目,其中包含文件中的所有html和别名缓存的url。您的路由将以相同的方式指向部分视图,但不需要存在html文件,只需要具有模板的.js文件。这样做的美妙之处在于它仅加载一次,并在整个会话期间在客户端上可用。这减少了http调用,可以将流量仅减少到web服务调用。

这是来自上面github链接的模板示例:

angular.module('app').run(["$templateCache", function($templateCache) {
$templateCache.put("home.html",
// contents for home.html ...
);
...
$templateCache.put("src/app/templates/button.html",
// contents for button.html
);
}]);

如果你不熟悉Grunt,请查看一下。它对于自动化构建、缩小、合并、转译等方面非常有用。

http://gruntjs.com/


5
除非您的应用程序非常庞大,否则请务必避免单独提供小的js文件。即使您按照您在问题中建议的方式懒惰地按需获取文件,这也会明显减慢您的应用程序。
更好的方法(并且AngularJS团队使用和建议的方法)是使用构建过程(您应该使用grunt)将所有javascript文件连接起来,并将它们作为单个app.js文件提供。这样,您可以维护一个有组织的代码库,其中包含任意数量的微小js文件,但将脚本获取降低到一个请求。

请注意,这在http2中已不再适用,实际上现在更喜欢许多较小的文件。 - Adam Reis

4

如何安装OCLazyLoad。

1. 下载ocLazyLoad.js 这里

它可以在git存储库的“dist”文件夹中找到。您还可以使用bower install oclazyloadnpm install oclazyload进行安装。

2. 将oc.lazyLoad模块添加到您的应用程序:

var myApp = angular.module("MyApp", ["oc.lazyLoad"]);

3. 根据路由按需加载JS文件:

    myApp.controller("MyCtrl", function($ocLazyLoad){
       $ocLazyLoad.load('testModule.js');
    }});`

使用$ocLazyLoad,您可以加载Angular模块,但如果您想加载任何组件(控制器/服务/过滤器/...)而不定义新模块,这是完全可能的(只需确保您在现有模块中定义此组件)。
有多种使用$ocLazyLoad来加载文件的方法,只需选择您喜欢的即可。
还要记住,如果您想开始并且文档不足够,请查看“examples”文件夹中的示例!
快速入门: https://oclazyload.readme.io/docs

1

我不确定标准的angular是如何工作的,但您可以使用angular-ui-router:

控制器是在需要时实例化的,即当它们对应的作用域被创建时,例如用户通过URL手动导航到一个状态时,$stateProvider将把正确的模板加载到视图中,然后将控制器绑定到模板的作用域中。

https://github.com/angular-ui/ui-router/wiki


我非常确定这就是普通的Angular路由工作方式。OP的观点是,他们不想在需要之前加载控制器(文件)...而我认为ui-router在这方面也没有做任何不同的事情。 - Ian

1

RequireJS在这种情况下非常有用。您可以使用RequireJS声明JS文件的依赖关系。

您可以参考this简单的教程。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接