一. 简单HTTP服务器
http服务作为前端人员来说,比较熟悉,具体就不介绍了。
(1)通过浏览器来访问,请求资源的方式
下载:
npm install http
// or
yarn add http
代码:
const http = require("http")
http.createServer(function(request, response) {
console.log(request.url)
if(request.url == '/favicon.ico') {
response.writeHead(200)
request.end();
return
}
response.writeHead(200);
res.setHeader('Content-Type', 'text/plain;charset=utf-8');
response.end("你好")
})
.listen(3000)
在浏览器输入localhost:3000
,从这段代码运行结果种,我们可以发现打印出了/
和/favicon.ico
,实际我们是访问了/
,返回了你好的文本,如果返回的是中文需要设置setHeader;后面那个是浏览器的默认行为,会去寻找网页显示的图标
如果我们想要根据url返回文件,则
const http = require("http")
const fs = require('fs')
http.createServer(function(request, response) {
console.log(request.url);
if(request.url == '/favicon.ico') {
response.writeHead(200)
response.end();
return
}
response.writeHead(200);
fs.createReadStream(__dirname + '/pages/hello.html').pipe(response)
})
.listen(3000)
使用fs读取html文件流,__dirname 表示当前目录,并将结果通过pip管道传输给response
(2)请求url中带有参数
这时候的request.url是包含请求路径,请求参数,所以我们需要使用url来获取
const url = require('url')
console.log(url.parse(request.url))
//访问: localhost:3000/test?id=1结果
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?id=1',
query: 'id=1',
pathname: '/',
path: '/?id=1',
href: '/?id=1'
}
从打印结果可以得到:url.pathname才是路径,url.query可以获取参数,但是又需要解析,这时候引入querystring
const querystring = require("querystring")
const query = querystring.decode(url.query)
const id = query.id
二. Express
1. 介绍
(1)express是什么呢?
npm expree 官网的解释如下:
- Robust routing
- Focus on high performance
- Super-high test coverage
- HTTP helpers (redirection, caching, etc)
- View system supporting 14+ template engines
- Content negotiation
- Executable for generating applications quickly
- 健壮的路由功能(一个请求包通过服务器分发的过程称为路由)
- 专注高性能
- 高测试覆盖率
- http重定向功能(302)
- 模板引擎
- accept内容协商
- 提供强大脚手架
其实就是一个独立的路由和中间件web框架
(2)下载
cnpm install express --save
(3)代码
const express = require("express");
const app = express();
const url = require('url')
const querystring = require("querystring")
app.get("/favicon.ico", function(req, res) {
res.writeHead(200);
res.end();
return;
})
app.get("/test", function(req, res) {
const query = req.query
res.setHeader('Content-Type', 'text/plain;charset=utf-8');
res.writeHead(200);
// 可以用res.status(200)代替
res.end("id" + query.id);
// 可以用res.send("....")代替
return;
})
app.listen(3000)
我们可以直接使用url匹配,获取参数也不需要querystring了。如果是返回一个html文件,则如下所示:
app.get("/test", function(req, res) {
res.status(200);
res.send(fs.readFileSync(__dirname + '/pages/hello.html', "utf-8"));
})
那么,问题来了:
2. res.end, res.send, res.json的区别在哪呢?
- res.end()
Ends the response process. Use to quickly end the response without any data.
用于快速响应,不需要数据的情况,比如res.status(404).end()
- res.send([body])
Sends the HTTP response. The
body
parameter can be aBuffer
object, aString
, an object, or anArray
.
发送http响应,body部分可以是buffer对象,string字符串,一个对象,或者是一个数组
res.send({ some: 'json' })
res.send('<p>some html</p>')
res.status(404).send('Sorry, we cannot find that!')
res.status(500).send({ error: 'something blew up' })
这个方法对简单的的非流式响应,执行了有用的任务,例如,它自动分配Content-Length HTTP响应标头字段(除了先前定义),并提供自动的HEAD和HTTP实时缓存支持。
(1) 如果是一个buffer对象,则默认设置了Content-Type
为application/octet-stream
除非我们自定义了:
res.set('Content-Type', 'text/html')
res.send(Buffer.from('<p>some html</p>'))
(2) 如果是一个string,则默认设置了Content-Type
为text/html
res.send('<p>some html</p>')
(3)如果是一个数组或者对象,则默认设置了Content-Type
为application/json
res.send({ user: 'tobi' })
res.send([1, 2, 3])
- res.json()
发送JSON响应, 此方法发送响应(具有正确的内容类型),发送的是json字符串。这个和上面的res.send的第三种方式一样,都是将Content-Type
设为application/json
,但是区别是,可以将其他类型,比如: object, array, string, Boolean, number, 或者 null
res.json(null)
res.json({ user: 'tobi' })
res.status(500).json({ error: 'message' })
①res.json("1")
②res.send("1")
3. express中间件和next()
中间件是指可以访问请求对象request和响应对象response以及next()应用程序请求。
next表示可以继续执行下一个中间件。
这些中间件的执行就像洋葱一样:
const express = require('express')
const app = express()
app.use(function middleware1(req, res, next) {
console.log('1 开始')
next()
console.log('1 结束')
})
app.use(function middleware2(req, res, next) {
console.log('2 开始')
next()
console.log('2 结束')
})
app.get('/', function handler(req, res) {
res.send('ok')
console.log('=================')
})
app.listen(8080)
执行结果:
1 开始
2 开始
=================
2 结束
1 结束
express的缺点就是不能很好处理异步请求的函数,我们可以来看一下,如果在中间件中执行了异步函数
let count = 0;
app.use(function middleware1(req, res, next) {
console.log('1 开始')
next()
if(res.win) {
count++;
}
console.log('1 结束')
console.log(count)
})
app.use(function middleware2(req, res, next) {
console.log('2 开始')
setTimeout(() => {
console.log("异步函数执行完了");
res.win = true
}, 500)
next()
console.log('2 结束')
})
app.get('/', function handler(req, res) {
res.send('ok')
console.log('=================')
})
app.listen(8080)
执行结果:
1 开始
2 开始
=================
2 结束
1 结束
0
异步函数执行完了
我们可以看出这个next并不会等待异步函数执行完再去执行下一步,而是直接结束,而异步函数通过一个新的调用栈去这行这个代码,导致最终获取的count并没有被加上1。如果再加上延时获取数据的代码,则发现可以加1。
app.use(function middleware1(req, res, next) {
console.log('1 开始')
next()
if(res.win) {
count++;
}
console.log('1 结束')
console.log(count)
setTimeout(() => {
if(res.win) {
count++;
}
console.log(count);
}, 1000)
})
执行结果:
1 开始
2 开始
=================
2 结束
1 结束
0
异步函数执行完了
1
为了解决这个缺点,就诞生了koa。
三、Koa
Koa is a middleware framework that can take two different kinds of functions as middleware:
- async function
- common function
官方解释,这是个koa中间件框架,主要有两大功能:
- 使用async function 实现的中间件
- 有“暂停执行”的能力
- 在异步的情况下也能符合洋葱模型
- 有conntext, request, response处理(采用context.response, context.request访问)
- 精简内核,所有额外功能都移动到中间件里实现,提高程序的灵活性和性能
跟express不同的是,去掉了路由,如果想要,我们可以安装koa-mount,作用是:挂载其他Koa应用程序作为中间件。
安装
npm i koa koa-mount
代码
const koa = require('koa')
const mount = require('koa-mount')
const fs = require("fs")
let count = 0;
const app = new koa()
app.use(
mount('/favicon.ico', function(ctx) {
ctx.sttaus = 200;
})
)
app.use(
mount('/test', function(ctx) {
ctx.status = 200
ctx.body = fs.readFileSync(__dirname + '/pages/hello.html', 'utf-8')
}, )
)
app.listen(3000)
我们可以在mount中设置对应的koa实例,在koa实例中使用更多的中间件
const koa = require('koa')
const mount = require('koa-mount')
const fs = require("fs")
let count = 0;
const app = new koa()
const gameKoa = new koa()
gameKoa.use(
async function(ctx, next) {
console.log('1 开始')
await next()
if(ctx.response.win) {
count++;
}
console.log('1 结束')
console.log(count)
}
)
gameKoa.use(
async function(ctx, next) {
console.log('2 开始')
await new Promise((resolve, reject) => {
setTimeout(() => {
ctx.status = 200
ctx.body = 'yes'
console.log("异步函数执行完了");
ctx.response.win = true
resolve()
}, 500)
})
console.log('2 结束')
next()
}
)
app.use(
mount('/game', gameKoa)
)
app.listen(3000)
执行结果:
1 开始
2 开始
异步函数执行完了
2 结束
1 结束
1
这时候count的值达到了我们预期想要的结果
Express和Koa的比较
① express 门槛更低,koa更强大优雅
② express封装更多东西,开发更快速,可定制型更高
四 、express-generator
express-generator相当于express的脚手架工具,对应kao也有koa-generator,那么接下来,
就来讲一下如何使用express-generator运行打包后的react项目
4.1 安装
npm install express
npm install -g express-generator
4.2新建
①express demo
不设定模板,view目录下文件默认使用jade模板
②express --view=pug ./temp/demo
切换到./temp/demo,view目录下的文件默认使用pug模板
③express demo -e
view目录下文件默认使用ejs模板
4.3目录结构
—demo
——bin // 其中有www启动文件,可以设置启动端口号
——public // 存放静态文件
——routes // 路由定义url和资源的映射
——views // 放置模板文件
——app.js // 入口文件
——package.json //依赖
4.4react的打包
(1)homepage配置
在package.json
文件中配置
"homepage": "/"
设置为在服务器的那个目录下
(2)url配置
在url的请求中配置,生产环境为具体的地址
(3)配置打包路径
使用react-app-rewired
,可以在不eject暴露所有配置文件的情况下,更改配置文件
在根目录下的config-overrides.js文件中
const {
override,
fixBabelImports,
addLessLoader,
} = require("customize-cra");
module.exports = override(
fixBabelImports("import", {
libraryName: "antd",
libraryDirectory: "es",
style: "css"
}),
addLessLoader({
strictMath: true,
noIeCompat: true,
localIdentName: "[local]--[hash:base64:5]" // if you use CSS Modules, and custom `localIdentName`, default is '[local]--[hash:base64:5]'.
}),
// 在此处配置
(config) => {
const path = require('path');
const paths = require('react-scripts/config/paths');
// 修改配置里的appBuild目录
paths.appBuild = path.join(path.dirname(paths.appBuild), 'manager');
// 修改项目打包地址
config.output.path = path.join(path.dirname(config.output.path), 'manager');
return config;
}
);
使用yarn build
打包,即可发现有了manager文件
在serve.js中配置
const express = require('express')
var path = require('path');
var proxy = require('http-proxy-middleware')
const app = express()
var targetPath1 = "https://i.cn"
var targetPath2 = "https://i:2443"
var proxyOption1 = {
target: targetPath1,
changeOrigoin: true,
ws: true,
}
var proxyOption2 = {
target: targetPath2,
changeOrigoin: true,
ws: true,
pathRewrite: { '/api': '/' }
}
app.use(express.static(__dirname, "/manager"));
app.use('/micro-course', proxy.createProxyMiddleware(proxyOption1))
app.use('/api', proxy.createProxyMiddleware(proxyOption2))
app.get('*', (req, res) => res.sendFile('index.html', { root: __dirname + '/manager' }))
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500);
res.render('error');
});
app.set('port', 3000)
app.listen(3000, function() {
console.log('Listening on port 3000');
});