概述
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模块
- 读取文件
- 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 路径操作
- 路径拼接 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插件使用
- 在命令行工具下载插件
- 查看相应文档
- 调用插件
- 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 ‘别名’ 来运行
-
项目依赖
-
在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖。
-
使用
npm install 包名
命令下载的文件会默认被添加到package.json文件的dependencies字段中。 -
在dependencies字段中的第三方模块就是项目依赖
- 开发依赖
-
在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖。
-
使用
npm install 包名 --save-dev
命令下载的文件会默认被添加到package.json文件的devDependencies字段中。 -
在devDependencies字段中的第三方模块就是开发依赖
-
为什么记录依赖项
-
Node.js中下载的第三方包文件拥有非常多的细碎文件,将项目通过移动硬盘传递给别人时传输速度非常慢.
-
使用git工具管理项目时,不希望git管理node_modules文件夹,也不会将其上传到github中.
- 当其他人获取到项目时,可以在项目根目录下执行
npm install
命令,npm工具会自动去package.json文件中查找项目依赖文件并下载.
- 当项目上线以后,可以直接运行
npm install --production
下载项目依赖。如果不加–production,会下载全部依赖
模块查找规则
- require(’./find’); 当模块拥有路径 没有后缀时
- 先查找有没有同名js文件 ,再查找同名文件夹
- 有同名文件夹,就会去当前文件夹中的package.js文件中main选项中的入口文件
- 如果main选中的没有不存在或者没有指定入口文件, 找同名文件夹中的index.js
- 如果文件夹中没有index.js文件 就会报错
- 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响应和请求传递的数据块就是报文,又分请求报文和响应报文
- 请求报文
-
请求方式(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)
- req.headers //获取请求头的报文
- 响应报文
-
响应头 :返回的数据类型 : res.writeHead(HTTP状态码,{content-type:‘内容类型 ; charset=utf8’}) 服务端是通过响应对象res的writeHead方法响应给服务端请求的结果
res.writeHead(200,{'content-type':'text/html;charset=utf8'})
- HTTP状态码 响应此次请求的结果
- 100系列 代表服务器在等待
- 200系列 请求成功 206 断点续传成功 (迅雷下载暂停任务,关闭后再打开还可以继续下载)
- 300系列 重定向 (重新跳转)
- 400系列 客户端请求语法有误 404 请求的资源没有被找到 前端出现问题
-
500系列 服务端错误(505) 服务器崩溃 后端出现问题
- 内容类型 此次请求成功时 响应(返回)的内容的文件类型
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等
- 相同的请求地址,响应不同的资源,就是动态资源.
- 先通过url 获取用户输入的路径
- 然后通过 path 来拼接 文件的 绝对路径
- 利用fs 读取文件内容
- 把内容响应给客户端浏览器
第三方模块mime
- 下载npm install mime
- const mime = require(‘mime’)
- mime.getType(‘要判断的路径’) 会自动根据路径 返回文件的类型
- 通常在响应客户端 返回资源的类型不确定时使用
同步API和异步API(异步API返回结果的获取)
- 同步API会从上至下依次执行,只有一个执行完成后才会执行下一个
- 异步API需要等同步API执行完毕后 才会根据 触发的先后顺序执行
- 获取返回结果都是同步API 所以return 无法获得异步API的返回结果
- 要想获取异步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要加入系统环境变量 不然无法使用
向数据库中加入数据
- 连接MongoDB数据库
- 创建规则
- 创建集合实例
- 插入集合实例
- 保存
- 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() 响应客户端才会结束
- 可以将复杂的请求分步骤进行处理
应用场景:
-
路由守卫
- 我们项目中 有一些页面是需要登录才能进行访问的,此时我们就可以通过中间件来进行拦截。 如果是登录状态,那么就可以下发到对应的中间件,如果不是登录状态,提示用户进行登录
-
网站维护
- 利用中间件来进行全局的请求的监听,只要网站进行了维护,那么就阻止我们的请求下发
-
自定义404页面
- 把我们这个中间件写在 最下面
-
异常的处理
- 同步错误处理中间件
- app.use((err, req, res, next) => {}) 全局的异常的捕获的中间件, 只要出现了throw抛出异常,就会执行到这个中间件里面 err.message属性中就是自定义的错误信息
- 回调函数错误处理中间件
- 如果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) })
- 异步函数错误处理中间件 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 才可以访问到’内容’
参数的获取
- get 方式
- 直接利用 req.query 就能拿到 请求的参数,并且我们框架帮我们把这个参数转成了对象
- post 方式
- 建议使用第三方的模块 body-parser
- const parse = require(‘body-parser’)
- app.use(bodyParser.urlencoded({ extended: false })) 配置路由
-
- 通过req.body 就能得到数据
- 路由参数
- 配置路由参数的规则
- 配置了几个参数 请求路径(路径传递参数)就要填写几个参数
- 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
- cookie供服务器端存储数据(以域名的形式区分)
- cookie有过期时间,超过时间就会自动删除
- 每次请求都会自动发送到服务器
- session则是一个对象 存储在服务器端
- 验证账号密码正确时会在session中生成一个sessionID发送写入客户端的cookie中
- 每次客户端发送请求时, 服务端会判断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项目初始化
- 建立项目所需文件夹
- public 静态资源
- model 数据库操作
- route 路由
- views 模板文件
- 生成项目描述文件
- npm init -y
- 下载模板引擎第三方模块
- npm install express mongoose art-template express-art-template
- 入口文件app.js中 创建网站服务器
- 引入express模块
- 创建服务器对象 express()
- 监听端口listen
- 创建不同路由对象
- const home = express.Router()
- const list = express.Router()
mongoDB数据库添加账号
- 以管理员身份运行powershell
- 连接数据库mongo
- 查看数据库 show dbs
- 切换到admin数据库集合 use admin
- 切换后为这个集合添加创建超级管理员账户 db.createUser({user:‘root’,pwd:‘root’,roles:[‘root’]})
- user账号 pwd 密码 roles角色权限(root超级管理员, readWrite 读写权限)
- 卸载mongodb服务
- 停止服务 net stop mongodb
- mongod -remove
- 创建mongodb服务
- mongod --logpath=“C:Program FilesMongoDBServer4.1logmongod.log” --dbpath=“C:Program FilesMongoDBServer4.1data” --install --auth
- logpath 填mongod.log路径 dbpath 填 data文件夹路径
- –auth 不填 就不用验证数据库账号密码
- 开启服务 net start mongodb
- mongod --logpath=“C:Program FilesMongoDBServer4.1logmongod.log” --dbpath=“C:Program FilesMongoDBServer4.1data” --install --auth
在项目中使用账号连接数据库(为数据库创建普通账号)
- 以管理员身份运行PowerShell
- mongo进入mongodb数据库操作环境
- use admin 切换到admin数据库
- db.auth(‘root’,‘root’) 登录admin数据库
- use alibaixiu 切换到要创建普通账户的数据库(没有会自动创建这个数据库)
- db.createUser({user:‘账号名称’,pwd:‘账号密码’,roles:[‘readWrite’]}) 创建账户 权限为读写即可
- 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文件中获取配置信息
- 下载 npm install config
- 项目根目录创建config文件夹
- 创建default.json development.json production.json 文件
- 默认/开发环境/生产环境 搜索对应的运行环境json文件没有配置信息属性就会去 default(可以用于存储公共部分) 中查找
- json文件中至少要放一个空对象 不然会报错
- json文件中配置信息的属性名也要用引号""
- 创建default.json development.json production.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数据') })
最后
以上就是清秀蜡烛为你收集整理的8-NodeJS基础数据库的全部内容,希望文章能够帮你解决8-NodeJS基础数据库所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复