【译】《通晓运用AngularJS开辟Web App》(三)--- 深切scope,继续构造,事宜体系和生命周期

上一篇:【译】《通晓运用AngularJS开辟Web App》(二)
下一篇:【译】《通晓运用AngularJS开辟Web App》(四)

书名:Mastering Web Application Development with AngularJS

Chapter 1

深切探讨 Scope 作用域

每一个 $scope 都是类 Scope 的一个实例。类 Scope 具有能够掌握 scope 生命周期的要领,供应事宜流传的才,并支撑模板衬着。

作用域的条理组织

让我们再来看看这个简朴的 HelloCtrl 的例子:

var HelloCtrl = function($scope){
    $scope.name = 'World';
}

HelloCtrl 看起来就跟一般的 JavaScript 组织函数没什么区分,事实上,除了 $scope 这个参数以外,确切没什么新颖的地方。不过,这个参数究竟是从那里来的呢?

这个新的作用域是由 ng-controller指令运用 Scope.$new() 要领天生的。等一下,这么说来我们必需最少具有一个 scope 的实例才建立新的 scope!没错,AngularJS其实有一个 $rootScope(这个是一切其他作用域的父级)。这个 $rootScope 实例是在一个新的运用启动的时刻建立的。

ng-controller指令就是 能够建立作用域 指令的个中一个。AngularJS 会在任何它在DOM树中遇到这类 能够建立作用域 指令的时刻建立一个新的 Scope类的实例。这些新建立的作用域经由过程 $parent 属性指向它自身的父作用域。DOM树中会有许多 能够建立作用域 的指令,效果就是,许多作用域被建立了。

作用域的情势类似于父子、树状的关联,而且最根部的就是 $rootScope 实例。就像作用域是被DOM树驱动着建立的一样,作用域树也是在模拟 DOM 的组织。

如今你已知道了,一些指令会建立新的子级的作用域,你能够会想,为何会须要这些庞杂的东西。要想明白这一点,我们来演示一个例子,个中运用了 ng-repeat 轮回指令。

掌握器以下:

var WorldCtrl = function ($scope) {
    $scope.population = 7000;
    $scope.countries = [
        {name: 'France', population: 63.1},
        {name: 'United Kingdom', population: 61.8},
    ];
};

模版以下:

<ul ng-controller="WorldCtrl">
    <li ng-repeat="country in countries">
        {{country.name}} has population of {{country.population}}
    </li>
    <hr>
    World's population: {{population}} millions
</ul>

这个 ng-repeat 指令能够迭代一个 countries 的鸠合,而且为鸠合中的每一项都建立新的DOM 元素。ng-repeat 指令的语法异常轻易明白;个中每一项都须要一个新的变量 country,并把它挂到 $scope 上面,以便视图衬着运用。

但这里有一个题目,就是,每一个 country 都须要将一个新的变量挂载到一个 $scope 上去,而我们也不能就简朴的覆蓋掉前面被挂在上去的值。AngularJS 经由过程为鸠合中的每一个元素都建立一个新的作用域来处理这个题目。新建立的这些作用域跟相匹配的DOM树组织异常相像,我们也能经由过程之前提到的谁人牛逼的 Chrome 扩大 Batarang 来可视化的看到这一点,下面是屏幕截图:
《【译】《通晓运用AngularJS开辟Web App》(三)--- 深切scope,继续构造,事宜体系和生命周期》
正如我们在截图中所看到的,每一个作用域(以矩形标注边境)保护属于她自身的一段数据模子。给差别的作用域增添同名的变量是完整没有题目的,不会发作定名争执(差别的DOM元素会指向差别的作用域,并运用相对应的作用域的变量来衬着模板)。这样一来,每一个元素又有自身的定名空间,在前面的例子中,每一个 <li> 元素都有自身的作用域,而 country 变量就定义在各自的作用域上面。

Scope的条理组织和继承

定义在作用于上的属性对他的子级作用于来讲是可见的,试想一下,子级作用域并不须要反复定义同名的属性!这在实践中是异常有用的,由于我们没必要一遍又一遍的反复定义原本能够经由过程作用域链获得的那些属性。

再来看看前面的例子,假定我们想要显现给出的这些国度与天下总人口的百分比。要完成这个功用,我们能够在一个作用域上定义一个 worldsPercentage 的要领,并由 WorldCtrl 来治理,以下所以:

$scope.worldsPercentage = function (countryPopulation) { 
    return (countryPopulation / $scope.population)*100;
}

然后被 ng-repeat 建立的每一个作用域实例都来挪用这个要领,以下:

<li ng-repeat="country in countries">
    {{country.name}} has population of {{country.population}},
    {{worldsPercentage(country.population)}} % of the World's
   population
</li>

AngularJS中作用域的继承划定规矩跟 JavaScript 中原型的继承划定规矩是雷同的(在须要读取一个属性的时刻,会一向向继承树的上方查询,直到找到了这个属性为止)。

贯串作用域链的继承的风险

这类透过作用域条理关联的继承,在读数据的时刻显得异常的直观、易于明白。但是在写数据的时刻,就变的有点庞杂了。

让我们来看看,假如我们在一个作用域上定义了一个变量,先不论是不是在子级作用域上。JavaScript代码以下:

var HelloCtrl = function ($scope) {
};

视图的代码以下:

<body ng-app ng-init="name='World'"> 
    <h1>Hello, {{name}}</h1>
    <div ng-controller="HelloCtrl">
        Say hello to: <input type="text" ng-model="name">
        <h2>Hello, {{name}}!</h2> 
    </div>
</body>

运转一下这段代码,就能够发明,这个 name 变量只管仅仅是定义在了最顶级的作用域上,但在全部运用中都是可见的!这说明变量是从作用域链上继承下来的。换句话说,变量是在父级作用域上定义的,然后在子级作用域中接见的。

如今,我们一同来看看,假如在 <input> 中写点字会发作什么,效果以下图所示:
《【译】《通晓运用AngularJS开辟Web App》(三)--- 深切scope,继续构造,事宜体系和生命周期》
你能够会以为受惊,由于 HelloCtrl 掌握器所初始化的作用域建立了一个新的变量,而不是直接去修正 $rootScope 实例中的值。不过当我们认识到作用域也只不过是在彼此间进行了原型继承,也就不会以为那末受惊了。一切能够用在 JavaScript 对象上的原型继承的划定规矩,都能够一致的用在 作用域 的原型链继承上去。毕竟 Scopes 作用域就是 JavaScript 对象嘛。

在子级作用域中去转变父级作用域上面的属性有几种要领。第一种,我们就直接经由过程 $parent 属性来援用父级作用域,但我们要看到,这是一个异常不牢靠的处理计划。贫苦的地方就在于,ng-model 指令所运用的表达式异常严峻的依赖于全部DOM组织。比方就在 <input> 标签上面的那里插进去另一个 可建立作用域 的指令,那 $parent 就会指向一个完整差别的作用域了。

就履历来讲,只管防止运用 $parent 属性,由于它强迫的把 AngularJS 表达式和你的模板所建立的 DOM 组织捆绑在了一同。这样一来,HTML组织的一个小小的修改,都能够会让全部运用崩溃。

另一个处理计划就是,不要直接把属性绑定到 作用域上,而是绑到一个对象上面,以下所示:

<body ng-app ng-init="thing = {name : 'World'}"> 
    <h1>Hello, {{thing.name}}</h1>
    <div ng-controller="HelloCtrl">
        Say hello to: <input type="text" ng-model="thing.name">
        <h2>Hello, {{thing.name}}!</h2> 
    </div>
</body>

这个计划会更牢靠,由于他并没有假定 DOM 树的组织是什么模样。

防止直接把数据绑定到 作用域的属性上。应优先选择把数据双向绑定到对象的属性上(然后再把对象挂到 scope 上)。
就履历而言,在给 ng-model 指令的表达式中,你应该有一个点(比方, ng-model="thing.name")。

作用域层级和事宜体系

层级关联中的作用域能够运用 event bus(一种事宜体系)。AngularJS能够在作用域层级中流传签字的装备齐全的事宜。事宜能够从任何一个作用域中发出,然后向上($emit)和向下($broadcast)四周流传。
《【译】《通晓运用AngularJS开辟Web App》(三)--- 深切scope,继续构造,事宜体系和生命周期》
AngularJS中心效劳和指令运用这类事宜巴士来发出一些运用程序状况变化的主要事宜。比方,我们能够监听 $locationChangeSuccess 事宜(由 $rootScope 实例发出),然后在任何 location(浏览器中就是URL)变化的时刻都邑获得关照,以下所示:

$scope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl){ 
    //react on the location change here
    //for example, update breadcrumbs based on the newUrl
});

每一个作用域对象都邑有这个 $on 要领,能够用来注册一个作用域事宜的侦听器。这个函数所饰演的侦听器在被挪用时会有一个 event 对象作为第一个参数。背面的参数会依据事宜范例的差别与事宜自身的装备一一对应。

类似于 DOM 事宜,我们能够挪用 event 对象的 preventDefault()stopPropagation() 要领。stopPropagation() 要领将会阻挠事宜沿着作用域层级继承冒泡,而且只在事宜向上层流传的时刻($emit)才有用。

只管 AngularJS 的事宜体系是模拟了 DOM 的,但两个事宜流传体系是完整自力的,没有任何共同的地方。

虽然在作用域层级中流传事宜对一些题目来讲是一种异常文雅计划(特别是对全局的,异步的状况变化来讲),但照样要适度运用。通常情况下,能够依托双向数据绑定来获得一个比较清洁的计划。在全部 AngularJS 框架中,一共只发出($emit)了三个事宜($includeContentRequested,$includeContentLoaded,$viewContentLoaded)和七个播送($broadcast)($locationChangeStart, $locationChangeSuccess, $routeUpdate, $routeChangeStart, $routeChangeSuccess, $routeChangeError, $destroy)。正如你所看到的,作用域事宜运用的异常少,我们应该在发送自定义的事宜之前仔细的评价一下其他的可选计划(多半会是双向数据绑定)。

万万不要在 AngularJS 中模拟 DOM 的基于事宜的编程体式格局。大多半情况下,你的运用会有更好的架构体式格局,你也能够在双向数据绑定这条路上深切探究。

作用域的生命周期

作用域须要供应互相断绝的定名空间,防止变量的定名争执。作用域们都很小,而且被以层级的体式格局组织起来,对内存运用的治理来讲很有协助。当个中一个作用域不再须要 ,它就能够被烧毁了。效果就是,这个作用域所暴露出来的模子和要领就相符的渣滓接纳的规范。

新的作用域通常是被 可建立作用域 的指令所天生和烧毁的。不过也能够运用 $new()$destroy() 要领来手动的建立和烧毁作用域。

转载请说明来自[超2真人]
本文链接:http://www.peichao01.com/static_content/doc/html/Mastering_Web_Application_Development_with_AngularJS_3.html

    原文作者:超2真神
    原文地址: https://segmentfault.com/a/1190000000361245
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞