8-NodeJS基础

2023-10-26

Node

  • Node是一个基于Chrome V8引擎的 javaScript代码运行环境
    • 什么软件可以运行javaScript ,它就是javaScript的运行环境
  • Node.js是运行在代码环境之上的语言
    • 由ECMAScript 和模块的API组成
Node.js全局对象global
  • 在浏览器中全局对象是window,在Node中全局对象是global
  • 全局对象global 中 以下方法可以在任何地方使用, global可以省略
  • console.log()
  • setTimeout()
  • clearTimeout()
  • setInterval()
  • clearInterval()
javaScript的弊端
  • 浏览器端javaScript在使用时存在两大问题,命名冲突(命名污染)和文件依赖
  • 模块化开发需要依赖第三方库文件,具有代表性有require.js 和sea.js
环境变量Path
  • 将一些要执行的软件的根目录存储到系统当中,在命令行中当前目录没有找到要执行的文件名时,系统会自动去这些目录下查找有没有同名的文件名,有就会执行.

模块化开发(导入导出)

  • 一个javaScript文件就是一个模块.
  • 模块化开发的好处就是当一个模块出问题时,我们只需要将这个模块取出 进行修复就好,其他模块还是正常在运行的
  • 模块内部定义的变量和函数默认情况下在外部无法得到
  • 模块内部可以使用exports对象进行成员导出,使用require方法导入其他模块
要导出暴露(共享)出去的变量或函数的 a.js 文件:
const add = (n1,n2) => n1+n2;    //定义一个加法函数
exports.add = add;				//第一个add为exports对象的属性,第二个add为函数

要导入外部变量或函数的 b.js 文件:
let a = require('./a.js');      //用一个变量接收返回值 
a.add(10,20);				    //输出a可以查看共享的变量或函数  函数可以代入参数调用来使用
  • exports是module.exports的别名(地址引用关系) ,所以导出导入变量用module.exports对象也是可以的
  • 当exports和module.exports指向同一个对象时,可以同时添加属性和方法,当给module.exports对象重新赋值(覆盖原有的属性方法)时,导出对象最终以module.exports准

系统模块 fs 文件操作

  • 系统模块: Node环境提供的API ,这些API都是以模块的方式进行开发的,所以这些API又叫系统模块

  • 文件模块fs 可以 读取文件, 写入文件, 创建文件夹

  • 要使用fs的方法 必须先引入fs模块

  1. 读取文件
    • fs.readFile(‘文件路径/文件名称’,[‘文件编码’],callback) callback 就是回调函数
    • 读取文件是硬盘的操作,需要时间(耗时操作), 所以用一个回调函数来返回读取的结果
const fs = require('fs')    //引入fs模块
//要读取其他模块的 js文件b.js内
fs.readFile('./a.js','UTF8',(err,doc) => {  
		//读取a.js文件 如果读取成功err=null 失败 err就是一个对象,里面存储错误信息
	if (err == null) {		//如果读取成功
		console.log(doc)	//输出doc(文件读取的结果,就是文件的内容) 
	}
})

2.写入文件

  • fs.writeFile(‘文件路径/文件名称’ , ‘要写入的内容’ , callback)
  • 如果没有要写入的这个文件,系统会自动帮我们创建
const fs = require('fs')    //引入fs模块
fs.writeFile('./dome.txt','要写入的内容可以是变量',err => {
	if (err != null) {			//当写入失败的时候 
		console.log(err);		//输出打印错误信息 并且直接退出函数
		return;
	}
	console.log('文件内容写入成功')  //写入成功时,控制台打印成功
})
系统模块path 路径操作
  1. 路径拼接 path.join(‘路径’,‘路径’…)
  • 由于不同操作系统的分隔符不同 \ 或 / 我们需要用路径拼接方法让系统自动帮我们拼接相应的分隔符
const path = require('path')   //引入path模块
//因为路径拼接不是耗时操作 可以直接用变量保存返回值
let finalPath = path.join('视频','动漫','喜洋洋与灰太狼')   
console.log(finalPath)  // window以\分割  所以会分割成 视频\动漫\喜洋洋与灰太狼
相对路径and绝对路径
  • 相对路径有时候相对的是命令行工具的当前工作目录,容易路径错误,所以大多数情况下使用绝对路径
  • 在读取文件或者设置文件路径都会选择绝对路径
  • 可以使用**__dirname**获得当前文件所在目录的绝对路径
const fs = require('fs')    //引入fs模块
const path = require('path')   //绝对路径是路径操作要引入 路径模块
//读取文件API      自动获取当前文件的绝对路径          //报错回调函数 
fs.readFile(path.join(__dirname,'文件名'),'uft8',(err,doc) => {
	console.log(err)
	console.log(doc)
})

第三方模块

  • 别人编译好,具有特定功能 可以直接使用的模块叫第三方模块.
  • 由于第三方模块通常由多个文件组成放在一个文件夹中,又名为 包
  • 第三方模块两种存在形式
    • 以js文件的形式存在,提供实现具体功能的API接口 类似jQuery
    • 以命令行工具形式存在,辅助项目开发
  • 第三方模块下载平台: npmjs.com 第三方模块的存储和分发仓库
  • 下载: npm install 模块名称 (默认下载到命令行工作目录下)
    • 本地安装就是模块安装到当前项目当中 (库文件)
    • 全局安装就是安装到公共目录,所有的项目都可以使用 (命令行工具)
  • 卸载 npm uninstall 模块名称 (直接删除文件夹也可以)
第三方模块nodemon 保存自动运行
  • 命令行工具中 : npm install nodemon -g 下载(-g全局匹配)
  • 当这个文件被保存时,命令行工具会自动再运行这个被保存的文件, 大大提高了工作效率
  • ctrl+c在命令行工具中是终止操作的意思
第三方模块 nrm 切换国内下载地址
  • 下载地址切换工具,因为npm默认的下载地址在国外,国内下载速度慢. 所以要切换到国内同步了国外npm的服务器网址.这样可以提高下载速度
  • 命令行工具使用: npm install nrm -g 下载(-g全局匹配)
  • nrm ls 查询可用下载地址列表 (前面有*号的是当前默认的下载地址)
  • nrm use 要切换的下载地址

第三方模块 gulp

  • 构建项目,HTML CSS JS文件压缩合并
  • 语法转换(es6,less)
  • 公共文件抽离
  • 修改文件浏览器自动刷新
  • 1.命令行工具使用 npm install gulp 下载(要使用的库文件下载)
    • npm install gulp -cli 安装gulp命令工具
  • 2.在项目根目录创建gulpfile.js文件 (名字不能更改)
  • 3.新建一个src目录文件 将原代码全部放入其中, 新建一个dist文件 用于放置构建后的文件
  • 4.在gulpfile.js文件中编写任务
  • 5.在命令行工具中执行gulp任务
  • 一个html要运行 需要拷贝html css js 还有图片音频等文件
gulp中提供的方法:
  • gulp.src() 获取任务要处理的文件
    • .pipe 匹配当前任务获取的文件 , 进行何种操作 (指派任务)
  • gulp.dest() 输出文件
  • gulp.task() 建立gulp任务 (第一个参数是任务名,第二个参数是回调函数)
  • gulp.watch 监控文件的变化
const gulp = require('gulp')   //引入gulp模块
gulp.task('要建立的任务名',() => {			//task 建立任务
	gulp.src('./src/css/base.css')		//要处理的文件
	.pipe(gulp.dast('dist/css'))		//要输出的文件(相当于拷贝到另一个文件夹中)
})
  • 要使用gulp的方法 需要下载一个东西
    • 命令行工具 : npm install gulp-cli -g
    • 使用时在当前目录名 命令行工具输入 : gulp 要输出的任务名
gulp插件使用
  1. 在命令行工具下载插件
  2. 查看相应文档
  3. 调用插件
    • gulp-htmlmin html文件压缩
    • gulp-csso 压缩css
    • gulp-babel javaScript语法转换(es6转es5)
      • 会根据当前运行环境,转换支持当前环境的代码
    • gulp-less less语法转换css 等等
    • gulp-uglify 压缩混淆javaScript (压缩js文件)
      • 在语法转换完毕后使用
1. require()引用插件的模块
2.使用gulp.task()创建任务   
task的回调函数中进行:
3.gulp.src()获取要处理的文件
  //*代表所有的, less文件或css文件 可以以数组的方式传递多个
  gulp.src(['./src/css/*.less','./src/css/*.css'])  
4.	pipe(调用要对要处理文件使用的下载好的插件的方法)
	pipe(less())   prpe(csso())   //将less转换为css   将css进行压缩
5.	pipe(gulp.dest(输出处理后的文件到相应文件夹)    //复制处理后的文件至构建后的文件夹

任务构建(命令行工具执行多个任务)
task第二个参数 如果不写函数 写中括号,数组元素写任务名,当这个任务被执行时,会依次执行数组里的任务
gulp.task('default',['任务名1','任务名2','任务名3','任务名4'])
package.json项目描述文件

项目描述文件,记录当前项目信息,例如项目名称,版本号,作者,github地址,当前项目依赖了哪些第三方模块, 可以让他人快速了解项目信息,下载依赖文件. 使用npm init -y 生成 路径不能有中文

  • 下载第三方模块时会自动产生package-lock.json,它的作用是, 锁定包的版本号 加快下载速度

  • package.json中scripts选项中的作用

    • 存储命令的别名 , 当我们要频繁使用的命令比较长的时候 可以给它起个别名
    • 通过 npm run ‘别名’ 来运行
  1. 项目依赖
  • 在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖。

  • 使用npm install 包名命令下载的文件会默认被添加到package.json文件的dependencies字段中。

  • 在dependencies字段中的第三方模块就是项目依赖

  1. 开发依赖
  • 在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖。

  • 使用npm install 包名 --save-dev命令下载的文件会默认被添加到package.json文件的devDependencies字段中。

  • 在devDependencies字段中的第三方模块就是开发依赖

  1. 为什么记录依赖项
  2. Node.js中下载的第三方包文件拥有非常多的细碎文件,将项目通过移动硬盘传递给别人时传输速度非常慢.

  3. 使用git工具管理项目时,不希望git管理node_modules文件夹,也不会将其上传到github中.

  • 当其他人获取到项目时,可以在项目根目录下执行npm install 命令,npm工具会自动去package.json文件中查找项目依赖文件并下载.
  1. 当项目上线以后,可以直接运行npm install --production下载项目依赖。如果不加–production,会下载全部依赖

模块查找规则

  1. require(’./find’); 当模块拥有路径 没有后缀时
  • 先查找有没有同名js文件 ,再查找同名文件夹
  • 有同名文件夹,就会去当前文件夹中的package.js文件中main选项中的入口文件
  • 如果main选中的没有不存在或者没有指定入口文件, 找同名文件夹中的index.js
  • 如果文件夹中没有index.js文件 就会报错
  1. require(‘find’); 当模块没有路径也没有后缀时,会先被当作一个系统模块
  • 如果参数不是一个系统模块,会去node_modules文件夹中看看是否有同名的js文件
  • 接下来的查询步骤和当模块拥有路径没有后缀时一样

服务器端基础

网站的组成 :
  • 客户端(html css js) : 在浏览器运行的部分, 能让用户看到并与之交互的界面程序.
  • 服务器端(node.js) : 在服务器中运行的部分 , 负责 存储数据和处理应用逻辑.
  • 客户端 通过请求 服务器 , 服务器端接收请求并逻辑处理 响应回 客户端
  • Node网站服务器:它能够接收客户端的请求,将接收的请求逻辑处理后响应
IP地址 域名 端口
  • IP地址 : 网络环境下的唯一标识,相当于计算机的编号吧,用于访问服务器,但是由于ip地址都是数字,不便记忆,所以就有了域名.
  • 域名 : 就是上网所用的网址,计算机会在DNS服务器自动将域名转换为IP地址 然后使用IP地址请求服务器
  • 端口 : 用于区分客户端的不同的软件请求是哪一种应用程序, 是在一段范围内的数字(0~65535),每一个应用程序都有一个不同的端口号,如果一个端口号被一个软件占用了, 另一个软件再去绑定就会报错.
    • netstat -anolfindstr 3000 查看3000端口是哪个进程的PID在占用
    • 3000网站服务 110 邮件服务 27017 数据库服务 21文件上传服务
URL的组成(统一资源定位符)
  • URL传输协议组成 : 协议//域名:端口/ 路径?参数
  • http: // www.baidu.com / news / 20190110/123456789.html
  • 网站一般使用的都是http协议.

uri : 统一资源标识符

  • 作用 : 本地资源定位

创建Web服务器 (http模块)

//引用系统模块htttp
const http = require('http')
//http的一个方法获取http网站服务器对象
const app =http.createServer();
//为网站服务器添加事件(request请求事件)  req请求对象  res响应对象
app.on('request',(req,res) => {
//res响应对象的end方法 响应请求对象 参数写响应的内容
	res.end()
})
//监听3000窗口   网站服务器对象方法linsten 监听
app.listen(3000)
//因为这个服务器是在本机上的,访问使用:localhost:3000    (localhost访问本机域名)
//netstat -anolfindstr 3000 查看3000端口是哪个进程的PID在占用
http协议

http协议就是超文本(可以传输文本图片音频视频)传输协议 ,协议规定了客户端和服务端网址交互的一些规则

  • http属于短连接 一次请求 一次响应
  • 而长连接 是一直连接的 一般都是聊天通讯用的
报文
  • http响应和请求传递的数据块就是报文,又分请求报文和响应报文
  1. 请求报文
  • 请求方式(method) : 客户端发送请求报文有两种方式get(请求数据)和post(上传数据)

    • 在获取数据时一般用get,上传(修改)数据用post(安全但是数据量大一些)
    • post方式请求头比get多 一个content-type 内容类型和content-length 内容长度
      • 服务端内部根据length判断传递是否完毕
  //修改成post请求方式   客户端写入进来
  //method: 指定当前表单提交的方式(post或get)
  //action: 指定当前表单提交的地址
  <from method='post' action='http://localhost:3000'>
  	<input type='submit' name=''/>
  </from>
  • 在请求事件中可以通过 if 判断req.method的值是get或post 进行不同的操作

  • 请求地址: 请求事件内请求对象 req的一些方法

    • req.headers //获取请求头的报文
      • 通过 req.headers[‘属性’] 可以输出请求报文内不同属性的值
    • req.url //获取请求地址
      • 获取用户输入请求过来的域名后的主页名(如/index 或 /list)
      • 通过 if 判断用户输入不同的网址进入不同的页面
    • req.method //获取请求的方式
      • 通过 req 请求对象的属性method获得 (请求方式默认是get)
  1. 响应报文
  • 响应头 :返回的数据类型 : res.writeHead(HTTP状态码,{content-type:‘内容类型 ; charset=utf8’}) 服务端是通过响应对象res的writeHead方法响应给服务端请求的结果

    res.writeHead(200,{'content-type':'text/html;charset=utf8'})
    
    1. HTTP状态码 响应此次请求的结果
    • 100系列 代表服务器在等待
    • 200系列 请求成功 206 断点续传成功 (迅雷下载暂停任务,关闭后再打开还可以继续下载)
    • 300系列 重定向 (重新跳转)
    • 400系列 客户端请求语法有误 404 请求的资源没有被找到 前端出现问题
  • 500系列 服务端错误(505) 服务器崩溃 后端出现问题

  1. 内容类型 此次请求成功时 响应(返回)的内容的文件类型
text/html : HTML格式
    text/plain :纯文本格式      
    text/xml :  XML格式
    image/gif :gif图片格式    
    image/jpeg :jpg图片格式 
    image/png:png图片格式
   以application开头的媒体格式类型:
   application/xhtml+xml :XHTML格式
   application/xml     : XML数据格式
   application/atom+xml  :Atom XML聚合格式    
   application/json    : JSON数据格式
   application/pdf       :pdf格式  
   application/msword  : Word文档格式
   application/octet-stream : 二进制流数据(如常见的文件下载)
   application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编    码为key/value格式发送到服务器(表单默认的提交数据的格式)
   另外一种常见的媒体格式是上传文件之时使用的:
    multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
  • application/json

    3.charset 编码类型 如果不写 输入中文会乱码

get请求参数 (url内置模块)
  • 客户端向服务器发送请求时,有时需要携带一些参数 这些请求参数被放在浏览器地址栏中, ?之后的都是请求参数 多个参数以& 分开
  • 服务端通过请求对象 req.url() 方法 可以获得请求参数
  • node提供了一个url 内置模块 里面的 url.parse() 可以更方便得获取客户端传递过来的参数
    • parse解析 解析req.url 各个部分 第二个参数false以字符串表现 true 以对象形式表现
//获取 url内置模块
const url = require('url')
//  以字符串表现
url.parse(req.url,false) 
// query为?后面的请求参数 以对象的形式接收请求参数
url.parse(req.url,true).query       
//pathname 获取不包含参数的请求地址    
//在传递参数时/index后面会有很多参数 所以需要用这个来逻辑处理给客户端进入哪个页面
url.parse(req.url,true).pathname 
post请求参数
  • post请求参数不在地址栏,而是在请求报文当中,通过事件的方式接受的,并且会将数据分割为多份 传递过来,所以需要以字符串拼接的方式接受
const http = require('http')
const app =http.createServer();
//处理请求参数模块
const querystring = require('querystring')
app.on('request',(req,res) => {
	//创建一个变量 用于拼接传递过来的参数
	let str = '';
	//当有参数传递过来时触发data事件 
	req.on('data', chunk(每次传递过来的参数) => str += chunk ;)
	//当有参数传递完时 触发end事件
	req.on('end', () => {
		//使用querystring模块的parse()方法将字符串转换成对象
		querystring.parse(str)
	})
	res.end('欢迎登录')
})
app.listen(3000)
路由
  • 路由就是指根据客户端的请求地址,服务器端接收后进行逻辑处理再响应客户端,这个过程就叫路由.
//获取http模块 和http网站服务器对象
//获取 url 模块
//为网站服务器对象添加请求事件{
//先通过req.method判断请求方式是get还是post
//再if判断 用户输入的不包含参数的请求地址来显示不同的内容
}
静态资源
  • 服务端响应不需要进行处理的文件,就是静态资源. 例如:html css javaScript等
  • 相同的请求地址,响应不同的资源,就是动态资源.
    1. 先通过url 获取用户输入的路径
    2. 然后通过 path 来拼接 文件的 绝对路径
    3. 利用fs 读取文件内容
    4. 把内容响应给客户端浏览器
第三方模块mime
  • 下载npm install mime
  • const mime = require(‘mime’)
  • mime.getType(‘要判断的路径’) 会自动根据路径 返回文件的类型
  • 通常在响应客户端 返回资源的类型不确定时使用
同步API和异步API(异步API返回结果的获取)
  • 同步API会从上至下依次执行,只有一个执行完成后才会执行下一个
  • 异步API需要等同步API执行完毕后 才会根据 触发的先后顺序执行
  • 获取返回结果都是同步API 所以return 无法获得异步API的返回结果
  1. 要想获取异步API的返回结果就需要用到回调函数
function getResult(callback) {
	setTimeout(function(){
		callback({				//两秒后调用callback函数 里面填想要异步返回的结果
			想要异步返回的结果
		})
	},2000)
}
getResult(function(data){		//函数中写一个形参data用于接收 想要异步返回的结果
	console.log(data)			//同步输出返回的结果
})
  • 函数调用的时候才会运行里面的代码,所以异步API触发时调用函数 就可以实现异步进程执行后再执行同步进程
异步进程回调地狱和Promise解决方案
  • 多层重复的异步进程进行嵌套,就叫回调地狱.
  • Promise本身是一个构造函数,要使用promise解决回调地狱的问题 需要使用new运算符创建Promise构造函数的实例对象
  • 在创建对象的时候需要传入一个匿名函数,匿名函数中有两个参数 resolve,reject
  • Promise实例对象方法:
    • then()方法 ,用于调用resolve函数
    • catch()方法也有个方法用于调用reject函数
    • finally()方法 成功与否都会执行(非标准)
  • 如果返回值是个普通值,自动转换成promise对象,并作为新promise对象的resolv
function p1() {
	return new Promise((resolve,reject) => {
		fs.readFile('./1.txt','utf8',(err.data) => {
			resolve(data)
		})   
	})
}
function p2() {
	return new Promise((resolve,reject) => {
		fs.readFile('./2.txt','utf8',(err.data) => {     //当文件读取的时候
			resolve(data)								//
		})   
	})
}
p1().then((r1) => {			//先调用p1函数
	console.log(r1)         //r1就是接收Promise的 函数resolve(data) 内的返回值data
	return p2();			//当第一个异步API读取完毕后 再返回包裹第二个异步API的函数调用
})
.then((r2) => {				//上一个函数返回了的调用链式链接了这个 可以达到依次执行的效果
	console.log(r2)			//r2就是接收Promise的 函数resolve(data) 内的返回值data
})  
promise对象方法
  • all 同时处理多个异步任务,所有任务都完成才能得到返回结果
  • rece 同时处理多个异步任务,只要有一个完成就返回结果,
promise.all([p1,p2,p3]).then(function(result){
	console.log(result)
})
promise.race([p1,p2,p3]).then(function(result){
	console.log(result)
})
异步函数 async (es7)

异步函数就是异步编程语法的优化版,可以让异步代码写成同步形式,让代码不再有回调函数嵌套.

语法: const fn = async () => {}

​ async funciton fn() {}

  • 普通函数定义前面加 asnc 就是异步函数
  • 异步函数的returrn返回结果会被包裹在promise对象中. return替代了resolve方法
  • 在异步函数内部使用throw关键字抛出程序异常 后面可以填错误的信息
    • throw一旦执行,后面的代码将不再执行 ,可以利用catch去捕获这个错误信息
  • 调用异步函数再链式调用then方法获取异步函数的执行结果
  • 调用异步函数再链式调用catch方法获取异步函数执行的错误信息

await关键字

  • 只能出现在异步函数中, await后面只能写promise对象.
  • await关键字可以暂停异步函数向下执行,直到promise对象获得返回结果
async function p1() {
	return 'p1';
}
async function p2() {
	return 'p2';
}
async function p3() {
	return 'p3';
}
async function run() {
    let r1 = await p1()      //r1获取完p1异步函数的返回值才会执行后面的代码
    let r2 = await p2()
    let r3 = await p3()
    console.log(r1,r2,r3)   //r1,r2,r3获取完毕后输出返回结果
}
异步函数对返回值不是Promise的异步API需要进行包装
//此时并没有调用promisify()方法
let promisify = require('util').promisify
//调用promisify包装异步API就能返回Promise对象
let readFile = promisify(fs.readFile)
创建一个异步函数
async function Run() {
    let r1 = await readFile('需要读取的文件','utf8')  //r1接收读取的文件内容
    console.log(r1)
}
Run()

数据库

是专门用来存储数据的,可以把数据持久化的,有序的存储起来.

数据库MongoDB概念
  • database 数据库 mongoDB数据库软件中可以建立多个数据库
  • collection 集合 一组数据的集合,可以理解为JavaScript中的数组
  • document 文档 一条具体的数据,可以理解为JavaScript中的对象
  • field 字段 文档中的属性名称,可以理解为JavaScript中的对象属性
启动服务或者是停止服务
  • 命令行 net start mongodb
  • 命令行 net stop mongodb
利用第三方mongoose包操作数据库
  • 下载 npm install mongoose
// 引用mongoose包
const mongoose = require('mongoose');
// 数据库链接connect()方法      playground 如果没有会自动创建这个集合
mongoose.connect('mongodb://localhost/playground',{ useNewUrlParser:
true, useUnifiedTopology: true})
	.then(() => console.log('数据库连接成功'))
	.catch(err => console.log('数据库连接失败', err));
向数据库中导入数据
  • mongoimport -d 数据库名称 -c 集合名称 -file 要导入的数据文件路径
  • mongoimport要加入系统环境变量 不然无法使用
向数据库中加入数据
  1. 连接MongoDB数据库
  2. 创建规则
  3. 创建集合实例
  4. 插入集合实例
  5. 保存
    • user.save()
创建集合
  • 分为两步, 一是对集合设定规则, 二是创建集合

  • 字段的规则:


type:  String, Boolean,Object//类型
    type: mongoose.Schema.Types.ObjectId //规定类型只能是唯一标识_id 用于关联
	ref:'user'  //要关联的集合 
minlength:  2, //最小长度
maxLength: 5, //最大长度
required: true,// 必填项
trim: true// 是否去除两边的空格
min: //最小值 判断number
max: //最大值 判断number
enum: //枚举 只能在规定的内容中进行选择
validate: //自定义验证器
	validator //函数中去定义我们的规则
    message //里面设置错误信息
default: //默认值
unique:true   查询数据库是否有这个值 如果有就会报错

// 设置集合规则
const userSchema = new mongoose.Schema({
    name: String,
    author: String,
    tags: [ String ],
    data: { 
        type: Date, default: Date.now 
    },
    isPublished: Boolean
});
// 创建集合并应用规则
const Course = mongoose.model('Course', courseSchema);
//暴露这个集合
module.exports = {
    Course
}

数据库增删改查

  • 实际上就是向集合中的数据进行各种操作。
  • 因为是耗时操作 所以要用异步函数 async和 await
增create()
User.create({name: '张三'}).then().catch()
删Delete()
//找到符合条件的第一个删除
User.findOneAndDelete({条件}).then()  
//  可以删除多个,如果不带参数,代表清空当前集合
User.deletMany().then()
改date()
  • 返回值是一些修改结果的信息 如: 是否修改成功 修改成功几个等
// 修改一条数据  第二个值填修改后的对象
User.updateOne({查询的条件},{要修改的值}).then()
//如果第一个参数不写,代表修改所有
User.updateMany({查询的条件},{要修改的值}).then() 
User.find().sort('age').then()  升序查找
User.find().sort('-age').then()  降序查找
User.find().skip(跳过几项).limit(查询几条数据).then()
User.find().select('name age').then()  指定去查询集合中的  name 和age 字段,如果你不想去查询某个字段  在这个字段前面加一个 -
User.find({hobbies: {$in: ['足球']}}).then(result => console.log(result))  in代表是包含
User.find({age: {$gt: 20, $lt: 40}}).then(result => console.log(result))  $gt 代表是大于   $lt 代表是小于

集合关联
  • 描述集合与集合之间的关系
    • 一对多的关系
      • 关系是 一对多 一个用户会有多篇文章
      • 在多的一方去设置 关联字段(外键)
      • 我们在 文字的集合规则中我们去定义
        • type: mongoose.Schema.Types.ObjectId,
        • ref: ‘User’ 设置要关联的 集合
      • 查询的时候就可以把关联集合的信息都查询过来
        • 文章集合对象.find().populate(‘关联字段’)
    • 多对多的关系
    • 一对一的关系

模板引擎

是因为我们之前大量拼接字符串,导致我们代码很臃肿,而且逻辑跟 结构 混在了一起,不方便进行维护

模板引擎的出现,让我们结构和逻辑分离开来了。这样后续方便进行维护

art-template 第三方的模板引擎 是 腾讯出品

  • 安装 npm install art-template
  • 导入 模板引擎 : const template = require(‘art-template’)
  • 我们最好用 绝对路径 path.join(__dirname,‘views’,‘index.art’)
    • html文件也是可以的
  • 然后用 template(path, 数据对象), 返回一个字符串,而这个字符串就是我们和页面结构拼接好的内容
    • art文件中 可以通过这个数据对象的属性将其输入到art文件结构中

模板的语法

原始语法<% %>
标准语法: {{ 数据 }}
原文输出:{{@ value}}  //前面加@可读取html标签
原始语法:<%= 数据 %>
//如果数据中携带HTML标签,默认不会解析标签,会将其转义后输出。使用以下方式可以解析标签。
{{@ value }}
条件判断
<!-- 标准语法 --> 
 {{if 条件判断1}}
 ... 
 {{else if 条件判断2}}
 ... 
 {{/if}}
 <!-- 原始语法 -->
 <% }  if (条件判断1) { %>
 ... 
 <% } else if (条件判断2) { %>
 ... 
 <% } %>
数据循环
//标准写法  index 索引 value 此次循环的内容
{{each target}}
     {{$index}} {{$value}}
 {{/each}}
  <!-- 原始语法 -->
 <% for(var i = 0; i < target.length; i++){ %>
     <%= i %> <%= target[i] %>
 <% } %>
子模板

可以将网站公共固定不动区块(头部、底部)抽离到单独的文件中,再通过include插入到需要的模板中.

<!-- 标准语法 -->
 {{include './header.art'}}
  <!-- 原始语法 -->
 <% include('./header.art') %>
     
     
模板继承
  • 搭建架构的时候使用 可以封装 共同的骨架,可以使用 block 将内容插入到想要插入的地方
//基础模块art文件中在要插入内容的地方使用block代表插槽,每个插槽设置不同的名字用于区分
{{block 'head'}} ... {{/block}}
//在要继承的文件中 使用extend +路径 代表要继承的art文件   block标签中输入要插入的内容即可
{{extend './layout.html'}}
{{block 'head'}} ... {{/block}}
defaults.imports模板引擎导入第三方模块方法
  • 所有模板都可以使用这个变量
//导入日期
template.defaults.imports.变量名 = 变量值
//template是要导入的第三方模块第一个dateFormat是传入进去使用的变量名  第二个是第三方模块的dateFormat函数
template.defaults.imports.dateFormat = dateFormat;
//使用  模板中
{{$imports.dateFormat('要处理的日期')}}
配置模板引擎根目录和后缀名
  • 利用 template(就不需要写前面的路径,也不需要写后缀名)
//template.defaults.root   配置根目录  
template.defaults.root  = path.join(__dirname,'views','06.art')
//template.defaults.extname   配置模板引擎的 后缀名
template.defaults.extname = ('.art')

const html = template('0.6', { time:new Date()});

Express框架 第三方模块

  • 基于Node平台的web服务器应用开发框架
  • 把一些共性的方法都进行了封装,不需要我们去再做处理
  • 下载 npm install express
const express = require('express')
//创建web服务器
const app = express()
//监听get请求
app.get('/',(req,res) => {
    //响应浏览器
    res.send()
})
//监听get请求
app.post('/',(req,res) => {
    res.send()
})
//监听端口
app.listen(8080);

res.send() 响应浏览器res,end()优化版

  • send方法会自动检测响应的内容类型
  • send方法会自动设置http状态码(200 404等)
  • send方法会自动设置响应的内容类型及编码(content-type内的文件类型和utf8)
中间件
  • 也是用于监听请求,但是中间件可以进行传递 next()
    • next()只能传递字符串 想要传递对象(对象可以存放多个参数) ,使用JSON.stringify()转换成字符串 再通过JSON.parse() 转换回对象
  • 直到 res.send() 响应客户端才会结束
  • 可以将复杂的请求分步骤进行处理

应用场景:

  1. 路由守卫

    • 我们项目中 有一些页面是需要登录才能进行访问的,此时我们就可以通过中间件来进行拦截。 如果是登录状态,那么就可以下发到对应的中间件,如果不是登录状态,提示用户进行登录
  2. 网站维护

    • 利用中间件来进行全局的请求的监听,只要网站进行了维护,那么就阻止我们的请求下发
  3. 自定义404页面

    • 把我们这个中间件写在 最下面
  4. 异常的处理

    1. 同步错误处理中间件
    • app.use((err, req, res, next) => {}) 全局的异常的捕获的中间件, 只要出现了throw抛出异常,就会执行到这个中间件里面 err.message属性中就是自定义的错误信息
    1. 回调函数错误处理中间件
    • 如果next() 参数是错误信息 将会直接触发错误处理中间件
    app.use('/index',(req,res,next)=> {
    	fs.readFile('/随便写了个错的.txt','uft8',(err.date)=> {
    		if (err != null) {
    			next(err)
    		} else {
    			res.send(date)
    		}
    	})
    })
    //错误处理中间
    app.use((err, req, res, next) => {
    	res.status(500).send(err.message)
    }) 
    
    1. 异步函数错误处理中间件 try catch
    app.get('/', async (req,res,next)=> {
    //如果try中的条件错了 进入catch内执行ex为错误信息  反之跳过catch向下执行
    	try {
    		await User.find({name:'张三'})
    	}catch(ex) {
    		next(ex)
    	}
    })
    //错误处理中间 status()修改状态码
    app.use((err, req, res, next) => {
    	res.status(500).send(err.message)
    }) 
    
路由模块化
  • 将不同的路由抽取成一个个js文件 通过暴露的方式在主js文件中使用
    • app.use(’/user’, home) home 为暴露出来 自定义名字的路由
//home.js
const home = express.Router()  创建路由对象
home.get('/index'(req,res)=>{ res.send('内容') })
module.exports = home;
//app.js
const home = require('home.js路径')
app.use('/home',home)  
  • 通过localhost:端口/home/index 才可以访问到’内容’
参数的获取
  1. get 方式
    • 直接利用 req.query 就能拿到 请求的参数,并且我们框架帮我们把这个参数转成了对象
  2. post 方式
    • 建议使用第三方的模块 body-parser
    • const parse = require(‘body-parser’)
    • app.use(bodyParser.urlencoded({ extended: false })) 配置路由
      1. 通过req.body 就能得到数据
  3. 路由参数
    • 配置路由参数的规则
    • 配置了几个参数 请求路径(路径传递参数)就要填写几个参数
    • app.get(’/list/:id/:name’)
    • localhost:8080/list/123/zhangsan
    • req.params 获取路径参数
      • {id: ‘123’, name: ‘zhangsan’}

express-art-template模板引擎

  • 为了让art-template和express更好地配合,封装了express-art-template.
  • 下载: npm install art-template express-art-template 两个都要下载
//设置后缀名为art的文件使用express-art-template模板引擎
app.engine('art',require('express-art-template'))
//设置模板默认存放目录
app.set('views',path.join(__dirname,'views'))
//默认拼接art后缀
app.set('views engine','art')
  • res.render(‘路径’) 响应浏览器 模板引擎用 和end() send()一样
静态资源托管static()

app.use(express.static(path.join(__dirname)))

路径符 ‘/’ 是服务器根目录绝对路径 根目录就是静态资源托管的位置

app.locals 模板引擎导入变量

将变量设置到app.locals对象下面,这个数据在所有的模板中都可以获取到。

 //app.js中 也可以存放复杂数据类型
 app.locals.userName = '张三';
 //模板art中 输入 即可使用
 {{ userName }}
创建不同路由对象
  • const home = express.Router()
  • const list = express.Router()
location.href 修改跳转页面
location.href='index.html';
express重定向

res.redirect(‘路径’)

设置登录状态cookie与session
  1. cookie供服务器端存储数据(以域名的形式区分)
    • cookie有过期时间,超过时间就会自动删除
    • 每次请求都会自动发送到服务器
  2. session则是一个对象 存储在服务器端
    • 验证账号密码正确时会在session中生成一个sessionID发送写入客户端的cookie中
  3. 每次客户端发送请求时, 服务端会判断cookie中的sessionID是否存在在服务端,如果有就说明登录过,没有就说明没登录过
  • npm install express-session
const session =require('express-session')
//配置session   
app.use(session({
	//secret:后面的参数是可以自定义的 
	secret:'secret key',
	//不默认生成sessionid
	saveUninitialized:false,
	//设置一天过期时间
	cookie:{
		maxAge:24*60*60*1000
	}
}))
//登录成功时 在session中存入一些数据 服务端会随机生成sessionID
req.session.username = user.username

删除登录状态cookie与session

//在要推出登录的路由中
app.get('/logout',(req,res)=> {
    //删除session
    req.session.destroy(function(){
        //删除cookie
        res.clearCookie('connect.sid');
        //重定向
        res.redirect('/admin/login')
    })
})
joi表单验证模块
const Joi = require('joi')
const Schema = {
	//	alphanum只能是字母或者数字  require必选  
	username: Joi.string()alphanum().min(3).max(10).required().error(new Error('错误信息'))
	//  regex 使用正则规则验证这个字段
	password:Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/)
	//[]  只能是数组中的数据类型
	acssess_token:[Joi.string(),Joi.Number()]
	//integer  只能是整数
	birthyear: Joi.number().integer().min(1900).max(2013),
	//email 只能是邮箱格式
    email: Joi.string().email()
}
//validate 验证 第一个参数验证对象, 第二个参数验证的规则 返回poi
const result = Joi.validate({ username: 'abc', birthyear: 1994 }, schema);

获取数据user集合中数据的数量: let count = user.countDocuments({})

input文件上传和解析表单模块formidable第三方模块
  • 文件上传是以二进制上传的,非二进制上传的数据相对简单点
  • form表单域 要上传文件的话 必须要有enctype=“multipart/form-data” 属性
  • 安装: npm install formidable
 // 引入formidable模块
 const formidable = require('formidable');
 // 创建表单解析对象
 const form = new formidable.IncomingForm();
 // 设置文件上传路径 推荐写绝对路径
 form.uploadDir = path.join(__dirname,"/public/uploads");
 // 是否保留表单上传文件的扩展名
 form.keepExtensions = true;
 // 对表单进行解析
 form.parse(req, (err, fields, files) => {
     // fields 存储普通请求参数 相当于req.body
         // files 存储上传的文件信息  
     res.send('ok')
 });

js实现图片即时预览

  • 需要获取要上传的表单控件和显示预览的图片
  • 为表单添加multiple属性可以上传多个文件
//客户端中 
//file上传文件表单控件添加change事件  当选择完要上传的文件时触发
file.onchange = function() {
	//创建文件读取对象
	var reader = new FileReader();
	//读取要上传的文件  因为会有多个,读取的文件是个数组 所以有[0]获取第一个上传的文件  
	reader.readAsDataURL(this.files[0]);
	//读取文件是异步API 所以要创建onload事件进行读取完成后的操作
	reader.onload = function () {
		//preview 为预览图片  将这个图片的属性  修改为读取结果
		preview.src = reader.result;
	}
}

express项目初始化

  1. 建立项目所需文件夹
    • public 静态资源
    • model 数据库操作
    • route 路由
    • views 模板文件
  2. 生成项目描述文件
    • npm init -y
  3. 下载模板引擎第三方模块
    • npm install express mongoose art-template express-art-template
  4. 入口文件app.js中 创建网站服务器
    • 引入express模块
    • 创建服务器对象 express()
    • 监听端口listen
  5. 创建不同路由对象
    • const home = express.Router()
    • const list = express.Router()

mongoDB数据库添加账号

  1. 以管理员身份运行powershell
  2. 连接数据库mongo
  3. 查看数据库 show dbs
  4. 切换到admin数据库集合 use admin
  5. 切换后为这个集合添加创建超级管理员账户 db.createUser({user:‘root’,pwd:‘root’,roles:[‘root’]})
    • user账号 pwd 密码 roles角色权限(root超级管理员, readWrite 读写权限)
  6. 卸载mongodb服务
    1. 停止服务 net stop mongodb
    2. mongod -remove
  7. 创建mongodb服务
    1. mongod --logpath=“C:\Program Files\MongoDB\Server\4.1\log\mongod.log” --dbpath=“C:\Program Files\MongoDB\Server\4.1\data” --install --auth
      • logpath 填mongod.log路径 dbpath 填 data文件夹路径
      • –auth 不填 就不用验证数据库账号密码
    2. 开启服务 net start mongodb
在项目中使用账号连接数据库(为数据库创建普通账号)
  1. 以管理员身份运行PowerShell
  2. mongo进入mongodb数据库操作环境
  3. use admin 切换到admin数据库
  4. db.auth(‘root’,‘root’) 登录admin数据库
  5. use alibaixiu 切换到要创建普通账户的数据库(没有会自动创建这个数据库)
  6. db.createUser({user:‘账号名称’,pwd:‘账号密码’,roles:[‘readWrite’]}) 创建账户 权限为读写即可
  7. exit 退出mongodb数据库操作环境
mongoose.connect('mongodb://blogs:blog@localhost/blogs',{ useNewUrlParser:
true, useUnifiedTopology: true})
//('mongodb://账号:密码@域名/集合)
//blogs是账户blog是密码  

开发环境和生产环境

环境就是项目运行的地方,项目处在开发阶段运行在开发人员的电脑上,就是开发环境。

项目开发完成后,将项目放到真实的网站服务器上,此时的环境就是生产环境。

  • 开发环境和生产环境需要连接的数据库是不同的,所以我们的代码需要通过判断运行环境来运行不同的项目配置。
设置系统环境变量判断环境
  • 为此电脑添加一个环境变量名:NODE_ENV
  • 开发环境值设置成development
  • 生成环境设置成production
//可以通过process.env.NODE_ENV 获取设置的环境变量NODE_ENV的值
if (process.env.NODE_ENV === 'development') {
	//开发环境
} else {
	//生产环境
}
开发环境打印客户端发送服务端的请求 morgan模块
  • npm install morgan
  • const morgan = require(‘morgan’)
  • app.use(morgan(‘dev’)) 生产环境无法打印请求
config模块

根据不同的运行环境自动去对应的json文件中获取配置信息

  1. 下载 npm install config
  2. 项目根目录创建config文件夹
    • 创建default.json development.json production.json 文件
      • 默认/开发环境/生产环境 搜索对应的运行环境json文件没有配置信息属性就会去 default(可以用于存储公共部分) 中查找
      • json文件中至少要放一个空对象 不然会报错
    • json文件中配置信息的属性名也要用引号""
const config = require('config')
//获取配置信息属性值
config.get('配置信息属性名') 
将密码存储到环境变量中
  • config文件夹下创建custom-environment-variables.json
  • 将密码值存储到一个系统环境变量中
//在custom-environment-variables.json文件中
{
	"db": {
		pwd:"环境变量名"
	}
}
ajax可以使用四种请求方式便于浏览代码 (restful API)
  • get 获取 post 创建添加 put 修改 delete 删除
  • 特点为路由名一般为集合名称 请求路径相同 请求方式不同
  • 对单独数据操作 ajax请求时 url:’/users/1’ req.params.id=1
app.get('/users',(req,res)(){})
app.post('/users',(req,res)(){})
app.put('/users/:id',(req,res)(){
      req.params.id; //可以获取路径参数中的id  
})
app.delete('/users/:id',(req,res)(){})
xml基础介绍
  • xml代表可扩展标记语言,他的作用和html一样是用来传输和存储数据的,一样可以通过html的dom操作来操作xml

  • 简单来说就是服务器返回一些类似html的代码 虽然 获取这些数据的代码不同,但是同样可以使用dom方式操作他们

    //当服务器返回的是xhr数据时 使用xhr.responseXML 获取传递过来的xml代码
    var xhr = new XMLHttpRequest();
    xhr.open('get','/xml')
    xhr.send()
    xhr.onload = function() {
    	console.log(xhr.responseXML)
    }
    //服务器路由 中 通过响应头告诉客户端返回的是xml数据
    app.get('/xml',(req,res)=>{
        res.header('content-type','text/xml')
        res.send('xml数据')
    })
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

8-NodeJS基础 的相关文章

随机推荐

  • Everything+cpolar内网穿透轻松实现公网远程访问本地硬盘文件

    公网远程访问本地硬盘文件 内网穿透 文章目录 公网远程访问本地硬盘文件 内网穿透 前言 1 下载cpolar和Everything软件 3 设定http服务器端口 4 进入cpolar的设置 5 生成公网连到本地内网穿透数据隧道 总结 前言
  • Request和ThreadLocal

    Web容器中有三个周期 request Httpsession application 其中request是客户端发出的一个请求 这个request的载体就是一个 线程 实际等同于一个线程的生命周期 Request是封装在线程上面一个抽象概
  • windows下pip安装mysqlclient失败

    环境 windows8家庭版 python3 6 7 在虚拟环境中pip install mysqlclient报错 解决方案 下载地址 ctrl f键入mysqlclient 找到对应的版本即可 博主是python3 6 7 所以选择了倒
  • 2021 程序媛跳槽记:学习计划篇

    三妹跳槽系列文章 2021 程序媛跳槽记 百度阿里字节等各大厂面经篇 2021 程序媛跳槽记 必刷LeetCode算法题 附解题报告 坦白说 我这个人不算聪明 基础也不咋样 这次跳槽我一开始是很没信心的 甚至想把这次尝试当做试水 如果受打击
  • Go_数组遍历、最大值、求和、多维数组

    数组 数组就是用来存储数据的容器 存储多个数据时数据类型要一致 如果想要保存任意类型数据 需要声明为接口类型数组 数组定义完成后 可以对数组进行赋值操作 数组是通过下标来进行操作的 下标的范围是从0开始到数组长度减1的位置 特点 数组是一种
  • 关闭windows defender教程

    由于windows自带的防护软件在后台占用大量内存 然后可以使用其他第三方软件来 然后本人使用的是火绒 这里平时的内存占用了不到100MB 然后其实这里本来应该插入一个windows defender的占用内存 我记得是在200 MB 反正
  • git 上传 github报错 (Permission denied)

    文章目录 结论 起因 新建github仓库 本地仓库初始化 结论 ssh config 中 Host 值可以随意写 cat git config 中 remote origin url git B test demo git url 值 后
  • 微服务讲堂--【5】系统自举

    这里的 系统自举 借用了操作系统的概念 在操作系统启动之前 计算机要先加载自举程序 再由自举程序加载操作系统的启动程序 整个详细过程不在这里描述 可以在网络查阅相关资料 为什么要在微服务系统中特别提及系统自举这个概念呢 因为这内容很重要 而
  • Unity 勾选development Build 区别,引起的Bug,记录一下

    Unity 勾选development Build 区别 引起的Bug 记录一下 问题 编辑器运行正常 安卓真机 Build And Run 就出现了奇怪的问题 类似数组数据出现了误差 勾选development Build 想要真机调试的
  • 第十四届蓝桥杯校内模拟赛(第二期) C++题解分享

    本人是在学校机房参加的第二期模拟赛有些题目没有忘海涵 若有什么好的建议可以提出来来分享这是本小白第一篇CSDN希望能帮助到大家 第一题没啥好说的就是直接暴力枚举 这边直接上代码 稍微注释 答案 2048 include
  • CTF—web题库笔记(难度2)

    CTF web题库笔记 难度1 CTF web题库笔记 难度4 本篇文章共12道题 目录如下 目录 1 warmup 2 supersqli 3 Web php include 4 php rce 5 Web php unserialize
  • HDFS分布式文件系统(2)Java API操作HDFS

    文章目录 1 创建Maven项目 2 添加相关依赖 3 创建日志属性文件 4 启动集群HDFS服务 5 在HDFS上创建文件 6 写入HDFS文件 6 1 将数据直接写入HDFS文件 6 2 将本地文件写入HDFS文件 7 读取HDFS文件
  • Java中使用this调用构造方法

    在 Java 中 可以使用this 关键字来调用同一个类中的其他构造方法 这种方式通常被用于避免代码重复 或者在构造方法中需要进行额外的初始化操作时 在一个类中 可以定义多个不同参数列表的构造方法 如下所示 public class MyC
  • Submitting multiple batch scripts to LSF

    原文链接 https hpc ncsu edu Documents lsf scripts php Many workflows involve submitting multiple compute jobs with slightly
  • D盘新建删除移动文件需要管理员权限怎么办?

    问题描述 好像是我在进行一次d盘大瘦身之后 Program Files中的进行新建 删除时候就变得需要管理员权限了 但是d盘的其他文件夹就可以正常操作 唯独这一个Program Files 这让我很不爽 所以就想办法解决嘛 解决办法 网上说
  • 支付宝商户支付接口接入流程

    支付宝商户支付接口接入流程 详细说明支付宝商户支付接口接入流程和注意事项 便于大家在对接过程中少走弯路 实现快速对接 目前网上也有资料 这些资料不够完整全面 导致在对接过程中也会出现一些问题 其中支付功能主要包括 支付 APP支付 WAP支
  • linux如何获得宝塔账号密码信息

    在ssh终端输入 etc init d bt default 会得到如下信息
  • 14 Binder通信之应用层AIDL实现示例

    Binder通信之应用层AIDL实现示例 一 什么是AIDL AIDL Android Interface Definition Language 即Android接口定义语言 Android系统中 每个进程都运行在一块独立的内存中 在其中
  • 最小生成树的权值之和-Prim算法

    问题描述 已知含有n个顶点的带权连通无向图 采用邻接矩阵存储 邻接矩阵以三元组的形式给出 只给出不包括主对角线元素在内的下三角形部分的元素 且不包括不相邻的顶点对 请采用Prim算法 求该连通图从1号顶点出发的最小生成树的权值之和 输入形式
  • 8-NodeJS基础

    Node Node是一个基于Chrome V8引擎的 javaScript代码运行环境 什么软件可以运行javaScript 它就是javaScript的运行环境 Node js是运行在代码环境之上的语言 由ECMAScript 和模块的A