JavaScript 入门指南(三)
十二、JavaScript 和应用框架:React
在上一章中,你能够创建一个 Angular 应用,并通过使用一个代理连接到一个可以访问 MySQL 数据库的节点服务器。通过这种设置,您能够检索数据并将其显示在屏幕上。您还更新了代码,以便可以使用 REST 谓词POST将数据从表单发送到服务器,然后在数据库更新后查看结果。
这一章将涵盖一些相同的基础,但你将使用反应,而不是角度。这样做的目的是展示不同的框架如何以不同的方式解决问题。React 让开发人员可以自由选择他们认为能够解决他们所面临的挑战的库。
因为 React 有这个选项,所以很多开发人员喜欢自己把所有东西放在一起。
脸书创造了一种快速组装 React 应用的方法。它类似于 CLI 的角度使用。然而,重要的是要注意,这个工具只是启动一个应用,并不包含像 Angular 那样添加新文件的命令。
与 Angular 类似,如果你想在任何你喜欢的文件夹中创建一个 React 应用,你首先需要安装这个应用。
在命令行中,键入
npm install -g create-react-app
这使您能够在任何您喜欢的文件夹中创建 React 应用。
要创建一个全新的 React 应用,请在命令行中键入
npx create-react-app my-app
面试问题
npm 和 npx 有什么区别?答:npx 允许您执行 npm 注册表中的软件包,而不需要安装它们,而 npm 帮助您管理全局或本地安装在您机器上的软件包。
这个命令为一个基本的 React 应用创建所有的文件和文件夹。要启动该应用,您可以键入
cd my-app
npm start
这段代码以与 Angular 应用相同的方式启动应用。浏览器应该如图 12-1 所示。
图 12-1
使用 create-react-app 时的默认屏幕
现在,您已经在本地机器上运行了 React,您可以创建组件来调用 API 并在屏幕上显示数据。
React 将自己描述为一个高效的、声明性的 JavaScript 库,用于构建用户界面。记住这一点,您将使用 React 构建一个组件,该组件将从您的 web 服务中检索数据。
由于 React 不能在命令行创建文件,所以您必须手动创建您需要的文件。
所以创建一个名为components的文件夹。在该文件夹中,创建另一个名为boroughs的文件夹和一个名为boroughs.js的文件。这是您创建 React 组件的地方。
为了确保您的新组件将由更大的应用呈现,您需要做一些更改。
在boroughs.js文件中,您将导入确保这个 JavaScript 类将与 React 一起工作的代码。该组件看起来应该如清单 12-1 所示。
import { Component } from 'react';
export class Boroughs extends Component {
render() {
return "this is my component!"
}
}
Listing 12-1Basic React Component
React 组件的语法非常类似于 JavaScript 类。为了让这个类与 React 一起工作,首先要导入Component类,并确保当前类扩展了它。
这个类还包含一个名为render的方法。它返回所有需要在浏览器中显示的 HTML 代码的结果。
创建好组件后,您需要一种在屏幕上显示它的方式。要加载这个组件,您需要更新App.js文件。
每个组件都被视为一个 HTML 标签。您可以导入您创建的类,并让 React 在应用中呈现它。
打开App.js并导入新创建的组件。组件的名称是应用中使用的 HTML 标记。更新后的文件应该如清单 12-2 所示。
import React, { Component } from 'react';
import logo from './logo.svg';
import '.App.css';
import { Boroughs } from '../components/boroughs/boroughs';
export class App extends Component {
render() {
return(
<div className="App">
<header className="App-header">
<Boroughs/>
</header>
</div>
);
}
}
Listing 12-2
Updating App.js
既然文件已经更新,浏览器应该有更新的版本。你的浏览器应该如图 12-2 所示。
图 12-2
呈现基本组件的默认屏幕
React 不像 Angular 那样有服务的概念。但是,React 确实有生命周期方法的概念,就像 Angular 一样。
记住这一点,您将创建一个调用 API 的函数,并在浏览器控制台中显示结果。
添加代理和检索数据
就像上一章中的 Angular 应用一样,您需要一个代理将这个应用连接到单独的节点应用。
使用 Create React App 建立代理连接非常简单。在package.json文件中,您需要添加一个代理部分。这将指向您的节点应用。
打开package.json并添加这一行:
"proxy": "http://localhost:3001",
您可能会注意到,在其他示例中,节点应用指向端口 3000。默认情况下,React 和 Node 运行在同一个端口上,所以这将产生冲突。您必须更改节点的端口。这是对节点应用中的App.js文件的简单更新。
第十章有一个设置 Express 服务器的例子,你可以设置节点监听的端口。如果需要如何更新节点端口的说明,请参考第十章。
打开节点应用的文件,将端口号更改为 3001。更新后的变量应该如下所示:
CONST port = 3001;
在这两种情况下,您都需要重新启动应用以确保更改生效。您可以更新组件,以便从数据库中检索结果。
React 拥有生命周期事件,可以让您了解组件的当前状态。
这里您将使用componentDidMount功能。这将在组件呈现在屏幕上时执行。关于生命周期方法及其工作方式的更详细列表,请查看 React 官方网站上的文档( https://reactjs.org/docs/react-component.html )。
在这个函数中,您将使用一个名为fetch的函数。它不是 React 的一部分;它是 JavaScript 语言的一部分( https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch )。这将使您对服务器的 HTTP 调用通过您的代理路由到节点应用。它将返回所谓的 ?? 承诺。
承诺是表示某种事件完成的对象。在本例中,您正在发出一个 HTTP 请求,并期望得到一个回答。该承诺将解决要么完成该请求,在那里你得到一个结果,或失败的请求。
此示例从服务器返回数据。在then方法中使用的函数获取结果,将它们转换成 JSON 对象,并将它们返回给fetch。第二个then方法获取 JSON 对象,并将其转换为要在浏览器控制台中显示的字符串。
现在您已经有了 API 调用的结果,并且知道您有查看结果的方法,本节的最后一部分将介绍如何在屏幕上显示结果。
React 有一个概念叫做状态。你可以把状态想象成应用此刻正在做的事情。您可以通过在constructor函数中创建一个state对象来设置组件的状态。
下一节将展示如何创建默认的state,更新state,并显示当前在state对象中的值。
在 React 组件中创建、更新和显示状态
上一节展示了如何在本地机器上使用代理并检索数据。不属于 React 的fetch方法用于向服务器发出GET请求,并将结果返回给组件。
你现在面临的挑战是将结果显示在屏幕上。
React 中的每个组件都有一个状态概念。在constructor函数中,你将创建一个state对象。该对象将具有您将创建并赋值的属性。
这两个属性将被称为boroughs和states。每个属性将保存在一个数组中,你将在一会儿设置这个数组的值*。*
在这个例子中,您将获得 API 的结果,并使用内置的setState方法来更新您创建的状态。您的组件应该看起来如清单 12-3 所示。
import React, { Component } from 'react';
export class Boroughs extends Component {
constructor(props){
super(props);
this.state = {
boroughs: [],
states:[]
}
}
componentDidMount(){
let boroughArray = [];
let stateArray = [];
fetch('/boroughs').then( (results) => {
return results.json();
}).then ( (resultJson) => {
resultJson.map( (value, index) => {
boroughArray.push(value.name);
stateArray.push(value.state);
});
this.setState({
boroughs: boroughsArray,
state: stateArray
});
});
}
createList(list){
if (list.length !== 0) {
const stateList = list.map( (value, index) => {
return (<div> {value} </div>)
});
return stateList
}
};
render() {
return(
<div>
<div>
<div>Boroughs:</div>
<div> { this.createList(this.state.boroughs)} </div>;
</div>
<div>
<div>States:</div>
<div> { this.createList(this.state.states)} </div>;
</div>
</div>
}
}
Listing 12-3boroughs.js Making an API Call and Displaying the Results on the Screen
您导入 React 和component类的方式与使用 ES6 导入类的方式相同。React 组件具有与 ES6 类相同的格式。您扩展了component类,为这个类提供了框架提供的额外能力。
在这个类中,您使用了constructor函数。这个函数在组件被挂载之前被调用。您调用的第一个函数是super方法。这样做的原因是,万一你想做的事情,如绑定一个事件处理方法到当前的类。
您还可以访问名为state *的属性。*这是你让 React 跟踪变量或对象的地方。这个框架甚至可以让你知道当它被更新时以前的值是什么。
现在,您创建这些属性,并将它们分配给一个空数组,稍后您将对其进行更新。
接下来,你看看你的生命周期方法componentDidMount。当组件被插入到 DOM 树中时,就会执行这个操作。这种方法是开始处理任何类型的外部数据的好地方。
您创建两个本地数组,然后使用fetch方法。它会让你打电话到远程服务器。由于您的代理,对/boroughs的调用将被重定向到您的节点服务器,就像在 Angular 应用中一样。
fetch方法允许您链接一个then方法,在这里您可以调用一个函数来处理 API 调用的结果。在该函数中,您获取结果并将其作为 JSON 对象返回。随着一个新对象的返回,你链接第二个then方法并调用另一个函数。该函数用于使用map方法循环数据。当调用map中的一个函数时,你遍历每个值,并根据名字或状态将它们添加到本地数组中。
一旦循环结束,您就可以获取两个数组并更新应用的状态。使用内置的setState方法,将数组分配给state属性。
我们暂且跳过下一个函数,来说说render函数。这里是 React 开始处理要在屏幕上显示的 HTML 的地方。花括号让 React 接受变量,并将它们显示为有效的 HTML 标记。从这里,您调用createList方法并传递来自您的state对象的每个属性。
这就把你带到了createList方法。它接受一个名为list的参数,这实际上是 React 应用中boroughs或states状态的替代。
该方法做的第一件事是确保传递的数组长度不为零,这意味着它不能是空数组。确定之后,使用map遍历数组并返回一个包含数组中值的 HTML div元素列表。这个列表被返回给render方法以显示结果。
在render方法中调用的函数现在将在浏览器中生成 HTML。见图 12-3 。
图 12-3
从节点 API 在屏幕上显示结果
现在 React 已经在屏幕上显示了您的结果,您可以使用 CSS 来显示它们。在下一节中,您将添加 Bootstrap 并更新应用的布局。
添加 Bootstrap 以进行反应
就像 Angular 一样,有几种方法可以将 Bootstrap 添加到 React 应用中。添加 Bootstrap 的方法是使用一个名为 reactstrap 的项目。
在 install reactstrap 中,您需要返回到应用底部的命令行,并使用 NPM 来安装库。在命令行中,键入
npm install bootstrap –save
npm install –save reactstrap react react-dom
这将在您的应用中安装 Twitter Bootstrap 和 reactstrap。现在,您可以使用相同的 CSS 类来布局应用。为了让您的应用利用作为应用一部分的 Bootstrap,将其导入到index.js文件中,如下所示:
import 'bootstrap/dist/css/bootstrap.min.css'
现在,无需更改任何 JavaScript,您就可以更新 React 应用的 HTML 部分。参见清单 12-4 。
<div className='container-fluid'>
<div className="row">
<div className="col">Boroughs:</div>
<div className='col>States:</div>
</div>
<div className="row">
<div className="col"> { this.createList(this.state.boroughs)} </div>;
<div className="col"> { this.createList(this.state.states)} </div>;
</div>
</div>
Listing 12-4Using reactstrap to Lay Out the Content of Your Component
如果您再次启动应用,屏幕应该如图 12-4 所示。
图 12-4
使用 reactstrap 获得与 Bootstrap 相同的 CSS 类
您的应用现在拥有从数据库返回的信息,并使用 Bootstrap 进行格式化。本章的最后一节将介绍如何创建一个表单,并通过 web 服务将信息发送到数据库。
从 React 应用发布数据
您可以从数据库中检索数据,并使用 Bootstrap 将结果格式化为类似表格的布局。现在,您将通过添加一个 HTML 表单来更改这种布局,这样您就可以向数据库中提交新数据,并且在向数据库中添加新信息后,有一个单独的按钮来检索所有结果。
您在前面的示例中使用的一些函数在本练习中仍然有效。但是,该组件将负责调用服务器,然后在浏览器中呈现结果。
现在您已经将 reactstrap 添加到项目中,您可以使用它来使您的表单成形。通过从 reactstrap 库中导入组件,它们将使用 Bootstrap 提供的格式在浏览器中呈现。
你将从把组件作为一个整体来看开始,然后分解所有的单个部分。参见清单 12-5 。
import React, { Component } from 'react';
import { Container, Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';
import './css/boroughs.css';
export class Boroughs extends Component {
constructor(props)} {
super(props);
this.state = {
boroughs: [],
states: [],
boroughInput: '',
stateInput: ''
}
};
componentDidMount(){}
createList = (list) => {
if(list.length !== 0) {
const itemList = list.map( (value, index ) => {
return (<div> {value} </div>)
});
return itemList;
}
}
onSubmitForm = (e) => {
e,prventDefault();
fetch( '/boroughs', {method: "POST",
headers:{'Accept': 'application/json', 'Content-type': 'application/json'},
body: JSON.stringify({boroughName: this.state.boroughInput, state: this.state.stateInput}) }).then((result) => {
console.log(result);
});
} )
}
onBoroughsUpdate = (event) => {
this.setState({boroughInput: event.target.value});
}
onStatusUpdate = (event) => {
this.setState({stateInput: event.target.valiue});
}
showResults = () => {
let boroughArray = [];
let stateArray = [];
fetch('/boroughs').then( (results) => {
return results.json();
}).then( (resultJson) => {
resultJson.map( (value, index ) => {
boroughArray.push(value.name);
stateArray.push(vaue.state);
});
this.setState({
boroughs: this.createList(broughsArray),
states: this.createList(stateArray)
});
});
}
render(){
return(
<Container className="borough-container">
<Form className="form" onSubmit={this.onSubmitform}>
<Col>
<FormGroup>
<Label>Add Name</Label>
<Input type="text" value={this.state.boroughInput} onChange={this.onBoroughUpdate) onBlur={this.onBoroughUpdate}/>
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label>Add State</Label>
<Input type="text" value={this.state.stateInput} onChange={this.onStateUpdte}
onBlur={this.onStateUpdate}/>
</FormGroup>
</Col>
<Button>Submit</Button>
</Form>
<br/>
<div>
<div className="row">
<div className="col">
<button className="btn btn-primary" onClick={this.showResults}>Show Results</button>
</div>
</div>
<div className="row resultsPadding">
<div className="col">
Boroughs:
</div>
<div className="col">
{this.state.boroughs}
</div>
<div className="col">
States:
</div>
<div className="col">
{this.state.states}
</div>
</div>
</div>
</Container>
)
}
}
Listing 12-5boroughs.js Using reactstrap for Formatting Both Sending and Receiving Data from a Database
这个类做了很多工作。好好看看并理解它正在做的一切是有帮助的。让我们从顶部开始,进入所有的细节。
在最顶层,您导入帮助组成您的界面的组件。您以前遇到过 React 组件。其他成分来自反应堆。这些组件使您可以构建看起来相同的表单、列表和按钮,就像您手动为它们分配了引导 CSS 类一样。
第三行允许您导入将在该组件内部使用的 CSS。
在constructor函数中,你为你的state对象创建一些属性。前两个是数组,后两个是字符串。
您可能会注意到,在本例中没有使用生命周期事件。在这种情况下,请将它们留空。
createList函数的工作方式与上一个例子完全相同。它根据传递给它的数组生成一个div元素列表。有一点需要注意,因为您正在动态创建新的div元素:React 有一个名为key 的属性。这就像给 React 渲染的元素一个唯一的 id。更多信息,看一下官方文档( https://reactjs.org/docs/lists-and-keys.html )。
onSubmitForm函数首先通过preventDefault函数阻止浏览器刷新。然后,它将输入到表单域中的值发送到数据库。
使用fetch方法,首先定义端点,然后创建一个对象,通知fetch应该如何将数据传递给服务器。
该对象包含一些属性。第一个是您希望fetch进行的 REST 跟注类型。在这种情况下,您正在创建一个POST,以便更新数据库。第二个属性定义了将要发送到服务器的头。这让服务器知道您将向它发送一些 JSON 数据。
第三个属性是数据本身。它作为发送到服务器的消息正文的一部分发送出去。
数据将由服务器拾取,而不需要对角度示例进行任何更改,并将向数据库添加新记录。
功能onBoroughsUpdate和onStatesUpdate执行相同类型的动作。在这两种情况下,当有人在输入域中输入时,结果被保存在state对象中。当用户点击文本字段之外的内容时,就会执行onBlur事件。它将获取文本字段的当前值,并将其分配给state对象。
showResults函数的执行方式与之前的componentDidMount函数相同。这里唯一的区别是,当使用setState方法时,你分配createList方法的结果。
一旦你使用了render方法,当浏览器使你的 UI 可见时,你就进入了你看到的大多数 HTML 元素。例如,您可以看到按钮响应类似于onClick的事件,并调用组件中定义的函数。
当运行应用和服务器时,如果您向数据库中添加新的内容,您的屏幕应该看起来如图 12-5 所示。
图 12-5
使用 reactstrap 构建表单并从数据库中检索结果
向 React 应用添加强类型
React 不像 Agular 那样使用 TypeScript 强制强类型。您可以开发一个 React 应用,并且永远不需要在 JavaScript 中强制类型。
然而,对于大型应用来说,类型安全和 JavaScript 强制数据类型可能会有所帮助。
在 React 应用中使用 Flow ( https://flow.org/en/ )是可选的,但是它使您能够让编译器检查数据类型,就像 TypeScript 或 Java 等其他语言强制类型一样。同样需要注意的是,如果你喜欢的话,可以使用 React with TypeScript(https://facebook.github.io/create-react-app/docs/adding-typescript)。
在前面使用 React 的例子中,您使用了create-react-app来构建您的应用。记住这一点,您可以快速地向您的应用添加流。在命令行中,键入
npm install –-save-dev flow-bin
如果您使用纱线,请键入
yarn add flow-bin
安装后,将这一行添加到package.json文件的脚本部分:
"scripts":{
"flow":flow
}
现在在命令行中,通过键入以下命令创建一个名为.flowconfig的文件
npm run flow init
如果您使用纱线,请键入
yarn flow init
安装好所有东西后,你现在可以添加//@flow或/* @flow */到你希望编译器开始检查类型的文件中。要检查您的文件并了解您需要在命令行添加类型的位置,您可以键入
npm run flow check
这将寻找每个添加了//@Flow的文件。命令行将显示每个文件以及类型的预期位置。
您现在需要了解如何在代码中添加类型。在下一节中,您将看到如何做到这一点。
向 React 代码添加类型
每个 React 组件都有自己的内部状态。state对象跟踪您希望组件一直知道的值。分配给state对象的所有属性都可以被分配强类型。您可以在定义类之前定义这些类型。它应该是这样的:
type State = {
boroughs: Array<string>,
states: Array<string>,
boroughInput: string,
stateInput: string
}
此示例定义了数据类型。您学习了数组是如何工作的;在这里,您定义了数据类型将是一个数组,但是您也定义了在该数组中使用什么类型的数据。在这种情况下,boroughs和states都将保存一个数组。这里的主要区别是,它将是一个以字符串为值的数组。
如果您向该数组添加不同的数据类型,编译器将会抛出一个错误,因为这里您已经确切地告诉了它将会发生什么。
另外两个属性表示用于向数据库添加新数据的文本字段。在这种情况下,它们都被类型化为字符串。
React 还允许您以同样的方式设置属性的数据类型。一旦设置了类型,您就可以将它们应用到组件:
export class Boroughs extends Component<Props, State>{ }
现在如果你把一个数字赋给一个已经被赋了字符串的东西,编译器会抛出一个错误。
使用函数时,语法就像 TypeScript 一样。参数可以设置类型,因此当您将数据传递给函数时,它们必须是该类型。这里有一个例子:
function doMath(num1: number, num2: number){
return num1 + num2;
}
doMath(5,2);
您还可以为变量分配类型:
let username: string = "Hack One";
const accountNumbber: number = 11220;
var currentTime: string = "Time to make the donuts";
虽然这只是一个概述,但它让您深入了解了如何确保在处理数据时使用正确的类型。Flow 是可选的,但是很容易将它一次一个文件地添加到现有代码中,或者在开发新项目时对每个文件添加类型检查。
摘要
本章探讨了 React 库的一些基础知识。虽然您可以自己将所有部分组合在一起,但 React 有一个命令行界面,可以让您快速地将应用组合在一起。
React 和 Angular share 的一个想法是生命周期事件的概念。这些事件内置于框架中,让您知道组件当前处于哪个阶段。
第一个例子使用了componentDidMount事件。当这个事件被触发时,它调用数据库。
另一个例子引入了 Bootstrap 进行布局,并能够设计按钮和表单等样式。
最后一个例子使用了一个向数据库发送POST并添加新信息的表单。您还添加了一个按钮,该按钮发出一个GET请求并显示来自数据库的所有更新信息。
虽然 Angular 和 React 之间有一些差异,但围绕生命周期事件等概念也有一些非常相似的想法。
Flow 类似于 TypeScript,因为您可以在 JavaScript 应用中实施类型安全。虽然 TypeScript 作为一种语言是 JavaScript 的超集,但 Flow 允许您使用当前的 JavaScript 并对其应用类型。
使用 Flow 的一个好处是可以在编译时发现错误。此外,您可以确保您的函数接收和返回正确类型的数据,以便它们能够正确执行。一个很好的例子就是当你想要更新一个字符串或者计算一个值的时候。
通过利用框架和 JavaScript 知识,您已经能够在本地计算机上开发应用。下一章将向您展示如何将一个应用部署到一个实时服务器上,供其他人查看。
十三、JavaScript 和静态部署
您已经在本地机器上进行了大量开发工作,直接在浏览器中使用 JavaScript,然后使用 Angular 和 React 等框架开发依赖于服务器数据的应用。既然您已经了解了使用大型应用是什么样子,那么您可以将这些应用从您的笔记本电脑移动到服务器上,让每个人都可以看到。
对于这个示例,您将使用 JSONPlaceholder 作为您的数据源,并专注于前端开发。
在本书的前几章中,我讨论了如何使用 Git 作为一种跟踪应用随时间推移而发生的所有变化的方式。虽然这很重要,但是 Git 并不能让其他人将网站视为成品。
您需要的是一种将您的代码自动部署到远程服务器的方法,这样访问者就可以看到您所有的努力工作。
本章将首先描述什么是静态站点,以及如何使用 Netlify 这样的服务将你的代码从 GitHub 这样的服务转移到一个动态服务器上。
开发 Angular 应用并将其连接到 GitHub
静态网站实际上是使前端工作的所有部分。在这种情况下,它是 HTML、JavaScript、CSS、图像和任何其他媒体,您需要将网站交付给浏览器。
与数据库的所有交互将由一个不同的服务器来处理,该服务器将响应所有的 API 调用。
对于此示例,您将进行基本的角度应用。这就像你的另一个例子。
确保在项目中添加角度路由器。在命令行中,键入
ng new static-app
这段代码把你的裸露的角度应用放在一起。创建好应用后,现在创建一个照片组件和一个相册组件:
cd static-app
ng generate module media
ng generate component media/photos
ng generate component media/albums
最后,创建一个将为这两个组件检索数据的服务:
ng generate service /media/services/service
一旦所有这些组件和服务都被添加到项目中,您就可以添加 Bootstrap 了,但是方式与过去不同。这里您将使用一个名为 ng-bootstrap ( https://ng-bootstrap.github.io )的项目。它将为您提供 Bootstrap 的所有功能,而无需安装 jQuery 之类的库。
在命令行中,安装 ng-bootstrap:
npm install --save bootstrap
npm install -–save @ng-bootstrap/ng-bootstrap
添加 Bootstrap 后,您需要确保 Angular 应用可以利用它。有两种方法可以做到这一点。一种是将主模块安装到应用的根目录中。另一种方法是为每个模块只添加您需要的模块,以使该模块工作。
为了让您的应用简单,请打开app.module.ts。在这里您可以将 NgbModule 导入到应用中,并将其添加到imports数组中。
完成后,打开angular.json文件。在 styles 部分中,添加以下代码行,以获取所有内置到 Bootstrap 中的 CSS,以便在您的应用中工作:
"styles": [
"src/styles.sass",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node-modules/font-awesome/css/font-awesome.min.css"
]
您的应用现在正处于备份的良好阶段。如果你有一个像 GitHub 或 BitBucket 这样的网站的帐户,你可以为你的代码创建一个新的存储库。
以 GitHub 为例,开源项目可以免费托管。单击 Create repository 按钮,将项目命名为static-site。见图 13-1 。
图 13-1
使用 GitHub 创建公共存储库
一旦创建了存储库,您将获得将您计算机上的文件连接到远程服务器的指令。
当您使用 CLI 创建站点时,Angular 在您的机器上创建了一个本地存储库。这对于跟踪代码中的变化非常有用,但是如果你想和其他人一起做这个项目,这就没什么帮助了。将代码放在远程服务器上的另一个好处是便于自动部署。你可以抓取网站的最新版本,将编译好的代码转移到一个实时服务器上。
当使用命令行连接 GitHub 时,您可能需要配置一个 SSH 密钥。这将允许您登录 GitHub 并控制您的远程存储库。有关如何设置的信息,请参考 GitHub 的 https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/ 。
在命令行中,添加连接到远程服务器的功能:
git add .
git commit -m "first commit"
git remote add origin git@github.com:USER-NAME/static-site.git
下一行将文件复制或“推”到服务器:
git push -u origin master
现在,您应该在远程服务器上有了站点的副本。这也将为您提供一个可视化的参考,让您了解随着时间的推移文件所发生的变化。
Git 作为工作流工具有一个branches的概念。分支只是整个项目的副本。这允许您对项目进行单独的更改。这些更改可以合并到主分支中。
本章并不是 Git 工作流的完整教程;在 www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow 可以找到一个很好的带有视觉参考的解释。
现在您已经将应用和存储库放在一起了,下一节将让您使用 Angular router 和 Bootstrap 来创建站点的两个部分。
使用角度路由器
上一节让您创建一个添加了路由器的全新 Angular 应用。然后添加引导组件,并将整个项目连接到 GitHub 上的公共存储库。
这一节将向你展示如何使用角度路由器为网站创建一个菜单。当您进行更新时,您将使用 Git 跟踪它们。
这个例子将展示一个非常快速的使用角度路由器的方法。更详细的解释可以在 https://angular.io/guide/router 找到。
要创建简单的路由函数,向app.module.ts添加一些代码。首先,从@ angular/router路径导入两个项目。进口RouteModule和Routes。该行应该如下所示:
import { RouterModule, Routes } from '@angular/router';
您还需要导入您的两个可视化组件,PotosComponent和AlbumsComponent:
import { PotosComponent } from '../media/photos/photos.component';
import { AlbumsComponent } from '../media/albums/albums.component';
记住获取这些新导入的组件,并将它们添加到app.module.ts文件中的declarations数组中:
@NgModule ({
declarations: [
AppComponent ,
PhotosComponent,
AlbumsComponent
]
})
之后,制作一个对象数组来代表你的路线。这个数组的数据类型为Routes,并被分配给一个名为appRoutes的变量。路由对象具有预定的属性。这些属性告诉 Angular 路线应该如何工作。
一些属性是
-
path:匹配 URL 的路径 -
patchMatch:匹配 URL 的正则表达式 -
component:当路线被解析时,您希望看到的组件 -
redirectTo:找到路径后需要重定向的地方 -
data:需要分配给该路线的任何数据 -
children:任何子程序,例如/borough/id
您的示例将利用这些属性中的两个,path和component 。
您的路线将如下所示:
const appRoutes: Routes = [
{path: 'photos', components: PhotoComponent}.
{path: 'albums', components: AlbumsComponent}
]
制定这些路线很重要。你可以让他们变得更复杂,让他们处理一些问题,比如如果有人没有 Angular 应用可以识别的 URL。您可以设置默认路线。
设置好数组后,您可以将它传递给imports数组中的RouterModule:
imports: [
RouterModule.forRoot(appRoutes)
]
为了确保您的更新有效,您可以在应用中添加一些按钮,当单击这些按钮时,它们将更新路线。
打开app.component.html文件。这里有您构建应用时生成的默认欢迎页面。您需要删除所有这些代码,并添加一些新的内容。
在前面的例子中,您使用 Bootstrap 来布局页面。这个例子将使用相同的技巧。从一个容器开始,然后定义组成页面的行和列。然后,您需要添加一个由 Angular 定义的定制元素来处理您的路线。
清单 13-1 显示了更新后的页面应该是什么样子。
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h3>Static Site</h3>
</div>
</div>
<div class="row">
<div class="col-1">
<button class="btn btn-primary" routerLink='/photos'>Photos</button>
</div>
<div class="col-1">
<button class="btn btn-primary" routerLink='/albums'>Albums</button>
</div>
</div>
</div>
<router-outlet></router-outlet>
Listing 13-1The Updated Page
这与您在其他示例中使用的布局类型相同。一个很大的区别是你如何使用按钮。该按钮添加了一个名为routerLink的属性。该值被传递给路由器,当发现匹配时,在页面上的router-outlet元素所在的区域对组件进行角度加载。见图 13-2 。
图 13-2
Angular Router 允许您在单页应用中导航
保存文件后,浏览器应该更新,您的按钮应该处于活动状态,能够更改浏览器中的位置并加载适当的组件。
随着文件更新和您的站点提供新功能,现在是保存文件的当前状态并将其保存在存储库中的好时机。
要查看哪些文件已更改,请在命令行中键入
git status
你可以看到到目前为止你所处理的文件列表。下一步是告诉 Git 将这些文件添加到 Git 认为的临时区域中:
git add angular.json
git add package-lock.json
git add package.json
git add src/app/app.component.html
git add src/app/app.module.ts
git add src/app/media/media.module.ts
现在,您已经有了暂存的文件,是时候进行提交了。这是 Git 保存更改的地方,所以它现在有一种方法来比较文件过去的样子和现在的样子。除了提交之外,您还可以添加一条消息来描述此阶段的更改。
要提交,请键入
git commit -m "Angular routing added to the application"
这一行中的-m告诉 Git 您想在提交中添加一条消息。它后面是用引号括起来的消息。
您已提交,但所有更改都保存在本地计算机上。您需要做的是将这些更改保存在远程服务器上。这是通过使用push命令与 GitHub 上的远程存储库共享更新来实现的:
git push
可能会要求您输入 GitHub 密码。然后,更新将被发送到您的存储库中,现在在网站上可以看到这些更改。见图 13-3 。
图 13-3
随着时间的推移对应用的更改
您已经能够将导航添加到您的应用中,并随时跟踪您的更改。现在您可以在本地和 GitHub 存储库中看到这些变化。
下一节将向您展示如何使用角度服务提取每个部分的数据。
使用角度服务
Angular 中的服务允许您将相同的代码注入或使用到多个组件中。这为您提供了工作分离,组件可以接收并显示数据,服务可以发出远程服务的请求并解析结果。
为了让服务能够调用远程服务,主应用需要将HttpClientModule导入并添加到imports数组中。打开app.module.ts文件,导入模块:
import { HttpClientModule } from '@angular/common/http';
现在,您可以指导您的服务进行远程调用。打开service.service.ts *。*在这里,您可以导入HttpClient库进行调用:
import _{ HttpClient } from '@angular/common/http';
使用constructor函数,您创建了您的http实例:
constructor ( private http: HttpClient ) { }
最后,添加两个方法,一个获取所有照片,另一个获取所有相册:
getAlbums() {
return this.http.get('https://jsonplaceholder.typicode.com/albums ');
}
getPhotos() {
return this.http.get('https://jsonplaceholder.typicode.com/photos ');
}
服务的一个好处是可以在多个组件中使用它们。在下一个示例中,您将服务注入到组件中。这两个组件的工作方式相同。每个组件都需要导入服务,并在构造函数中创建一个实例。
下一个例子展示了如何使用这个服务在PhotosComponent中检索照片。该服务也可以以同样的方式在AlbumsComponent内部使用。
在构造函数中已经创建了service对象。当组件运行生命周期方法ngOnInit时,服务将运行方法getPhotos并将结果返回给组件。在AlbumsComponent的情况下,它将运行方法getAlbums。
部分PhotosComponent代码应该是这样的:
constructor (private: service: ServiceService) {}
ngOnInit() {
this.service.getPhotos().subscribe( (results) => {
this.photoResults = results;
});
}
现在可以用 web 服务的结果更新 HTML 模板了。使用*ngFor指令遍历结果数组,显示从服务返回的标题和缩略图。见清单 13-2 。
<div class="container-fluid">
<div *ngFor="let photo of photosResult" class="row">
<div class="col-1">
<p>Title</p>
</div>
<div class="class=col-6">
<p>{{photo.title}}</p>
</div>
<div class="col-2">
<img src="{{photo.thumnailURL}}">
</div>
</div>
</div>
Listing 13-2The HTML Template
这个例子展示了如何在结果中创建一个for循环并获取细节。这些细节将生成创建所需的所有行和列所需的所有 HTML。需要指出的重要一点是图像的生成。因为 URL 是由 web 服务调用的结果生成的,所以您可以将该值应用于图像的来源,并且该图像将在浏览器中呈现。见图 13-4 。
图 13-4
调用 photos API 的渲染结果
您的站点现在有一些路线,您可以通过使用按钮或键入 URL 来直接访问。这些路由呈现使用相同服务类的组件,以调用您的数据源并在屏幕上呈现结果。
您对一些文件进行了更新以使其工作。现在是记录这些变化的好时机。回到命令行,使用相同的 git 命令保存应用的当前状态。
首先,检查已更改文件的状态:
git status
如果所有已更改的文件都在同一个文件夹中,例如src文件夹,您可以使用快捷方式转移它们:
git add src
如果更新的文件在其他文件夹中,您必须添加这些文件的路径。如果你愿意,你可以再次检查状态,看看是否所有的文件现在都在隐藏中。
接下来,您提交:
git commit -m "Routing and Service added"
最后,您将更改推送到服务器:
git push
像前面的例子一样,这会将所有文件从您的本地机器复制到 GitHub 服务器,并记录对这些文件所做的所有更改。
现在,您既有了一个工作站点,又有了站点工作所需文件的备份。有了 GitHub 上的源代码,您现在可以将它连接到一个服务,该服务将部署您的应用,并使它对全世界可用。
下一节将讨论如何连接到 Netlify,这是一种旨在提供应用前端的服务,也称为静态站点。
将静态站点部署到网络
Netlify ( 对于在个人网站上工作的人来说,有一个免费层。一旦注册,开发者可以使用他们已经拥有的由该服务开发的域名,或者购买一个他们可以通过该服务自行管理的域名。
部署一个站点使你能够指向你自己的当前在 GitHub 中的源代码。让服务复制文件,构建项目,并将最终结果推送到一个具有公共地址的服务器上,在那里每个人都可以看到您的站点。
浏览器窗口的右上角是一个名为“来自 Git 的新站点”的按钮。点击它。下一个屏幕询问源代码的位置。这就是为什么把它从本地机器转移到像 GitHub 这样的服务上是很重要的。选择您正在使用的服务。Netlify 将请求您允许访问您在其他服务上的帐户。一旦授予了权限,您就可以指向您创建的用来保存静态站点的存储库。见图 13-5 。
图 13-5
创建新站点时,您需要指向代码存储库
当选择包含所有代码的存储库时,Netlify 会询问您想要部署哪个分支。在这种情况下,您只有一个分支,所以它是主。它还会询问应该运行什么命令来编译应用。在本例中,您使用的是 Angular,所以命令是ng build。请记住,您需要构建网站,以便所有代码都可以转换为浏览器可以理解的 JavaScript。它需要知道的最后一件事是在哪里找到完成的站点。这个在dist/static-app文件夹里。见图 13-6 。
图 13-6
将 Netlify 引导到构建站点,并指出在哪里可以找到完成的文件
给它几分钟,Netlify 会给你一个网址,你可以在那里看到你的生活网站。然后,您可以在浏览器中查看该 URL,甚至可以将该 URL 发送给朋友,他们可以在那里看到您网站的实时版本。
现在您的帐户和项目已经运行,您可以使用该服务提供的一些其他功能,如设置自定义域或为您的站点添加安全性的能力。
摘要
这一章在一个非常基础的层面上讲述了构建一个 web 应用的生产过程。开发人员在本地机器上工作,从远程数据源检索数据,并显示结果。组成项目的文件保存在一个存储库中,其中包含一段时间内的更改历史。最后,文件被转移到一个服务中进行编译,最终版本被发布到网站现在所在的服务器上。
您的 Angular 应用使用 routes 来更新浏览器中的 URL,并按需显示某些组件。您还创建了一个服务,它负责向 web 服务发出请求,并将结果发送回组件,以便组件可以在浏览器中呈现结果。
随着时间的推移,保存文件的当前状态是一个好主意。这是通过 Git 这样的版本控制软件完成的。使用 Git 这样的工具不仅可以让您了解文件是如何随时间变化的,而且当项目在公共存储库中时,您还可以与其他人共享这些文件。当你在你的项目中工作时,当你觉得你处于一个好的位置时,这是一个用 Git 保存文件当前状态的好时机。 www.atlassian.com/git/tutorials 有非常好的教程。
在开发项目并跟踪源代码之后,是时候部署应用了。在本章中,您使用了一个名为 Netlify 的服务。还有其他方法来部署你的站点,但是本章的重点是展示如何获取源代码并自动将完成的版本部署到服务器上。