使用ocLazyLoad实现AngularJS模块按需懒加载

复杂的AngularJS SPA项目需要用路由来管理各个产品模块之间的切换。各个模块的代码如何引入?通常我们的简单做法是在首页将各个模块的代码全部引入,即使首页本身很简单也需要加载很多无用的代码。这样 一来会造成首屏加载时间过长。怎么做到首屏只引入需要显示的必要代码,而在必要的时候再去加载各个模块的代码,做到按需懒加载呢?

可能最容易想到的是使用RequireJS,网上也有这样的方案介绍,甚至有一些脚手架工程供我们使用。不过RequireJS的侵入式代码恐怕不是每个人都很接受的,万一哪天使用改成CMD、XMD又不得不改动代码,使用RequireJS只能算可行方案。有没有更好的方案呢?试一试ocLazyLoad,目前已经是1.0版本,比较稳定了。

一、快速入门

1. 下载ocLazyLoad.js。可以在git存储库的dist文件夹中找到该文件,也可以通过bower install oclazyload or npm install oclazyload来安装
2. 将oclazyLoad模块添加到应用程序中:

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

3. 按需加载:

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

使用$ocLazyLoad可以加载AngularJS模块, 但如果要加载任何组件(控制器/服务/过滤器/…) 而不用定义新模块也是完全有行的 (只是确保在现有模块中定义了此组件)。

二、实例

下面的实例使用angular-ui-router和ocLazyLoad实现了AngularJS模块按需懒加载。

index.html

<body ng-app="mainApp">
  <nav>
    <ul>
      <li><a ui-sref="home" ui-sref-active="active">Home</a></li>
      <li><a ui-sref="product" ui-sref-active="active">Product</a></li>
    </ul>
  </nav>
  <ui-view></ui-view>
  <script src="app.js"></script>
</body>

views/product.html

<section>{{text}}</section>

app.js

angular.module('mainApp', ['ui.router', 'oc.lazyLoad'])
  .config(function($urlRouterProvider, $stateProvider) {
    $urlRouterProvider.otherwise('/home');

    $stateProvider
      .state('product', {
        url: '/product',
        templateUrl: 'views/product.html',
        controller: 'ProductCtrl',
        resolve: {
          loadProduct: ['$ocLazyLoad', function($ocLazyLoad) {
            return $ocLazyLoad.load('./js/product.js');
          }]
        }
      })
      .state('home', {
        url: '/home',
        template: '<div>This is the home page</div>'
      });
  });

js/product.js

angular.module('productApp', [])
  .controller('ProductCtrl', function($scope) {
    $scope.text = 'This is Product';
  });

css/style.css

a {
  text-decoration: none;
  color: #333;
}
a:hover {
  color: #900;
  text-decoration: underline;
}
a.active {
  text-decoration: underline;
}

上述实例中,最关键的步骤是我们设置路由状态对象的resolve参数,指定当前controller依赖于$ocLazyLoad.load()方法动态加载的模块或其它任何组件,这实现了模块按需懒加载。剩下的就是分模块开发了,是不是比RequireJS方式更简单友好呢?

使用navbar和ui-router动态加载外部模板

使用AngularJS material navbar(导航栏组件)可以动态加载外部模板吗?看了AngularJS material官网的Demo,觉得还是没有交行清楚,导航栏的数据也是写死的。当然这对入门知识点很有用,不过对于实际项目,帮助就不大了。首先导航栏应该基于数据驱动来渲染,而且点击导航项时应该从动态加载外部模板。基于此,我们对md-nav-bar进行了扩展。 继续阅读“使用navbar和ui-router动态加载外部模板”

自定义AngularJS指令

Angular指令主要是对html的封装复用扩展。封装复用的一个很典型的场景就是多页面相同的底部,我们完全可以设定指令template属性内部嵌入代码模版,然后在每个页面中调用。类似后端语言中的include方法。更多时候可复用的html代码块作为一个模版视图,然后用ng-include或者是templateUrl来动态加载,这已经不局限于指令本身了。相比内部嵌入的方式,动态加载模板视图至少会多一次请求。 继续阅读“自定义AngularJS指令”

为MXItemRenderer添加自定义状态

MXItemRenderer是专为MX DataGrid、MX AdvancedDataGrid及MX Tree这类MX组件提供Spark项目渲染器的基类。与其它Spark渲染器一样,它有normal、 hovered、selected和dragging等状态。但在项目渲染器中添加了自定义状态后,默认状态会覆盖原有状态,保留不了新增状态,因而会出现项目渲染器界面错乱的问题(如实例中第一个MX DataGrid)。怎么解决这一个问题呢? 继续阅读“为MXItemRenderer添加自定义状态”