如何将Angular项目更新到最新版本

在本文中,我们将研究如何将Angular项目更新到最新版本。

本文是SitePoint Angular 2+教程的第6部分,该教程介绍如何使用Angular CLI创建CRUD应用程序。

  1. 第0部分— Ultimate Angular CLI参考指南
  2. 第1部分-启动并运行我们的Todo应用程序的第一个版本
  3. 第2部分-创建单独的组件以显示待办事项列表和一个待办事项
  4. 第3部分-更新Todo服务以与REST API通信
  5. 第4部分-使用Angular路由器解析数据
  6. 第5部分-添加身份验证以保护私有内容
  7. 第6部分-如何将Angular项目更新到最新版本。

在第1部分中,我们学习了如何启动和运行Todo应用程序并将其部署到GitHub页面。 这样做很好,但是不幸的是,整个应用程序都挤在一个组件中。

在第2部分中,我们研究了模块化程度更高的组件体系结构,并学习了如何将单个组件分解为较小的组件的结构树,这些树更易于理解,重用和维护。

在第3部分中,我们更新了应用程序以使用RxJS和Angular的HTTP服务与REST API后端进行通信。

在第4部分中 ,我们介绍了Angular Router,并了解了当浏览器URL更改时路由器如何更新我们的应用程序,以及如何使用路由器从后端API解析数据。

在第5部分中 ,我们向应用程序添加了身份验证,并了解了如何保护应用程序中的部分免受未经授权的访问。

不用担心 您无需遵循本教程的第1、2、3、4或5部分就可以使6有意义。 您可以简单地获取我们的仓库的副本,查看第5部分中的代码,并将其用作起点。 下面将对此进行详细说明。

启动并运行

要开始我们更新Angular的目标,请确保您已安装最新版本的Angular CLI。 如果没有安装,则可以使用以下命令进行安装:

npm install -g @angular/cli@latest

如果您需要删除以前版本的Angular CLI,则可以:

npm uninstall -g @angular/cli angular-cli
npm cache clean
npm install -g @angular/cli@latest

之后,您将需要第5部分中的代码的副本。可以在GitHub上找到它 。 本系列中的每篇文章在存储库中都有一个相应的标记,因此您可以在应用程序的不同状态之间来回切换。

我们在第5部分结尾并在本文中开始的代码被标记为part-5 。 本文结尾处的代码标记为part-6 。

您可以将标签视为特定提交ID的别名。 您可以使用git checkout在它们之间切换。 您可以在此处阅读更多内容 。

因此,要启动并运行(安装了最新版本的Angular CLI),我们可以这样做:

git clone [email protected]:sitepoint-editors/angular-todo-app.git
cd angular-todo-app
git checkout part-5
npm install
ng serve

然后访问http:// localhost:4200 / 。 如果一切顺利,您应该会看到正在运行的Todo应用程序。

更新角度:我们的攻击计划

在本文中,当我们更新Angular时,我们将学习以下内容:

  • Angular版本如何工作
  • 在哪里可以找到有关如何更新Angular的说明
  • 如何将我们的代码从Angular 4更新到Angular 5(在撰写本文时,Angular 5是最新版本)。

到本文结尾,您将了解:

  • 特定Angular版本的基本含义
  • 在哪里可以找到有关如何更新Angular应用程序的确切说明
  • 如何确定Angular 5需要哪些代码更改(如果有)。

让我们开始吧!

角度版本的含义

为了支持蓬勃发展的生态系统,Angular必须既稳定又不断发展。

一方面,Angular旨在为开发人员提供关键任务应用程序的最大稳定性。 另一方面,它不断需要适应和发展以支持Web技术的最新变化。

因此,Angular团队决定使用带有语义版本控制 的基于时间的发布周期 。

基于时间的发布周期意味着我们可以每两周或几个月获得Angular的新版本(Angular 5,Angular 6,Angular 7等)。

语义版本控制意味着Angular的版本号使我们能够预测,如果我们升级到它,是否会破坏我们的应用程序。

本质上,语义版本如下: Major.Minor.Patch

因此,版本v1.3.8的主要组件值为1,次要组件值为3,补丁组件的值为1。

发布新版本时,新版本将隐式指示对代码进行更改的类型。

增加语义版本时,将应用以下规则:

  1. 每个增量以数字形式发生,增量为1。

  2. 修复错误并保持代码向后兼容后,补丁组件就会增加:

    v0.0.3 // Before bugfix
    v0.0.4 // After bugfix
    
  3. 添加功能并且代码保持向后兼容时,将增加次要组件,并将修补程序组件重置为零:

    v0.2.4 // Before addition of new functionality
    v0.3.0 // After addition of new functionality
    
  4. 当实现导致代码向后不兼容的更改 (也称为中断更改)时 ,将增加主要部分,次要和补丁部分重置为零:

    v7.3.5 // Before implementing backwards incompatible changes
    v8.0.0 // After implementing backwards incompatible changes
    

如果您不熟悉语义版本控制,请确保查看此简单的语义版本控制指南 。

Angular团队将语义版本控制与基于时间的发布周期相结合,旨在:

  • 每周都有新的补丁发布
  • 每月有一个新的次要版本
  • 每6个月发布一次新的主要版本

发布时间表并不是一成不变的,因为可能会有假期或特殊事件,但这很好地表明了我们对即将发布的版本的期望。

您可以关注Angular官方博客和官方更改日志 ,以了解最新动态。

语义版本的一个巨大好处是,我们可以使用补丁程序或次要版本安全地更新Angular应用程序,而不必担心会破坏我们的应用程序。

但是,如果有新的主要版本怎么办?

Angular更新指南

我们已经了解到,重大发布可能伴随重大更改。 那么,如何知道我们现有的应用程序是否会中断,如果我们更新它呢?

一种方法是阅读正式的更改日志并浏览更改列表。

一种简单得多的方法是使用《 Angular更新指南》来更新Angular。 您选择当前的Angular版本以及要升级到的版本,应用程序会告诉您需要采取的确切步骤:

对于我们的Angular Todo应用程序,我们希望从Angular 4.0升级到Angular 5.0。

让我们选择应用程序复杂性级别“ 高级”,以便我们看到需要采取的所有可能措施:

如何将Angular项目更新到最新版本_第1张图片

我们获得了更新应用程序所需采取的所有步骤的完整概述。

多么甜蜜!

更新之前

更新之前列表包含12个项目。 没有任何一项适用于我们的Angular Todo应用程序,因此我们可以安全地进行下一步。

更新期间

在“更新期间”列表中,仅最后一项适用于我们的应用程序。 我们需要更新依赖关系,因此让我们在项目的根目录中运行建议的命令:

$ npm install @angular/{animations,common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router}@'^5.0.0' [email protected] rxjs@'^5.5.2'

$ npm install [email protected] --save-exact

因为我们在“ 启动和运行”部分中将Angular CLI更新为最新版本,所以我们也更新了本地版本:

$ npm install @angular/cli@latest --save-dev

为了验证我们的应用程序是否正常运行,我们运行:

$ ng serve

如果ng serve无法启动,请尝试删除您的node_modules目录和package-lock.json文件,然后运行npm install重新创建一个干净的node_modules目录和package-lock.json文件。

更新后

更新后列表包含四个项目,其中第一个和最后一个适用于我们的应用程序:

  • HttpModule切换到HttpClientModule
  • rxjs/operators导入RxJS运算rxjs/operators并使用RxJS管道运算符

让我们一一解决。

从HttpModule切换到HttpClientModule

Angular Update Guide告诉我们应该从HttpModule切换到HttpClientModule

如果我们查看Angular 5.0.0发行说明 ,就会了解到Angular 4.3及更高版本附带了一个新的HttpClient ,它可以自动处理JSON响应并支持HTTP拦截器。

它指出,要更新我们的代码,我们必须用HttpClientModule替换HttpModule ,注入HttpClient服务并删除所有map(res => res.json())调用,因为新的HttpClient自动解析JSON响应。

让我们打开src/app/app.module.ts并替换HttpModule

// ...
import { HttpModule } from '@angular/http';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    // ...
    HttpModule,
  ],
  providers: [
    // ...
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

使用HttpClientModule

// ...
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    // ...
    HttpClientModule,
  ],
  providers: [
    // ...
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

接下来,我们必须使用HttpClient服务而不是Http服务,并删除代码中的所有map(res => res.json())调用,因为新的HttpClient自动为我们解析响应。

在第3部分中 ,我们将所有与HTTP相关的代码集中在一个名为ApiService的服务中,现在,我们收获了该方法的好处。

结果,我们只需要更新一个文件,所以让我们打开src/app/api.service.ts并替换:

import {
  Http,
  Headers,
  RequestOptions,
  Response
} from '@angular/http';

// ...

@Injectable()
export class ApiService {

  constructor(
    private http: Http,
    private session: SessionService
  ) {
  }

  public signIn(username: string, password: string) {
    return this.http
      .post(API_URL + '/sign-in', {
        username,
        password
      })
      .map(response => response.json())
      .catch(this.handleError);
  }

  public getAllTodos(): Observable {
    const options = this.getRequestOptions();
    return this.http
      .get(API_URL + '/todos', options)
      .map(response => {
        const todos = response.json();
        return todos.map((todo) => new Todo(todo));
      })
      .catch(this.handleError);
  }

  public createTodo(todo: Todo): Observable {
    const options = this.getRequestOptions();
    return this.http
      .post(API_URL + '/todos', todo, options)
      .map(response => {
        return new Todo(response.json());
      })
      .catch(this.handleError);
  }

  public getTodoById(todoId: number): Observable {
    const options = this.getRequestOptions();
    return this.http
      .get(API_URL + '/todos/' + todoId, options)
      .map(response => {
        return new Todo(response.json());
      })
      .catch(this.handleError);
  }

  public updateTodo(todo: Todo): Observable {
    const options = this.getRequestOptions();
    return this.http
      .put(API_URL + '/todos/' + todo.id, todo, options)
      .map(response => {
        return new Todo(response.json());
      })
      .catch(this.handleError);
  }

  public deleteTodoById(todoId: number): Observable {
    const options = this.getRequestOptions();
    return this.http
      .delete(API_URL + '/todos/' + todoId, options)
      .map(response => null)
      .catch(this.handleError);
  }

  private handleError(error: Response | any) {
    console.error('ApiService::handleError', error);
    return Observable.throw(error);
  }

  private getRequestOptions() {
    const headers = new Headers({
      'Authorization': 'Bearer ' + this.session.accessToken
    });
    return new RequestOptions({ headers });
  }
}

import {
  HttpClient,
  HttpErrorResponse,
  HttpHeaders
} from '@angular/common/http';

// ...

@Injectable()
export class ApiService {

  constructor(
    private http: HttpClient,
    private session: SessionService
  ) {
  }

  public signIn(username: string, password: string) {
    return this.http
      .post(API_URL + '/sign-in', {
        username,
        password
      })
      .catch(this.handleError);
  }

  public getAllTodos(): Observable {
    const options = this.getRequestOptions();
    return this.http
      .get(API_URL + '/todos', options)
      .map(response => {
        const todos =  response;
        return todos.map((todo) => new Todo(todo));
      })
      .catch(this.handleError);
  }

  public createTodo(todo: Todo): Observable {
    const options = this.getRequestOptions();
    return this.http
      .post(API_URL + '/todos', todo, options)
      .map(response => {
        return new Todo(response);
      })
      .catch(this.handleError);
  }

  public getTodoById(todoId: number): Observable {
    const options = this.getRequestOptions();
    return this.http
      .get(API_URL + '/todos/' + todoId, options)
      .map(response => {
        return new Todo(response);
      })
      .catch(this.handleError);
  }

  public updateTodo(todo: Todo): Observable {
    const options = this.getRequestOptions();
    return this.http
      .put(API_URL + '/todos/' + todo.id, todo, options)
      .map(response => {
        return new Todo(response);
      })
      .catch(this.handleError);
  }

  public deleteTodoById(todoId: number): Observable {
    const options = this.getRequestOptions();
    return this.http
      .delete(API_URL + '/todos/' + todoId, options)
      .map(response => null)
      .catch(this.handleError);
  }

  // ...
}

我们将HttpModule的旧类替换为HttpClientModule的新类。

更具体地说,我们替换为:

  • import { Http, Headers, RequestOptions, Response } from '@angular/http'; import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
  • 第81行: HttpErrorResponse Response
  • 线90: HeadersHttpHeaders
  • 第93行: return new RequestOptions({ headers }); return { headers };

如果我们运行:

$ ng serve

并将浏览器导航到http://localhost:4200 ,我们看到我们的应用程序仍然可以按预期运行,但是现在在幕后使用HttpClientModule

是时候解决项目2:从rxjs/operators导入RxJS运算rxjs/operators并使用RxJS管道运算符了。

使用RxJS管道运算符

Angular 5已更新为使用RxJS 5.5.2或更高版本。

从5.5版开始,RxJS附带了可管道运算符 。 官方文件说:

管道运算符是返回带有签名的函数的任何函数: (source: Observable) => Observable

您可以从rxjs/operators下的多个位置拉入所需的任何运算rxjs/operators (复数!)。 还建议直接使用所需的Observable创建方法,如下所示,范围为:

import { range } from >'rxjs/observable/range';
import { map, filter, scan } from >'rxjs/operators';

const source$ = range(0, 10);

source$.pipe(
 filter(x => x % 2 === 0),
 map(x => x + x),
 scan((acc, x) => acc + x, 0)
)
.subscribe(x => console.log(x))

尽管这听起来很复杂,但从根本上讲,这意味着我们以前使用链式方法的地方:

source$
  .operatorOne()
  .operatorTwo()
  .subscribe()

现在,我们应该从rxjs/operators导入运算rxjs/operators并使用.pipe()方法来应用它们:

source$
  .pipe(
    operatorOne(),
    operatorTwo()
  )
  .subscribe()

管道运算符的主要好处是:

  1. 它们是可摇树的,允许工具通过删除未使用的代码来减小应用程序捆绑包的大小
  2. 它们是简单的函数,因此我们可以轻松创建自己的自定义管道运算符 。

.pipe()方法将对代码的影响降至最低。

我们的应用程序中有两项需要重构: ApiServiceTodosComponent

首先,让我们打开src/app/api.service.ts来更新我们的ApiService

// import operators from rxjs/operators
import { map } from 'rxjs/operators';

// ...

@Injectable()
export class ApiService {

  constructor(
    private http: HttpClient,
    private session: SessionService
  ) {
  }

  // ...

  // update .map() to .pipe(map())
  public getAllTodos(): Observable {
    const options = this.getRequestOptions();
    return this.http
      .get(API_URL + '/todos', options)
      .pipe(
        map(response => {
          const todos =  response;
          return todos.map((todo) => new Todo(todo));
        })
      )
      .catch(this.handleError);
  }

  // update .map() to .pipe(map())
  public createTodo(todo: Todo): Observable {
    const options = this.getRequestOptions();
    return this.http
      .post(API_URL + '/todos', todo, options)
      .pipe(
        map(response => {
          return new Todo(response);
        })
      )
      .catch(this.handleError);
  }

  // update .map() to .pipe(map())
  public getTodoById(todoId: number): Observable {
    const options = this.getRequestOptions();
    return this.http
      .get(API_URL + '/todos/' + todoId, options)
      .pipe(
        map(response => {
          return new Todo(response);
        })
      )
      .catch(this.handleError);
  }

  // update .map() to .pipe(map())
  public updateTodo(todo: Todo): Observable {
    const options = this.getRequestOptions();
    return this.http
      .put(API_URL + '/todos/' + todo.id, todo, options)
      .pipe(
        map(response => {
          return new Todo(response);
        })
      )
      .catch(this.handleError);
  }
}

我们从rxjs/operators导入map管道运算rxjs/operators并将所有出现的事件从.map(fn)更新为.pipe(map(fn))

接下来,让我们打开src/app/todos/todos.component.ts将相同的更改应用于TodosComponent

// import operators from rxjs/operators
import { map } from 'rxjs/operators';

// ...

@Component({
  selector: 'app-todos',
  templateUrl: './todos.component.html',
  styleUrls: ['./todos.component.css']
})
export class TodosComponent implements OnInit {

  // ...  

  // update .map() to .pipe(map())
  public ngOnInit() {
    this.route.data
      .pipe(
        map((data) => data['todos'])
      )
      .subscribe(
        (todos) => {
          this.todos = todos;
        }
      );
  }

  // ...

}

同样,我们从rxjs/operators导入map管道运算rxjs/operators并将.map(fn)更新为.pipe(map(fn))

而已! 正如Angular Update Guide所指示的那样,我们应用程序中的链式运算符已被可管道运算符取代。

如果将浏览器导航到http://localhost:4200http://localhost:4200看到我们的应用程序仍然可以正常运行。

为了验证我们是否确实在运行Angular 5,我们可以打开元素检查器:

如何将Angular项目更新到最新版本_第2张图片

Angular将ng-version属性添加到app-root并带有其运行版本的值。 我们看到ng-version="5.2.9" ,表明我们正在运行Angular 5.2.9。

任务完成! 我们的应用程序已成功升级到Angular 5.2.9。

我们涵盖了很多内容,所以让我们回顾一下我们学到的东西。

摘要

在第一篇文章中 ,我们学习了如何:

  • 使用Angular CLI初始化我们的Todo应用程序
  • 创建一个Todo类来代表单个Todo
  • 创建TodoDataService服务以创建,更新和删除待办事项
  • 使用AppComponent组件显示用户界面
  • 将我们的应用程序部署到GitHub页面。

在第二篇文章中 ,我们将AppComponent重构为将其大部分工作委托给:

  • TodoListComponent以显示TodoListComponent列表
  • TodoListItemComponent以显示单个待办事项
  • 一个TodoListHeaderComponent来创建一个新的待办事项
  • TodoListFooterComponent来显示还剩下多少个TodoListFooterComponent

在第三篇文章中 ,我们学习了如何:

  • 创建一个模拟REST API后端
  • 将API URL存储为环境变量
  • 创建一个ApiService与REST API通信
  • 更新TodoDataService以使用新的ApiService
  • 更新AppComponent以处理异步API调用
  • 创建一个ApiMockService以避免在运行单元测试时进行真正的HTTP调用。

在第四篇文章中 ,我们了解到:

  • 为什么应用程序可能需要路由
  • 什么是JavaScript路由器
  • 什么是Angular Router,它如何工作以及它能为您做什么
  • 如何为我们的应用程序设置Angular路由器和配置路由
  • 如何告诉Angular路由器在DOM中放置组件的位置
  • 如何正常处理未知URL
  • 如何使用解析器让Angular路由器解析数据。

在第五篇文章中,我们了解到:

  • Cookie和令牌之间的区别
  • 如何创建AuthService来实现身份验证逻辑
  • 如何创建一个SessionService来存储会话数据
  • 如何使用Angular反应形式创建登录形式
  • 如何创建路由防护以防止未经授权访问应用程序的某些部分
  • 如何在HTTP请求中将用户令牌作为授权标头发送到您的API
  • 为什么您永远不应该将用户的令牌发送给第三方。

在有关如何更新Angular的这篇文章中,我们了解到:

  • Angular版本如何工作
  • 语义版本号是什么意思
  • 语义版本控制如何保护我们避免盲目地在应用程序中引入重大更改
  • 《 Angular更新指南》如何帮助我们找到有关如何更新Angular的详细说明
  • 如何用HttpClientModule替换HttpModule
  • 如何使用管道运算符更新我们的RxJS代码
  • ng-version属性如何让我们验证我们正在运行的Angular版本。

在即将发布的版本中,Angular CLI将引入ng update命令来帮助更新Angular应用程序。 一旦有更多详细信息可用,我们将为您提供有关此新命令如何使我们的生活更加轻松的后续文章。

在此之前,您可以将本文用作如何将Angular应用程序更新到最新版本的指南。

这篇文章中的所有代码都可以在GitHub上找到 。

有一个很棒的!

From: https://www.sitepoint.com/update-angular-projects/

你可能感兴趣的:(如何将Angular项目更新到最新版本)