什么是服务端:
- 服务端,又称后端、server 端
- 前端是用户可见、可操作的部分,如树枝树叶
- 服务端为前端提供“支撑”和“营养”,如树根
服务端的职责:
- 提供前端要展示的数据
- 接收前端要提交的数据
- 存储数据
服务端的表现形式:
- 前端 ajax 要调用某个接口
- 这个接口,就是服务端提供的
- 如 get 请求获取数据,post 请求提交数据
服务端处理数据返回前端的返回格式:
- errno: 0 (即 error number 简写)
- data: { …… }
- message: “……”
路由是什么:
- router
- 作用是定义服务端的入口规则,入口即规则
- 路由定义 method,如 GET / POST
- 定义 url 规则,如 /api/list 或 /api/add
- 定义输入(Request body)和输出(Response body)格式
Chrome V8 引擎:
- Chrome 是一个浏览器,他可以执行 js 代码
- V8 就是 chrome 的 js 引擎,以速度著称
- nodejs 也是基于 js 语法的,因此也可以借用 V8 引擎
运行时:
- 代码的运行环境
- 有了运行时,代码才能被执行
- 没有运行时,代码就是一堆静态文本,就像 txt 一样
NodeJs 出现之前:
- nodejs 出现之前,只有浏览器可以执行 js 代码
- 浏览器主要是显示网页,所有的 js 也被当做网页的一部分
- 除此之外,没有其它应用场景,更不用说做服务端
NodeJs 出现之后:
- 除了浏览器,nodejs 又是一个新的 js 运行时
- 哪里安装 nodejs,哪里就可以运行 js 代码
- 可以用在本机(如使用 webpack 打包),也可以做服务端
如何使用 NodeJs 做服务端:
- 安装 nodejs
- 编写 js 代码(处理 http 请求)
- 使用 nodejs 执行 js 代码
node -v
npm -v
// 在控制台编写执行 js 代码,使用 node 命令进入 js 运行环境
node
// 在控制台输入 node + js文件,是执行该 js 文件的代码
node index.js
npm install -g cnpm --registry=https://registry.npm.taobao.org
// 安装 n
npm install -g n
// 查看版本
n -V
// 安装最新稳定版本
n stable
// or 安装指定版本
n 9.10.0
npm 是什么:
- node package manager,即 nodejs 软件包管理者
- nom 官网:https://www.npmjs.com/
- 有几百万的软件包,开源免费
开始使用 npm:
- npm 会随着 nodejs 一起被安装
- 通过 npm init 初始化环境
- 使用 npm install –save lodash 安装 lodash
- 使用 npm install –save-dev nodemon 安装 nodemon
使用 node 要手动重启服务,而使用 nodemon 会自动重启服务
"script": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon index.js"
}
- -save 和 –save-dev 的区别:
- --save 是生产环境和开发环境都使用到的包;--save-dev 中的包只在开发环境中使用
- --save 安装的软件包需要在代码中直接使用,会放在 dependencies 里
"dependencies": {
"lodash": "^4.17.15"
}
"devDependencies": {
"nodemon": "^2.0.2"
}
sudo npm install -g nrm
npm install -g nrm
nrm -V
// or
nrm --version
nrm ls
nrm test 镜像源名称
nrm use 镜像源名称
nrm current
nrm add 镜像源名称 镜像源链接
nrm del 镜像源名称
ES6 Module 回顾:
- export default 和 import … from … :一个模块只能有一个 export default
- export 和 import … from … :export 后面不能直接跟值,必须是声明或语句;导入时的名称不能随便取,必须与导出时的名称一致,且用花括号包裹名称
CommonJS 语法介绍:
- commonjs 使用 module.exports 输出内容
- 使用 require(…) 引入内容
- commonjs 主要用于 nodejs 开发
require(…) 的三个层级:
- 系统自带模块,如 require(‘http’)
- npm 包,如 require(‘lodash’)
- 自定义模块,如 require(‘./utils’);自定义模块一般要写相对目录用于区分
- require 会先判断是不是系统自带模块,然后再判断是不是 npm 引入的包,最后才判断是不是自定义模块
CommonJS 和 ES6 Module 的区别:
- 两者的语法不一样
- commonjs 是执行时引入,是动态的,可以在使用到的地方再引入
- ES6 Module 是打包时引入,是静态的 ,必须在代码最外层提前引入
为何要使用模块化:
- 模块拆分开,便于代码的组织和管理
- 便于多人协作开发,各写各的互不干扰
- 成熟的语言都支持模块化,如:C、C++、Java、PHP、python等
commonjs 使用示例:
// utils.js
// 单个
function sum(a, b) {
return a + b;
}
module.exports = sum
// 多个
function sum(a, b) {
return a + b;
}
function test() {
console.log("this is test");
}
module.exports = {
sum,
test
}
// index.js
// 单个
const sum = require('./utils.js'). // .js 可写可不写
const result = sum(10, 20);
console.log(result); // 30
// 多个
const { sum, test } = require('./utils');
const result = sum(10, 25);
console.log(result); // 35
test(); // this is test
什么是 debug:
- bug 即错误
- debug 即排错,也叫调试
- 编程语言必须有成熟的 debug 机制,否则将不可用
debug 的重要性:
- 程序出现 bug 很常见,因此 debug 也很常用
- 使用 debug 才能快速定位出错的位置
inspect 调试法:
- 修改 scripts,增加 –inspect,启动服务
// 9229 是默认端口号
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon --inspect=9229 index.js"
}
// 示例
const http = require('http')
const a = undefined
const server = http.createServer((req, res) => {
debugger
const url = req.url
const path = url.split('?')[0]
a()
res.end(path)
})
server.listen(3000)
nodejs 启动 web 服务:
- 使用 http 模块,启动服务
- 本机的 IP:127.0.0.1
- 本机域名:localhost
nodejs 如何监听 http 请求:
const http = require('http')
const server = http.createServer((req, res) => {
// 对监听到的请求进行处理
// 如果此时请求地址为:http://localhost:3000/insex.html
const url = req.url
console.log(url) // 则此处得到的 url 为:/index.html
// res 将服务端的响应返回给前端
res.end('返回的内容')
})
server.listen(3000) // 可以监听 http 请求
路由包含什么:
- 路由定义 method,如 GET / POST
- 定义 url 规则,如 /api/list 或 /api/add
- 定义输入(Request body)和输出(Response body)格式
nodejs 定义路由:
- 从 req 中获取 url 和 method
- 判断 method 是否符合
- 看 url 是否符合规则
拿到 req 中的 url 和 method:
- method 中的方法 GET / POST 必须大写
const http = require('http')
const server = http.createServer((req, res) => {
// 获取到请求地址中的 url
const url = req.url
// 这一步是为了去掉 url 中的参数,从而获得真正的 url
const path = url.split('?')[0]
// 获取到请求地址中的 method
const method = req.method
// 模拟获取信息的 url
if(path === '/api/list' && method === 'GET') {
// 命中该 url 后响应
res.end('响应的内容')
}
// 模拟发送信息的 url
if(path === '/api/create' && method === 'POST') {
// 命中该 url 后响应
res.end('响应的内容')
}
// 不命中 url 后的响应
res.end('404')
})
server.listen(3000) // 可以监听 http 请求
什么是 querystring:
- http://localhost:3000/api/list?keyword=abc&lang=en&a=120
- url 问号 ? 后面的都是 querystring(也叫 url 参数)
- 使用 & 符号分割,以 key=value 的形式存在,可继续扩展
querystring 的作用:
- 服务端拿到 querystring
- 根据参数(querystring)的不同,返回不同的内容,实现动态网页
- 即变化 querystring,就是变换内容(只要服务端支持)
const http = require('http')
const server = http.createServer((req, res) => {
// 获取到请求地址中的 url
const url = req.url
// 这一步是为了去掉 url 中的参数,从而获得真正的 url
const path = url.split('?')[0]
// 获取 querystring
const queryStr = url.split('?')[1] // type=0 或 1
// 获取到请求地址中的 method
const method = req.method
// querystring 解析原理
const query = {}
if(queryStr) {
queryStr.split('&').forEach(item => {
const key = item.split('=')[0] // type
const val = item.split('=')[1] // 0 或 1
query[key] = val // {'type':'0'}
})
}
// 模拟获取信息的 url
if(path === '/api/list' && method === 'GET') {
if(query.type === '0') {
// 当类型为 0 时执行的操作
res.end('响应的内容')
}
if(query.type === '1') {
// 当类型为 1 时执行的操作
res.end('响应的内容')
}
}
// 不命中 url 后的响应
res.end('404')
})
server.listen(3000) // 可以监听 http 请求
直接使用 nodejs 自带的 querystring:
const http = require('http')
// nodejs 自带的 querystring
const querystring = require('querystring')
const server = http.createServer((req, res) => {
// 获取到请求地址中的 url
const url = req.url
// 这一步是为了去掉 url 中的参数,从而获得真正的 url
const path = url.split('?')[0]
// 获取到请求地址中的 method
const method = req.method
// 使用 nodejs 自带的 querystring 解析
const query = querystring.parse(queryStr || '')
// 模拟获取信息的 url
if(path === '/api/list' && method === 'GET') {
if(query.type === '0') {
// 当类型为 0 时执行的操作
res.end('响应的内容')
}
if(query.type === '1') {
// 当类型为 1 时执行的操作
res.end('响应的内容')
}
}
// 不命中 url 后的响应
res.end('404')
})
server.listen(3000) // 可以监听 http 请求
url 的 hash 是否能起 querystring 同样的作用:
- hash 不能让服务端获取,以实现动态网页
- hash 形式:http://localhost:3000/api/list/#/home
- “#”后面的就是 hash
论:结构化与非结构化:
- 结构化的数据,易于通过程序访问和分析,如:对象、数组
- 非结构化的数据,不易通过程序分析,如:字符串
- 编程中的数据,都尽量结构化
使用 res 设置返回状态码、Content-type、Body:
- res.writeHead(状态码, 请求头参数)
res.writeHead(200, { 'Content-type': 'application/json' })
如何返回 JSON 数据:
- res.end() 只能返回字符串形式的内容
- 字符串的 Content-type 值是: text/plain
const http = require('http')
// nodejs 自带的 querystring
const querystring = require('querystring')
const server = http.createServer((req, res) => {
// 获取到请求地址中的 url
const url = req.url
// 这一步是为了去掉 url 中的参数,从而获得真正的 url
const path = url.split('?')[0]
// 获取到请求地址中的 method
const method = req.method
// 使用 nodejs 自带的 querystring 解析
const query = querystring.parse(queryStr || '')
// 模拟获取信息的 url
if(path === '/api/list' && method === 'GET') {
const result = {
errno: 0,
data: [
{ username: 'zll', content: 'content1'},
{ username: 'lgk', content: 'content2'}
]
}
res.writeHead(200, { 'Content-type': 'application/json' })
// JSON.stringify(result) 将 JSON 转为 字符串形式
res.end(JSON.stringify(result))
}
// 模拟发送信息的 url
if(path === '/api/create' && method === 'POST') {
const result = {
errno: 0,
message: '发送信息成功'
}
res.writeHead(200, { 'Content-type': 'application/json' })
// JSON.stringify(result) 将 JSON 转为 字符串形式
res.end(JSON.stringify(result))
}
// 不命中 url 后的响应
res.writeHead(404, { 'Content-type': 'text/plain' })
res.end('404 Not Found')
})
server.listen(3000) // 可以监听 http 请求
如何返回 html 数据:
- 设置 Content-type: text/html
- res.end(…)
- 浏览器会根据 Content-type 识别出 html 格式
// 示例
res.writeHead(404, { 'Content-type': 'text/html' })
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>404</title>
</head>
<body>
<h1>404 Not Found</h1>
</body>
</html>
`)
流 stream 数据:
- 服务端 res.end(),会自动以 流 的形式返回
- 浏览器会识别到 流,并持续接收信息
- 待全部接收完,再做处理(视频是一段一段的播放)
演示获取 Request Body:
// 模拟发送信息的 url
if(path === '/api/create' && method === 'POST') {
const reqType = req.headers['content-type']
let bodyStr = ''
// req.on('data') 方法用于服务端去识别“流”,并接受数据
req.on('data', chunk => {
// chunk 即“流”的每一段数据
// 拼接每一段流形式完整的数据
// 这里要使用 toString() 将每一段流转为字符串形式
bodyStr = bodyStr + chunk.toString()
})
// 该方法可以使服务端知道数据传完了
req.on('end', () => {
if(reqType === 'application/json') {
// 将数据转为 JSON 格式
const body = JSON.parse(bodyStr)
}
res.end('接收完成') // 异步
})
return
}