前端实现全栈开发成为“人才”的必经之路:Node.js-基础版

目录

一、Node下载

二、模块的引入与js文件的运行

三、Node常用全局模块

1、querystring模块

2、url模块

3、fs模块

1)、写入

2)、读取        

3)、追加    

4、http模块

5、http、fs、url的综合运用

四、思考:如何不借助url.parse方法,解析地址得到的结果和使用该方法相同?

五、三方模块的下载

1、npm命令的了解与使用

2、npm的常见命令

 六、mongoose:连接与使用mongodb数据库

1、mongoose的下载

2、使用mongoose创建集合

1)、连接 mongodb

2)、创建集合

 2、创建集合规则的另外一些语法

1)、必填字段

2)、限制字符串的最小长度和最大长度

3)、去除两边的空格

4)、限制数字的最大和最小值

5)、设置默认值

6)、指定该字段能够拥有的值

7)、自定义验证

3、对文档的操作(增删改查)

1)、创建文档

2)、查找文档

3)、删除文档

4)、更新文档

七、模块化开发

1、一个简单的模块开发项目目录

八、AJAX的使用

1、什么是AJAX

2、AJAX的优缺点

3、AJAX的基本使用

1)、创建一个AJAX对象

2)、配置连接信息

3)、发送请求

4、一个基本的AJAX请求

1)、ajax状态码

2)、readyStateChange

3)、xhr.onload事件

4)、responseText

5、AJAX案例

6、使用 AJAX 发送请求时携带参数

1)、发送一个带有参数的 get 请求

2)、发送一个带有参数的 post 请求

九、总结


        后端的实现不仅仅只有JAVA、Python、PHP等语言,前端也有对应的语言,那就是Node.js。Node号称前端中的后端,作用和其他后端语言一样,可以搭建服务器、连接数据库、返回数据等。并且Node也有对应的框架,例如Express、Remix等等,能够快速搭建服务器,让程序员更加轻松。

        ******注意,在Node中,我们之前使用的部分JS方法是不存在的,例如:元素节点的获取、localStorage、location等等,如果使用了就会报错。

一、Node下载

        在这就不多说如何下载了,不懂下载和安装的朋友自行去其他帖子查看,在这提供一篇个人认为比较详细的博客:Node的下载与安装。提醒一句,建议下载长期维护版本,不要下载新版,新版可能出现一些未知的Bug,到时候会比较麻烦。

二、模块的引入与js文件的运行

什么是模块?

        模块其实就是一个个已经被他人封装好的js文件,能够直接被我们使用。在Node中,可以使用require来引入全局模块、三方模块以及自定义模块

var 变量=require("模块路径")
  • 全局模块:就是下载Node时自带的模块;
  • 三方模块:就是Node中没有,但已经被别人写好并开源在某个网站上的模块,我们可以通过某种方式去下载;
  • 自定义模块:顾名思义,就是自己定义、封装的js文件,能够在其他文件内引入并使用;

如何启动已经引入模块的JS文件

        终端进入保存着该文件的文件夹后,使用node 文件名.js运行文件。

我的app.js保存在某个文件夹中,进入该文件夹后直接使用上述命令运行指定文件。

三、Node常用全局模块

1、querystring模块

        querystring模块作用是拆解参数,变为键值对形式。

例如:

var qs = require("querystring");
var url = "name=txl&age=18";
console.log(qs.parse(url));  //{ name: 'txl', age: '18' }

        可以看出,querystring.parse根据&符号进行分割。

        但该模块有很大的局限性:只能解析传递的参数部分,如果传递的是一个完整的url,那么解析的结果就不是我们想要的,所以不推荐使用该模块。

var qs = require("querystring");
var url = "https://localhost:8080?name=txl&age=18";
console.log(qs.parse(url));
/*
    {
      'https://localhost:8080?name': 'txl',
      age: '18'
    }
*/

2、url模块

        url模块可以解析完整的网址,即可以得到网址的各个部分,例如:协议、域名、端口号、请求路径、请求参数等。

        url中有很多的方法,但我们最常用的就是url.parser()。该方法能帮助我们得到上述的所有东西。至于其他方法,有兴趣的小伙伴自行查阅官方文档。Node.js官方中文文档

        语法:url.parse(地址,true/false)

  • 第一个参数就是要解析的地址;
  • 第二个参数是一个布尔值,表示是否要调用querystring.parse()解析地址中的参数部分,true表示需要,false表示不需要,默认是false
var url = require("url");
var urlstr = "https://localhost:8080/s/index.html?name=txl&age=18";
console.log(url.parse(urlstr, true));
/*
    Url {
      protocol: 'https:',
      slashes: true,
      auth: null,
      host: 'localhost:8080',
      port: '8080',
      hostname: 'localhost',
      hash: null,
      search: '?name=txl&age=18',
      query: [Object: null prototype] { name: 'txl', age: '18' },
      pathname: '/s/index.html',
      path: '/s/index.html?name=txl&age=18',
      href: 'https://localhost:8080/s/index.html?name=txl&age=18'
    }
*/

3、fs模块

        fs模块:FileSystem,文件系统,提供了可以操作文件的API,例如:读取、写入、追加

1)、写入

        语法:fs.readFile("文件路径","可选参数:设置读取结果的字符编码",回调函数),异步的读取文件。

  • 第二个参数是可选参数,如果不设置字符编码,那么读取的结果就是十六进制的;
  • 回调函数有两个形参,第一个是err,也就是当读取错误时,err才有值,否则为null,第二个是data,也就是读取的结果,所以是一个错误优先的回调函数;
const fs = require("fs");

// 读取文件
fs.readFile("./public/css/style.css", "utf8", (err, data) => {
	if (err) throw err;
	console.log(data)
});

2)、读取        

        语法:fs.writeFile("文件路径","要写入的内容","可选参数:设置字符编码",回调函数),异步的写入文件,会把原本的内容覆盖

fs.writeFile("./public/css/style.backup.css", "123456", () => {
    console.log("写入完毕");
});

注意:当文件的读取和写入同时存在于相同作用域时,会先执行写入方法,后执行读取方法。

3)、追加    

        语法:fs.appendFile("文件路径","要写入的内容","可选参数:设置字符编码",回调函数),异步追加文件内容,不会把原本的内容覆盖。

fs.appendFile("./public/css/style.backup.css", "123456", () => {
    console.log("追加完毕");
});

常用模块肯定不止这些,但目前这三个(可用的就两个)其实已经够初学者使用了。

4、http模块

        该模块用于使用代码搭建服务器

        前端的一切action、href、src,所有引入路径的地方,全都被node.js当作了是一个路由请求,解析请求,读取对应的文件给用户看,所以需要我们手动的搭建服务器,返回这些用户所需要的资源。

        在我们开发时,搭建的都是本地服务器,即只有自己和同一局域网中的人才可以访问。

  • 如果自己想要访问自己,打开浏览器,可以使用127.0.0.1(或者localhost)+端口号进行访问;
  • 访问别人(前提:防火墙要关闭),需要别人把自己的ipv4地址给你,地址栏输入别人的ip+端口号进行访问;

        使用http模块搭建服务器的固定步骤:

  • 第一步:引入http模块
const http=require("http")
  •  第二步:使用createServer方法创建服务器
const a=http.createServer()
  • 第三步:使用listen方法为服务器指定一个端口进行监听
const http=require("http")
const a=http.createServer()
app.listen(端口号)
  • 第四步:使用on方法给服务器绑定请求事件
const http=require("http")
const a=http.createServer()
app.listen(端口号)

app.on("request",(req,res)=>{})
  • app.on()的第一个参数必须传入"request";
  • app.on()的第二个参数是一个回调函数,该回调函数有两个参数,第一个参数是前端发来的请求,第二个参数是后端返回给前端的东西;
    • req:request,保存着请求对象,有一个req.url属性,我们拿到这个属性值之后,就需要进行解析操作(url.parse方法),获取有用的信息;

    • res:response,保存响应对象,提供了一个方法:res.end("要响应的东西"),可以把数据返回给前端;

5、http、fs、url的综合运用

// 引入http模块
var http = require("http");
var url = require("url");
var fs = require("fs");
// 创建服务器
var app = http.createServer();

// 为服务器绑定请求事件,回调函数内执行操作
app.on("request", (req, res) => {
	/* 
        req:request,保存着请求对象,有一个req.url属性,
            我们拿到这个属性值之后,就需要进行解析操作,获取有用的信息
   */
	var urlobj = url.parse(req.url, true);
	// 设置响应头,可以设置返回的编码格式
	// res.writeHead(200, { "content-type": "text/plain;charset=utf-8" });
	if (urlobj.pathname == "/" || urlobj.pathname == "/index.html") {
		/* 
        res:response,保存响应对象,提供了一个方法:res.end("要响应的东西")
    
    */

		fs.readFile("./public/html/index.html", (err, data) => {
			res.end(data);
		});
	} else if (urlobj.pathname.match(/html/)) {
		console.log(urlobj.pathname);
		fs.readFile("./public" + urlobj.pathname, (err, data) => {
			res.end(data);
		});
	} else if (urlobj.pathname.match(/jpg|png|webp|js|css/) != null) {
		console.log(urlobj.pathname);
		fs.readFile("./public" + urlobj.pathname, (err, data) => {
			res.end(data);
		});
	}
});

// 为服务器设置监听的端口号
app.listen(3000, () => {
	console.log("服务器启动成功");
});

四、思考:如何不借助url.parse方法,解析地址得到的结果和使用该方法相同?

        咱就是说,直接上代码吧~注释什么的都有。

function UrlParse(url) {
	// 以 ftp://www.baidu.com:443/index.html?name=dy&pwd=123123 为例
	// 创建解析后保存值的空对象
	var urlobj = {};
	// 根据 : 截取字符串,第一个:前的是协议
	if (url.indexOf(":") >= 8) {
		// 如果获取到的第一个:下标超过了8,则说明网址不是从从浏览器复制的,默认添加协议http
		urlobj.protocol = "http";
	} else {
		urlobj.protocol = url.substring(0, url.indexOf(":"));
		// 在获取到协议后,应该将原本的路径改变,从第一个:开始,+3是因为:后还有两个/,要保存的后续路劲不需要 协议:// 这些东西
		url = url.slice(url.indexOf(":") + 3); //此时的路径变为 www.baidu.com:443/index.html?name=dy&pwd=123123
	}

	// 提前设置好主机名
	var hostname = null;
	// 如果除去 协议:// 后,还存在 / ,表示请求的是当前网站下的子网页
	if (url.indexOf("/") != -1) {
		// 那么就截取到 / 之前就是主机名
		hostname = url.slice(0, url.indexOf("/"));
	} else {
		// 没有 / 就代表只是www.baidu.com:443,即没有子路由,直接赋值
		hostname = url;
	}

	// 根据 : 分割,即将域名和端口号分隔开,不论是否存在端口号,都至少能找到域名
	var host = hostname.split(/:/);
	urlobj.hostname = host[0];

	// host[1]就是表示端口号,如果端口号不为false,则说明存在端口号
	if (host[1]) {
		urlobj.port = host[1];
	} else if (urlobj.protocol == "http" && !host[1]) {
		// 如果不存在端口号,但是协议为http的话,说明使用的是默认端口号80
		urlobj.port = "80";
	} else if (urlobj.protocol == "https" && !host[1]) {
		// 如果不存在端口号,但是用的协议是https的话,则说明使用的是默认端口号443
		urlobj.port = "443";
	} else {
		// 既不是http,也不是https,也没有端口号的话,表示的是使用的其他协议的默认端口,我这设置为/
		urlobj.port = "/";
	}

	// 提前设置好urlobj中的路径属性
	urlobj.pathname = null;
	// 提前设置好参数对象
	urlobj.query = {};
	// 如果除去 协议:// 后,还存在 / ,表示请求的是当前网站下的子网页
	if (url.indexOf("/") != -1) {
		// 从 / 开始截取,即舍去主机名,只要子路由和参数部分
		url = url.slice(url.indexOf("/")); //此时的url为:/index.html?name=dy&pwd=123123
		// 根据?分割,不论是否存在?,都至少会出现一个数组,只是说当没有?时,该数组的长度为1,里面保存的是子路由,长度为2表示的是子路由和请求的参数。
		urlobj.pathname = url.split("?")[0];
	} else {
		// 没有 / 代表没有子路由,所以设置pathname为null
		urlobj.pathname = "null";
		// 没有子路由就代表着没有参数,之前已经设置好了query为空对象了,所以可以直接返回解析好的路径对象urlobj
		return urlobj;
	}

	// 进入到这说明是有子路由的,就判断有没有参数,如果url.split("?")[1]为true,就说明是有参数
	if (url.split("?")[1]) {
		// 有参数的话,就根据&进行分割
		var queryarr = url.split("?")[1].split("&");
		for (var i = 0; i < queryarr.length; i++) {
			// 根据=分割,分割之后只会出现长度为2的数组

			// 下标为0代表键
			var key = queryarr[i].split("=")[0];
			// 下标为1代表值
			var item = queryarr[i].split("=")[1];
			urlobj.query[key] = item;
		}
	}

	return urlobj;
}

var urlobj = UrlParse(
	"ftp://www.jd.com:443/index.html/a/b.html?name=dy&pwd=123123&age=18"
);
console.log(urlobj);

五、三方模块的下载

        之前有提到过除了官方模块之外,还有第三方模块的存在。那么第三方模块是存在于哪里呢?所有的三方模块(这里说的是JavaScript的第三方模块)都存在于npm官网上。

        那么如何下载三方模块?答案是通过npm命令。

1、npm命令的了解与使用

npm 为你和你的团队打开了连接整个 JavaScript 天才世界的一扇大门。它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package) (即,代码模块)。来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您能够轻松跟踪依赖项和版本。

--来自npm中文文档

        说白了就是专门管理第三方模块的,用于模块的下载、安装、删除、更新、维护包的依赖关系 等等。

        npm的使用方式需要在终端界面下才可以,windows系统可以使用“win”键+R键,然后输入“cmd”回车打开终端

        在打开终端后,需要使用cd命令进入到你想把三方模块下载到的文件夹中 ,cd命令不知道的朋友,可以自行查找相关资料,或者查看我这提供的别的博客:Window系统 cd命令

        或者直接在存储三方模块的文件夹中,在顶部地址栏输入cmd+回车也可以直接进入终端界面。

2、npm的常见命令

  • npm下载命令:npm install 包名。

        例如我们想要下载一个mysql包:

        也可以使用简写形式:npm i 包名。

  • npm删除命令:用于删除已经安装好的包,npm uninstall 包名 

        也可以使用简写形式:npm un 包名。

  • npm更新命令:npm update 包名。 

 六、mongoose:连接与使用mongodb数据库

        以mongodb数据库为例,通过使用mongoose对mongodb进行操作(增删改查等)。

        注意:使用mongoose之前,要先下载mongoddb数据库才行:Mongodb的下载与安装

        后续内容会出现Promise的语法,我主要使用了then和catch回调,不了解的同学可以简单的理解一下:

        then和catch都是回调函数:

  • then只会在方法成功使用时被调用,并打印成功的结果;
  • catch只会在方法调用失败是被调用,并能够输出错误结果; 
xxxx().then(res=>{
    //有一个res形参,如果方法调用后有返回值,那么res就能够接收这个返回值
    console.log("then回调会在xxx方法执行成功时被调用")
}).catch(err=>{
    //有一个err形参,如果方法调用后报错,那么errr就能够接收这个错误信息
    console.log("catch回调会在xxx方法执行失败时被调用",err)
})

1、mongoose的下载

        进入终端并进入指定文件夹后,使用npm install mongoose进行下载。

        出现以上结果表示下载成功!

        在文件夹中会多出一个名为:node_modules的目录,保存的就是下载的文件。

2、使用mongoose创建集合

  • 创建一个JS文件,在其中引入mongoose模块。
const mongoose = require("mongoose");

1)、连接 mongodb

//在 MongoDB 中,不需要显示的去创建数据库,如果正在使用的数据库不存在,MongoDB 会自动创建
mongoose
	.connect("mongodb://localhost/数据库名") 
	.then(() => {
		console.log("数据库连接成功");
	})
	.catch((err) => {
		console.log(err);
	});

//这里的then和catch都是使用Promise封装后出现的回调函数,不了解Promise的可以去学习一下
//当然,这里的then和catch都不是必要的,可以省略不写

2)、创建集合

        在mongodb中,数据表被叫做集合,数据被叫做文档

        创建集合分为两步:

  • 设定集合规则:设定规则的目的就是为了限制插入集合中的文档类型要相同,如果不同的话则会很混乱,到时候前端渲染会很麻烦。通过mongoose中的Schema方法,传入一个对象,该对象就是集合的规则。
const cursorSchema=new mongoose.Schema({
    name:String, //name列为字符串类型
    author:String, //author列为字符串类型
    isPublished:Boolean //isPublish列为布尔类型
})
  • 创建集合并应用规则:使用model方法应用集合规则
const Course=mongoose.model("Course",courseSchema)

        model方法可以传入三个参数:

  • 第一个参数:模型名,后面就是根据模型名来对数据库进行操作,并且建议将模型名和变量名取相同的名字;
  • 第二个参数:就是创建的规则变量名;
  • 第三个参数:要将规则应用到哪个集合上。可以不填,不填的话就默认是模型名的小写复数形式,如courses;

 2、创建集合规则的另外一些语法

        集合规则也可以叫做mongoose验证。上面说到创建集合规则时,只是简单的限制了集合中每一列的数据类型,其实还有一些更加高级的限定,当要给某一项设置多个验证时,就要使用对象写法了。

1)、必填字段

        语法:required:[true/false,'文本']

  • 第一个参数:表示是否必填,true为必填,false表示不是必填项,不过一般设置required就是为了设置成必填;
  • 第二个参数是自定义错误信息,当没有传入该字段时,会显示你在这设置的错误信息;
const Schema=mongoose.Schema({
    //设置多项验证,采用对象的写法
    name:{
        type:String, //设置数据类型
        required:[true,"name不能为空!"]
    }
})

2)、限制字符串的最小长度和最大长度

        语法:

  • 最小长度:minlength:[num,"文本"]
  • 最大长度:maxlength:[num,"文本"]

        注意:

  • 第一个参数是Number类型的数字;
  • 第二个参数是自定义错误信息;
const Schema = mongoose.Schema({
  name: {
    type: String,
    required: [true, "name不能为空"],
    // 最小长度
    minLength: [2, "name不能小于2个字符"],
    // 最大长度
    maxlength: [5, "name不能大于5个字符"]
  },
});

3)、去除两边的空格

        有时候前端传过来的数据没有做数据处理,比如去除数据左右的空格,那么这时候就可以通过集合规则来实现最后的一道防线。 

        语法:trim:true/false;

const Schema = mongoose.Schema({
  title: {
    type: String,
    required: [true, "标题不能为空"],
    // 最小长度
    minLength: [2, "标题不能小于2个字符"],
    // 最大长度
    maxlength: [5, "标题不能大于5个字符"],
    // 去除字符串两边空格
    trim: true,
  },
});

4)、限制数字的最大和最小值

        之前的maxlength和minlength都是针对字符串类型的数据,数值类型的数据有专门的限制属性。

         语法:

  • min:[num,"文本"]
  • max:[num,"文本"]

        注意:

  • 第一个参数是一个数值类型的参数;
  • 第二个参数是自定义错误文本;
const Schema = mongoose.Schema({
  price: {
    type: Number,
    // 最小长度
    min: [2, "价格不能低于2元"],
    // 最大长度
    max: [11, "价格不能高于11元"]
  },
});

5)、设置默认值

        如果没有给某一项传值,那么可以设置一个默认值,用于解决数据缺失问题。

        语法:default:xxx

const Schema = mongoose.Schema({
  publishDate: {
    type: Date,
    //default 默认值
    default: Date.now,
  }
});

6)、指定该字段能够拥有的值

        可以通过enum属性,设置当前字段能够有哪些值,如果传入的值不在列举的这些值当中,那么就会报错。

        语法:enum:{values:["值1","值2","值3"...],message:"文本"}

        注意:

  • enum是一个对象;
  • 可以设置两个属性:
    • 第一个属性是values:是一个数组类型的数据,用于列举当前字段能够传入哪些值,传入不在values中的值则会报错 ;
    • 第二个属性是message:也就是自定义报错信息;
const Schema = mongoose.Schema({
  category: {
    type: String,
    //   enum 只能是指定的值,即可以列举去当前字段的可以拥有的值
    enum: {
      values: ["html", "css", "javascript", "vue", "react", "node", "mongoose"],
      message: "请输入正确的分类",
    },
  }
})

7)、自定义验证

        除了可以设置已经规定好的验证之外,还可以设置自定义验证,用自己的想法去验证文档是否是自己想要的 。

        语法:

validate:{
    validator:(v)=>{
        return 要执行的操作
    },
    message:"文本"
}

        注意:

  • validate是一个对象;
  • validate中的validator是一个函数类型的属性;
    • validator有一个形参v,v表示要被验证的值;
    • validator中写验证v的代码,如v&&v>10
    • 返回值是一个布尔值; 
  • message就是自定义错误信息;
const Schema = mongoose.Schema({
  //   自定义验证信息
  author: {
    type: String,
    validate: {
      validator: (v) => {
        // 返回布尔值,true验证成功,false验证失败,v就是要被验证的值
        // v不为空且长度大于4
        return v && v.length > 4;
      },
      // 自定义错误信息
      message: "作者名称不能为空且长度大于4",
    },
  },
});

        注意:以上部分方法的第二个参数不是必须的,可以只传第一个参数。如果只传一个参数的话,那就不需要写中括号的形式了。

const Schema = mongoose.Schema({
  title: {
    type: String,
    required: true,
    // 最小长度
    minLength:2,
    // 最大长度
    maxlength:5,
  },
});

3、对文档的操作(增删改查)

1)、创建文档

        创建文档,其实就是向数据库中插入文档,常用方法有两种:

方法一:创建实例对象并调用save方法

        方法一分两步:

  • 创建集合实例,集合就是在应用集合规则时创建的集合;
const Course=mongoose.model("Course",courseSchema)
  • 调用实例下的save方法将文档保存到集合中;
// 创建集合实例,并在构造函数中传入对象,对象的内容就是要插入的文档
const course = new Course({
  name: "Node.js course",
  author: "xxxx",
  isPublished: true,
});
//将数据保存到数据库中
course.save();

方法二:调用集合模型下的create方法

        给create方法传入一个对象,对象中的内容就是要插入的文档。

Course.create({
  name: "HTML",
  author: "xxxx",
  isPublished: false,
})

        注意,这里的Course不是实例对象,是使用集合规则时的那个变量名,也就是创建实例对象时的那个构造函数。(实例对象按照规定一般首字母是小写的,如果是构造函数的话,首字母要大写。)

2)、查找文档

  • find方法
Course.find().then((result) => {
  console.log(result);
});

        find方法无论找到几条文档,返回的都是一个数组,如果没有返回的文档,则是一个空数组。

  • findOne方法
Course.findOne();

        返回一条文档,默认返回当前集合中的第一条文档。也可以给条件。

  • 可以指定范围查找文档
Course.find({ age: { $gt: 18, $lt: 50 } }).then((result) => {
  console.log(result);
});

        使用表达式查询某一范围的文档,find方法内传入一个对象类型的参数,对象内表示要根据哪一项来查找。$gt表示大于,$lt表示小于

  • 可以查找是否包含指定值
Course.find({ hobies: { $in: ["足球"] } }).then((result) => {
  console.log(result);
});

        同样的,也是在find方法内传入一个对象,$in表示要包含什么值。

  • 查看指定字段
Course.find()
  .select("name author -_id")
  .then((result) => {
    console.log(result);
  });

        在find方法后使用 .select 方法,传入一个字符串类型的参数,字符串内表示想查看哪些字段,各字段使用空格隔开。字段前面加上一个短横线(-)表示不想查看该字段,即最后的结果不会出现_id字段的值。

  • 将查找出来的结果升序/降序排列
Course.find()
  .sort("age")
  .then((result) => {
    console.log(result);
  });

        在find方法后使用 .sort 方法就可以实现排序,sort方法中传入一个字符串类型的参数,表示要根据哪个字段进行排序,默认是升序排序;

        如果想要降序排序,就在传入的字段前加上一个负号(-)

Course.find()
  .sort("-age")
  .then((result) => {
    console.log(result);
  });

        此时的结果就是降序排序了。

  • 跳过指定数量的文档以及限制查询文档结果的数量
Course.find()
  .skip(2)
  .limit(2)
  .then((result) => {
    console.log(result);
  });
  • skip方法是跳过指定数量的文档;
  • limit方法是限制查找的结果为指定数字;
  • 一般这两个方法搭配使用,进而实现数据的分页效果

3)、删除文档

  • findOneAndDelete()
Course.findOneAndDelete({}).then((result) => {
  console.log(result);
});

        该方法是找到一条文档并删除,并返回删除的文档,then的形参接收的就是被删除的文档。如果不使用条件,那么默认找到的是第一条文档。

  • deleteMany()

Course.deleteMany({}).then((result) => {
  console.log(result);
});

        返回的结果是一个对象,表示删除了几条文档。同样的可以传入条件,根据条件删除多条文档。

4)、更新文档

  • updateOne()
Course.updateOne({ 查询条件 }, { 要修改的值 }).then((result) => {
  console.log(result);
});

        该方法只会找到符合条件的第一个文档并修改。

        传入两个参数,每个参数都是对象类型的:

  • 第一个参数:是指定查找的条件;
  • 第二个参数:是要修改值;
Course.updateOne({ name: "Node.js基础" }, { name: "Python" }).then((result) => {
  console.log(result);
});
  • updateMany()

Course.updateMany({ 查询条件 }, { 要修改的值 }).then((result) => {
  console.log(result);
});

        同updateOne,只是该方法是查找多个结果并修改。

Course.updateMany({}, { name: "Python" }).then((result) => {
  console.log(result);
});

七、模块化开发

        本篇博客的最开始就提到过一个概念:模块。现在正式的说一下模块在程序开发中的概念:

为完成某一功能所需的一段程序或子程序;或指能由编译程序、装配程序等处理的独立程序单位;或指大型软件系统的一部分。

        个人理解:也就是将我们所写的部分代码封装成一个独立的JS文件,该文件能够被其他文件引入并使用。 

        那么什么是模块化开发?

  • 在之前我们使用JS代码写页面功能时,都是讲所有的代码写在一个JS文件中,当代码量很多时,就不便于查看和维护;
  • 模块划开发就是将各个功能部分的代码分别封装成不同的JS文件,然后引入同一个主JS文件中,当某一部分功能出现Bug时,就可以直接找到代码的位置了;

1、一个简单的模块开发项目目录

  •  文件夹1:数据库相关文件,有的数据库代码只能执行一次,如果多次执行服务器会报错,所以需要单独的建立JS文件保存相关代码;
  • 文件夹2:该文件夹是使用npm命令第一次下载三方模块后会自动出现的文件夹,里面保存着已下载模块;
  • 文件6和7:这两个文件是第一次使用npm命令下载三方模块时自动出现的文件,保存着一些配置信息,这里不详细说明;
  • 文件夹3:保存着前端的相关文件,例如HTML、CSS、JS等;
  • 文件夹4:保存着一些自定义工具模块,例如手机号的正则验证等;

2、模块的导入和导出

        导入很简单:require("要导入模块的路径")。如果是全局模块,直接写模块名就好;

        导出自定义模块:

  • exports.方法名=方法名
  • module.exports={方法1,方法2...}
  • 其实exports和module.exports是同一个,但如果两个导出的内容发生了冲突,那么以module.exports为准;

        当然模块的导入和导出形式不止这一种,还有其他方式,有兴趣的自行去了解。

        根据前面的所有内容,已经可以实现:后端连接数据库、对数据库进行操作、将数据返回给前端,但还有一个点没有实现,那就是前端如何主动的向后端索要数据?

八、AJAX的使用

        前端要想主动的找后端要数据,那就需要通过发送网络请求才可以,这时候ajax就闪亮登场了。

1、什么是AJAX

        ajax全名 async javascript and XML,是能够实现前后端交互的能力的途径,也是我们客户端给服务端发送消息以及接受响应的工具,是一个 默认异步 执行机制的功能。

2、AJAX的优缺点

        优点

  1. 不需要插件的支持,原生 js 就可以使用;

  2. 用户体验好(不需要刷新页面就可以更新数据);

  3. 减轻服务端和带宽的负担;

        缺点

  1. 搜索引擎的支持度不够,因为数据都不在页面上,搜索引擎搜索不到;

3、AJAX的基本使用

  • 在 js 中有内置的构造函数来创建 ajax 对象

  • 创建 ajax 对象以后,我们就使用 ajax 对象的方法去发送请求和接受响应

1)、创建一个AJAX对象

// IE9及以上
const xhr = new XMLHttpRequest()

// IE9以下
const xhr = new ActiveXObject('Mricosoft.XMLHTTP')

2)、配置连接信息

const xhr = new XMLHttpRequest()

// xhr 对象中的 open 方法是来配置请求信息的
// 第一个参数是本次请求的请求方式 get / post / put / ...
// 第二个参数是本次请求的 url 
// 第三个参数是本次请求是否异步,默认 true 表示异步,false 表示同步
// xhr.open('请求方式', '请求地址', 是否异步)
xhr.open('get', './data.php')

3)、发送请求

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')

// 使用 xhr 对象中的 send 方法来发送请求
xhr.send()

4、一个基本的AJAX请求

  • 一个最基本的 ajax 请求就是上面三步

  • 但是光有上面的三个步骤,我们确实能把请求发送的到服务端

  • 如果服务端正常的话,响应也能回到客户端

  • 但是我们拿不到响应

  • 如果想拿到响应,我们有两个前提条件

    1. 本次 HTTP 请求是成功的,也就是我们之前说的 http 状态码为 200 ~ 299

    2. ajax 对象也有自己的状态码,用来表示本次 ajax 请求中各个阶段

1)、ajax状态码

  • ajax 状态码 - xhr.readyState

  • 是用来表示一个 ajax 请求的全部过程中的某一个状态

    • readyState === 0: 表示未初始化完成,也就是 open 方法还没有执行

    • readyState === 1: 表示配置信息已经完成,也就是执行完 open 之后

    • readyState === 2: 表示 send 方法已经执行完成

    • readyState === 3: 表示正在解析响应内容

    • readyState === 4: 表示响应内容已经解析完毕,可以在客户端使用了

  • 这个时候我们就会发现,当一个 ajax 请求的全部过程中,只有当 readyState === 4 的时候,我们才可以正常使用服务端给我们的数据

  • 所以,配合 http 状态码为 200 ~ 299

    • 一个 ajax 对象中有一个成员叫做 xhr.status

    • 这个成员就是记录本次请求的 http 状态码的

  • 两个条件都满足的时候,才是本次请求正常完成

2)、readyStateChange

  • 在 ajax 对象中有一个事件,叫做 readyStateChange 事件

  • 这个事件是专门用来监听 ajax 对象的 readyState 值改变的的行为

  • 也就是说只要 readyState 的值发生变化了,那么就会触发该事件

  • 所以我们就在这个事件中来监听 ajax 的 readyState 是不是到 4 了

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')

xhr.send()

xhr.onreadyStateChange = function () {
  // 每次 readyState 改变的时候都会触发该事件
  // 我们就在这里判断 readyState 的值是不是到 4
  // 并且 http 的状态码是不是 200 ~ 299
  if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
    // 这里表示验证通过
    // 我们就可以获取服务端给我们响应的内容了
  }
}

3)、xhr.onload事件

        该事件只会在readyState为4时触发,相比于readystatechange,可以直接判断status。

<body>
    <script>
    	const xhr=new XMLHttpRequest()
        xhr.open("get","./data.php")
        xhr.send()
        xhr.onload=function(){
            console.log(xhr.readyState)
        }
    </script>
</body>

4)、responseText

  • ajax 对象中的 responseText 成员

  • 就是用来记录服务端给我们的响应体内容的

  • 所以我们就用这个成员来获取响应体内容

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')

xhr.send()

xhr.onreadyStateChange = function () {
  if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
    // 我们在这里直接打印 xhr.responseText 来查看服务端给我们返回的内容
    console.log(xhr.responseText)
  }
}

5、AJAX案例

<body>
    <div id="div"></div>
    <script>
        // http://xiongmaoyouxuan.com/api/tabs
        //创建ajax实例化对象
        const xhr = new XMLHttpRequest();
		//配置请求信息
        xhr.open("get", "http://xiongmaoyouxuan.com/api/tabs");
		//发送请求
        xhr.send();
		//请求完成后
        xhr.onload = function () {
            //获取到的数据是JSON格式字符串,需要JSON.parse解析为JSON格式的数据
            var resObj = JSON.parse(xhr.responseText).data;
            render(resObj);
        };

        function render(obj) {
            console.log(obj);
            //获取到的obj.lis是一个数组,所以通过map将数组内的每一项都进行模板字符串操作并用新的变量接收
            var str = obj.list.map(
                (item) =>
                `<li>
			          <img src="${item.imageUrl}">
			          <div>${item.name}</div>
        		</li>`
            );
            console.log(str);
            div.innerHTML = str.join("");
        }
    </script>
</body>

6、使用 AJAX 发送请求时携带参数

  • 我们使用 ajax 发送请求也是可以携带参数的

  • 参数就是和后台交互的时候给他的一些信息

  • 但是携带参数 get 和 post 两个方式还是有区别的

1)、发送一个带有参数的 get 请求

        get 请求的参数就直接在 url 后面进行拼接就可以。

const xhr = new XMLHttpRequest()
// 直接在地址后面加一个 ?,然后以 key=value 的形式传递
// 两个数据之间以 & 分割
xhr.open('get', './data.php?a=100&b=200')

xhr.send()

2)、发送一个带有参数的 post 请求

        post 请求的参数是携带在请求体中的,所以不需要再 url 后面拼接。

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')

// 如果是用 ajax 对象发送 post 请求,必须要先设置一下请求头中的 content-type
// 告诉一下服务端我给你的是一个什么样子的数据格式
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')

// 请求体直接再 send 的时候写在 () 里面就行
// 不需要问号,直接就是 'key=value&key=value' 的形式
xhr.send('a=100&b=200')

九、总结

  1. Node是前端中的后端语言,能够帮助我们成为全栈开发工程师,实现前端与后端的交互;

  2. 前端只要做出这些行为:地址栏输入地址并按下回车、src、href、form表单提交等,后端都会认为是在发起一个请求,然后进行响应,返回对应的资源;

  3. 后端通过http模块搭建好服务器,通过请求事件中回调函数的两个形参来进行请求的获取与响应:

    1)、req:能够获取请求的路径以及参数等;

    2)、res:能够将数据返回给前端;

  4. 前端通过创建ajax请求,向后端主动索要数据;

  5. 日后建议使用模块化的开发形式,保证各功能块之间的代码独立,以便日后维护;

               

猜你喜欢

转载自blog.csdn.net/txl2498459886/article/details/127011308