第三章 angular是怎么工作的

注:学习使用,禁止转载

在这章中,我们会去讨论angular2中的高级概念,我们需要退一步,这样我们才能看清楚所有这些片段是怎么组装起来的.

:fa-info-circle: 如果你使用angular1,你会发现angular2使用了不一样的基础模型,别慌,angular2的基础模型更加直接和熟悉,本书的后面,我们会告诉你,怎样将你的angular1编写的程序转换成angular2程序

在本章中,我们会深入每一个概念,但是我们先有一个概览并且学习基本的技能.

angular2的第一个特性就是angular2应用程序是由组件组成的.组件是一种告诉浏览器新标签的方式.如果你学习过ng1,你会发现,组件跟ng1里面的directive很像,也就是说ng2也是有directive的,但是稍后我们会谈论他们的区别.

然而,ng2的组件对比ng1的directive来说有很多优势,我们将会在后面谈到.让我们从最顶端开始,Application.

Application

一个ng2应用程序其实就是一棵组件树.

在这棵树的根部,最顶层的组件就是这个应用程序本身.这就是浏览器启动应用程序是将要渲染的东西(bootstrap).

关于组价比较重要的事情就是它是可以组合的.这意味着我们可以从一个小的组件组装成一个很大的组件,Application是一个简单的组件,它负责渲染其他的组件.

因为组件是父子树结构,所以每一个组件渲染的时候他会迭代渲染它的子组件.

输入图片说明

得到这个原型图,考虑要编写这个应用程序,第一步需要做的就是分离组件,在这个例子中,我们分离出来三个顶级组件:

  1. 导航组件
  2. 面包屑(路径)组件
  3. 产品信息组件

导航组件

这个组件将会渲染导航区域.它允许用户去访问应用程序的其他区域.

输入图片说明

面包屑(路径)组件

路径组件显示属性结构视图的路径,代表当前页面的路径.

输入图片说明

产品列表组件

产品列表组件显示产品的集合.

输入图片说明

将这个组件继续往小的方向分离,它是由很多产品行组成的.

输入图片说明

当然,我们可以继续分离产品行,它由下面三个更小的组件组成:

  • 产品图像组件,显示产品的图片
  • 产品组织部件,它显示产品的分类,如:男人->鞋->运动鞋.
  • 产品价格组件,显示产品的价格

最后,将所有这些东西组织起来成为一个组件树,如下:

输入图片说明

在最上方我们看到Inventory Management App,这个就是我们的应用程序.
在Inventory Management App下面,有导航组件,路径组件和产品列表组件.
产品列表组件又包含产品行组件,它渲染每一个产品.
产品行组件又由三个部分组成:图像组件,分类组件和价格组件.
让我们将这些组合起来编译这个应用程序.

:fa-key:你可以在how_angular_- works/inventory_app路径下面找到这章的所有代码.

下面是我们应用程序的界面:

输入图片说明

产品模型

关于angular2的一个关键的事情是它没有定义一个特定的模型库.

angular是非常灵活的,它可以支持很多的模型(或者叫数据结构).这也意味着你自己必须选择一个适合你自己的模型.

下面章节中,我们有很多关于模型的讨论.但是现在,我们使用JavaScript的plain Object模型.

code/how_angular_works/inventory_app/app.ts

/**
 * Provides a `Product` object
 */
class Product {
  constructor(
    public sku: string,
    public name: string,
    public imageUrl: string,
    public department: string[],
    public price: number) {
  }
}

如果你没有接触过ES6/Typescript,上面的语法可能有点不熟悉.

创建一个Product类,并且它的构造器里面有5个参数,当我们编写public sku: string时,说明了下面两件事.

  • 这个类有一个公共的sku属性
  • sku的类型是string

:fa-key:如果你熟悉JavaScript,你可以迅速发现这些不同.

这个Product类没有任何依赖,它仅仅是一个在我们应用程序中的模型.

组件(Component)

像我们在前面提到的一样,组件是我们构建angular2应用程序的基础.”application”自己就是一个顶层的组件.然后我们在细化我们的应用程序到更小的组件.

:fa-key:提示:当构建一个angular2应用程序时,获得原型并且将他们分解成更小的组件.

我们将会经常使用组件,所以他的价格很大.

每一个组件由三部分组成.

  • 一个组件装饰器 (Component Decorator)
  • 一个视图(View)
  • 一个控制器(Controller)

为了说明关键概念,我们首先需要理解组件,我们使用上面的应用程序,然后将焦点放在产品列表组件和它的子组件.

输入图片说明

这里就是基本的顶层InventoryApp像下面这样:

@Component({
  selector: 'inventory-app',
  template: `
  <div class="inventory-app">
   (Products will go here soon)
  </div>
  `
})
class InventoryApp{

}
bootstrap(InventoryApp);

如果你使用过angular1,这个语法看起来很漂亮.但是想法是相似的,让我们一步一步来.

@Component叫着装饰器(decorator),它向类InventoryApp增加元数据.@Component注解指明了两件事:

  • 一个选择器(selector),它告诉angular,匹配的元素四什么.
  • 一个模板(template),它定义了视图(view)

组件的控制器被定义为了一个类,InventoryApp.让我们仔细看看每一部分.

Component装饰器

@Component是用来配置你的组件的.主要是@Component定义了组件怎么与外部世界进行交互.

这里有许多可以选择的配置,具体的在组件那章,这里我们先讲解一些基础的.

组件选择器(selector)

使用selector,指明了你的组件怎么在html模板中使用.这与css选择器和xpath选择器相似.选择器定义了一种方式,关联html模板与该组件的关联.在这个例子中,selector: ‘inventory-app’代表该组件与html中的inventory-app标签对应.这也就是说,我们定义了一个新的html标签.不管我们何时使用它,它都具有一个新的功能.

<inventory-app></inventory-app>

angular将会使用InventoryApp组件去实现它的功能.
使用这个的另外一种方式就是使用带有属性的div标签.如下:

<div inventory-app></div>

组件模板(template)

视图是组件的可视部分,通过使用template选项,我们可以声明组件的HTML模板.

@Component({
  selector: 'inventory-app',
  template: `
  <div class="inventory-app">
   (Products will go here soon)
  </div>
  `
})

对于这个模板来说,我们使用Typescript的反引号语法来包含多行字符串.我们的模板目前是很简单的.就是在一个div标签中显示一个字符串.

增加一个产品

如果没有产品显示的话,我们的应用程序是无趣的.让我们增加一些产品.我们可以创建一个Product像下面这样.

let newProduct = new Product(
  'NICEHAT',
  'A Nice Black Hat',
  '/resources/images/products/black-hat.jpg',
  ['Men', 'Accessories', 'Hats'],
  29.99);

我们的产品有五个参数,我们可以通过new关键字创建一个Product实例.

:fa-info-circle:通常,你可能不会传递5个参数给这个函数,这里的另外一种选择是配置Product类的构造器接受一个对象,然后如果我们不记得那个顺序,我们可以像下面这样初始化Product:

 new Product({sku: "MYHAT", name: "A green hat"})
````
但是现在,我们的五个参数的构造器是很好的.

我想去显示这个产品.为了在template中访问这些属性,我们在我们的组件中增加一个实例变量.

比如,如果我们想去访问newProduct我们可以像下面这样写:




<div class="se-preview-section-delimiter"></div>

class InventoryApp {
product: Product;
constructor() {
let newProduct = new Product(
‘NICEHAT’, ‘A Nice Black Hat’,
‘/resources/images/products/black-hat.jpg’,
[‘Men’, ‘Accessories’, ‘Hats’],
29.99);
this.product = newProduct;
}
}

或者更加简单点:




<div class="se-preview-section-delimiter"></div>

class InventoryApp {
product: Product;
constructor() {
this.product = new Product(
‘NICEHAT’, ‘A Nice Black Hat’,
‘/resources/images/products/black-hat.jpg’,
[‘Men’, ‘Accessories’, ‘Hats’],
29.99);
}
}

注意,我们做了三件事情:
>- 增加一个构造器
- 定义了一个实例变量
- 分配了一个产品给实例变量





<div class="se-preview-section-delimiter"></div>

##使用模板绑定显示产品信息
现在我们有了分配的product,我们可以在视图中显示这个变量.让我们改变我们模板,如下:




<div class="se-preview-section-delimiter"></div>

@Component({
selector: ‘inventory-app’,
template:
<div class="inventory-app">
How Angular Works 81
<h1>{{ product.name }}</h1>
<span>{{ product.sku }}</span>
</div>

})

使用{{}}语法叫着模板绑定.它告诉视图我们想去使用大括号里面的表达式的值在这个地方.所以,在这个地方,我们有两个绑定:




<div class="se-preview-section-delimiter"></div>

{{ product.name }}
{{ product.sku }}

这个product变量来自于我们组件类里面的product实例变量.

在{{}}里面的是一个表达式,所以你可以像下面这样:




<div class="se-preview-section-delimiter"></div>

{{count+1}}
{{ myFunction(myArguments) }}





<div class="se-preview-section-delimiter"></div>

##增加更多的产品
其实我们不是只想显示一个产品,我们希望显示列表里面的所有产品,所以,我们可以将InventoryApp里面的product实例变量编程products,像下面这样:




<div class="se-preview-section-delimiter"></div>

class InventoryApp {
products: Product[];
constructor() {
this.products = [];
}
}

在钩子函数中,products是一个空的数组,我们现在添加一些产品:




<div class="se-preview-section-delimiter"></div>

class InventoryApp {
products: Product[];

constructor() {
this.products = [
new Product(
‘MYSHOES’, ‘Black Running Shoes’,
‘/resources/images/products/black-shoes.jpg’,
[‘Men’, ‘Shoes’, ‘Running Shoes’],
109.99),
new Product(
‘NEATOJACKET’, ‘Blue Jacket’,
‘/resources/images/products/blue-jacket.jpg’,
[‘Women’, ‘Apparel’, ‘Jackets & Vests’],
238.99),
new Product(
‘NICEHAT’, ‘A Nice Black Hat’,
‘/resources/images/products/black-hat.jpg’,
[‘Men’, ‘Accessories’, ‘Hats’],
29.99)
];
}
}

这个代码可以给我们一些产品,这些产品可以在我们应用程序中使用.





<div class="se-preview-section-delimiter"></div>

##选择一个产品
我们希望去添加交互支持,比如,用户可以会选择我们的产品去查看产品的详细信息或者将其加入购物车等.

让我们增加一些函数,去处理当一个新的产品被选中的时候,我们定义一个新的函数productWasSelected:




<div class="se-preview-section-delimiter"></div>

productWasSelected(product: Product): void {
console.log(‘Product clicked: ‘, product);
}





<div class="se-preview-section-delimiter"></div>

##使用<products-list>列出产品
现在我们有一个顶层InventoryApp组件,我们需要添加一个新的组件去渲染产品列表.在下面的部分,我们需要创建一个ProductsList组件,它的标签为products-list.在我们深入学习这个新组件之前,我们看看我们会怎么使用它.




<div class="se-preview-section-delimiter"></div>

@Component({
selector: ‘inventory-app’,
directives: [ProductsList],
template:
<div class="inventory-app">
<products-list
[productList]="products"
(onProductSelected)="productWasSelected($event)">
</products-list>
</div>

})

这里有一些新的语法,让我们讨论下他们.




<div class="se-preview-section-delimiter"></div>

###directives选项
这个选项的意思是标明我们这个组件需要使用到的其他组件列表,它是一个数组.

不想angular1,所有directive都是全局的.在angular2中,你必须标明你要使用的组件.在这里,我们标明该组件需要使用ProductsList组件.





<div class="se-preview-section-delimiter"></div>

###输入和输出(Inputs和Outputs)
当我们使用products-list的时候,我们使用angular2中的一个关键特性:输入和输出.




<div class="se-preview-section-delimiter"></div>

完整应用程序

输入图片说明

数据流

你可能会想知道,当向我们的应用程序增加更多的功能的时候,我们怎么管理数据流.

比如,当我们增加一个购物车视图,并需要将产品添加到购物车中去的时候,我们怎么做?

到现在为止,我们仅仅有一种方式,就是使用发射事件.当我们添加产品给购物车的时候,我们简单的冒泡addedToCart事件,并在根组件中去处理它,这个感觉有点不对.

数据流是一个大的问题.幸运的是,angular提供了一个非常灵活且各种不同的数据流方式,这也就意味着你必须自己选择使用什么数据流方式.

在angular1中,默认的数据流是双向绑定的.双向绑定在刚开始的时候是很简单的:你的控制器提供数据,表单处理数据并且视图显示数据.双向数据流的一个问题就是导致数据流跟踪会很难.

双向绑定的另外一个问题,就是当一个数据改变的时候,会强制数据流树和dom树改变,这个会导致效率降低.

处理这个问题的一种方式是提供一个单例的服务,它保存列表,并且当列表修改的时候通知所有对其感兴趣的对象.

这个方法是简单的,但是在实践中,有很多细节需要处理.

angular2使用很多其他框架的建议模式,单项数据流,也就是说,只有你的数据流向视图,没有视图流向数据.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值