Angular項(xiàng)目構(gòu)建指南:不再為angular構(gòu)建而猶豫不決
前言
接觸Angular也有小半個(gè)月了,雖然沒有使勁折騰,不過正所謂"no zuo no die".學(xué)一門新東西,不好好折騰一下總覺得對(duì)不起祖國(guó),最不起人民...好像扯遠(yuǎn)了,想寫前言來著.為什么要寫這篇構(gòu)建指南?***的原因是為了給正在找這方面資料,掙扎于各種說法中的同學(xué)一個(gè)借鑒,同時(shí)我也把自己的經(jīng)驗(yàn)記錄下來,兩全其美.
正文
如果你不知道什么是Angular或者根本沒聽說過,那么我接下來所說的對(duì)你來說毫無益處,不過如果你打算以后會(huì)接觸Angular或者干脆要漲漲姿勢(shì)~讀下去還是有點(diǎn)用的.
Angular和它之前所出現(xiàn)的其余前端框架***的不同,在于它的核心不再是DOM,而是數(shù)據(jù),是model.我們慣用的不管是單純的jQuery還是MVC的Backbone,它們本質(zhì)仍是讓我們更方便更有條理的操作DOM,但是Angular不是.通過一系列魔術(shù)般的手法,它將一切的重心轉(zhuǎn)移到數(shù)據(jù)上.以開發(fā)應(yīng)用而不是操作節(jié)點(diǎn)的方式去開發(fā)Web,一切以數(shù)據(jù)為中心,數(shù)據(jù)的變化驅(qū)動(dòng)了一切,包括行為.
文本主題,如何構(gòu)建一個(gè)angular項(xiàng)目?
坦白說最開始構(gòu)建一個(gè)項(xiàng)目的時(shí)候,雖然很小但是很糾結(jié).我本身是有點(diǎn)***主義的,所以雖然一開始什么都沒有也想做到盡善盡美.因?yàn)槁犨^很多前輩的經(jīng)驗(yàn),說如果框架基礎(chǔ)沒搭好,等到后來不管是重構(gòu)還是維護(hù)都是一場(chǎng)噩夢(mèng).所以一開始小心意義,希望能將項(xiàng)目盡量搭建的結(jié)實(shí)并且益于維護(hù)和開發(fā).
在搭建伊始首先遇到的一個(gè)問題,就是到底要不要引入requirejs或者seajs這類依賴管理的工具?
我本身沒有多少語言或者技術(shù)的上的情節(jié),對(duì)于各個(gè)大神也沒有多少膜拜的憧憬(更多的是我根本不清楚誰是大神,也從沒去找過).所以對(duì)于我來講不管是requirejs的AMD還是seajs的CMD,從實(shí)現(xiàn)的角度上來講都是做了同一個(gè)工作.在考慮一個(gè)Angular應(yīng)用到底需不需要這種工具的時(shí)候,我也在網(wǎng)上看了很多人的說法.我總結(jié)一句就是,基本都和沒說一樣,也就是用不用隨便,看情況.
那么我能有什么好的答案,其實(shí)我現(xiàn)在的答案就是:"可以不用".怎么說是可以不用呢,如果你不用requirejs也能滿足項(xiàng)目的開發(fā)以及各種需求,那么就別用了.angular本身的模塊已經(jīng)做到了依賴注入,所以我們不需要通過requirejs進(jìn)行異步加載也可以很好的用下去.
當(dāng)然,如果你開發(fā)過程中發(fā)覺還是有些地方需要,那么也可以加上去.本文里我會(huì)詳細(xì)說明這兩種方式的構(gòu)建方法.但是這里我的觀點(diǎn)已經(jīng)表明了:在不需要的情況下,不要用.
(1) 不用requirejs直接構(gòu)建Angular
之所以不使用requirejs就直接構(gòu)建angular,因?yàn)閍ngular對(duì)于依賴的管理以及angular的使用場(chǎng)景完全可以做到這一點(diǎn).首先在以來上,angular的依賴注入是個(gè)好東西,不了解的同學(xué)可以去搜一下資料.我這里簡(jiǎn)單的說,就是當(dāng)我需要一個(gè)module的時(shí)候,我不用管它在哪,它是什么.我只要知道它的名字然后告訴angular就可以了,至于怎么將它的對(duì)象傳遞過來,怎么找到的,angular自己會(huì)去處理.
- angular.module('myApp', [
- 'ngRoute',
- ]);
例如這里的ngRoute,我需要知道ngRoute怎么來的,在哪里.只要有一個(gè)模塊定義為ngRoute我就可以直接拿來用。
鑒于Angular如此的給力,剩下的事情就好辦了.我們只需要從功能和業(yè)務(wù)兩方面將文件劃分成module就可以了,然后將所有的庫(kù)文件在頁面上通過script標(biāo)簽引用,再將所有的業(yè)務(wù)文件也即是我們自己寫的js合并為一個(gè)all.js加載到頁面上即可。
這里文件的劃分遵循angular官方的推薦方式:
|--js |--app.js // app啟動(dòng)文件,用于app配置 |--controllers.js // controllers也就是存放我們自己的業(yè)務(wù)文件 |--directives.js // 指令文件(指令可共用) |--fliters.js // 過濾器文件(過濾器可共用) |--services.js // 服務(wù)文件(可共用,一般是與服務(wù)器交互的服務(wù)) |--partials |--html1.html |--html2.html |--index.html
app.js
- 'use strict';
- // Declare app level module which depends on filters, and services
- angular.module('myApp', [
- 'ngRoute',
- 'myApp.filters',
- 'myApp.services',
- 'myApp.directives',
- 'myApp.controllers'
- ]).
- config(['$routeProvider', function($routeProvider) {
- $routeProvider.when('/view1', {templateUrl: 'partials/partial1.html', controller: 'MyCtrl1'});
- $routeProvider.when('/view2', {templateUrl: 'partials/partial2.html', controller: 'MyCtrl2'});
- $routeProvider.otherwise({redirectTo: '/view1'});
- }]);
controllers.js
- 'use strict';
- /* Controllers */
- angular.module('myApp.controllers', [])
- .controller('MyCtrl1', ['$scope', function($scope) {
- }])
- .controller('MyCtrl2', ['$scope', function($scope) {
- }]);
directives.js
- 'use strict';
- /* Directives */
- angular.module('myApp.directives', []).
- directive('appVersion', ['version', function(version) {
- return function(scope, elm, attrs) {
- elm.text(version);
- };
- }]);
filters.js
- 'use strict';
- /* Filters */
- angular.module('myApp.filters', []).
- filter('interpolate', ['version', function(version) {
- return function(text) {
- return String(text).replace(/\%VERSION\%/mg, version);
- };
- }]);
services.js
- 'use strict';
- /* Services */
- // Demonstrate how to register services
- // In this case it is a simple value service.
- angular.module('myApp.services', []).
- value('version', '0.1');
index.html
- <!DOCTYPE html>
- <!--[if lt IE 7]> <html ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
- <!--[if IE 7]> <html ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
- <!--[if IE 8]> <html ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
- <!--[if gt IE 8]><!--> <html ng-app="myApp"> <!--<![endif]-->
- <head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <title>My AngularJS App</title>
- <meta name="description" content="">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" href="bower_components/html5-boilerplate/css/normalize.css">
- <link rel="stylesheet" href="bower_components/html5-boilerplate/css/main.css">
- <link rel="stylesheet" href="css/app.css"/>
- <script src="bower_components/html5-boilerplate/js/vendor/modernizr-2.6.2.min.js"></script>
- </head>
- <body>
- <ul>
- <li><a href="#/view1">view1</a></li>
- <li><a href="#/view2">view2</a></li>
- </ul>
- <!--[if lt IE 7]>
- <p>You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
- <![endif]-->
- <div ng-view></div>
- <div>Angular seed app: v<span app-version></span></div>
- <!-- In production use:
- <script src="//ajax.googleapis.com/ajax/libs/angularjs/x.x.x/angular.min.js"></script>
- -->
- <script src="bower_components/angular/angular.js"></script>
- <script src="bower_components/angular-route/angular-route.js"></script>
- <script src="js/app.js"></script>
- <script src="js/services.js"></script>
- <script src="js/controllers.js"></script>
- <script src="js/filters.js"></script>
- <script src="js/directives.js"></script>
- </body>
- </html>
如此在不使用requirejs的情景下,項(xiàng)目就構(gòu)建完成了.還有幾個(gè)補(bǔ)充點(diǎn)就是其一你可以將controllers繼續(xù)拆分為多個(gè)controller模塊,這里可以完全按照你的業(yè)務(wù)進(jìn)行劃分.比如user目錄下userController等等.然后將所有這些我們自己寫的文件通過grunt或者gulp進(jìn)行合并為一個(gè)單獨(dú)的總的文件all.js這樣在頁面中除了庫(kù)文件只要這一個(gè)文件就行了.angular的module所帶來的好處就是這樣合并的文件,不用在乎js合并的順序,因?yàn)樗峭ㄟ^angular依賴注入的。
(2) 通過requirejs構(gòu)建
這種方式的構(gòu)建可能對(duì)于某些人來講更加清晰,結(jié)構(gòu)和上面的基本一樣,多了一個(gè)man.js用來配置requirejs,單獨(dú)拆分出routes.js以及一個(gè)controller文件夾通過requirejs將controller一個(gè)個(gè)拆分出來,按需的異步加載。
index.html
- <!doctype html>
- <html ng-app>
- <head>
- <title>Angular-RequireJS sample app</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" type="text/css" media="all" href="app/css/app.css" />
- </head>
- <body >
- <h1>AngularJS + RequireJS</h1>
- <ul>
- <li><a href="#/view1">View 1</a></li>
- <li><a href="#/view2">View 2</a></li>
- </ul>
- <div ng-view></div>
- <script data-main="app/js/main" src="/bower_components/requirejs/require.js"></script>
- </body>
- </html>
#p#
main.js
- require.config({
- paths: {
- angular: '../../bower_components/angular/angular',
- angularRoute: '../../bower_components/angular-route/angular-route',
- angularMocks: '../../bower_components/angular-mocks/angular-mocks',
- text: '../../bower_components/requirejs-text/text'
- },
- shim: {
- 'angular' : {'exports' : 'angular'},
- 'angularRoute': ['angular'],
- 'angularMocks': {
- deps:['angular'],
- 'exports':'angular.mock'
- }
- },
- priority: [
- "angular"
- ]
- });
- //http://code.angularjs.org/1.2.1/docs/guide/bootstrap#overview_deferred-bootstrap
- window.name = "NG_DEFER_BOOTSTRAP!";
- require( [
- 'angular',
- 'app',
- 'routes'
- ], function(angular, app, routes) {
- 'use strict';
- var $html = angular.element(document.getElementsByTagName('html')[0]);
- angular.element().ready(function() {
- angular.resumeBootstrap([app['name']]);
- });
- });
app.js
- define([
- 'angular',
- 'filters',
- 'services',
- 'directives',
- 'controllers',
- 'angularRoute',
- ], function (angular, filters, services, directives, controllers) {
- 'use strict';
- // Declare app level module which depends on filters, and services
- return angular.module('myApp', [
- 'ngRoute',
- 'myApp.controllers',
- 'myApp.filters',
- 'myApp.services',
- 'myApp.directives'
- ]);
- });
controllers.js
- define(['angular', 'services'], function (angular) {
- 'use strict';
- /* Controllers */
- return angular.module('myApp.controllers', ['myApp.services'])
- // Sample controller where service is being used
- .controller('MyCtrl1', ['$scope', 'version', function ($scope, version) {
- $scope.scopedAppVersion = version;
- }])
- // More involved example where controller is required from an external file
- .controller('MyCtrl2', ['$scope', '$injector', function($scope, $injector) {
- require(['controllers/myctrl2'], function(myctrl2) {
- // injector method takes an array of modules as the first argument
- // if you want your controller to be able to use components from
- // any of your other modules, make sure you include it together with 'ng'
- // Furthermore we need to pass on the $scope as it's unique to this controller
- $injector.invoke(myctrl2, this, {'$scope': $scope});
- });
- }]);
- });
directives.js
- define(['angular', 'services'], function(angular, services) {
- 'use strict';
- /* Directives */
- angular.module('myApp.directives', ['myApp.services'])
- .directive('appVersion', ['version', function(version) {
- return function(scope, elm, attrs) {
- elm.text(version);
- };
- }]);
- });
filters.js
- define(['angular', 'services'], function (angular, services) {
- 'use strict';
- /* Filters */
- angular.module('myApp.filters', ['myApp.services'])
- .filter('interpolate', ['version', function(version) {
- return function(text) {
- return String(text).replace(/\%VERSION\%/mg, version);
- };
- }]);
- });
routes.js
- define(['angular', 'app'], function(angular, app) {
- 'use strict';
- return app.config(['$routeProvider', function($routeProvider) {
- $routeProvider.when('/view1', {
- templateUrl: 'app/partials/partial1.html',
- controller: 'MyCtrl1'
- });
- $routeProvider.when('/view2', {
- templateUrl: 'app/partials/partial2.html',
- controller: 'MyCtrl2'
- });
- $routeProvider.otherwise({redirectTo: '/view1'});
- }]);
- });
services.js
- define(['angular'], function (angular) {
- 'use strict';
- /* Services */
- // Demonstrate how to register services
- // In this case it is a simple value service.
- angular.module('myApp.services', [])
- .value('version', '0.1');
- });
controllers文件夾中一個(gè)單獨(dú)controlle文件,myCtrl2.js
- define([], function() {
- return ['$scope', '$http', function($scope, $http) {
- // You can access the scope of the controller from here
- $scope.welcomeMessage = 'hey this is myctrl2.js!';
- // because this has happened asynchroneusly we've missed
- // Angular's initial call to $apply after the controller has been loaded
- // hence we need to explicityly call it at the end of our Controller constructor
- $scope.$apply();
- }];
- });
結(jié)尾
寫到這應(yīng)該差不多了,就快超字?jǐn)?shù)了.通常情況下Angular應(yīng)用的構(gòu)建這樣就可以了,因?yàn)楸绕饌鹘y(tǒng)框架angular的代碼量上肯定會(huì)有優(yōu)勢(shì),所以一些不必要的東西就不用引入了.上面這些也是我在這段時(shí)間的項(xiàng)目中遇到并且做過的,已經(jīng)實(shí)戰(zhàn)過了,所以如果有類似需求的同學(xué)可以不必在此填坑。
***留個(gè)彩蛋吧,在不用requirejs的情況下,angular也是可以實(shí)現(xiàn)異步加載的,只要通過一個(gè)非常小巧的庫(kù)就可以,名字叫script.js.https://github.com/ded/script.js