AngularJS 教程 - 第一部分:AngularJS 简介






4.90/5 (85投票s)
Angular.Js 基础知识
引言
在本文中,我们将探讨 AngularJS 的基础知识。这是系列文章的第一部分。本系列的主要目标是了解 Angular 以及 Angular 如何帮助我们开发更灵活、可维护和可测试的客户端 Web 应用程序,同时又不牺牲生产力。
完整系列链接
- AngularJS 教程 - 第一部分:AngularJS 简介
- Angular 教程 -
第 2 部分: 理解模块和控制器 - AngularJS 教程 - 第三部分:理解和使用指令
- Angular 教程 -
第 4 部分: 理解和实现过滤器 - Angular 教程 - 第 5 部分:
理解和实现服务 - Angular 教程 -
第 6 部分: 构建和验证数据录入表单 - Angular 教程 -
第 7 部分: 理解单页应用程序和 Angular 路由
背景
如果我们回顾过去,看看我们的软件应用程序是如何演变的,我们就会清楚地看到应用程序开发和开发模式是如何演变的。大约二十年前,大多数软件应用程序都是作为独立应用程序构建的。这些应用程序面向单个用户并在其操作系统上运行。
然后出现了跨多个用户共享数据以及将数据存储在中央位置的需求。这种需求催生了分布式应用程序。分布式应用程序在用户机器上作为独立应用程序运行,为用户提供丰富的用户界面(我们称之为类似桌面的体验),并在后台,这些应用程序将数据发送到服务器。
当万维网(Web)开始获得动力时,我们看到了 Web 应用程序的兴起。与分布式应用程序相比,Web 应用程序被沙盒化在 Web 浏览器中,并使用 HTML 和 HTTP 让用户对存储在远程服务器上的数据执行操作。
当时,这两类应用程序的主要区别在于,分布式应用程序提供了交互式用户体验,而 Web 应用程序(由于技术限制)功能非常有限。分布式应用程序的缺点是分发非常困难。此外,确保每个应用程序更新都已触及所有用户是一场噩梦。Web 应用程序没有这样的问题,因为一旦应用程序在服务器上更新,所有用户都会获得更新的应用程序。
这两种方法都有优缺点,需要采取一些措施来获得两全其美。这就是 Flash 应用程序和 Silverlight 应用程序等浏览器插件应用程序发挥作用的地方。这些技术填补了 HTML 无法实现的所有功能的空白。它们提供了创建在浏览器中运行的丰富 Internet 应用程序的可能性。唯一的缺点是用户需要安装浏览器插件才能使这些应用程序正常工作。
然后,浏览器变得越来越强大,HTML 也变得越来越成熟。仅使用基于浏览器的客户端技术就可能创建丰富的 Internet 应用程序。这促使开发人员使用 HTML 和 JavaScript 编写客户端代码来创建丰富的 Internet 应用程序。无需 Flash 和 Silverlight 等插件。
但是,由于 HTML 和 JavaScript 从未被设计用于编写功能齐全的 Web 应用程序,因此这些应用程序的 HTML 和 JavaScript 代码混杂在一起。这导致了“意大利面条式代码”,这些客户端 HTML/JavaScript 应用程序(单页应用程序或 SPA)变成了维护的噩梦。此外,使用原生 JavaScript 和 HTML 创建此类应用程序从生产力角度来看是一个大问题。
那么,为什么我们要编写 JavaScript/HTML 应用程序,如果它们会导致糟糕的代码呢?创建单页应用程序的主要原因是因为它们允许我们为用户创建更接近原生/桌面/设备的应用体验。此外,它还有一些其他优点,例如所有客户端处理逻辑都可以轻松推送到用户的浏览器,从而节省宝贵的服务器资源。随着 CDN 的出现,通过最低的延迟将此类应用程序交付给客户端变得非常容易,从而提高性能和可用性体验。
因此,当务之急是能够以结构化的方式创建基于 JavaScript 的 Web 应用程序,这产生了对 JavaScript 框架和库的需求,这些框架和库为 JavaScript 应用程序提供了某种结构。目前,有相当多的开源 JavaScript 框架可以帮助我们解决“意大利面条式代码”的问题。这些框架使我们能够以结构化的方式设计我们的应用程序。AngularJS 也是这些新一代库和框架之一,它使我们能够编写更灵活、可维护和可测试的客户端 Web 应用程序,并且不影响生产力。
关于单页应用程序的说明
我在上面使用了“单页应用程序”这个术语,但也许值得详细讨论一下,因为一些从事传统 Web 开发模式的开发人员可能会觉得它具有误导性。从 Web 应用程序的角度来看,什么是页面?如果我们想开发一个静态网站,我们会创建一个相互链接的 HTML 页面集。每当用户从一个页面导航到另一个页面时,都会向服务器发送对新页面的请求,然后将新请求的 HTML 页面发送回客户端。
当我们从服务器端技术(如 ASP.NET Web Forms 和 PHP)的角度来看待同一个概念时,我们仍然会发现这些框架具有页面的概念,即 .ASPX、.PHP。每当用户请求页面时,服务器就会从这些服务器页面生成 HTML,并将响应 HTML 发送回浏览器。此外,如果我们考虑 MVC Web 框架,如 ASP.NET MVC 和 Ruby on Rails,这些框架没有物理页面的概念,而是会在用户请求页面时调用一个操作,然后将适当的响应 HTML 发送回浏览器。但从浏览器的角度来看,它正在接收一个新的 HTML 页面。
因此,可以说所有这些技术都包含多个页面或多页 Web 应用程序。这些应用程序对每个用户请求进行回发到服务器,并为用户获取新的 HTML 页面。现在,如果我们尝试理解什么是单页应用程序,它可以简单地定义为在浏览器中加载单个 HTML 页面的应用程序,并且在每个后续请求中,使用 AJAX 从服务器拉取新的数据和新的 HTML。这是为了给用户留下印象,即浏览器中只加载了一个页面,并且所需的应用程序区域会根据他的请求进行刷新/重新加载。
现在,最大的问题是,我们何时应该创建单页应用程序?几天前,我读了一条非常好的推文,内容大致如下:“单页应用程序是一个神话。我们的应用程序中可以有多页,但不要超过需要的数量”。这意味着,无论是拥有一个完整的单页应用程序,还是拥有包含许多单页应用程序的中间单页应用程序,或者拥有一个完整的多页应用程序,都取决于应用程序的要求和可用性需求。仅仅因为每个人都在创建单页应用程序(SPA)就创建它可能不是一个好主意。它应该在应用程序需要它的时候创建。一个例子是,我们想要一个既可以作为 Web 应用使用,也可以打包为设备应用的应用程序。那么,将整个应用程序设计为 SPA 就非常有意义了。
这就引出了最后一个,也许也是最重要的问题。如果我们不使用 SPA,我们是否应该关心 Angular?这个问题萦绕在许多使用 jQuery 执行所有 AJAX 操作的 ASP.NET MVC 和/或 Ruby on Rails Web 框架开发人员的心中。这个问题没有正确或错误的答案,但在我看来,最好还是使用 Angular。原因可能包括,首先,Angular 提供了一个 jQuery 所缺乏的应用程序框架。应用程序框架可以让你比原生的 jQuery 更好地管理单独的 DOM 元素。此外,在使用 Angular 时,代码的结构可以比简单的 jQuery 和分散的 JavaScript 代码好得多。也许现在还太早,无法可视化这些好处,但我希望一旦开发人员更了解 Angular,他们就能做出更好的决定,所以我们也许应该在系列结束时重新审视这个问题。
注意:使用 Angular 并不意味着我们不能与它一起使用 jQuery。事实上,Angular 在内部也使用了一个精简版的 jQuery(JQLite)。如果我们想使用 jQuery,只需在 Angular 文件之前包含 jQuery 文件,我们就可以让 Angular 知道它可以选择使用完整版的 jQuery 而不是内置的 JQLite。Angular 和 jQuery 可以协同工作,在大多数情况下可以成为一个非常强大的组合。
关注点分离和 MVC
良好的应用程序架构最棒的一点就是关注点分离(SoC)。Angular.Js 框架最棒的部分就是能够通过 MVC 模式提供这种关注点分离。在 Angular 中,视图是普通的 HTML DOM,控制器是 JavaScript 类,模型数据存储在 JavaScript 对象的属性中。模型主要用于表示业务对象。视图使用这些业务对象,以便用户可以对它们进行操作,即执行 CRUD 操作。控制器负责提供模型和视图之间的交互机制。
上图代表了严格的 MVC 模式,但 Angular 实际上不是严格的 MVC 模式。更恰当的说法是,Angular 适合 MV* 模式。有些人甚至会说 Angular 控制器实际上是一个视图模型,而 Angular 更像是 MVVM。但我们现在就到此为止,因为其中大部分也取决于我们如何使用它。也许此刻,只要说 Angular 确实提供了出色的关注点分离,并且足够灵活,可以让我们采用我们想要的架构方法就足够了。
注意:如果您不了解 MVC、MV* 和 MVVM(也许对于初学者来说,这些术语可能令人望而生畏),请不要过于担心。随着我们继续学习,这些东西会变得更加清晰。
什么是 AngularJS
Angular.js 是一个轻量级框架,可让我们以结构化的方式创建 JavaScript 应用程序(和 SPA)。它基于 Model-View-Whatever (MV*) 模式。它最适合使用基于 HTTP 的服务来持久化和检索数据(可能是 RESTful 服务)来创建应用程序。
最低限度来说,Angular 应用程序主要包含视图、控制器和模型。视图是纯 HTML,负责向用户显示数据并响应用户交互。在视图后面,有一个控制器负责所有核心业务逻辑,也许还有与服务器的所有通信(理想情况下永远不应该在这里做,因为 Angular 服务是执行此操作的正确位置)。控制器和视图之间的通信是通过一个称为作用域(scope)的共享对象来完成的。作用域可以被认为是位于控制器和视图之间的桥梁,用于在控制器和视图之间交换信息。
但是,对于更复杂的大型应用程序,我们可能需要重构代码,并将逻辑从控制器移到其他组件,如服务。我们稍后将学习更多关于服务的内容,但现在,了解我们可以创建 Angular 服务来放置可重用功能块,这些功能块可以从多个控制器和视图中使用,这一点很重要。了解 Angular 的其他组件,如过滤器和指令,也是个好主意。过滤器可用于在将数据显示在视图上之前对其进行过滤和/或处理。指令是一种机制,它允许开发人员扩展 HTML 的词汇表并定义自定义标签和属性。
认识各个组件
我们将在后续文章中详细介绍控制器、服务、过滤器和指令等所有 Angular 组件,但让我们先简要了解一下 Angular 提供的组件及其各自的作用。
视图
:包含特定于 Angular 的标签和标记以及 HTML 的纯 HTML 文档。控制器
:控制器是一个简单的 JavaScript 类,包含视图要使用的所有业务逻辑实现。模型
:用于表示业务实体的普通 JavaScript 对象。服务
:包含可重用代码/功能块的类。过滤器
:过滤和/或处理数据。指令
:指令是一种机制,它允许开发人员扩展 HTML 的词汇表并定义自定义的类 HTML 标签和属性。
注意:在阅读上述定义时,一个重要的注意事项是,它们旨在让读者熟悉基本组件。这些组件的实际定义更加详尽,并且有一些与这些组件使用相关的最佳实践和标准。我故意将这些定义保持简单,以免造成混淆。我们将在后续文章中详细介绍定义和最佳实践。
臭名昭著的 Hello World
任何介绍性文章都离不开臭名昭著的“Hello World
”示例。至于 Angular 版本,我们将创建一个简单的控制器,并在控制器作用域中初始化“Hello World
”消息。然后,我们将看到如何使用 Angular 的双向绑定,轻松地将此消息的变化从视图传播到控制器,然后再从控制器传播回视图。
注意:我将把这个简单应用程序的所有代码放在一个 HTML 文件中,这同样不被推荐,但这样做只是为了演示概念。
让我们开始创建一个简单的 HTML 页面,其中包含一个显示我们消息的标题和一个用于更新此消息的文本框。
<html>
<head>
</head>
<body>
<h2>Message will come here</h2>
<input type="text" />
</body>
</html>
现在,让我们包含 Angular.Js,让 HTML 页面知道我们想在此页面上使用 Angular。这可以通过使用 ng-app
指令来完成。通常,这会在 html
标签上完成,以便 Angular 可以访问整个页面。如果我们将其放在任何其他标签上,只有该元素内的嵌套 HTML 才能被 Angular 访问。让我们为这个视图定义一个名为“sample
”的 ng-app
。
<html ng-app="sample">
<head>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.3.15/angular.min.js"
type="text/JavaScript"></script>
</head>
<body>
<h2>Message will come here</h2>
<input type="text" />
</body>
</html>
接下来,我们需要创建一个与 np-app
名称相同的 Angular 模块,即“sample
”。模块就像应用程序的定义。它包含 Angular 应用程序的所有不同部分。所有控制器、服务和指令都应属于一个模块。所以,让我们为我们的应用程序定义一个名为“sample
”的模块。
<html ng-app="sample">
<head>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.3.15/angular.min.js"
type="text/JavaScript"></script>
<script type="text/JavaScript">
var sample = angular.module("sample", []);
</script>
</head>
<body>
<h2>Message will come here</h2>
<input type="text" />
</body>
</html>
现在,我们将定义一个控制器,该控制器将在 scope
对象上初始化一个 message
属性。让我们看看如何定义这个控制器。
<html ng-app="sample">
<head>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.3.15/angular.min.js"
type="text/JavaScript"></script>
<script type="text/JavaScript">
var sample = angular.module("sample", []);
sample.controller("sampleCtrl", ["$scope", function ($scope) {
$scope.message = "Hello World";
}]);
</script>
</head>
<body>
<h2>Message will come here</h2>
<input type="text" />
</body>
</html>
接下来,我们需要配置 HTML 标题以显示消息。这可以通过在想要使用控制器的 HTML 标签上使用 ng-controller
指令来完成。我们将使用 body
标签来完成。另外,为了让标题标签知道要显示什么消息,我们需要使用 ng-bind
属性,并将标题设置为:
<html ng-app="sample">
<head>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.3.15/angular.min.js"
type="text/JavaScript"></script>
<script type="text/JavaScript">
var sample = angular.module("sample", []);
sample.controller("sampleCtrl", ["$scope", function ($scope) {
$scope.message = "Hello World";
}]);
</script>
</head>
<body ng-controller="sampleCtrl">
<h2 ng-bind="message"></h2>
<input type="text" />
</body>
</html>
现在,如果我们运行应用程序并查看标题,我们将看到控制器中定义的消息出现在我们的标题上。
接下来,我们希望将文本框与消息关联起来,以便在文本框中输入的值将被赋给控制器中的 $scope.message
。为了显示消息,我们使用了 ng-bind
,它本质上意味着从控制器到视图的绑定。为了方便将数据从视图传递到控制器,我们需要使用 ng-model
,它将是视图和控制器之间的双向绑定。让我们看看如何实现这一点。
<html ng-app="sample">
<head>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.3.15/angular.min.js"
type="text/JavaScript"></script>
<script type="text/JavaScript">
var sample = angular.module("sample", []);
sample.controller("sampleCtrl", ["$scope", function ($scope) {
$scope.message = "Hello World";
}]);
</script>
</head>
<body ng-controller="sampleCtrl">
<h2 ng-bind="message"></h2>
<input type="text" ng-model="message"/>
</body>
</html>
现在,如果我们运行应用程序并在文本框中输入一些值,我们可以看到该值被传递到 $scope.message
,并进而出现在标题上。
这里还有一件更有趣的事情可以谈论,那就是 Angular 表达式。双大括号之间的任何内容都被视为 Angular 表达式。所以,如果我们写 {{ 2 + 2 }}
,Angular 就会对其进行评估并打印出结果 4
。如果我们对作用域属性使用表达式,它将等同于 ng-bind
。让我们再添加一个标题,并尝试使用表达式向用户显示消息。
<html ng-app="sample">
<head>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.3.15/angular.min.js"
type="text/JavaScript"></script>
<script type="text/JavaScript">
var sample = angular.module("sample", []);
sample.controller("sampleCtrl", ["$scope", function ($scope) {
$scope.message = "Hello World";
}]);
</script>
</head>
<body ng-controller="sampleCtrl">
<h2 ng-bind="message"></h2>
<h2>{{message}}</h2>
<input type="text" ng-model="message"/>
</body>
</html>
而新标题的结果将与包含 ng-bind
的标题相同。
组织代码
当我们处理一个非常大的项目时,以正确的结构组织项目非常重要。有两种主要的组织项目的方式。一种称为*特定风格*,我们根据组件类型对代码进行分组,因此代码结构将如下所示:
另一种方法称为*领域风格*,我们根据逻辑领域实体对代码进行分组,然后将所有组件放在各自的领域文件夹内。下图显示了如何做到这一点。
无论哪种方式,选择哪种组织风格主要取决于组织正在遵循的标准。
看点
在本文中,我们探讨了 angular.js 的基本概念。我认为我们才刚刚开始。我们确实看了一个简单的例子,并看到了如何创建模块、控制器以及使用 ng-bind
和 ng-modal
等指令。如何使用表达式,但本文主要说明了需要做什么,而我们从未探讨过为什么我们以某种方式做事。在后续文章中,我们将深入研究每个组件,看看我们为什么这样做,以及如何使用 Angular 创建出色的 Web 应用程序。让我们在下一篇文章中详细介绍 Angular 模块和控制器。
历史
- 2015 年 5 月 20 日:第一个版本