Node学习笔记2
一、模块化回顾
注意:js文件的执行均是在命令行窗口,不是在vscode的终端噢!
执行js文件,要在文件目录下输入命令行:node b.js
1.初始化npm环境
在命令行输入:
npm init -y
2.安装在npm中常用的工具库lodash
npm i lodash --save
-
使用lodash,在js文件中导入,lodash常用_表示
const _ = require('lodash')
3.代码展示(01_commonjs-test):
- a.js文件
function add(a, b) {
return a + b
}
function mul(a, b) {
return a * b
}
//只导出add方法
// module.exports = add
// 导出多个方法
module.exports = {
add,
mul
}
-
b.js
const {add, mul} = require('./a') const _ = require('lodash') const sum = add(5, 6) const ji = mul(2, 3) console.log(sum, ji); const arr = _.concat([1,2], 3) console.log('arr=', arr);
二、debugger调试
1.启用调试
在调试中选择Node.js,然后进入lauch.json文件。如下是一个已经配置好的lauch.json文件:
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"runtimeExecutable": "C:/Program Files/nodejs/node.exe",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\app.js"
}
]
}
会遇到的问题主要有两点:
- 首先,可能会报找不到node的错误。
- 第二,会报找不到调试文件。
解决方法:
-
对于问题1,首先打开命令行窗口,输入where node命令,找到node的位置。然后在配置中添加 "runtimeExecutable"属性。
"runtimeExecutable": "C:/Program Files/nodejs/node.exe"
-
对于问题2,调试工具的运行的位置在" w o r k s p a c e F o l d e r a p p . j s " , 其 中 {workspaceFolder}\\app.js",其中 workspaceFolderapp.js",其中{workspaceFolder}表示vscode打开的文件夹位置,你需要检查该路径是否为你需要调试文件的路径。
2.使用js自带的一个http模块在3000端口开启一个简单服务器,代码如下:
//http是自带的一个模块
const http = require('http')
const server = http.createServer((req, res) => {
res.writeHead(200, {'content-type': 'text/html'})
res.end('<h1>hello world!</h1>')
})
server.listen(3000, () => {
console.log('listening on 3000 port');
})
- 可以在res.writeHead处打一个断点,然后进行调试;同时使用Chrome浏览器打开localhost:3000网址。
三、前端和服务器端的五点区别
- 服务稳定性
- server端可能会遭受各种恶意攻击和误操作
- 单个客户端可以意外挂掉,但是服务端不能
- 使用PM2做进程守候,即当服务器挂掉时会自动重启
- 考虑CPU和内存(优化、扩展)
- 客户端独占一个浏览器,内存和CPU都不是问题
- server端要承载很多请求,CPU和内存都是稀缺资源
- 后续会讲使用stream写日志,使用redis存session
- 日志记录
- 前端也会参与写日志,但只是日志的发起方,不关心后续
- server端要记录日志、存储日志、分析日志,前端不关心
- 后续会讲多种日志记录方式,以及如何分析日志
- 安全
- server端要随时准备接收各种恶意攻击,前端则少很多
- 如:越权操作(删除别人的博客),数据库攻击等
- 后续会讲解登录验证,预防xss攻击和sql注入
- 集群和服务拆分
- 产品发展速度快,流量可能会迅速增加
- 如何通过扩展机器和服务拆分来承载大流量
- 本课题虽是但机器开发,但从设计上支持服务拆分
四、博客项目介绍
1.项目需求分析
目标:
- 开发一个博客系统,具有博客的基本功能
- 只开发server端,不关心前端
需求:
- 首页,作者主页,博客详情页
- 登录页
- 管理中心,新建页,编辑页
总结:
- 需求一定要明确,需求指导开发
- 不要纠结于简单的页面样式,并不影响server端的复杂度
2.技术方案
- 数据如何存储
- 博客
- 用户
- 如何与前端对接,即接口设计
3.关于登陆
- 业界有统一的解决方案,一般不用再重新设计
- 但实现起来也比较复杂
五、开发接口(不用任何框架)
- nodejs处理http请求
- 搭建开发环境
- 开发接口(暂不连接数据库,暂不考虑登陆)
六、http请求概述
面试常见问题1:输入URL按下enter之后发生了什么
- 首先是DNS解析(将域名和ip地址相对应),建立TCP连接(发生三次握手),发送http请求
- 然后server接收到http请求,处理,并返回
- 最后,客户端接收到返回数据,处理数据(如渲染页面,执行js)
面试常见问题2:三次握手分别是什么?
- 第一次握手:客户端询问服务器你是否可以用
- 第二次握手:服务器告诉客户端自己可用
- 第三次握手:客户端再次告诉服务器我知道了,接下来开始访问
七、nodejs处理http请求
-
nodejs处理get请求和querystring
-
get请求,即客户端要向server端获取数据,如查询博客列表
-
通过querystring来传递数据,如a.html?a=100&b=200
-
浏览器直接访问,就发送get请求
-
如下是一个接口:
// 导入nodejs自带的两个模块 const http = require('http') const querystring = require('querystring') // 创建一个服务器对象,参数是一个函数 const server = http.createServer((req, res) => { console.log(req.method); const url = req.url console.log('url:', url); // 根据'?'将url的参数分开,再转换成一个对象 req.query = querystring.parse(url.split('?')[1]) console.log('query:', req.query); res.end( // 将对象转成字符串 JSON.stringify(req.query) ) }) server.listen(8000, () => { console.log('listening on 8000'); })
-
-
nodejs处理post请求
-
post请求,即客户端要像服务端传递数据,如新建博客
-
通过post data传递数据
-
浏览器无法直接模拟,需要手写js,或者使用postman
-
接口代码如下所示,对于该接口的访问请求可以使用postman
-
打开postman,新建一个标签页
-
选择post,输入http://localhost:8000/
-
点击Body,选择raw,格式选择JSON
-
JSON内容输入
-
{
“name”: “张三”,
“age”: 25
}
-
最后点击send
const http = require('http') const server = http.createServer((req, res) => { if(req.method === 'POST') { //req数据格式 console.log('req content-type', req.headers['content-type']); let postData = '' req.on('data', chunk => { postData += chunk.toString() }) req.on('end', () => { console.log('postData:', postData); res.end('hello world!') }) } }) server.listen(8000, () => { console.log('listening on 8000'); })
-
-
-
nodejs处理路由
- https://github.com/
- https://github.com/username
- https://github.com/username/xxx项目
八、blog-1项目
1.搭建开发环境
-
从0开始搭建,不适用任何框架
-
使用nodemon监测文件变化,自动重启node
- 全局安装
npm install -g nodemon
- 本地安装
npm install --save-dev nodemon
-
使用cross-env设置环境变量,兼容mac linux 和 windows
- 安装
npm install --save-dev cross-env@5.2.0
-
配置,在package.json中添加:
“scripts”: {
“dev”: “cross-env NODE_ENV=dev nodemon ./bin/www.js”,
“prd”: “cross-env NODE_ENV=production nodemon ./bin/www.js”
}
2.开发接口
主体分为四层,第一层是bin下的www.js,里面就是创建server的过程;第二层是app.js,根据url不同转向不同的路由;第三层是router,判断GET/POST请求,调用数据方法返回数据;第四层是controller,里面是对数据的处理方法。
-
**初始化路由:**根据之前技术方案的设计,作出路由。进行模块化拆分
-
bin/www.js文件,导入http模块,创建服务器server,开启监听
const http = require('http') const PORT = 8000 const serverHandle = require('../app') const server = http.createServer(serverHandle) server.listen(PORT) console.log('listening on 8000')
-
app.js文件,主入口是serverHandle()方法,表示如何处理数据
const handleBlogRouter = require("./src/router/blog") const handleUserRouter = require("./src/router/user") const serverHandle = (req, res) => { // 设置返回格式JSON // JSON格式对于浏览器的解析比较友好 res.setHeader('Content-type', 'application/json') // 处理blog路由 const blogData = handleBlogRouter(req, res) if(blogData) { res.end( JSON.stringify(blogData) ) return } // 处理user路由 const userData = handleUserRouter(req, res) if(userData) { res.end( JSON.stringify(userData) ) return } // 未命中路由,返回404 res.writeHead(404, {"content-type": "text/plain"}) res.write("404 Not Found\n") res.end() } module.exports = serverHandle
-
src/router文件下
-
blog.js文件,对/api/blog接口请求的处理方法
const handleBlogRouter = (req, res) => { const url = req.url const method = req.method const path = url.split('?')[0] //获取博客列表 if(method === 'GET' && path === '/api/blog/list') { return { msg: '这是获取博客列表的接口' } } // 获取博客详情 if(method === 'GET' && path ==='/api/blog/detail') { return { msg: '这是获取博客详情的接口' } } // 新建一篇博客 if(method === 'POST' && path === '/api/blog/new') { return { msg: '这是更新博客的接口' } } // 删除一篇博客 if(method === 'POST' && path === '/api/blog/del') { return { msg: '这是删除博客的接口' } } } module.exports = handleBlogRouter
-
user.js文件,对/api/user接口请求的处理方法
const handleUserRouter = (req, res) => { const url = req.url const method = req.method const path = url.split('?')[0] // 登陆 if(method === 'POST' && path === '/api/user/login') { return { msg: '这是登陆的接口' } } } module.exports = handleUserRouter
-
-
-
返回假数据:将路由和数据处理分离,以符合设计原则