Webpack與AngularJS整合之代碼邏輯與架構(gòu)設(shè)計(jì)

前言

公司項(xiàng)目的前端使用Ionic開發(fā),但是代碼的組織方式略顯臃腫,代碼的書寫規(guī)范性不佳。一方面造成了嚴(yán)重的性能瓶頸,另一方面也無法應(yīng)對(duì)未來頻繁的需求變更,所以準(zhǔn)備結(jié)合構(gòu)建工具對(duì)前端做設(shè)計(jì)重構(gòu)??疾炝烁鱾€(gè)構(gòu)建工具包括Glup、Grunt、Webpack之后還是決定使用Webpack(最新的主流一般都不會(huì)有錯(cuò)的)。查閱了不少資料,大部分是關(guān)注的技術(shù)細(xì)節(jié),對(duì)于如何進(jìn)行代碼的組織設(shè)計(jì)較少,所以本文更多的關(guān)注的可能是代碼的組織方式,探索一個(gè)比較好的組織方案

預(yù)備知識(shí)

  • AMD/CMD
  • npm
  • AngularJS
  • Webpack

Webpack

Webpack通過一個(gè)入口文件(當(dāng)然可以多入口,不是重點(diǎn)),將所有依賴文件打包到一個(gè)文件中,從而減少SAP應(yīng)用初次加載時(shí)http請(qǐng)求到個(gè)數(shù)。并且可以通過各種loader去處理優(yōu)化不同的文件,如js、css、svg等。一般一個(gè)通行的做法是將webpack構(gòu)建時(shí)的配置信息寫入到一個(gè)名為 “webpack.config.js”的文件中。這部分的基礎(chǔ)知識(shí)以及demo一般稍作學(xué)習(xí)便可掌握。官網(wǎng)文檔本身很不錯(cuò)。

所以目錄可以這樣組織:


  • app:存放源代碼
  • build: 存放webpack構(gòu)建完成的代碼
  • node_modules: npm管理的包庫
  • webpack.config.js: webpack構(gòu)建配置
    這個(gè)是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是一個(gè)層次相對(duì)較高的聚合,把相關(guān)的controller、service等封裝到一個(gè)module,以實(shí)現(xiàn)一組特定的相對(duì)完備的功能。
對(duì)一個(gè)中大型的項(xiàng)目理想情況下是拆分為幾個(gè)相對(duì)比較獨(dú)立的module。

以passport模塊為例,包含登錄注冊(cè)等功能,組織形式如下:


  • index.js:將該模塊內(nèi)各個(gè)部分聯(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ī)號(hào)" 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;">
                            用戶注冊(cè)
                        </div>
                        <button class="col button button-clear button-positive" style="color:#7b7b7b;">
                            重置密碼
                        </button>
                    </div>
                </div>
            </form>
        </div>
    </ion-content>
</ion-view>

主要思想就是對(duì)于模塊內(nèi)的無論是controller、config也好,將它們的定義放到單獨(dú)文件中,通過index.js文件,將它們組裝定義成目標(biāo)module

** * 這邊的定義都采用數(shù)組的方式進(jìn)行,是為了以后進(jìn)行代碼混淆,函數(shù)的參數(shù)名即使被替換,依然不影響正常的inject * **

通過這種模式我們就完成了一個(gè)模塊的定義。

2.項(xiàng)目

完成了一個(gè)模塊的定義,那么整個(gè)項(xiàng)目的integration也就比較明了了。大致結(jié)構(gòu)如下圖:



首先是各個(gè)模塊的定義,然后將跨模塊的復(fù)用代碼以service的形式提取出來,作為通用模塊。這里我是放置在services目錄下。整合時(shí),定義app module作為項(xiàng)目的主模塊,然后將其他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

這篇文章主要講個(gè)大致思路,項(xiàng)目webpack-angular的完整代碼已經(jīng)放到github上面,有需要可以參考。代碼的組織方式本身就是仁者見仁,智者見智,沒有最好一說,所以還是希望可以一起探討。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,359評(píng)論 7 35
  • 在現(xiàn)在的前端開發(fā)中,前后端分離、模塊化開發(fā)、版本控制、文件合并與壓縮、mock數(shù)據(jù)等等一些原本后端的思想開始...
    Charlot閱讀 5,653評(píng)論 1 32
  • 最近在學(xué)習(xí) Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學(xué)習(xí) Webpack 的...
    My_Oh_My閱讀 8,320評(píng)論 40 247
  • GitChat技術(shù)雜談 前言 本文較長,為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,874評(píng)論 7 110
  • 《新余志不約》 余良天日在東方, 絕路不悔吾愁棱。 志悔無醋幻物虧, 傲氣凜然固畫國。 2017年11月10日作
    春城怡景閱讀 201評(píng)論 4 8

友情鏈接更多精彩內(nèi)容