寒叶笔记

React 牵手 Express

仅仅有了 API ,功能还不完全。需要有前端来调用 API ,完成 整个小功能。

在我们《JS 独孤求败》课程体系里面,前端代码我们都是用 React 来写, 所以前端和后端都是用 JS 来写。但是这里要提醒大家的是,前端和后端是完全独立的两个项目,通过 API 来作为桥梁。其实,我们后端虽然是 Nodejs 写的,但是前端用 PHP/JAVA 也能写。

前端项目准备

前面的课程中,我们已经学习了 React 开发。那么先在就来写一个完全跟后台无关的 React 的 Hello World ,要求:

  • 页面最终显示出来的,就是你的用户名,例如 luckyhanye

  • 要用到 react 的 state ,constructor ,生命周期

  • 用 ES6/Babel/Webpack

创建 React 应用的脚手架项目 可以推荐的是 https://github.com/facebookincubator/create-react-app 但是,我们需要对 Webpack 底层做一些了解,所以暂时不推荐使用 脚手架 小贴士结束

所有代码都放到 react-frontend 这个文件夹中,代码如下:

src/index.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';


class App extends Component {
  constructor(){
    super();
    this.state = {
      username: ''
    };
  }
  componentWillMount() {
    this.setState({username: 'happypeter'});
  }
  render(){
    return(
      <div>
        {this.state.username}
      </div>
    )
  }
}

ReactDOM.render(<App/>,document.getElementById('app'));

package.json

{
  "name": "hanye-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "pack": "./node_modules/.bin/webpack",
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.23.1",
    "babel-loader": "^6.3.2",
    "babel-preset-env": "^1.1.8",
    "babel-preset-react": "^6.23.0",
    "webpack": "^2.2.1",
    "webpack-dev-server": "^2.4.1"
  },
  "dependencies": {
    "react": "^15.4.2",
    "react-dom": "^15.4.2"
  }
}

webpack.config.js

module.exports={
  entry:'./src/index.js',
  output:{
    path:'build',
    filename:'bundle.js',
    publicPath:'build/'
  },
  devtool: 'eval',
  resolve: {
    extensions: ['.js','.jsx','.css','.jpg','png']
  },
  module: {
    rules: [
      { test: /.js$/, exclude: /node_modules/, use: "babel-loader" }
    ]
  }
};

.babelrc

{
  "presets": ["env","react"]
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>

  <div id="app"></div>
  <script src="./build/bundle.js" charset="utf-8"></script>
</body>
</html>

为何 state 要设置两次

上面代码都是 React 的基础,我们不在重复,唯一可能感觉奇怪的是为何 constructor 中设置了一个 username 的初始值,然后又在生命周期函数 componentWillMount 中对 username 进行了覆盖。

为何要这么麻烦呢?这个后面结合 axios 向后台请求数据的代码,就会比较容易看出作用了。

下面来看如何发请求到服务器端。

实现API

开发一个功能,好的做法是,先修改后端代码,也就是先实现API,然后下一步就是用curl这样的命令,来测试一下API,发现API没毛病,然后在动手去屑前端代码。

打开express-backend中的index.js代码,添加下面这个API

app.get('/username',function(req,res){
  res.send({username:'luckyhanye'})
})

用curl 来测试API

curl 是一个安装在系统上的命令,可以用来发http请求,最适合用来测试API

动手写前端代码之前,如果用curl测试一下API,会让写前端代码时候心里更踏实;

curl -X GET http://localhost:3000/username

如果后端代码没问题,应该可以看到下面的输出:

{username:'luckyhanye'}

这样,后端API测试通过

如果执行出现如下错误

Failed to connect to localhost port 3000: Connection re 大专栏  寒叶笔记fused //链接localhost:3000失败,链接被拒绝 错误原因:后端未启动

安装 axios 发送 http 请求

引入axios。axios 按照官网的说法,他是一个http client(http 的客户端),换句话说,他是专门用来发http请求的

axios 是常用的发 http 请求的工具(现在一般不提发 ajax 请求这个说法了)。

首先来进行装包:

$ npm install --save axios

把 axios 安装到 react-frontend 这个项目中。

装包之后,就可以到 src/index.js 中去使用了,代码如下

import axios from 'axios';

我们当前的请求不希望是通过按钮来触发,而是希望,页面加载的时候,自动发出 http 请求,向服务要数据,所以,代码非常适合写到生命周期函数中:

componentWillMount() {
  axios.get('http://localhost:3000/')
  .then(res=>console.log(res))
}

跨域请求 Access-Control-Allow-Origin

代码进行到上面,浏览器中用前台请求后台,会在 chrome console 中看到,如下 错误:

XMLHttpRequest cannot load http://localhost:3000/. No 'Access-Control-Allow-Origin'
header is present on the requested resource.
Origin 'null' is therefore not allowed access.

XMLHttpRequest 是发 HTTP 请求的底层机制,是浏览器自带功能。上面的报错翻译如下:

无法加载后台 http://localhost:3000 . 被请求的资源中没有设置 Access-Control-Allow-Origin 头部。源头设置为 Null ,所以不允许 访问。

Access-Control-Allow-Origin 字面意思:访问控制允许来源。服务器上的默认是不允许其他网址(或者网址相同,但是端口号不同)的网站请求资源的(也就是默认不允许跨域请求),如果需要开通权限,就需要设置这个选项。

那么,如何开通服务器上的这个资源访问权限呢?就是要在服务器上做下面的设置

Access-Control-Allow-Origin: *

有了这个设置,所有的第三方网站都可以访问服务器上的资源了。

跨域请求的解决方案

解决方案采用: https://github.com/expressjs/cors

cors 是 Cross Origin Resource Share(译:跨域资源共享) ,安装了这个包就可以完成

Access-Control-Allow-Origin: *

这个设置了。

保证后台启动状态,现在我们看看当前后台资源的 header 设置,可以用下面的 curl 命令

$ curl -I http://localhost:3000/username
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 11
ETag: W/"b-sQqNsWTgdUEFt6mb5y4/5Q"
Date: Thu, 08 Dec 2016 01:51:44 GMT
Connection: keep-alive

curl 的 -I 选项用来拿到服务器返回的 header 。命令返回的信息,就是服务器端被请求资源的的 header 。 很明显是没有 Access-Control-Allow-Origin 这一项的。下面我们安装 cors 这个包,就可以解决这个问题。

具体步骤如下:

到 https://www.npmjs.com/package/cors 可以看到装包命令如下:

$ npm install --save cors

再次提醒:这个包要安装到后台代码中。

cors = require('cors')

app.use(cors());

然后按照文档,添加下面两行代码,再重启服务器代码:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 11
ETag: W/"b-sQqNsWTgdUEFt6mb5y4/5Q"
Date: Thu, 08 Dec 2016 02:17:23 GMT
Connection: keep-alive

这样,我们就看到了 Access-Control-Allow-Origin: * 。

浏览器中,刷新一下,可以看到后台返回的 response 数据了。错误没有了。

调整接口数据格式

后台代码中,添加下面两个 API

app.get('/username', function(req, res){
  res.json({"username": "happypeter"});
})

对应,到前台代码中,调整 componentWillMount ,如下:

componentWillMount() {
  axios.get('http://localhost:3000/username').then(function(response){
      return console.log(response.data.username);
  })
}

这样,前台的 console 中,就可以看到返回数据

luckyhanye

下面进一步调整 componentWillMount 如下:

componentWillMount() {
  axios.get('http://localhost:3000/username').then(function(response){
      return this.setState({username: response.data.username});
  })
}

重新 build 代码,然后浏览器中运行,报错:

bundle.js:89 Uncaught (in promise) TypeError: Cannot read property ‘setState’ of undefined(…)(anonymous function) @ bundle.js:89

上面的错误,就意味着 this 没有被定义。这个是一个 JS 基础问题,跟 React 没有 直接联系。相关知识,需要理解 bind(this) 才能透彻理解这个问题。

bind(this) 的视频: http://haoqicat.com/o-o-js/2-2-this

这里呢,我们直接给出解决方法,就是使用 ES6 的箭头函数:

componentWillMount() {
  axios.get('http://localhost:3000/username').then((response) => {
      this.setState({username: response.data.username});
  })
}

总结

至此,前台页面上成功显示出了,后台的数据。这样,一个前后端分离架构,通过 API 通信的应用,我们就完成了。

猜你喜欢

转载自www.cnblogs.com/liuzhongrong/p/11874907.html