Angularjs作为mvc(或者说mvvm)框架,同样具备模板这一基本概念。NG加载模板的顺序为:内存加载,AJAX加载。
一.内存加载
如果之前使用过Bootstrap 插件的ng版,即angular-ui,就会了解到这种方式的具体应用。模板本质上是字符串,把字符串直接写入内存,加载时直接从内存获取,速度会更快,有两种方式显式启用内存加载。
1.通过使用$templateCache service来实现
<html>
<head>
<title>调用$templateCache服务获取模板文件的方式</title>
<script src="jquery-1.8.3.js"></script>
<script src="angular1.2.9.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl">
<div ng-include="'lovestory.html'" class="well"></div>
</body>
</html>
app.js
var app = angular.module('myModule', []); app.controller('myCtrl', ['$scope','$templateCache', function($scope,$templateCache){ var tmp = '<h4>lovestory</h4>' + '<p>这是直接调用$templateCache服务获取模板文件的方式</p>' + '<a href="http://www.baidu.com">服务启用templateCache方式</a>'; $templateCache.put('lovestory.html',tmp); }]); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
$templateCache服务put方法负责向内存写入模板内容。
2.通过script标签引入
<html>
<head>
<title>script标签获取模板文件的方式</title>
<script src="jquery-1.8.3.js"></script>
<script src="angular1.2.9.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl">
<div ng-include="'lovestory.html'" class="well"></div>
<script type="text/ng-template" id="lovestory.html">
<h4>lovestory</h4>
<p>这是script标签获取模板文件的方式</p>
<a href="http://www.baidu.com">标签启用templateCache方式</a>
</script>
</body>
</html>
app.js
var app = angular.module('myModule', []); app.controller('myCtrl', ['$scope', function($scope){ }]); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
这里需要注意,type="text/ng-template"是指明这是ng模板,id属性是指实际使用模板时的一个引用,标签之间的内容才是实际的模板内容。而且,需要注意,id绝对不是URL,这个script标签绝对不会发出HTTP请求。
实际应用模板时候,使用ID属性,即可从内存中获取对应数据。
<div ng-include="'lovestory.html'" class="well"></div>
使用ng-include的时候,应该注意,id相当于一个字符串,不是ng-expression,所以不要忘了加单引号。
二.AJAX加载
上述的内存加载,相当于一个预定义模板,定义在client-side,不会与服务器有任何交互,适合变化频率低的模板。
当NG在内存中找不到对应模板时,就会启用AJAX请求,去拉取对应模板。
<html>
<head>
<title>templateCache中不存在模板发Ajax请求获取</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK"/>
<script src="jquery-1.8.3.js"></script>
<script src="angular1.2.9.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl">
<div ng-include="'lovestory.html'" class="well"></div>
</body>
</html>
app.js
var app = angular.module('myModule', []); app.controller('myCtrl', ['$scope','$templateCache', function($scope,$templateCache){ var tmp = '<h4>lovestory</h4>' + '<p>这是直接调用$templateCache服务获取模板文件的方式</p>' + '<a href="http://www.baidu.com">服务启用templateCache方式</a>'; //$templateCache.put('lovestory.html',tmp); }]); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
lovestory.html
<div>
<h4>lovestory</h4>
<p>$templateCache get template file</p>
<a href="http://www.baidu.com">Ajax request templateCache</a>
<div>
用Firefox运行,效果如下:
须特别说明的是,如果templateCache中存在模板不再发Ajax请求获取,如下实例所示:
<html>
<head>
<title>templateCache中存在模板不发Ajax请求获取</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK"/>
<script src="jquery-1.8.3.js"></script>
<script src="angular1.2.9.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl">
<div ng-include="'lovestory.html'" class="well"></div>
</body>
</html>
app.js
var app = angular.module('myModule', []); app.controller('myCtrl', ['$scope','$templateCache','$http', function($scope,$templateCache,$http){ var tmp = '<h4>lovestory</h4>' + '<p>这是直接调用$templateCache服务获取模板文件的方式</p>' + '<a href="http://www.baidu.com">服务启用templateCache方式</a>'; $templateCache.put('lovestory.html',tmp); //$http.get('lovestory.html').success(function(data, status, headers, config){ // $templateCache.put('lovestory.html',data); //}); /* $.ajax({ type: "get", url: "lovestory.html", dataType: 'html', //async: false, async: true, success: function(data, textStatus){ $templateCache.put('lovestory.html',data); } });*/ }]); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
lovestory.html
<div>
<h4>lovestory</h4>
<p>$templateCache get template file</p>
<a href="http://www.baidu.com">Ajax request templateCache</a>
<div>
在实际项目中,我们可以$http或Ajax请求到服务端获取模板保存到$templateCache中,这样
<div ng-include="'lovestory.html'" class="well"></div>
将不会再向服务端发请求,而是从$templateCache中直接获取。
当然,在指令中同样可以使用,templateUrl对应值。如下所示:
<html>
<head>
<title>指令中运用templateUrl实例</title>
<script src="jquery-1.8.3.js"></script>
<script src="angular1.2.9.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl">
<div template-demo></div>
</body>
</html>
app.js
var app = angular.module('myModule', []); app.controller('myCtrl', ['$scope','$templateCache', function($scope,$templateCache){ }]); app.directive('templateDemo', ['$log', function($log){ return { restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment templateUrl: 'lovestory.html', //template: '<div><h4>lovestory</h4><p>这是直接调用$templateCache服务获取模板文件的方式</p><a href="http://www.baidu.com">Ajax request templateCache</a></div>', replace: true, link: function($scope, iElm, iAttrs, controller) {} } }]); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
lovestory.html
<div>
<h4>lovestory</h4>
<p>$templateCache get template file</p>
<a href="http://www.baidu.com">Ajax request templateCache</a>
</div>
内存中没有对应模板时,AJAX请求成功后将对应内容写入$templateCache,在页面不进行刷新,不手动删除的情况下,写入的内容不会丢失。而且,务必记住,AJAX是有缓存控制的。
三.内存模板优点
在雅虎前端优化34条里,有一条是“合并压缩文件”。
合并压缩文件可以减小HTTP请求量,又可以减小网络传输量,对于路径依赖并不严重的JS,CSS文件完全是必备,因为各JS,CSS文件开发时分割为不同的文件,实现各自的功能需求,上线时合并即可,但是,HTML文件可以压缩,但是无法合并,因为路径依赖严重。
以如下路由实例为例:
angular.module('administratorApp',[]) .config(function ($routeProvider,$locationProvider) { $locationProvider.html5Mode(false); $routeProvider .when('/manage', { templateUrl: 'views/manage.html', controller: 'ManageCtrl' }) .when('/diary/:key', { templateUrl: 'views/diaryDetail.html', controller: 'DiaryDetailCtrl', }) .when('/diary', { templateUrl: 'views/diaryList.html', controller: 'DiaryListCtrl' }) .when('/publish/:key', { templateUrl: 'views/update.html', controller: 'UpdateCtrl' }) .when('/publish', { templateUrl: 'views/publish.html', controller: 'PublishCtrl' }) .when('/record', { templateUrl: 'views/record.html', controller: 'RecordCtrl' }) .otherwise({ redirectTo: '/diary' }); });
六个控制器需要六个模板,六次HTTP请求加载数据量并不大的模板资源浪费严重。NG的优化方案是,通过虚拟路径取代实体路径,去除掉server-side的路径依赖。
好处就是,一个JS文件一次HTTP请求,而不是六次。坏处就是内存压力变大,PC上无所谓,开发web app(mobile)就需要注意几点:
a.移动端内存太脆,尽量不要使用上述所说的预定义模板,因为模板会全部加载到内存中
b.AJAX请求完毕,会自动把结果放入cache里,所以需要手动控制.模板与控制器存在对应关系,可以在控制器内部加上如下代码
$scope.$on('$locationChangeStart',function(){ $templateCache.remove('****.html'); })
c.$routeProvider的template,templateUrl可以是函数,通过函数返回值可以控制模板加载。
d.本人并未涉及到移动端开发,所以此处为思考所得,而且随着手机硬件性能提升,内存不再是个困扰。
四.$templateCache 方法
$templateCache基于cacheFactory而来,接口保持一致,可以认为$templateCache = $cacheFactory('template');
五.Grunt与ID属性误解
module.exports = function(grunt){ grunt.initConfig({ html2js : { simple : { options : { base : '', module : 'templateStore' }, files : [{ src : ['views/*.html'], dest : 'build/scripts/templateStore.js' }] } } }); grunt.loadNpmTasks('grunt-html2js'); grunt.registerTask('default',['html2js']); }
这是我目前使用Grunt--html2js的配置方案,目的是将views文件夹下的所有模板文件全部放入templateStore模块中,各模板对应ID即为路径,生成的部分代码如下:
angular.module("views/diaryList.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("views/diaryList.html", '*******' }]);
这部分代码等效于
<script type="text/ng-template" id="views/diaryList.html">
***********
</script>
现在应该明白,id只是个标示,不是URL。