打通全栈
所谓全栈,有下面几个技术层:
- 最贴近用户的 — React
- HTTP 请求 — axios
- 后台 — express
- 最底层,海量数据 — Mongodb
在这里我们打通了三层技术
还有这里我们get了 MongoDB
啦啦啦,这里我们就来综合使用以上两部分知识来打通全栈
,有了一个小目标,下面就分几个具体任务来完成
使用 Mongoose 来查询所有用户
首先,保证 mongodb 处于运行状态,然后,通过 mongo-express 查看一下, mongodb 中是否有多个用户。
Mongoose 的 API 文档超级好,只是还不适合我这个菜鸟,不过没关系,还有这个
到后台代码的 index.js 文件中,把 db.once 部分的修改成下面这样:
db.once('open', function() {
User.find().exec(function(err, users) {
console.log(users);
});
});
这样,我们到 express-backend 文件夹中,命令行运行,可以看到如下输出结果
$ nodemon index.js
running on port 3000...
[ { _id: 584b62b830a2a2cbf4c4c3f6,
username: 'ljm',
email: 'ljm@ljm.com' },
{ _id: 584bb045ff8f0f1c7ba4fe24,
username: 'fightingljm',
email: 'fightingljm@fightingljm.com',
__v: 0 } ]
可以看到,终端中可以打印出所有数据文档。证明我们的 mongoose 的 find() 接口 使用正确。
注意:这里 find() 接口是一个
异步函数
,所以它的返回值users只能在回调函数
中使用。.exec
字面意思就是执行
,我们把回调函数传给它做参数。所以不能写成这样—“let users = User.find();conole.log(users)”
About 同步和异步,It’s a very big subject !
用 API 来返回 JSON
上面数据虽然拿到,但是如果想提供给客户端使用还需要做到:
- 1 把它要封装成一个 API
- 2 数据格式转换为 JSON
先来做第一步,代码做出如下调整:
db.once('open', function() {
console.log('success');
});
app.get('/users', function(req, res){
User.find().exec(function(err, users) {
console.log(users);
});
})
上面把 User.find() 代码封装到了一个 API ( Web API ) 。这样, 触发条件就变了。只有当客户端发出 GET /users
请求时,User.find() 代码才会被执行。
之后要用 curl
来模拟一下客户端请求,来测试以下能不能收到反馈信息
$ curl -X GET http://localhost:3000/users
执行命令发现 curl 请求不到任何返回信息,因为 console.log(users)
只会把信息打印到后台终端。curl 请求不到信息,未来浏览器也就请求不到。所以要把这一行 改为
// res.send() 可以数据返回给客户端,但是我们要的是 json ,所以用下面接口
res.json()
即…
app.get('/users', function(req, res){
User.find().exec(function(err, users) {
res.json({users});
});
})
改完之后再用 curl 请求一下,前台就能读到下面的数据
$ curl -X GET http://localhost:3000/users
{"users":[{"_id":"584b62b830a2a2cbf4c4c3f6","username":"billie66","email":"billie@billie66.com"},{"_id":"584b760498d7b520b68a05cd","username":"pppaaa"},{"_id":"584bb045ff8f0f1c7ba4fe24","username":"inCode","email":"inCode@incode.com","__v":0}]}
这样,后台代码就大功告成辣。
来来来,瞅瞅完整的后台代码
express-backend 文件夹中,有下面的文件:
index.js
const express = require('express');
const app = express();
//解决跨域请求
const cors = require('cors')
app.use(cors());
//使用 Mongoose 连接 JS 和 MongoDB
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/express-react-demo');
// 执行此行代码之前,要保证 mongodb 数据库已经运行了,而且运行在 27017 端口
const User = require('./models/user')
let db = mongoose.connection;
db.on('error', console.log);
db.once('open', function() {
console.log('success!')
});
// 下面三行就是我们实现的一个 API //GET /users
app.get('/users',function (req,res) {
// res.send({username:'fightingljm'});
User.find().exec(function(err, users) {
// console.log(users);
res.json({users});
})
})
app.listen(3000,function () {
console.log('running on port 3000...');
})
models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const UserSchema = new Schema(
{
username: { type: String },
email: { type: String }
}
);
module.exports = mongoose.model('User', UserSchema);
// `User` 会自动对应数据库中的 users 这个集合
// 如果这里是 Apple 那么就会对应 apples 集合
// 如果这里是 Person 那么就会对应 people 集合
package.json
{
"name": "express-backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.1",
"express": "^4.14.0",
"mongoose": "^4.7.2"
}
}
前台书写 axios 请求
到前台项目 react-frontend
中,修改生命周期函数如下:
componentWillMount() {
axios.get('http://localhost:3000/users')
.then(response => console.log(response))
}
这样,就可以看出 response 中的数据结构了,我们想要的数据可以这样拿到
constructor(){
super();
this.state = {
users: []
};
}
componentWillMount() {
axios.get('http://localhost:3000/users')
.then((response) => this.setState({users: response.data.users}))
}
在 render 函数中
render(){
return(
<div>
{this.state.users}
</div>
)
}
打开浏览器发现可恶的 error
Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {_id, username, email}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `App`.(…)
报错信息的大致意思是:对象不是被允许的 React 子元素。
小 case ……使用 map 展开数组就好辣,let me try
render 函数做如下调整:
render(){
const userList = this.state.users.map((user, index) => {
return (
<div key={index}>
username: {user.username}
</div>
)
})
return(
<div>
{ userList }
</div>
)
}
看到了吧,页面中就可以显示出所有用户的用户名的列表了。
Give you 瞅瞅 my whole 前台代码
src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class App extends Component {
constructor(){
super();
this.state = {
users: []
};
}
componentWillMount() {
axios.get('http://localhost:3000/users')
.then((response) => {
this.setState({users: response.data.users});
})
}
render(){
const userList = this.state.users.map((user, i) => {
return (
<div key={i}>
username:
{user}
</div>
)
})
return(
<div>
{ userList }
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('app'));
webpack.config.js 如下:
module.exports = {
entry:'./src/index.js',
output:{
path:'build',
filename:'bundel.js',
publicPath:'build/'
},
devtool:'eval',//报错到源代码
resolve: {
extensions: [".js", ".jsx",".css",".jpg"],
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,//排除node_modules文件夹
use: "babel-loader"
},//js文件用babel加载
{
test: /\.css$/,
use: ['style-loader','css-loader']
},//css文件的加载,解析文件样式
{
test: /\.(jpe?g|png)$/,
use: 'file-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>
package.json
{
"name": "react-frontend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "./node_modules/.bin/webpack && http-server ."
},
"repository": {
"type": "git",
"url": "git+https://github.com/fightingljm/1610-demo.git"
},
"keywords": [
"react"
],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/fightingljm/1610-demo/issues"
},
"homepage": "https://github.com/fightingljm/1610-demo#readme",
"devDependencies": {
"babel-core": "^6.23.1",
"babel-loader": "^6.3.2",
"babel-preset-env": "^1.1.8",
"babel-preset-react": "^6.23.0",
"css-loader": "^0.26.1",
"file-loader": "^0.10.0",
"style-loader": "^0.13.1",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.4.1"
},
"dependencies": {
"axios": "^0.15.3",
"lodash": "^4.17.4",
"react": "^15.4.2",
"react-addons-css-transition-group": "^15.4.2",
"react-dom": "^15.4.2",
"react-router": "^3.0.2"
}
}