用Angular(1.X)開(kāi)發(fā)上一代web應(yīng)用

以前focus on 移動(dòng)開(kāi)發(fā)商沒(méi)怎么接觸過(guò)重量級(jí)的前端Pc框架,此次接手一個(gè)Angular的項(xiàng)目,在重構(gòu)前趁機(jī)學(xué)習(xí)一下。

在書(shū)中說(shuō)道“Angular的整個(gè)應(yīng)用都是由模型驅(qū)動(dòng)的,是同中所展示的內(nèi)容是模型,被存儲(chǔ)起來(lái)的內(nèi)容也是模型,幾乎所有的一切都是模型”。Angular的概念比較多有 module, confroller, Template, Filter , Directors , Service。以下逐個(gè)了解下。

controller

他有三個(gè)公用

  • 為應(yīng)用中的模型設(shè)置初始狀態(tài)
  • 通過(guò)$scope對(duì)象把數(shù)據(jù)模型和函數(shù)暴露給視圖管理模塊
  • 監(jiān)視模型其余部分的變量并采取相應(yīng)的動(dòng)作
    關(guān)于controller拆分粒度,個(gè)人認(rèn)為應(yīng)該向拆組件那么拆。
    controller可以嵌套,并且通過(guò)嵌套可以繼承$scope.
    使用注意事項(xiàng):
  • 不要試圖復(fù)用controller,一個(gè)controller一般只負(fù)責(zé)一小塊視圖
  • 不要再Controller中操作DOM,這不是控制器的職責(zé)
  • 不要再Controller里面做數(shù)據(jù)格式化,ng有很好用的表單控件
  • 不要在controller里面做數(shù)據(jù)過(guò)濾操作,ng有Filter服務(wù)
  • 一般來(lái)說(shuō)controller不會(huì)互相調(diào)用的,controller之間的交互通過(guò)事件進(jìn)行 emit向

Filter

數(shù)據(jù)格式轉(zhuǎn)換,作為數(shù)據(jù)在模板上展示的輔助工具

  • 內(nèi)置9個(gè)filter:
    currency,date,filter,json,limitTo,lowercase,number,orderBy,uppercase
  • filter 使用|符號(hào)嵌套使用
  • filter可以傳遞參數(shù)
  • 可以自定義filter
<span>{{11234567789 | data:"MM/DD/YYYY @ h:mma"}}</span>
model.filter('filtera',function(){
    return function(item){
        return item +'hahahhah';
    }
})

Template

沒(méi)什么好說(shuō)的 就是View層用來(lái)展示的模板

Directors

指令 有點(diǎn)像是組件,特點(diǎn)是可以做一定的DOM操作,如事件綁定,返回指定模板到容器等。
angular 沒(méi)有組件的概念,視圖的復(fù)用使用directory來(lái)實(shí)現(xiàn)的。
指令的四種類(lèi)型

restrict 匹配模式

restrict 元素 例子
A 標(biāo)簽(默認(rèn)) <div my-menu=Products></div>
E 元素 <my-menu title=Products></my-menu>
C 樣式類(lèi) <div class=my-menu:Products></div>
M 注釋方式

注意
當(dāng)需要?jiǎng)?chuàng)建帶有自己的模板的指令時(shí),使元素名稱(chēng)的方式創(chuàng)建指令
當(dāng)需要為已有的HTML標(biāo)簽增加功能的時(shí)候,使用屬性的方式創(chuàng)建指令

template 模板
templateUrl 模板路徑 支持異步請(qǐng)求
templateCatch 模板緩存

module.run(function($templateCatch){
//  run 方法在所有依賴(lài)注入加載完成后執(zhí)行,且僅執(zhí)行一次
    $templateCatch.put('hello.html',"<div>....</div>")
});
module.director('hello', function($templateCatch){
    return {
        restrict: 'AECM',
        template:$templateCatch.get('hello.html'),
        replace: true
    }
})

link
使用link來(lái)操作dom,綁定事件,類(lèi)似組件。

model.director('loader', function(){
return {
    restrict:'AE',
    link: function(scope, element, attr){
        element.bind('mouseenter', function(){
            scope.loadData();//controller中的scope
            or scope.$apply('loadData')
        })
    }
}
})

通過(guò)屬性來(lái)獲取不同的處理方法,達(dá)到指令復(fù)用的目的。

<div>
    <loader howtoload="loadData">hahaha</loader>
</div>
model.director('loader', function(){
    return {
        restrict:'AE',
        link: function(scope, element, attrs){
            element.bind('mouseenter', function(){
                scope.$apply(attrs.howtoload)//html 不區(qū)分大小寫(xiě) 統(tǒng)一使用小寫(xiě)
            })
        }
    }
})

指令間通信

通過(guò)暴露controller,其他指令依賴(lài)該指令,調(diào)用controller上暴露出來(lái)的方法

    <superman strength>動(dòng)感超人---力量</superman>
var mydoel = angular.module('model',[]);
myModule.directive('superman', function(){
    return {
        scope: {},//創(chuàng)建獨(dú)立作用域,多指令不共享作用域。 link controller 操作基于這個(gè)scope
        restrict: 'AE',
        controller: function($scope){
        // 指令暴露出去的方法寫(xiě)在這里
            $scope.ablities = [];
            this.addStrengh = function() {
                $scope.ablities.push('strength');
            }
            this.addSpeed = function() {
                $scope.ablities.push('addSpeed');
            }
            this.addLight = function() {
                $scope.ablities.push('addLight');
            }
        },
        link: function(scope, element, attrs){
        // 指令內(nèi)部綁定事件和數(shù)據(jù)
            element.addClass('btn btn-primary');
            element.bind('mousenter', function(){
                console.log(scope.abilities);
            })
        }
    }
});
myModule.directive('strength',function(){
    return {
        require: '^superman',// 指令依賴(lài)
        link: function(scope, element, attrs, supermanCtrl) {//第四個(gè)參數(shù)是required 中依賴(lài)的指令的作用域
            supermanCtrl.addStrength();
        }
    }
})

獲取model的controller上的屬性和方法

  • @ 為單向傳遞
    <drink flavor="{{ctrlFlavor}}"></drink>
mymodel.controller('MyCtrl', ['$scope', function($scope){
    $scope.ctrlFlavor = '百威';
}])
mymodel.directive('drink', function(){
    return {
        restrict: 'AE',
        template: "<div>{{flaver}}</div>",
        link: function(scope, element, attrs){
            scope.flavor = attrs.flaver;
        }
    }
});
//使用@綁定可以傳遞字符串 等價(jià)于下面
mymodel.directive('drink', function(){
    return {
        restrict: 'AE',
        scope:{
        flavor: '@'
        },
        template: "<div>{{flaver}}</div>"
    }
})
  • = 為雙向綁定
    此處不舉例
  • & 用于調(diào)用方法
    <greeting greet="sayhello(name)"></greeting>
mymodel.controller('MyCtrl', ['$scope', function($scope){
    $scope.sayhello = function(name){
        alert(name);
    }
}])
mymodel.directive('greeting', function(){
    return {
        restrict: 'AE',
        scope: {
            greet: '&'
        },
        template: '<input type="text" ng-model="userName" /><button ng-click="greeting({name:userName})"></button>'
    }
});

內(nèi)置指令

form
form可以嵌套
自動(dòng)校驗(yàn),防止重復(fù)提交
input元素類(lèi)型進(jìn)行擴(kuò)展
text,number,submit....
內(nèi)置樣式: ng-valid ng-invalid ng-pristine ng-dirty

    <form name="myform" ng-submit="save()" ng-controller="TestForModule">
    //如果這里寫(xiě)了ng-submit 不要在input中再寫(xiě)一遍 否則會(huì)重復(fù)遞交
        <input name="userName" type="text" ng-model="user.userName" required/>
        <input type="submit" ng-disabled="myForm.$invalid" />
        // $invalid 內(nèi)置校驗(yàn)方法
    </form>
    
        

replace 替換里面

service

服務(wù)獲取數(shù)據(jù)的方法的封裝,可以被傳到各個(gè)controller中調(diào)用。有點(diǎn)像是公共方法的提取。

// 定義一個(gè)服務(wù)時(shí) 名稱(chēng)不要以$開(kāi)頭
    model.factory('userlist',['$http', function($http){
        function($http){
            var doRequest = function(userName, path){
                return $http([
                    method: 'GET',
                    url:'users.json'
                ])
            }
            return {
                list: function(username){
                return doRequest(username, 'userList');
                }
            }
        }
    }])
    //自定義服務(wù)寫(xiě)在最后面
    model.controller('servertest',['$scope', '$timeout', '$http', 'userlist',function(){
    ...
    userlist.list($scope.name).success(function(){...})
    ...
    
    }])

service 特性

  • service都是單例的
  • service由$inject 負(fù)責(zé)實(shí)例化
  • service在整個(gè)應(yīng)用的生命周期中存在 可以用來(lái)共享數(shù)據(jù)
  • 在需要使用的地方利用依賴(lài)注入機(jī)制注入service
  • 自定義的service寫(xiě)在內(nèi)置Service之后
  • 自定義Service避免$開(kāi)頭

其他參數(shù)

$scope

scope 是一個(gè) plan old javascript object
scope 提供了一些工具方法 $watch $apply
$scope 是表達(dá)式的執(zhí)行環(huán)境-作用域
$scope 是一個(gè)樹(shù)形結(jié)構(gòu) 與DOM標(biāo)簽平行
子scope對(duì)象會(huì)繼承父scope對(duì)象
$scope 可以傳播事件 類(lèi)似DOM事件 可以向上向下傳
scope不僅是MVC的基礎(chǔ) 也是后面實(shí)現(xiàn)雙向數(shù)據(jù)綁定的基礎(chǔ)

$rootProvider 路由服務(wù)

hash后面匹配路徑,僅有以下兩個(gè)方法,支持匹配組

$rootProvider
.when('/list',{
    template:'listTemplate'//模板文件路徑,
    controller: 'listController'//控制器名稱(chēng)
})
.otherwise({
    redirectTo:'/list'// default router?
})

angular 模塊化

使用Angular.module來(lái)定義模塊,防止變量污染

var s = angular.module('modela', [])//后面數(shù)組中放置依賴(lài) 內(nèi)容為directors等 
s.controller('modelascontroller',['$scope', function($scope){
    $scope.greeting = ……
}]) 

Angular項(xiàng)目的gulp工作流搭建

處理Angular編譯問(wèn)題

值得注意的是,一般書(shū)寫(xiě)時(shí)按照簡(jiǎn)寫(xiě)的格式:

angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) {  
});

但是壓縮js會(huì)破快AngularJS文件所需的依賴(lài)注入,以至于無(wú)法工作,因此壓縮前你需要將代碼手動(dòng)修改為下面的形式:

angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) {  
}]);

在此著重介紹下ng-annotate這個(gè)項(xiàng)目,它會(huì)自動(dòng)幫你做這件事$_$,這個(gè)項(xiàng)目正好提供了gulp的插件.

為壓縮文件添加md5 并且替換html中的鏈接

gulp.task('concat',['prdPackLess', 'prdPackJs','prdPackIndex'],function() {
    return gulp.src(['static/tmp/**/*.css','!static/tmp/css/sdkheader.css','static/tmp/**/*.js', '!static/tmp/js/sdkheader.js'])  //- 需要處理的css/js文件,放到一個(gè)字符串?dāng)?shù)組里 !剔除掉你不想打版本的文件
        .pipe($.rev())
        .pipe(gulp.dest('static/'))
        .pipe($.rev.manifest())   //- 生成一個(gè)rev-manifest.json
        .pipe(gulp.dest('ref/')); //- 將 rev-manifest.json 保存到 rev 目錄內(nèi)
});

gulp.task('rev',['concat','compressHtml', 'copy'],function() {
    return gulp.src(['ref/rev-manifest.json', 'static/tmp/html/**/*.html'])   //- 讀取 rev-manifest.json 文件以及需要進(jìn)行css名替換的文件
        .pipe(revCollector()) //- 執(zhí)行文件內(nèi)css名的替換
        .pipe(gulp.dest('static/html'));  //- 替換后的文件輸出的目錄

});

參考鏈接
前端構(gòu)建的初步嘗試
使用gulp壓縮合并Angular項(xiàng)目中的Js
gulp學(xué)習(xí)指南之CSS合并壓縮與MD5命名及路徑替換
[用AngularJS開(kāi)發(fā)下一代Web應(yīng)用]

Angular 與 escript6

由于之前有使用es6開(kāi)發(fā)的體驗(yàn),于是好奇是不是可以使用es6來(lái)開(kāi)發(fā)Angular 開(kāi)來(lái)已經(jīng)有人這么做了Angular1.X和es6的結(jié)合
angular es6開(kāi)發(fā)指南

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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