1、node.js是 一个js的运行环境,node是一个后端环境,搭载的是js语言
2、它可用于服务端开发,这样可以避免我们开发语言的不同,导致开发量增加(全栈)
3、单线程,指主线程为一个单线程,可以是使用库来实现多线程(fork方法)
4、因为它是单线程,可以用于做高并发的应用 (高并发:它通常是指,通过设计保证系统能够同时并行处理很多请求)。但高密集型计算不使用
5、未来->deno.js它的升级版本,第一个版本使用的是typescript,现正式版本使用的是:Rust语言来开发的库
6、它的核心库是使用的是Chrome浏览器核心库来实现的
引入模块使用require,如果是内置模块,直接引入,如果是外置模块,需要先使用npm下载(根据项目需要,下载为开发依赖,还是项目依赖)在package.json文件里面的scripts属性配置自己的npm命令
//引入模块
const http = require('http')
const server = http.creatServer({},(req,res) =>{
//req为请求的数据,res为响应数据
//一般post,update,delete等 请求数据都放在req.body里面
//后端返回的话就用 res.send()
})
//监听一个端口
server.listen(9000,() =>{
console.log('server started on port 9000')
})
1、它是一个企业级的node后端开发框架,仅仅轻度封装后的开发框架:(企业开发:nest.js,基于Express来封装的)
2、另外一个框架,koa 洋葱卷模式(企业开发:egg.js,基于koa)
1、首先引入Express,使用npm安装为生成环境依赖,npm i -S express@4 指定安装的版本
2、创建一个express实例对象,开启服务
3、监听一个端口
4、连接数据库(mysql或mongo)
5、开发响应接口
开发接口的时候,应该注意接口的规范,使用统一标准
使用restful 接口规范:一个接口应该声明:它的操作类型,它的作用,它对资源的一个描述
常用请求类型:POST 增加数据 不安全(是对数据库的数据会发生影响) 不幂等的,GET 获取数据 安全并幂等的,PUT(单条数据)(PATCH(多条数据)) 修改数据 不安全但幂等,DELETE 删除数据 不安全但幂等
(幂等是指每次操作的结果对数据库的影响,如果一直改变就是不幂等)
企业为了服务器安全考虑:常常只使用POST、GET
//代码
const express = require('express'),
app = express(),
path = require('path'), //路径库
//__dirname 指当前文件的路径
//use使用插件,express.static指向静态文件,一般为入口的html文件
//express.static会直接访问index.html
app.use(express.static(path.resolve(__dirname, 'public'), {index: 'login.html'}))
// 因为post、put、delete、patch等请求,数据使用的请求体传送,后端是使用流的形式接收数据
// 所以需要使用插件来接收并处理好数据
app.use(express.json())
//编码转换false,一般在get请求的时候,获取地址栏的值
app.use(express.urlencoded({extended: false}))
app.listen(9000, () => {
console.log('server started on port 9000')
})
连接数据库mongodb
//引入mongo,一般使用mongoose,因为更好用,二次封装了的
const mongoose = require('mongoose')
//建立连接
const url = 'mogondb://localhost::27017'
mongoose.connect('url'+'dbname',err = >{ // dbname这里为所需要用到的数据库名称,如果mongodb没有这个数据库,则会自动创建
if(!!err){
console.log(db falied to connect)
}
else{
console.log(db connect success)
}
})
定义数据模型和描述对象,并在接口中,对数据库进行操作
// 在mongoose中定义:所有的操作都必须在数据模型上进行
// 定义数据模型描述对象,因为是db.Schema 是一个class对象,所以需要进行new 运算
// constructor Schema(definition): Schema
const userSchema = new mongoose.Schema({
userId: {required: true, type: String},
userName: {required: true, type: String},
userPwd: String,
userPhone: Number,
vtdCode: Number,
token: String
})
//定义数据模型,根据userSchema得到一个数据模型model
//其中第一个参数为,collectiongs的名字,要加s,如果不加的话,数据库会帮我们加上,第二个参数为我们描述对象,第三个参数user_info,为数据的库的表,在mongodb中加集合,collections,如果没有则会自动创建
//根据数据模型对数据库进行操作,只有存数据的时候才会受到描述对象(userSchema)的字段约束,其他的查询等操作,不会受到描述对象的影响
const UserModel = mongoose.model('Users', UserSchema, 'user_info')
//数据库连接成功,描述对象和模型都定义好后,就在接口中,对数据库进行操作了
开发接口,路由(创建项目路由文件夹,放置自己的接口响应文件,Router)
这里给出mongodb的数据常见操作
1、显示当前数据库的所有集合:show collections;
2、向一个集合中插入一条数据:db.user_info.insert({id: 'admin', name: '超级管理员', pwd: 'admin123'})
3、查询数据:db.user_info.find() 允许带一个查询条件以及获取到的字段({id: 'admin'}, {id:0/id:1})
4、删除数据:db.user_info.remove({id: 'admin'})
5、修改数据:db.user_info.update({id: 'admin'}, {$set: {name: 'zhangsan'}})
定义路由,接受前端请求,返回请求结果
这里给出请求过程状态码,一个请求,请求代码完整,都会经历0-4的过程,最后都会到达4
xhr.readyState
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了
这里给出常见的http状态码,大家在练习的时候,可以根据状态码来判断是那一部分出错了
1**:请求收到,继续处理
2**:操作成功收到,分析、接受
3**:完成此请求必须进一步处理
4**:请求包含一个错误语法或不能完成
5**:服务器执行一个完全有效请求失败
100——客户必须继续发出请求
101——客户要求服务器根据请求转换HTTP协议版本
200——交易成功
201——提示知道新文件的URL
202——接受和处理、但处理未完成
203——返回信息不确定或不完整
204——请求收到,但返回信息为空
205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件
206——服务器已经完成了部分用户的GET请求
300——请求的资源可在多处得到
301——删除请求数据
302——在其他地址发现了请求数据
303——建议客户访问其他URL或访问方式
304——客户端已经执行了GET,但文件未变化
305——请求的资源必须从服务器指定的地址得到
306——前一版本HTTP中使用的代码,现行版本中不再使用
307——申明请求的资源临时性删除
400——错误请求,如语法错误
401——请求授权失败
402——保留有效ChargeTo头响应
403——请求不允许
404——没有发现文件、查询或URl
405——用户在Request-Line字段定义的方法不允许
406——根据用户发送的Accept拖,请求资源不可访问
407——类似401,用户必须首先在代理服务器上得到授权
408——客户端没有在用户指定的饿时间内完成请求
409——对当前资源状态,请求不能完成
410——服务器上不再有此资源且无进一步的参考地址
411——服务器拒绝用户定义的Content-Length属性请求
412——一个或多个请求头字段在当前请求中错误
413——请求的资源大于服务器允许的大小
414——请求的资源URL长于服务器允许的长度
415——请求资源不支持请求项目格式
416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段
417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求
500——服务器产生内部错误
501——服务器不支持请求的函数
502——服务器暂时不可用,有时是为了防止发生系统过载
503——服务器过载或暂停维修
504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长
开发接口
这里会设计到es7异步代码书写,node加密,token,动态路由等知识,先做说明
这里会设计到es7异步代码书写,node加密等知识,先做说明
es7异步代码书写,使用async和await
await必须在async定义的异步方法里面使用,且只能是子级,不能是越过子级的后代级
async是定义一个方法为异步方法,则await则可以使用一个变量来接受一个异步方法成功的返回结果(promise的返回结果)
let user = await getOneDoc(UserModel, {userId: data.userId}, {_id: 0})
这里getOneDoc是一个异步的promise,返回的是一个promise,且这个promise只有resolve方法,没有reject方法,如果要处理失败的结果,只能在后面.catch(),不能用一个变量来接受失败的结果
node加密,使用crypto-js库,因为crypto-js比crypto更好用,所以我们就使用crypto-js
MD5加密,对称加密(AES),非对称加密(DES)
MD5加密:几乎不可逆的加密方法,只能加密不能解密,一般使用登录注册的时候使用,使用方法如下
const crypto = require('crypto-js')
const enCodeMD5 = msg => crypto.MD5(msg).toString() //toString()方法转换为字符串
对称加密(AES) 一般用于web端的加密,使用密钥加密,然后再用密钥解密 前端一般使用AES加密
(在前后端交互的时候,密钥一定要保存好)
const key = '0123456789ABCDEF'
const enCodeAes = msg => crypto.AES.encrypt(msg,key).toString()
非对称加密(DES):由解析/解密生成一个私钥和公钥。如果需要传递信息,加密使用公钥(公钥可以让全世界知道),私钥用来解密(只能让自己知道)
token的使用,为什么需要使用token
token与cookie都是为了解决http无状态的问题,在数据请求结束后,客户端与服务端就会断开连接
token不会每次都发送给服务器,而cookie在表单中,每次提交的时候,自己发送给服务器,这样就不安全了
token设计 根据auth2.0认证(微信公众号的设计理念)
token需包含几个东西:token类型,用户编码,时间戳,随机码
let tokenStr = 'TOKEN' + ‘&’ + 'usercode' + ‘&’ + Date.now() + ‘&’ + Math.random()
token认证是后端做的,前端只需要给后端传递token这个东西,创建成功后,需要更新到数据库里面
用户在第一次登录的时候存入一条token在数据库,下次前端用户登录的时候,获取这个token返回给后端
一般token是会加密的
动态路由,把简短的参数方法接口路由中进行传递(一般是再get方法中进行设置)
接口定义(使用:)
app.get("/user/info/:userId", async (req, res) => {
let userId = req.params.userId
let result = await getOneDoc(UserModel, {userId}, {_id: 0, userPwd: 0, token: 0})
res.send({code: 200, message: '', data: result})
})
接口请求(直接在地址上面+一个参数就可以了)
$.ajax({
url: '/user/info/' + userId, // /user/info/admin /user/info/zhangsan
success(data) {
sessionStorage.setItem('user-info', JSON.stringify(data.data))
console.log(data)
location.href = './index.html'
}
})
req.params,req.query是用在get请求当中,而req.body是用在post请求中的
一:当我们使用动态路由后,需要接受这个参数,我们就使用req.params.userId就可以接收到get传来的数据了
二:当我们使用http://localhost:3000/?id=2&name=node这样的传输方式(一个或多个参数的时候),就使用req.query这个方法了
req.query.id就得到id的值为2了,req.query.name就得到传过来的name的值了为node
//确定请求方式,一般为get或post
// post/put/patch/delete 传递的参数都是在req.body 中接收
//get传递的参数是在req.params中接收(动态路由)
//req.params,req.query是用在get请求当中,而req.body是用在post请求中的
// 用户登录接口
app.post('/user/info', async (req, res) => {
let data = req.body
// UserModel.findOne({userId: data.userId}, {_id: 0}, )
let user = await getOneDoc(UserModel, {userId: data.userId}, {_id: 0})
if (!!user && user !== 'error') {
let sCode = enCodeMD5(user.userPwd)
if (sCode === data.userPwd) {
let tokenStr = 'TOKEN' + '&' + data.userId + '&' + Date.now() + '&' + Math.random()
let token = enCodeAes(tokenStr)
await updateOneDoc(UserModel, {userId: data.userId}, {$set: {token}})
res.send({code: 200, message: "success", data: {userId: data.userId, token}})
} else {
res.send({code: 400, message: "用户密码错误"})
}
} else {
if (!!user) {
res.send({code: 404, message: "用户不存在"})
} else {
res.send({code: 500, message: "服务器错误,请重试"})
}
}
})
(这里参照熊大林老师的博客)
模块化的好处
就好比要生产一台挖掘机,是由各个厂商提供的配件组装出来的,而不是由一家公司从头到尾生产,这样的好处是各个零部件各司其职,如果有一个功能坏掉了只需要找到对应的零部件解决问题即可,从而避免将来维修的麻烦。
作为代码也是同样的道理,一个完整的项目可以由很多个模块组成,如果某部分功能需要修改或者升级只需要找到对应模块的代码进行维护即可,大大提高了代码的可读性以及节约了维护成本。
模块的使用一般分为导入和导出,定义一个模块需要导出出去在需要使用的地方导入。所谓模块化规范就是规定了模块的使用方式,不同的规范制定了不同的导入和导出的方式。常见的模块化规范有如下几种:
AMD
依赖前置:提前引入,文件开头把需要的模块一次性全部引入,后面直接使用
前期消耗比较大,后期执行效率很高
代表作是 require.js (导入:require 导出 define )
CMD
按需加载:在代码执行过程当中需要一个模块了才去加载
整个曲线比较平缓
代表作是sea.js,但是现在已经很少使用了
ES6 Module
浏览器都还不支持,但是可以借助像webpack 这样的打包工具来实现打包,从而使浏览器可以运行代码。
commonJS
服务器端Node.js遵循commonJS规范,module.exports 导出模块,require引入模块。
这里我们遵循commonjs规范来拆包
require引入模块 如果不是node的内置模块,需要先下载库 npm 。。。再 require 引入
module.exports 导出模块 这里有几种写法
CommonJS规范:暴露接口出去
exports.db = db => module.exports = {db}
引入的时候使用 const db = require('./db/db') db.db调用方法
引入的时候使用 const {db} = require('./db/db') db调用方法
默认导出方法
module.exports = {db}
拆包完成后,我们的路径规划如下:
(db文件夹,为数据的连接等操作)
(public为我们的项目静态文件夹)
(router :路由,为我们定义接口的文件夹)
(util:为我们引入一些的库的文件夹,如加密)
写代码的时候会遵循一些框架的方式或者某些技术,或我们自己处理的模块化,但是这种代码是不能直接在浏览器运行的,这个过程我们就需要使用自动化构建工具,这些工具都是基于node环境。
grunt 比较古老,功能少,更新少,插件少,现在用的很少
gulp pc端 jquery的项目打包更多使用gulp
webpack 最主流的打包工具,vue、react都是用的webpack。一般配置文件都是一次配置终身使用
最后上传我们的项目代码的时候,不需要把node_moudels上传,会添加一个 .gitignore文件,这样我们git上传的时候就会忽略我们.gitignore文件里面定义的文件