Maison  >  Article  >  interface Web  >  Explorez angulairejs+requirejs pour implémenter pleinement la routine de chargement à la demande_AngularJS

Explorez angulairejs+requirejs pour implémenter pleinement la routine de chargement à la demande_AngularJS

PHP中文网
PHP中文网original
2016-05-16 15:13:121450parcourir

Lorsque vous travaillez sur un projet d'une certaine envergure, vous souhaitez généralement atteindre les objectifs suivants : 1. Prendre en charge une logique de page complexe (affichage dynamique du contenu basé sur des règles métier, telles que les autorisations, l'état des données, etc.) ; .Adhérer au principe de base de séparation du front-end et du backend (lorsqu'il n'est pas séparé, vous pouvez utiliser le moteur de modèle pour générer directement une bonne page sur le backend) 3. Le temps de chargement de la page est court (une logique métier complexe nécessite le) ; référence d'une bibliothèque tierce, mais il est très probable que la bibliothèque chargée n'a rien à voir avec l'opération actuelle de l'utilisateur) ); 4. Le code doit être facile à maintenir (lors de l'ajout d'une nouvelle logique, affecter le moins de fichiers possible) ).

Pour atteindre ces objectifs en même temps, il doit y avoir un mécanisme de chargement à la demande. Le contenu affiché sur la page et tous les fichiers dépendants peuvent être chargés à la demande en fonction des besoins de la logique métier. Récemment, tous les développements sont basés sur Angularjs, donc cet article se concentre principalement sur les différents mécanismes fournis par Angularjs pour explorer les moyens d'implémenter pleinement le chargement à la demande.

1. Mise en œuvre étape par étape
Idées de base : 1. Développez d'abord une page de cadre, qui peut compléter certaines logiques métier de base et prendre en charge les mécanismes d'expansion ; , et une certaine logique doit être divisée en sous-pages, qui sont chargées à la demande ; 3. Le contenu affiché dans les sous-pages est également devenu complexe et doit être divisé et chargé à la demande ; les sous-pages sont complexes Pour s'appuyer sur des modules externes, vous devez charger le module angulaire à la demande.

1. Page Frame
Quand il s'agit de chargement à la demande du front-end, AMD (Asynchronous Module Definition) vient à l'esprit. de nombreux requirejs sont utilisés. Pensez donc d’abord à introduire des exigences.

index.html

<script src="static/js/require.js" defer async data-main="/test/lazyspa/spa-loader.js"></script>

Remarque : Angular est démarré manuellement, il n'y a donc pas de ng-app dans le code HTML.

spa-loader.js

require.config({
  paths: {
    "domReady": &#39;/static/js/domReady&#39;,
    "angular": "//cdn.bootcss.com/angular.js/1.4.8/angular.min",
    "angular-route": "//cdn.bootcss.com/angular.js/1.4.8/angular-route.min",
  },
  shim: {
    "angular": {
      exports: "angular"
    },
    "angular-route": {
      deps: ["angular"]
    },
  },
  deps: [&#39;/test/lazyspa/spa.js&#39;],
  urlArgs: "bust=" + (new Date()).getTime()
});

spa.js

define(["require", "angular", "angular-route"], function(require, angular) {
  var app = angular.module(&#39;app&#39;, [&#39;ngRoute&#39;]);
  require([&#39;domReady!&#39;], function(document) {
    angular.bootstrap(document, ["app"]); /*手工启动angular*/
    window.loading.finish();
  });
});

2. Charger des sous-pages à la demande
routeProvider+ng-view d'Angular fournit déjà une méthode complète de chargement de sous-page, qui peut être utilisée directement.
Notez que html5Mode doit être défini, sinon routeProvider n'interceptera pas l'URL après sa modification.

index.html

<div>
  <a href="/test/lazyspa/page1">page1</a>
  <a href="/test/lazyspa/page2">page2</a>
  <a href="/test/lazyspa/">main</a>
</div>
<div ng-view></div>

spa.js

app.config([&#39;$locationProvider&#39;, &#39;$routeProvider&#39;, function($locationProvider, $routeProvider) {
  /* 必须设置生效,否则下面的设置不生效 */
  $locationProvider.html5Mode(true);
  /* 根据url的变化加载内容 */
  $routeProvider.when(&#39;/test/lazyspa/page1&#39;, {
    template: &#39;<div>page1</div>&#39;,
  }).when(&#39;/test/lazyspa/page2&#39;, {
    template: &#39;<div>page2</div>&#39;,
  }).otherwise({
    template: &#39;<div>main</div>&#39;,
  });
}]);

3. Charger du contenu dans des sous-pages à la demande
utiliser Le principe de routeProvider est que l'URL doit changer, mais parfois seules certaines parties de la sous-page doivent changer. Si ces modifications sont principalement liées aux données liées et n'affectent pas la mise en page, ou si l'impact est très faible, alors des balises telles que ng-if peuvent fondamentalement résoudre le problème. Cependant, le contenu local doit parfois être complètement modifié en fonction de l'état de la page, comme des modifications locales avant et après la connexion de l'utilisateur, etc. Cela signifie que la mise en page locale peut être assez complexe et doit être traitée comme une unité indépendante. .

Utilisez ng-include pour résoudre le problème du chargement de contenu partiel sur la page. On peut cependant envisager une situation plus complexe. Le code correspondant à ce fragment de page est généré dynamiquement par le backend, et ce n'est pas seulement du html mais aussi du js. Le contrôleur correspondant au fragment de code est défini en js. Dans ce cas, non seulement la question du chargement dynamique du HTML doit être prise en compte, mais également celle de la définition dynamique du contrôleur. Le contrôleur est enregistré via la méthode de registre du contrôleurProvider d'angular, il est donc nécessaire d'obtenir une instance du contrôleurProvider.

spa.js

app.config([&#39;$locationProvider&#39;, &#39;$routeProvider&#39;, &#39;$controllerProvider&#39;, 
function($locationProvider, $routeProvider, $controllerProvider) {
  app.providers = {
    $controllerProvider: $controllerProvider //注意这里!!!
  };
  /* 必须设置生效,否则下面的设置不生效 */
  $locationProvider.html5Mode(true);
  /* 根据url的变化加载内容 */
  $routeProvider.when(&#39;/test/lazyspa/page1&#39;, {
    /*!!!页面中引入动态内容!!!*/
    template: &#39;<div>page1</div><div ng-include="\&#39;page1.html\&#39;"></div>&#39;,
    controller: &#39;ctrlPage1&#39;
  }).when(&#39;/test/lazyspa/page2&#39;, {
    template: &#39;<div>page2</div>&#39;,
  }).otherwise({
    template: &#39;<div>main</div>&#39;,
  });
  app.controller(&#39;ctrlPage1&#39;, [&#39;$scope&#39;, &#39;$templateCache&#39;, function($scope, $templateCache) {
    /* 用这种方式,ng-include配合,根据业务逻辑动态获取页面内容 */
    /* !!!动态的定义controller!!! */
    app.providers.$controllerProvider.register(&#39;ctrlPage1Dyna&#39;, [&#39;$scope&#39;, function($scope) {
      $scope.openAlert = function() {
        alert(&#39;page1 alert&#39;);
      };
    }]);
    /* !!!动态定义页面的内容!!! */
    $templateCache.put(&#39;page1.html&#39;, &#39;<div ng-controller="ctrlPage1Dyna">
    <button ng-click="openAlert()">alert</button></div>&#39;);
  }]);
}]);

4. Le module de chargement dynamique
adopte la sous-page ci-dessus fragment Il existe une limitation dans la méthode de chargement, c'est-à-dire que diverses logiques (js) doivent être ajoutées au module de démarrage, ce qui limite toujours l'encapsulation indépendante des fragments de sous-page. En particulier, si le fragment de sous-page nécessite l'utilisation d'un module tiers, et que ce module n'est pas chargé au préalable dans le module de démarrage, il n'y a pas de solution. Il est donc nécessaire de pouvoir charger dynamiquement les modules. Implémenter le chargement dynamique des modules consiste à extraire la méthode de chargement des modules lors du démarrage angulaire, puis à gérer certaines situations particulières.

Cependant, lorsque je l'ai exécuté, j'ai découvert qu'il y avait un problème avec le code dans l'article. Qu'est-ce que "$injector" exactement ? Après avoir étudié le code source d'Angular injector.js, j'ai à peu près compris ce qui se passait.

Une application possède deux $injectors, supplierInjector et instanceInjector. InvoqueQueue utilise ProviderInjector et runBlocks utilise instanceProvider. Si $injector est mal utilisé, le service requis sera trouvé.

Chargement dynamique des fichiers du module dans routeProvider.

template: &#39;<div ng-controller="ctrlModule1"><div>page2</div><div>
<button ng-click="openDialog()">open dialog</button></div></div>&#39;,
resolve: {
  load: [&#39;$q&#39;, function($q) {
    var defer = $q.defer();
    /* 动态加载angular模块 */
    require([&#39;/test/lazyspa/module1.js&#39;], function(loader) {
      loader.onload && loader.onload(function() {
        defer.resolve();
      });
    });
    return defer.promise;
  }]
}

Chargement dynamique des modules angulaires

angular._lazyLoadModule = function(moduleName) {
  var m = angular.module(moduleName);
  console.log(&#39;register module:&#39; + moduleName);
  /* 应用的injector,和config中的injector不是同一个,是instanceInject,返回的是通过provider.$get创建的实例 */
  var $injector = angular.element(document).injector();
  /* 递归加载依赖的模块 */
  angular.forEach(m.requires, function(r) {
    angular._lazyLoadModule(r);
  });
  /* 用provider的injector运行模块的controller,directive等等 */
  angular.forEach(m._invokeQueue, function(invokeArgs) {
    try {
      var provider = providers.$injector.get(invokeArgs[0]);
      provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
    } catch (e) {
      console.error(&#39;load module invokeQueue failed:&#39; + e.message, invokeArgs);
    }
  });
  /* 用provider的injector运行模块的config */
  angular.forEach(m._configBlocks, function(invokeArgs) {
    try {
      providers.$injector.invoke.apply(providers.$injector, invokeArgs[2]);
    } catch (e) {
      console.error(&#39;load module configBlocks failed:&#39; + e.message, invokeArgs);
    }
  });
  /* 用应用的injector运行模块的run */
  angular.forEach(m._runBlocks, function(fn) {
    $injector.invoke(fn);
  });
};

Définir le module
module1.js

define(["angular"], function(angular) {
  var onloads = [];
  var loadCss = function(url) {
    var link, head;
    link = document.createElement(&#39;link&#39;);
    link.href = url;
    link.rel = &#39;stylesheet&#39;;
    head = document.querySelector(&#39;head&#39;);
    head.appendChild(link);
  };
  loadCss(&#39;//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css&#39;);
  /* !!! 动态定义requirejs !!!*/
  require.config({
    paths: {
      &#39;ui-bootstrap-tpls&#39;: &#39;//cdn.bootcss.com/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.min&#39;
    },
    shim: {
      "ui-bootstrap-tpls": {
        deps: [&#39;angular&#39;]
      }
    }
  });
  /*!!! 模块中需要引用第三方的库,加载模块依赖的模块 !!!*/
  require([&#39;ui-bootstrap-tpls&#39;], function() {
    var m1 = angular.module(&#39;module1&#39;, [&#39;ui.bootstrap&#39;]);
    m1.config([&#39;$controllerProvider&#39;, function($controllerProvider) {
      console.log(&#39;module1 - config begin&#39;);
    }]);
    m1.controller(&#39;ctrlModule1&#39;, [&#39;$scope&#39;, &#39;$uibModal&#39;, function($scope, $uibModal) {
      console.log(&#39;module1 - ctrl begin&#39;);
      /*!!! 打开angular ui的对话框 !!!*/
      var dlg = &#39;<div class="modal-header">&#39;;
      dlg += &#39;<h3 class="modal-title">I\&#39;m a modal!</h3>&#39;;
      dlg += &#39;</div>&#39;;
      dlg += &#39;<div class="modal-body">content</div>&#39;;
      dlg += &#39;<div class="modal-footer">&#39;;
      dlg += &#39;<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>&#39;;
      dlg += &#39;<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>&#39;;
      dlg += &#39;</div>&#39;;
      $scope.openDialog = function() {
        $uibModal.open({
          template: dlg,
          controller: [&#39;$scope&#39;, &#39;$uibModalInstance&#39;, function($scope, $mi) {
            $scope.cancel = function() {
              $mi.dismiss();
            };
            $scope.ok = function() {
              $mi.close();
            };
          }],
          backdrop: &#39;static&#39;
        });
      };
    }]);
    /* !!!动态加载模块!!! */
    angular._lazyLoadModule(&#39;module1&#39;);
    console.log(&#39;module1 loaded&#39;);
    angular.forEach(onloads, function(onload) {
      angular.isFunction(onload) && onload();
    });
  });
  return {
    onload: function(callback) {
      onloads.push(callback);
    }
  };
});

2. Code complet
index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta content="width=device-width,user-scalable=no,initial-scale=1.0" name="viewport">
    <base href=&#39;/&#39;>
    <title>SPA</title>
  </head>
  <body>
    <div ng-controller=&#39;ctrlMain&#39;>
      <div>
        <a href="/test/lazyspa/page1">page1</a>
        <a href="/test/lazyspa/page2">page2</a>
        <a href="/test/lazyspa/">main</a>
      </div>
      <div ng-view></div>
    </div>
    <div class="loading"><div class=&#39;loading-indicator&#39;><i></i></div></div>
    <script src="static/js/require.js" defer async data-main="/test/lazyspa/spa-loader.js?_=3"></script>
  </body>
</html>

spa-loader.js

window.loading = {
  finish: function() {
    /* 保留个方法做一些加载完成后的处理,我实际的项目中会在这里结束加载动画 */
  },
  load: function() {
    require.config({
      paths: {
        "domReady": &#39;/static/js/domReady&#39;,
        "angular": "//cdn.bootcss.com/angular.js/1.4.8/angular.min",
        "angular-route": "//cdn.bootcss.com/angular.js/1.4.8/angular-route.min",
      },
      shim: {
        "angular": {
          exports: "angular"
        },
        "angular-route": {
          deps: ["angular"]
        },
      },
      deps: [&#39;/test/lazyspa/spa.js&#39;],
      urlArgs: "bust=" + (new Date()).getTime()
    });
  }
};
window.loading.load();

spa.js

&#39;use strict&#39;;
define(["require", "angular", "angular-route"], function(require, angular) {
  var app = angular.module(&#39;app&#39;, [&#39;ngRoute&#39;]);
  /* 延迟加载模块 */
  angular._lazyLoadModule = function(moduleName) {
    var m = angular.module(moduleName);
    console.log(&#39;register module:&#39; + moduleName);
    /* 应用的injector,和config中的injector不是同一个,是instanceInject,返回的是通过provider.$get创建的实例 */
    var $injector = angular.element(document).injector();
    /* 递归加载依赖的模块 */
    angular.forEach(m.requires, function(r) {
      angular._lazyLoadModule(r);
    });
    /* 用provider的injector运行模块的controller,directive等等 */
    angular.forEach(m._invokeQueue, function(invokeArgs) {
      try {
        var provider = providers.$injector.get(invokeArgs[0]);
        provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
      } catch (e) {
        console.error(&#39;load module invokeQueue failed:&#39; + e.message, invokeArgs);
      }
    });
    /* 用provider的injector运行模块的config */
    angular.forEach(m._configBlocks, function(invokeArgs) {
      try {
        providers.$injector.invoke.apply(providers.$injector, invokeArgs[2]);
      } catch (e) {
        console.error(&#39;load module configBlocks failed:&#39; + e.message, invokeArgs);
      }
    });
    /* 用应用的injector运行模块的run */
    angular.forEach(m._runBlocks, function(fn) {
      $injector.invoke(fn);
    });
  };
  app.config([&#39;$injector&#39;, &#39;$locationProvider&#39;, &#39;$routeProvider&#39;, &#39;$controllerProvider&#39;, 
  function($injector, $locationProvider, $routeProvider, $controllerProvider) {
    /**
     * config中的injector和应用的injector不是同一个,是providerInjector,获得的是provider,
     而不是通过provider创建的实例
     * 这个injector通过angular无法获得,所以在执行config的时候把它保存下来
    */
    app.providers = {
      $injector: $injector,
      $controllerProvider: $controllerProvider
    };
    /* 必须设置生效,否则下面的设置不生效 */
    $locationProvider.html5Mode(true);
    /* 根据url的变化加载内容 */
    $routeProvider.when(&#39;/test/lazyspa/page1&#39;, {
      template: &#39;<div>page1</div><div ng-include="\&#39;page1.html\&#39;"></div>&#39;,
      controller: &#39;ctrlPage1&#39;
    }).when(&#39;/test/lazyspa/page2&#39;, {
      template: &#39;<div ng-controller="ctrlModule1"><div>page2</div><div>
      <button ng-click="openDialog()">open dialog</button></div></div>&#39;,
      resolve: {
        load: [&#39;$q&#39;, function($q) {
          var defer = $q.defer();
          /* 动态加载angular模块 */
          require([&#39;/test/lazyspa/module1.js&#39;], function(loader) {
            loader.onload && loader.onload(function() {
              defer.resolve();
            });
          });
          return defer.promise;
        }]
      }
    }).otherwise({
      template: &#39;<div>main</div>&#39;,
    });
  }]);
  app.controller(&#39;ctrlMain&#39;, [&#39;$scope&#39;, &#39;$location&#39;, function($scope, $location) {
    console.log(&#39;main controller&#39;);
    /* 根据业务逻辑自动到缺省的视图 */
    $location.url(&#39;/test/lazyspa/page1&#39;);
  }]);
  app.controller(&#39;ctrlPage1&#39;, [&#39;$scope&#39;, &#39;$templateCache&#39;, function($scope, $templateCache) {
    /* 用这种方式,ng-include配合,根据业务逻辑动态获取页面内容 */
    /* 动态的定义controller */
    app.providers.$controllerProvider.register(&#39;ctrlPage1Dyna&#39;, [&#39;$scope&#39;, function($scope) {
      $scope.openAlert = function() {
        alert(&#39;page1 alert&#39;);
      };
    }]);
    /* 动态定义页面内容 */
    $templateCache.put(&#39;page1.html&#39;, &#39;<div ng-controller="ctrlPage1Dyna">
    <button ng-click="openAlert()">alert</button></div>&#39;);
  }]);
  require([&#39;domReady!&#39;], function(document) {
    angular.bootstrap(document, ["app"]);
  });
});

module1.js

&#39;use strict&#39;;
define(["angular"], function(angular) {
  var onloads = [];
  var loadCss = function(url) {
    var link, head;
    link = document.createElement(&#39;link&#39;);
    link.href = url;
    link.rel = &#39;stylesheet&#39;;
    head = document.querySelector(&#39;head&#39;);
    head.appendChild(link);
  };
  loadCss(&#39;//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css&#39;);
  require.config({
    paths: {
      &#39;ui-bootstrap-tpls&#39;: &#39;//cdn.bootcss.com/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.min&#39;
    },
    shim: {
      "ui-bootstrap-tpls": {
        deps: [&#39;angular&#39;]
      }
    }
  });
  require([&#39;ui-bootstrap-tpls&#39;], function() {
    var m1 = angular.module(&#39;module1&#39;, [&#39;ui.bootstrap&#39;]);
    m1.config([&#39;$controllerProvider&#39;, function($controllerProvider) {
      console.log(&#39;module1 - config begin&#39;);
    }]);
    m1.controller(&#39;ctrlModule1&#39;, [&#39;$scope&#39;, &#39;$uibModal&#39;, function($scope, $uibModal) {
      console.log(&#39;module1 - ctrl begin&#39;);
      var dlg = &#39;<div class="modal-header">&#39;;
      dlg += &#39;<h3 class="modal-title">I\&#39;m a modal!</h3>&#39;;
      dlg += &#39;</div>&#39;;
      dlg += &#39;<div class="modal-body">content</div>&#39;;
      dlg += &#39;<div class="modal-footer">&#39;;
      dlg += &#39;<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>&#39;;
      dlg += &#39;<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>&#39;;
      dlg += &#39;</div>&#39;;
      $scope.openDialog = function() {
        $uibModal.open({
          template: dlg,
          controller: [&#39;$scope&#39;, &#39;$uibModalInstance&#39;, function($scope, $mi) {
            $scope.cancel = function() {
              $mi.dismiss();
            };
            $scope.ok = function() {
              $mi.close();
            };
          }],
          backdrop: &#39;static&#39;
        });
      };
    }]);
    angular._lazyLoadModule(&#39;module1&#39;);
    console.log(&#39;module1 loaded&#39;);
    angular.forEach(onloads, function(onload) {
      angular.isFunction(onload) && onload();
    });
  });
  return {
    onload: function(callback) {
      onloads.push(callback);
    }
  };
});

Ce qui précède est le contenu de l'exploration d'angularjs+requirejs pour implémenter pleinement la routine de chargement à la demande_AngularJS, et plus Pour le contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn