前言
公司項目的前端使用Ionic開發(fā)舆吮,但是代碼的組織方式略顯臃腫,代碼的書寫規(guī)范性不佳队贱。一方面造成了嚴(yán)重的性能瓶頸色冀,另一方面也無法應(yīng)對未來頻繁的需求變更,所以準(zhǔn)備結(jié)合構(gòu)建工具對前端做設(shè)計重構(gòu)柱嫌》嫣瘢考察了各個構(gòu)建工具包括Glup、Grunt编丘、Webpack之后還是決定使用Webpack(最新的主流一般都不會有錯的)与学。查閱了不少資料,大部分是關(guān)注的技術(shù)細(xì)節(jié)嘉抓,對于如何進(jìn)行代碼的組織設(shè)計較少索守,所以本文更多的關(guān)注的可能是代碼的組織方式,探索一個比較好的組織方案
預(yù)備知識
- AMD/CMD
- npm
- AngularJS
- Webpack
Webpack
Webpack通過一個入口文件(當(dāng)然可以多入口抑片,不是重點)卵佛,將所有依賴文件打包到一個文件中,從而減少SAP應(yīng)用初次加載時http請求到個數(shù)敞斋。并且可以通過各種loader去處理優(yōu)化不同的文件截汪,如js、css植捎、svg等衙解。一般一個通行的做法是將webpack構(gòu)建時的配置信息寫入到一個名為 “webpack.config.js”的文件中。這部分的基礎(chǔ)知識以及demo一般稍作學(xué)習(xí)便可掌握鸥跟。官網(wǎng)文檔本身很不錯丢郊。
所以目錄可以這樣組織:
- app:存放源代碼
- build: 存放webpack構(gòu)建完成的代碼
- node_modules: npm管理的包庫
- webpack.config.js: webpack構(gòu)建配置
這個是webpack視角下大的框架
組織結(jié)構(gòu)
1. Module
關(guān)于module,angular的官方Developer Guide是這樣寫的:
You can think of a module as a container for the different parts of your app – controllers, services, filters, directives, etc.
所以在angular中module是一個層次相對較高的聚合医咨,把相關(guān)的controller枫匾、service等封裝到一個module,以實現(xiàn)一組特定的相對完備的功能拟淮。
對一個中大型的項目理想情況下是拆分為幾個相對比較獨立的module干茉。
以passport模塊為例,包含登錄注冊等功能很泊,組織形式如下:
- index.js:將該模塊內(nèi)各個部分聯(lián)系在一起
angular = require('angular');
uirouter = require('uirouter');
service = require('../services');
//define the app.passport module
module.exports = angular.module('app.passport', [uirouter, service.AuthService, service.Request])
.config(require('./passport.routes'))
.controller('SigninController', require('./signin.controller'))
.controller('SignupController', require('./signup.controller'))
.service('passportService', require('./passport.service'))
.name;
- routes.js:定義該模塊的路由
module.exports = ['$stateProvider',
function($stateProvider) {
$stateProvider
.state('signup', {
url: '/signup',
name: 'signup',
template: require('./signup.html'),
controller: 'SignupController'
})
.state('signin', {
url: '/signin',
name: 'signin',
template: require('./signin.html'),
controller: 'SigninController'
});
}];
- signin.controller.js: 登錄控制器角虫,這里export controller的定義函數(shù)沾谓,在index中完成最終定義
module.exports = ['$scope','authService','request','$state',
function($scope, authService,request,$state) {
$scope.user = {
phone : "",
password:""
};
//執(zhí)行用戶登錄操作
$scope.signin = function() {
request.post('signin', $scope.user)
.then(
function(data) {
authService.setToken(data.token, data.expire_in * 1000);
$state.go('home');
},
function(error) {
console.log(error);
}
);
};
}];
- signin.html 登錄頁面模版
<ion-view>
<ion-content class="padding">
<div style="margin-top: 120px;">
<form name="myForm" novalidate="">
<div class="list list-inset" style="background-color:transparent;">
<label class="item item-input">
<input name="phone" ng-maxlength="11" ng-minlength="11" ng-model="user.phone" ng-pattern="/^(((1[0-9]{2})|159|153)+\d{8})$/" placeholder="輸入手機(jī)號" required="" type="number"/>
</label>
<label class="item item-input">
<input name="password" ng-maxlength="32" ng-minlength="6" ng-model="user.password" placeholder="6-32位字母數(shù)字組合" required="" type="password">
</input>
</label>
<input class="button button-block button-small button-positive" ng-click="signin()" ng-disabled="myForm.$invalid" type="submit" value="登錄" />
<div class="row">
<div class="col button button-clear button-positive" ui-sref="register" style="color:#7b7b7b;">
用戶注冊
</div>
<button class="col button button-clear button-positive" style="color:#7b7b7b;">
重置密碼
</button>
</div>
</div>
</form>
</div>
</ion-content>
</ion-view>
主要思想就是對于模塊內(nèi)的無論是controller、config也好戳鹅,將它們的定義放到單獨文件中均驶,通過index.js文件,將它們組裝定義成目標(biāo)module
** * 這邊的定義都采用數(shù)組的方式進(jìn)行枫虏,是為了以后進(jìn)行代碼混淆妇穴,函數(shù)的參數(shù)名即使被替換,依然不影響正常的inject * **
通過這種模式我們就完成了一個模塊的定義隶债。
2.項目
完成了一個模塊的定義腾它,那么整個項目的integration也就比較明了了。大致結(jié)構(gòu)如下圖:
首先是各個模塊的定義死讹,然后將跨模塊的復(fù)用代碼以service的形式提取出來瞒滴,作為通用模塊。這里我是放置在services目錄下赞警。整合時妓忍,定義app module作為項目的主模塊,然后將其他module作為依賴的方式注入到app module中仅颇,從而完成整合单默。
- app.module.js
angular = require('angular');
uirouter = require('uirouter');
ionic = require('ionic');
ngCache = require('angular-cache');
config = require('./app.config');
home = require('./home');
passport = require('./passport');
angular.module('app', [ionic,uirouter, ngCache,home,passport])
.config(config.routing)
.config(config.providerConfig)
.constant('ENV', require('./app.env'));
- app.ENV.js 環(huán)境配置
module.exports = {
version: 1.0,
api: 'http://xxx.com/v1/',
appPath: 'http://localhost:8080/'
};
- app.config.js 配置信息
module.exports.providerConfig = ['$httpProvider', 'CacheFactoryProvider',
function($httpProvider, CacheFactoryProvider) {
var param = function(obj) {
var query = '',
name, value, fullSubName, subName, subValue, innerObj, i;
for (name in obj) {
value = obj[name];
if (value instanceof Array) {
for (i = 0; i < value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
} else if (value instanceof Object) {
for (subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
} else if (value !== undefined && value !== null) query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}
return query.length ? query.substr(0, query.length - 1) : query;
};
$httpProvider.defaults.transformRequest = function(obj) {
return angular.isObject(obj) && String(obj) !== '[object File]' ? param(obj) : obj;
};
$httpProvider.defaults.headers.post = {
'Content-Type': 'application/x-www-form-urlencoded'
}
$httpProvider.defaults.headers.put = {
'Content-Type': 'application/x-www-form-urlencoded'
}
angular.extend(CacheFactoryProvider.defaults, {
maxAge: 15 * 60 * 1000,
});
}
];
module.exports.routing = ['$urlRouterProvider', '$locationProvider',
function($urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/signin');
}
];
More
這篇文章主要講個大致思路,項目webpack-angular的完整代碼已經(jīng)放到github上面忘瓦,有需要可以參考。代碼的組織方式本身就是仁者見仁引颈,智者見智耕皮,沒有最好一說,所以還是希望可以一起探討蝙场。