您的位置:首页 > Web前端 > AngularJS

AngularJS学习笔记

2017-04-24 22:20 453 查看
AngularJS在加载启动时,会做3件事情:

1.依赖注入

2.创建 root scope作为整个模型的上下文

3.从ngApp开始编译DOM,处理后续的指令和绑定

当它启动后,它会等待浏览器的输入事件(鼠标、HTTP请求等),若输入事件改变了model,那么AngularJS会通过更新绑定,将model的改变反应到view上。

AngularJS中指令都是带有短划线(-)的,自定义的属性都是驼峰命名的

1.ng-app:

2.ng-controller:定义了一个叫做PhoneListController的控制器,并且向module(phonecatApp)注册。model模型(手机的数据)在controller中,model由controller实例化,controller函数就是一个简单的构造函数。参数scope在该controller范围内都是有效的。scope是template、model、controller之间的联系纽带(template可以理解为是view中的一部分,包括数据绑定、展示逻辑)。

我们在scope下分配参数,可以在其他页面(使用相同的controller)使用该参数,这样交互就会显得比较灵活。但是这种方法只适用于小型的系统,若在大型系统上,紧耦合就会成为一个问题。

3.ng-repeat:

PS:不要搞错module模块和model模型,MVC里的M是指model

// Define the `phonecatApp` module
var phonecatApp = angular.module('phonecatApp', []);

// Define the `PhoneListController` controller on the `phonecatApp` module
phonecatApp.controller('PhoneListController', function PhoneListController($scope) {
$scope.phones = [
{
name: 'Motorola XOOM™ with Wi-Fi',
snippet: 'The Next, Next Generation tablet.'
}, {
name: 'MOTOROLA XOOM™',
snippet: 'The Next, Next Generation tablet.'
}
];
});


component

相对于上述例子,还有一些地方我们可以改进:

1.如果我们要复用template和controller,在上面的例子中,我们需要把他们都复制到复用的地方,这样容易出错。

2.我们把变量都赋值到scope下面,这样会造成混乱。

//组件greetUser向模块myAPP注册
angular.
module('myApp').
component('greetUser', {
template: 'Hello, {{$ctrl.user}}!',
controller: function GreetUserController() {
this.user = 'world';
}
});


我们可以在html中使用标签
<greet-user></greet-user>
,然后AngularJS会用template去替换它。
$ctrl
在默认情况下,代表着controller。我们在controller的构造函数中用this代替了在scope下赋值。标签
<greet-user></greet-user>
可以自任何地方使用,也实现了最大限度的解耦。我们也不用担心component会在其他地方被修改,因为template、controller都在component里面。

避免直接使用scope,这是一条最佳实践。

文件组织的最佳实践

1.一个特性一个文件

不要把controller都放在一个js文件里,不要把component都放在一个js文件里。比如上述的component
greetUser
,需要放在greetUser.component.js里,那么在这js文件里就不要放其他东西了。

2.按特性组织

按照1中的方法,一个文件放置一个特性,那么在
app/
目录下会产生许多文件,我们需要将这些文件按特性组织成文件夹。如果在许多的地方都会用到同一个特性,那么我们就把这个特性文件夹放到
aap/core/
里面。

app/
phone-list/
phone-list.component.js
phone-list.component.spec.js(test)
core/
feature1/
xxx.js
xxx.spec.js
app.js


// phone-list.component.js
// Register `phoneList` component to module 'phonecatApp'
angular.
module('phonecatApp').
component('phoneList',...)


// app.js
// Define the `phonecatApp` module
// 模块phonecatApp没有依赖其他模块,只有它本身
angular.module('phonecatApp', []);


3.模块化

如果现在有其他应用要用到phone-list,我们不需要重复发明轮子,只需要把目录
phone-list/
复制到新应用下。但是我们发现,原来在
phone-list/
中,component
phoneList
是向module
phonecatApp
注册的,在新的应用中,应用名就不叫
phonecatApp
了,这时我们需要把
phonecatApp
都改为当前的应用名。显然,这样的改动容易出错且重复劳动。我们有更好的方法去做这件事。引用一句最近在《Head first 设计模式》上看到的话,对改动关闭,对扩展开放,我们不要去改动已有代码,这样可能在修改一个bug的同时代入另外一个bug,我们应该是封装、抽象、扩展我们已有的代码。

1.新建一个
phone-list.module.js
文件,把component提高到module层次。

2.在新的应用newApp中,添加phonecatApp依赖

app/
phone-list/
phone-list.module.js
phone-list.component.js
phone-list.component.spec.js
core/
feature1/
xxx.js
xxx.spec.js
app.js


//app/phone-list/phone-list.module.js:
// Define the `phonecatApp` module
angular.module('phonecatApp', []);


// phone-list.component.js
// Register `phoneList` component to module 'phonecatApp'
angular.
module('phonecatApp').
component('phoneList',{
template:
'<ul>' +
'<li ng-repeat="phone in $ctrl.phones">' +
'<span>{{phone.name}}</span>' +
'<p>{{phone.snippet}}</p>' +
'</li>' +
'</ul>',
...
})


// app.js
// Define the `newApp` module
angular.module('newApp', [
// ...which depends on the `phonecatApp` module
'phonecatApp'
]);


4.使用外部模板

在3中,component里面的template是内嵌式的html,这样不易于维护和模块化,我们把内嵌式的html改为外部的html。这里有一个点需要注意:当AngularJS解析到templateUrl时,会发起一个HTTP请求,虽然AngularJS使用了延时加载和缓存技术,但随着外部模板变多,HTTP请求也不可避免的变多。详细可以参阅
$templateRequest
$templateCache
这两项服务。

// phone-list.component.js
angular.
module('phoneList').
component('phoneList', {
// Note: The URL is relative to our `index.html` file
templateUrl: 'phone-list/phone-list.template.html',
controller: ...
});


// phone-list.template.html
<ul>
<li ng-repeat="phone in $ctrl.phones">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>


//最终的目录
app/
phone-list/
phone-list.component.js
phone-list.component.spec.js
phone-list.module.js
phone-list.template.html
app.css
app.js
index.html


第六节.双向数据绑定

双向数据绑定是指view和model在两个方向上都绑定住了。如果我们在浏览器上改变view,那么就会反应到model里面,进而改变model。如果我们在代码里面改变model,那么就会向外反应到view上。

第七节.依赖注入

使用内置的http服务为例子。

$http
服务会让AngularJS在客户端发起一个http请求,在下面的例子中,http去请求一个json文件,路径是相对于index.html来说的。服务的名字需要作为参数传入到controller的构造函数中。服务的名字很重要,不能写错,注入器就是依靠名字去寻找依赖的。注入器在加载一个服务时,会分析这个服务会依赖其他哪几个服务,然后依次去加载服务。

angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: function PhoneListController($http) {
var self = this;//this对象在闭包中会发生变化,在这里将this保存成一个变量,该变量在闭包中的含义不会发生变化
self.orderProp = 'age';
//phone的数据不像之前被写死,而是由http请求去json文件里面读取
$http.get('phones/phones.json').then(function(response) {
self.phones = response.data;
});
}
});


AngularJS内置的服务都是带有
$
前缀的,如果我们要定义自己的服务,就不要带
$
前缀。而且我们会看到在Scope下面,有些变量是带有
$$
前缀的,那说明这个变量是私有的,虽然我们还是可以去访问,但不建议这么做。

JS在发布前都会经过压缩混淆,混淆其中有个步骤就是替换局部变量,如果
$http
这个变量被替换了,AngularJS就识别不出来使用了内置的http服务,所以针对混淆,代码要换个结构。有以下两种方法:

1.创建
$inject
属性


$inject
后面跟一组字符串,字符串就不会被js混淆替换掉,这些字符串就是要依赖的服务。

function PhoneListController($http) {...}
PhoneListController.$inject = ['$http'];
...
.component('phoneList', {..., controller: PhoneListController});


2.使用内联的注解

在controller后面跟一组字符串。

function PhoneListController($http) {...}
...
.component('phoneList', {..., controller: ['$http', PhoneListController]});


2.1使用内联的注解

当我们使用方法2时,一般都是把构造函数也内联进来。(在刚接触AngularJS时,经常看到这种形式的内联,当时就感觉JS代码一层套一层好复杂,一句话好长,而且对
$http
出现2次感到不解)

.component('phoneList', {..., controller: ['$http', function PhoneListController($http) {...}]});


对本例代码使用2.1的注解形式:

//app/phone-list/phone-list.component.js
angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: ['$http',
function PhoneListController($http) {
var self = this;
self.orderProp = 'age';

$http.get('phones/phones.json').then(function(response) {
self.phones = response.data;
});
}
]
});


第九节.路由和多视图

依赖注射器做以下3件事:

1.加载定义的module

2.注册所有在module后面定义的Providers

3.当要使用服务了,这些服务和他们的依赖,就会通过他们的Providers被传入注射依赖函数

Providers是一个对象,这个对象可以创建相应的服务。

在url上加前缀
!
是一个最佳实践,如url
http://127.0.0.1:8000/#!/phones


关于自模块的依赖:

// app/app.js
angular.module('phonecatApp', [
'ngRoute',
'phoneDetail',
'phoneList'
]);


// app/phone-detail/phone-detail-module.js
angular.module('phoneDetail', [
'ngRoute'
]);


顶层模块phonecatApp和子模块phoneDetail都包含ngRoute服务,如果我们把phoneDetail中的ngRoute去掉,程序也是可以工作的,但不建议这么做,因为这样会破坏模块化,子模块不应该依赖由父模块继承下来的服务。假如phoneDetail模块被放到其他地方,这个顶层模块没有ngRoute,那么phoneDetail模块就不能正常工作。我们不用担心多个模块包含了多份一样的服务会造成多余的浪费,AngularJS只会加载一份服务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: