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

Posted

技术标签:

【中文标题】AngularJS - 基于路由动态加载外部 JS【英文标题】:AngularJS - dynamic loading of external JS based on routes 【发布时间】:2014-07-02 14:26:26 【问题描述】:

我正在尝试制作一个简单的单页移动应用程序,它具有多个视图和一个下一步\后退按钮来控制每个视图。我正在使用 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”等的顶部粘贴一个脚本标签吗?

【问题讨论】:

你想做的正是我现在的做法。每个控制器都在自己的 JS 文件中。我加载它们的方式是:app.controller("ControllerName", function () /* Controller code */ );(因为您将应用程序模块存储在全局变量app 中)。因此,我不使用app.controller(controllers) @lan 我的问题是如何在需要时提取控制器 JS 文件,而不是将它们全部放在脚本标签的主页顶部。假设我有 20 个控制器,每个组件一个。这将使加载时间变得可怕。我只想在需要时拉入 firstController 的 .JS。还是angular会自动搜索js文件?? 特别是对于移动解决方案,您应该更好地连接所有脚本,缩小它们并仅加载一个 javascript 文件。在大多数情况下,这比加载许多小文件要快。延迟是你的敌人。 @user3621014 啊,好吧,我想我现在明白了,抱歉误会。如果您担心加载时间,可以随时考虑缩小、gzip 甚至捆绑(如果这是一个真正的问题)。 看看 mrgamer 创建的这个非常酷的示例项目。不会把它当作所有项目的圣杯,但对你能做什么有另一个看法是很酷的。在解决方案中,为了便于开发,所有内容都是分开的,但在构建阶段所有内容都被组合在一起......确实非常酷。制作一个非常快速和流畅的 SPA 应用程序github.com/mrgamer/angular-login-example.git 【参考方案1】:

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

控制器根据需要实例化,当它们对应的 创建范围,即当用户通过 URL 手动导航到某个状态时, $stateProvider 会将正确的模板加载到视图中,然后绑定 控制器到模板的范围。

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

【讨论】:

我很确定这也是正常角度路由的工作原理。 OP 的观点是,他们甚至不想加载控制器(文件),直到需要它......而且我认为 ui-router 在这方面没有任何不同【参考方案2】:

如果我理解正确,您需要为每个视图加载特定脚本吗?我正在从一个使用ocLazyLoader 按需加载模块的插件的个人项目中分享这个sn-p。

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

然后在您的路由中,您可以相应地加载动态 JS/CSS 文件,在此示例中,我正在加载 UI 选择插件依赖项

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'
                    ] 
                ]);
            ]
        
    )

【讨论】:

你救了我!我对此有一个小问题并摆弄了 4 个多小时,但您的解决方案对我有用。谢谢伙计!【参考方案3】:

如果您熟悉 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/

【讨论】:

【参考方案4】:

如何安装 OLazyLoad。

1.下载ocLazyLoad.js here

它可以在 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,您可以加载角度模块,但如果您想加载任何组件(控制器/服务/过滤器/...)而不定义新模块,这是完全可能的(只需确保您在一个现有的模块)。

有多种方法可以使用$ocLazyLoad 加载文件,只需选择您喜欢的一种即可。

另外不要忘记,如果您想开始并且文档还不够,请查看“示例”文件夹中的示例!

Quick Start

【讨论】:

【参考方案5】:

我认为最好的方法是使用RequireJS,正如这里提到的Does it make sense to use Require.js with Angular.js? 这是完全允许的,它会让你达到你想要的。

这是一个示例代码

https://github.com/tnajdek/angular-requirejs-seed

【讨论】:

【参考方案6】:

除非您的应用是MASSIVE,否则您应该真正避免单独提供小型 js 文件。这会显着降低您的应用程序的速度,即使您要按照您在问题中的建议找到一种根据需要延迟获取文件的方法。

一个更好的方法(以及 AngularJS 团队使用和建议的方法)是有一个 BUILD PROCESS(你应该使用grunt)连接你所有的 javascript 文件,并将它们作为一个单一的app.js 文件。通过这种方式,您可以维护一个有组织的代码库,其中包含任意数量的小型 js 文件,但将脚本获取减少到单个请求。

【讨论】:

请注意,http2 不再是这种情况,实际上现在首选许多较小的文件。【参考方案7】:

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

你可以参考this简单教程。

【讨论】:

以上是关于AngularJS - 基于路由动态加载外部 JS的主要内容,如果未能解决你的问题,请参考以下文章

如何使用angularjs处理动态菜单

“高三”笔记之动态JS动态样式

基于路由动态加载 Node.js 模块

延迟加载基于控制器命名空间约定的外部 JS/CSS 文件?

AngularJS:通过 UI 路由器加载 js 文件

angularjs 路由 异步加载js