node.js入门

文章目录

Node.js全局对象

在浏览器中全局对象是window,在Node中全局对象是global

Node中全局对象下有以下方法,可以在任何地方使用,global可以省略

  • console.log() 在控制台输出
  • setTimeout() 设置超时定时器
  • clearTimeout() 清除超时定时器
  • setInterval() 设置间歇定时器
  • clearInterval() 清除间歇定时器

Node.js模块化开发

Node.js中模块化开发规范

在这里插入图片描述

模块成员导出
//a.js
// 在模块内部定义变量
let version = 1.0;
// 在模块内部定义方法
const sayHi = name => `你好,${name}`;
// 向模块外部导出数据
exports.version = version;
exports.sayHi = sayHi;
模块成员导入
// b.js
// 在b.js模块中导入模块a
let a = require('./a.js');
// 输出a模块中的version变量
console.log(a.version);
// 调用a模块中的sayHi方法并输出返回值
console.log(a.sayHi('刘德华'));
模块成员导出的另一种方式

在这里插入图片描述

系统模块

在这里插入图片描述

系统模块fs文件操作

f:file文件,s:system系统,文件操作系统

const fs = require('fs');
读取文件内容
fs.readFile('文件路径/文件名称',['文件编码'],callback)
读取文件代码示例
// 通过模块的名字fs对模块进行引用
const fs=require('fs');
// 通过模块内部的readFile读取文件内容
fs.readFile('./01.helloworld.js','utf8',(err,doc)=>{
    // 如果文件读取出错err是一个对象,包含错误信息
    // 如果文件读取正确err是null
    // doc是文件读取的结果
    console.log(err);
    console.log(doc);
});
写入文件内容
fs.writeFile('文件路径/文件名称','数据',callback);
写入文件内容代码示例
const fs = require('fs');
const content = '<h3>正在使用fs.readFile写入文件内容</h3>';
fs.writeFile('./demo.txt', content, err => {
    if (err != null) {
        console.log(err);
        return;
    }
    console.log('文件内容写入成功');
});

系统模块path路径操作

为什么要进行路径拼接

  • 不同操作系统的路径分隔符不统一
  • /public/uploads/avatar
  • Windows上是\ /
  • Linux上是/

路径拼接语法

path.join('路径','路径',...)

示例代码

// public/uploads/avatar
// 导入path模块
const path = require('path');
// 路径拼接
const finalPath = path.join('public', 'uploads', 'avatar');
// 输出结果public\uploads\avatar
console.log(finalPath);

相对路径vs绝对路径

  • 大多数情况下使用绝对路径,因为相对路径有时候相对的命令行工具的当前目录
  • 在读取文件或者设置文件路径时都会选择绝对路径
  • 使用__dirname获取当前文件所在的绝对路径

示例代码

const fs = require('fs');
const path = require('path');
console.log(__dirname);
console.log(path.join(__dirname, '01.helloworld.js'));
fs.readFile(path.join(__dirname, '01.helloworld.js'), 'utf8', (err, doc) => {
    console.log(err);
    console.log(doc);
});

第三方模块

什么是第三方模块

别人写好的、具有特定功能的、我们能直接使用的模块及第三方模块,由于第三方模块通常都是有多个文件组成并且被放置在文件夹中,所以又名包

第三方模块有两种存在形式
  • 以js文件的形式存在,提供·实现项目具体功能的API接口
  • 以命令行工具形式存在,辅助项目开发
获取第三方模块

npmjs.com:第三方模块的存储和分发仓库

npm:node的第三方模块管理工具

  • 下载:npm install 模块名称
  • 卸载:npm uninstall package 模块名称

全局安装与本地安装

  • 命令行工具:全局安装
  • 库文件:本地安装
第三方模块nodemon

nodemon是一个命令行工具,用以辅助项目开发

使用步骤

1.使用npm install nodemon -g下载它

2.在命令行工具中用nodemon命令代替node命令执行文件

第三方模块nrm

在这里插入图片描述

nrm:npm下载地址切换工具

npm默认的下载地址在国外,国内下载速度慢

使用步骤

1、使用npm install nrm -g下载它

2、查询可用下载地址列表nrm ls

3、切换npm 下载地址nrm use 下载地址名称

第三方模块Gulp

基于node平台开发的前端构建工具

将机械化操作编写成任务,想要执行机械化操作时执行一个命令行,命令任务就能自动执行

Gulp能做什么
  • 项目上线,HTML、CSS、JS文件压缩合并
  • 语法转换(es6、less…)
  • 公共文件抽离
  • 修改文件浏览器自动刷新
Gulp使用

1.使用npm install gulp下载gulp库文件

2.在项目根目录下建立gulpfile.js文件

3.重构项目的文件夹结构,src目录放置源代码文件,dist目录放置重构后的文件

4.在gulpfile.js文件中编写任务

5.在命令行工具中执行gulp任务

Gulp中提供的方法

在这里插入图片描述

Gulp插件

在这里插入图片描述

package.json文件

node_modules文件夹的问题

1.文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度很慢

2.复杂的模块依赖关系需要被记录,确保模块的版本的当前保持一致,否则会导致当前项目运行报错

package.json文件的作用

项目文件描述文件,记录了当前项目信息,例如项目名称、版本、作者、github地址、当前项目依赖了哪些第三方模块等。使用npm init -y命令生成

项目依赖
  • 在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖
  • 使用npm install包名命令下载的文件会默认被添加到package.json文件的dependencies字段中
开发依赖
  • 在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖
  • 使用npm install 包名–save-dev命令将包添加到package.json文件的devDependencies字段中
package-lock.json文件的作用
  • 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题
  • 加快下载速度,因为该文件中已经记录了项目依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要额外的工作

Node.js中模块加载机制

模块查找规则-当模块拥有路径但没有后缀时

在这里插入图片描述

模块查找规则-当模块没有路径且没有后缀时

在这里插入图片描述

服务器的基础概念

网站的组成

网站应用程序主要分为两大部分:客服端和服务器端

客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使用HTML、CSS、Javascript构建

服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑

Node网站服务器

能够提供访问服务的机器就是网站服务器,它能够接收客户端的请求,能够对请求做出相应

IP地址

互联网中设备的唯一标识,代表互联网协议地址

域名

由于IP地址难于记忆,所以产生域名的概念。所谓域名就是平时上网所用的网址

虽然在地址栏中输入的是网址,但是最终还是会将域名转换位ip才能访问到指定的网站服务器

端口

使用端口区分不同的服务,它是一些具有一定范围的数字,范围是0到65535,每一个向外界提供服务的软件,都要占用一个端口

URL

统一资源定位符,是专为标识Internet网上资源位置而设的一种编址方式,平时所见网页地址指的是URL

URL的组成

传输协议://服务器IP或域名:端口/资源所在位置表示

创建web服务器

// 用于创建网站服务器的模块
const http = require('http');
// app对象就是网站服务器对象
const app = http.createServer();
// 当客户端有请求来的时候
app.on('request', (req, res) => {
	//响应
    res.end('<h2>hello world</h2>');
});
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
HTTP协议
HTTP协议的概念

超文本传输协议,规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准

报文

在HTTP请求和响应过程中传递的数据块叫报文,包括要传送的数据和一些附加信息,并且要遵守好规定好的格式

请求报文

1.请求方式

  • GET 请求数据
  • POST 发送数据

2.请求地址

在这里插入图片描述

响应报文

1.http状态码

  • 200 请求成功
  • 404 请求的资源没有被找到
  • 500 服务器端错误
  • 400 客户端请求有语法错误

2.内容类型

  • text/html
  • text/css
  • application/javascript
  • image/jpeg

HTTP请求与相应处理

GET请求参数

参数被放置在浏览器地址栏中,例如:http://localhost:3000/?name=zhangsan&age=20

// 用于处理url地址
const url = require('url');
// 1.要解析的url地址
// 2.将查询参数解析成对象形式
let { query } = url.parse(req.url, true);
POST请求参数

在这里插入图片描述

路由

路由是指客户端请求地址和服务器端程序代码对应关系。简单地说,就是请求什么响应什么

// 1.引入系统模块http
// 2.创建网站服务器
// 3.为网站服务对象添加请求事件
// 4.实现路由功能
//   1.获取客户端的请求方式
//   2.获取客户端的请求地址
const http = require('http');
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
    // 获取请求方式
    const method = req.method.toLowerCase();
    // 获取请求地址
    const pathname = url.parse(req.url).pathname;
    res.writeHead(200, {
        'content-type': 'text/html;charset=utf8'
    });
    if (method == 'get') {
        if (pathname == '/' || pathname == '/index') {
            res.end('欢迎来到首页');
        } else if (pathname == '/list') {
            res.end('欢迎来到列表页');
        } else {
            res.end('你访问的页面不存在');
        }
    } else if (method == 'post') {

    }
});
app.listen(3000);
console.log('服务器启动成功');
静态资源

服务器不需要处理,可以直接响应给客户端就是静态资源,例如CSS、Javascript、image文件

动态资源

相同的请求地址不同的响应资源,这种资源就是动态资源

Node.js异步编程

同步API,异步API

同步API:只有当前API执行完成后,才能继续执行下

console.log('before');
console.log('after');

异步API:当前API的执行不会阻塞后续代码的执行

 console.log('before');
        setTimeout(
            () => {
                console.log('last');

            }, 2000)
 console.log('after');
同步API,异步API的区别(获取返回值)

同步API可以从返回值中拿到API执行的结果,但是异步API是不可以的

// 同步
function sum(n1, n2) {
    return n1 + n2;
}
const result = sum(10, 20);
console.log(result);//30
// 异步
function getMsg() {
    setTimeout(function () {
        return {
            msg: 'hello Node.js'
        }
    }, 2000)
}
const msg = getMsg();
console.log(msg);//undefined

通过回调函数执行异步代码

// 异步
function getMsg(callback) {
    setTimeout(function () {
        callback({
            msg: 'hello node.js'
        })
    }, 2000)
}
getMsg(function (data) {
    console.log(data);//{ msg: 'hello node.js' }
})

回调函数

自己定义函数让别人去调用

//getData函数定义
function getData (callback){}
//getData函数调用
getData(()=>{})

代码示例

function getData(callback) {
    callback(123);
}
getData(function (n) {
    console.log('callback函数被调用了');//callback函数被调用了
    console.log(n);//123

});
同步API,异步API的区别(代码执行顺序)

同步API从上到下依次执行,前面代码会阻塞后面的代码执行

异步API不会等待API执行完成后再向下执行代码

Node.js中的异步API

在这里插入图片描述

回调地狱问题
const fs = require('fs');
fs.readFile('./1.txt', 'utf8', (err, result1) => {
    console.log(result1);
    fs.readFile('./2.txt', 'utf8', (err, result2) => {
        console.log(result2);
        fs.readFile('./3.txt', 'utf8', (err, result3) => {
            console.log(result3);
        })
    })
});

输出

1
2
3
Promise

Promise出现的目的是解决Node.js异步编程中回调地狱的问题

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (true) {
            resolve({ name: '张三' });
        } else {
            reject('失败了');
        }
    }, 2000);
});
promise.then(result => console.log(result))//{ name: '张三' }
    .catch(error => console.log(error))//失败了
利用Promise解决回调地狱
const fs = require('fs');
function p1() {
    return new Promise((resolve, reject) => {
        fs.readFile('./1.txt', 'utf8', (err, result) => {
            resolve(result);
        })
    });
}
function p2() {
    return new Promise((resolve, reject) => {
        fs.readFile('./2.txt', 'utf8', (err, result) => {
            resolve(result);
        })
    });
}
function p3() {
    return new Promise((resolve, reject) => {
        fs.readFile('./3.txt', 'utf8', (err, result) => {
            resolve(result);
        })
    });
}
p1().then((r1) => {
    console.log(r1);
    return p2();
})
    .then((r2) => {
        console.log(r2);
        return p3();
    })
    .then((r3) => {
        console.log(r3)
    })
异步函数

异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不在有回调函数嵌套,是代码变得清晰明了

const fn = async () => { }
async function fn() { }
async关键字

在这里插入图片描述

// 1.在普通函数定义的前面加上async关键字,普通函数就变成了异步函数
// 2.异步函数默认返回值是promise对象
// 3.在异步函数内部使用throw关键字进行错误抛出
async function fn() {
    throw '发生了一些错误';
    return 123;
}
fn().then(function (data) {
    console.log(data);//123
}).catch(function (err) {
    console.log(err);//发生了一些错误

})
await关键字

在这里插入图片描述

// await关键字
// 1.它只能出现在异步函数中
// 2.await promise它可以暂停异步函数执行,等待promise对象返回结果后在向下执行函数
async function p1() {
    return 'p1';
}
async function p2() {
    return 'p2';
}
async function p3() {
    return 'p3';
}
async function run() {
    let r1 = await p1();
    let r2 = await p2();
    let r3 = await p3();
    console.log(r1);
    console.log(r2);
    console.log(r3);
}
run();

输出

p1
p2
p3

异步函数应用

const fs = require('fs');
//改造现有异步函数api,让其返回promise对象,从而支持异步函数语法
const promisify = require('util').promisify;
//调用promisify方法改造现有api,让其返回promise对象
const readFile = promisify(fs.readFile);
async function run() {
    let r1 = await readFile('./1.txt', 'utf8');
    let r2 = await readFile('./2.txt', 'utf8');
    let r3 = await readFile('./3.txt', 'utf8');
    console.log(r1);
    console.log(r2);
    console.log(r3);
}
run();

输出

1
2
3

数据库相关概念

在这里插入图片描述

Mongoose第三方包
  • 使用Node.js操作MongoDB数据库需要依赖Node.js第三方包mongoose
  • 使用npm install mongoose命令下载
启动MongoDB

在命令行工具中运行net start mongodb即可启动MongoDB,否则MongoDB无法连接

数据库连接

使用mongoose提供的connect方法即可连接数据库

//引入mongoose第三方模块用来操作数据库
const mongoose=require('mongoose');
//数据库连接
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true , useUnifiedTopology: true})
//连接成功
.then(()=>console.log('数据库连接成功'))
//连接失败
.catch(err=>console.log(err,'数据库连接失败'))
创建数据库

在MongoDB中不需要显式创建数据库,如果正在使用的数据库不存在,MongoDB会自动创建

MongoDB增删改查操作

创建集合

创建集合分为两步,一是对集合设定规则,二是创建集合,创建mongoose.Schema构造函数的实例即可创建集合

// 设定集合规则
const courseSchema = new mongoose.Schema({
    name: String,
    author: String,
    isPublished: Boolean
});
// 使用规则创建集合
const Course = mongoose.model('Course', courseSchema)//courses
创建文档方法一

创建文档实际上就是向集合中插入数据

分为两步:

1.创建集合实例

2.调用实例对象下的save方法将数据保存到数据库中

// 创建集合实例
const course = new Course({
    name: 'node.js',
    author: 'zhangsan',
    isPublished:true
});
// 将文档插入到数据库中
course.save();

创建集合和文档完整代码:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const courseSchema = new mongoose.Schema({
    name: String,
    author: String,
    isPublished: Boolean
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const Course = mongoose.model('Course', courseSchema)//courses
// 创建集合实例
const course = new Course({
    name: 'node.js',
    author: 'zhangsan',
    isPublished:true
});
// 将文档插入到数据库中
course.save();
创建文档方法二利用create方法接收异步API方式一
Course.create({ name: 'javascript', author: 'lisi', isPublished: false }, (err, result) => {
    console.log(err);
    console.log(result);
})
创建文档方法二利用create方法接收异步API方式二
/ 向集合中插入文档
Course.create({ name: 'javascript123', author: 'lisi', isPublished: false })
    .then(result => {
        console.log(result);
    })
MongoDB数据导入数据

mongoimport -d 数据库名称 -c集合名称 --file 到导入的数据文件

例如:mongoimport -d playground -c users --file ./user.json

找到mongodb数据库的安装目录,将安装目录下的bin目录放置在环境变量中

查询文档方法一find方法
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
//查询用户集合中所有文档
User.find().then(result => console.log(result));

输出

[ { hobbies: [ '足球', '篮球', '橄榄球' ],
    _id: 5c09f1e5aeb04b22f8460965,
    name: '张三',
    age: 20,
    email: 'zhangsan@itcast.cn',
    password: '123456' },
  { hobbies: [ '吃饭', '睡觉', '打豆豆' ],
    _id: 5c09f294aeb04b22f8460969,
    name: '赵六',
    age: 50,
    email: 'zhaoliu@itcast.cn',
    password: '123456' },
  { hobbies: [ '足球', '篮球' ],
    _id: 5c09f236aeb04b22f8460967,
    name: '李四',
    age: 10,
    email: 'lisi@itcast.cn',
    password: '654321' },
  { hobbies: [ '敲代码' ],
    _id: 5c09f267aeb04b22f8460968,
    name: '王五',
    age: 25,
    email: 'wangwu@itcast.cn',
    password: '123456' },
  { hobbies: [ '打豆豆' ],
    _id: 5c09f2d9aeb04b22f846096b,
    name: '狗蛋',
    age: 14,
    email: 'goudan@163.com',
    password: '123456' },
  { hobbies: [ '吃饭' ],
    _id: 5c09f2b6aeb04b22f846096a,
    name: '王二麻子',
    age: 32,
    email: 'wangermazi@itcast.cn',
    password: '123456' } ]

find方法条件查询

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
//通过_id字段查找文档,如果查询不到,则返回一个空数组
User.find({_id:'5c09f236aeb04b22f8460967'}).then(result => console.log(result));

输出

[ { hobbies: [ '足球', '篮球' ],
    _id: 5c09f236aeb04b22f8460967,
    name: '李四',
    age: 10,
    email: 'lisi@itcast.cn',
    password: '654321' } ]
查询文档方法二findone方法
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
// findOne方法返回一条文档,默认返回当前集合中的第一条文档
User.findOne().then(result=>console.log(result))

输出

{ hobbies: [ '足球', '篮球', '橄榄球' ],
  _id: 5c09f1e5aeb04b22f8460965,
  name: '张三',
  age: 20,
  email: 'zhangsan@itcast.cn',
  password: '123456' }

findone方法条件查询

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
// findOne方法返回一条文档,默认返回当前集合中的第一条文档
User.findOne({ name: '李四' }).then(result => console.log(result))

输出

{ hobbies: [ '足球', '篮球' ],
  _id: 5c09f236aeb04b22f8460967,
  name: '李四',
  age: 10,
  email: 'lisi@itcast.cn',
  password: '654321' }
查询文档–大于小于
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
//查询用户集合年龄字段大于20并且小于40的文档
User.find({ age: { $gt: 20, $lt: 40 } }).then(result => console.log(result))

输出

[ { hobbies: [ '敲代码' ],
    _id: 5c09f267aeb04b22f8460968,
    name: '王五',
    age: 25,
    email: 'wangwu@itcast.cn',
    password: '123456' },
  { hobbies: [ '吃饭' ],
    _id: 5c09f2b6aeb04b22f846096a,
    name: '王二麻子',
    age: 32,
    email: 'wangermazi@itcast.cn',
    password: '123456' } ]
查询文档–包含
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
//查询爱好字段中包含足球的文档
User.find({ hobbies: { $in:['足球'] } }).then(result => console.log(result))

输出

[ { hobbies: [ '足球', '篮球', '橄榄球' ],
    _id: 5c09f1e5aeb04b22f8460965,
    name: '张三',
    age: 20,
    email: 'zhangsan@itcast.cn',
    password: '123456' },
  { hobbies: [ '足球', '篮球' ],
    _id: 5c09f236aeb04b22f8460967,
    name: '李四',
    age: 10,
    email: 'lisi@itcast.cn',
    password: '654321' } ]
查询文档–选择要查询的字段
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
// 设定集合规则
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    eamil: String,
    password: String,
    hobbies: [String]
});
// 使用规则创建集合
// 1.集合名称
// 2.集合规则
const User = mongoose.model('User', userSchema);
// 选择要查询的字段
User.find().select('name email -_id').then(result => console.log(result))

输出

[ { name: '张三', email: 'zhangsan@itcast.cn' },
  { name: '赵六', email: 'zhaoliu@itcast.cn' },
  { name: '李四', email: 'lisi@itcast.cn' },
  { name: '王五', email: 'wangwu@itcast.cn' },
  { name: '狗蛋', email: 'goudan@163.com' },
  { name: '王二麻子', email: 'wangermazi@itcast.cn' } ]
查询文档–将数据按照年龄进行排序
// 根据年龄字段进行升序排列
// User.find().sort('age').then(result => console.log(result))
// 根据年龄字段进行降序排列
User.find().sort('-age').then(result => console.log(result))
查询文档–跳过数据和限制查询数量
// skip跳过2条数据,limit限制查询数量
User.find().skip(2).limit(3).then(result => console.log(result))
删除文档–删除单个
//删除一条文档
// 返回删除的文档
//如果查询条件匹配了多个文档,那么将会删除第一个匹配的文档
User.findOneAndDelete({_id:'5c09f1e5aeb04b22f8460965'}).then(result=>console.log(result))
删除文档–删除多个
// 删除多个 返回一个对象 两个属性 n:删除的数量 ok:1删除成功
User.deleteMany({}).then(result=>console.log(result))
更新文档–单个
// 更新集合中的单个文档
User.updateOne({ name: '李四' }, { name: '李狗蛋' }).then(result => console.log(result))

更新文档–多个
// 更新集合中的多个文档
User.updateMany({}, { age: 56 }).then(result => console.log(result))

mongoose验证

在创建集合规则时,可以设置当前字段的验证规则,验证失败就则输入插入失败

  • required:true 必传字段
  • minlength: 3 字符串的最小长度
  • maxlength : 20 字符串的最大长度
  • min: 2 数值最小为2
  • max: 100 数值最大为100
  • enum: [‘html’, ‘css’, ‘javascript’, ‘node.js’]
  • trim: true 去除字符串两边的空格
  • validate: 自定义验证器
  • default: 默认值
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
const postSchema = new mongoose.Schema({
    title: {
        type: String,
        // 必选字段
        required: [true, '请传入文章标题'],
        // 字符串的最小长度
        minlength: [2, '文章长度不能小于2'],
        // 字符串的最大长度
        maxlength: [5, '文章长度不能超过5'],
        // 去除字符串两边的空格
        trim: true
    },
    age: {
        type: Number,
        // 数值的最小范围
        min: 10,
        //数值的最大范围
        max: 100
    },
    publishDate: {
        type: Date,
        // 默认值
        default: Date.now
    },
    category: {
        type: String,
        //枚举 列举出当前字段可以拥有的值
        enum: ['html', 'css', 'javascript', 'node.js']
    },
    author: {
        type: String,
        validate: {
            validator: v => {
                // 返回值为布尔值
                // true 验证成功
                // false 验证失败
                return v && v.length > 4
            },
            // 自定义错误信息
            message: '传入的值不符合验证规则'
        }
    }
})
const Post = mongoose.model('Post', postSchema);
Post.create({ title: 'aaaa', age: 60, category: 'html', author: 'abcdv' }).then(result => console.log(result))
捕获mongoose错误信息
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
const postSchema = new mongoose.Schema({
    title: {
        type: String,
        // 必选字段
        required: [true, '请传入文章标题'],
        // 字符串的最小长度
        minlength: [2, '文章长度不能小于2'],
        // 字符串的最大长度
        maxlength: [5, '文章长度不能超过5'],
        // 去除字符串两边的空格
        trim: true
    },
    age: {
        type: Number,
        // 数值的最小范围
        min: 10,
        //数值的最大范围
        max: 100
    },
    publishDate: {
        type: Date,
        // 默认值
        default: Date.now
    },
    category: {
        type: String,
        enum: {
            values: ['html', 'css', 'javascript', 'node.js'],
            message: '分类名称要在一定的范围才可以'
        }
    },
    author: {
        type: String,
        validate: {
            validator: v => {
                // 返回值为布尔值
                // true 验证成功
                // false 验证失败
                return v && v.length > 4
            },
            // 自定义错误信息
            message: '传入的值不符合验证规则'
        }
    }
})
const Post = mongoose.model('Post', postSchema);
Post.create({ title: 'aaaa', age: 60, category: 'java', author: 'ab' })
    .then(result => console.log(result))
    .catch(error => {
        // 获取错误信息对象
        const err = error.errors;
        // 循环错误信息对象
        for (var attr in err) {
            console.log(err[attr]['message']);

        }
    })

输出

分类名称要在一定的范围才可以
传入的值不符合验证规则
集合关联

在这里插入图片描述

集合关联实现

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch(err => console.log(err, '数据库连接失败'))
//用户集合规则
const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    }
});
// 文章集合规则
const postSchema = new mongoose.Schema({
    title: {
        type: String
    },
    // 使用ID将文章集合和用户集合进行关联
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }
});
// 用户集合
const User = mongoose.model('User', userSchema);
// 文章集合
const Post = mongoose.model('Post', postSchema);
// 联合查询
Post.find().populate('author').then(result => console.log(result))

模板引擎的基础概念

模板引擎

模板引擎是第三方模块

让开发者以更加友好的方式拼接字符串,使项目代码更加清晰、更加易于维护

art-template模板引擎

1.在命令行工具中使用npm install art-template命令下载

2.使用const template=require(‘art-template’)引入模板引擎

3.告诉模板引擎要拼接的数据和模板在哪const html=template(‘模板路径’,数据)

示例代码

// 导入模板引擎
const template = require('art-template');
const path = require('path');
// 路径拼接
const views = path.join(__dirname, 'views', 'index.art');
// template方法是用来拼接字符串的
// 1.模板路径 绝对路径
// 2.要在模板中显示的数据 对象类型
// 返回拼接好的字符串
const html = template(views, {
    name: '张三',
    age: 20
})
console.log(html);

模板引擎语法

  • art-template同时支持两种模板语法:标准语法和原始语法
  • 标准语法可以让模板更容易读写,原始语法具有强大的逻辑处理能力

标准语法:{{数据}}

原始语法:<%=数据%>

输出
原文输出

如果数据中携带HTML标签,默认模板引擎不会解析标签,会将转义后输出

  • 标准语法:{{@数据}}
  • 原始语法:<%-数据%>
 <!-- 标准语法 -->
    {{@content}}
    <!-- 原始语法 -->
    <%-content%>
条件判断

在模板中可以根据条件来决定显示那块HTML代码

<!-- 标准语法 -->
{{if age>18}}
年龄大于18
{{else if age<15}}
年龄小于15
{{else}}
年龄不符合要求
{{/if}}

<!-- 原始语法 -->
<%if(age>18){%>
年龄大于18
<%}else if(age<15){%>
年龄小于15
<%}else{%>
年龄不符合要求
<%}%>
循环
  • 标准语法:{{each 数据}}{{/each}}
  • 原始语法:<%for(){%><%}%>
<!-- 标准语法 -->
<ul>
    {{each users}}
    <li>
        {{$value.name}}
        {{$value.age}}
        {{$value.sex}}
    </li>
    {{/each}}
</ul>
<!-- 原始语法 -->
<ul>
    <%for(var i=0;i<users.length;i++){%>
        <li>
            <%=users[i].name%>
            <%=users[i].age%>
            <%=users[i].sex%>
        </li>
    <%}%>
</ul>
子模版

使用子模版可以将网站公共区块(头部、底部)抽离到单独的文件中

  • 标准语法:{{include ‘模板’}}
  • 原始语法:<%include(‘模板’)%>
<!-- 标准语法 -->
{{include './common/header.art'}}
<div>{{msg}}</div>
{{include './common/footer.art'}}
<!-- 原始语法 -->
<%include ('./common/header.art')%>
<div>{{msg}}</div>
<%include ('./common/footer.art')%>
模板继承

在这里插入图片描述

在这里插入图片描述

模板继承示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    {{block 'link'}}{{/block}}
</head>
<body>
    {{block 'content'}}{{/block}}
</body>
</html>
{{extend './common/layout.art'}}
{{block 'content'}}
<p>{{msg}}</p>
{{/block}}
{{block 'link'}}
<link rel="stylesheet" href="style.css">
{{/block}}
模板配置

1.向模板中导入变量template.defaults.imports.变量名=变量值

2.设置模板根目录 template.defaults.root=模板目录

3.设置模板默认后缀template.defaults.extname=’.art’

Express框架简介及初体验

Express框架是什么

Express是一个基于Node平台的web应用开发框架,它提供了一系列的强大特性,帮助创建各种web应用,使用npm install express命令下载

Express框架特性
  • 提供了方便简洁的路由定义方式
  • 对获取http请求参数进行了简化处理
  • 对模板引擎支持程度高,方便渲染动态HTML页面
  • 提供了中间件机制有效控制HTTP请求
  • 拥有大量第三方中间件对功能进行扩展
// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
app.get('/', (req, res) => {
    // send()
    // 1.send方法內部会检测响应内容的类型
    // 2.send方法会自动设置http状态码
    // 3.send方法帮我们自动设置响应的内容类型及编码
    res.send('Hello,Express');
})
app.get('/list', (req, res) => {
    res.send({ name: '张三', age: 20 })
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

中间件

什么是中间件

中间件就是一堆方法,可以接收客户端发来的请求,可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理

中间件主要由两部分构成,中间件方法以及请求处理函数

中间件方法由Express提供,负责拦截请求,请求处理函数由开发人员提供,负责处理请求

可以针对同一个请求设置多个中间件,对同一个请求进行多次处理

默认情况下,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配

可以调用next方法将请求的控制权交给下一个中间件,直到遇到结束请求的中间件

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
app.get('/request', (req, res, next) => {
    req.name = "张三";
    next();
});
app.get('/request', (req, res) => {
    res.send(req.name);
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
app.use中间件用法

app.use匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求

app.use第一个参数也可以传入请求地址,代表不论什么请求方式,只要是这个请求地址就收这个请求

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
// 接收所有请求的中间件
app.use((req, res, next) => {
    console.log('请求走到app.use中间件');
    next();
})
// 当客户端访问/request请求的时候走当前的中间件
app.use('/request', (req, res, next) => {
    console.log('请求走了app.use/request中间件');
    next();
})
app.get('/list', (req, res) => {
    res.end('/list')
})
app.get('/request', (req, res, next) => {
    req.name = "张三";
    next();
});
app.get('/request', (req, res) => {
    res.send(req.name);
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
中间件应用

1.路由保护,客户端在访问需要登录的页面时,可以先使用中间件判断用户登录状态,如果用户未登录,则拦截请求,直接响应,禁止用户进入需要登录的页面

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
app.use('/admin', (req, res, next) => {
    // 用户没有登录
    let isLogin = false;
    // 如果用户登录
    if (isLogin) {
        // 让请求继续向下执行
        next();
    } else {
        // 如果用户还没有登录,直接对客户端做出响应
        res.send('你还没有登录,不能访问/admin这个页面');
    }
})
app.get('/admin', (req, res) => {
    res.send('您已经登录,可以访问当前页面')
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

2.网站维护公告,在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,网站正在维护中

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
// 网站公告
app.use((req,res,next)=>{
    res.send('当前网站正在维护...');
})
app.use('/admin', (req, res, next) => {
    // 用户没有登录
    let isLogin = false;
    // 如果用户登录
    if (isLogin) {
        // 让请求继续向下执行
        next();
    } else {
        // 如果用户还没有登录,直接对客户端做出响应
        res.send('你还没有登录,不能访问/admin这个页面');
    }
})
app.get('/admin', (req, res) => {
    res.send('您已经登录,可以访问当前页面')
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

3.自定义404页面

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();

app.use('/admin', (req, res, next) => {
    // 用户没有登录
    let isLogin = false;
    // 如果用户登录
    if (isLogin) {
        // 让请求继续向下执行
        next();
    } else {
        // 如果用户还没有登录,直接对客户端做出响应
        res.send('你还没有登录,不能访问/admin这个页面');
    }
})
app.get('/admin', (req, res) => {
    res.send('您已经登录,可以访问当前页面')
})
app.use((req, res, next) => {
    // 为客户端响应404状态码以及提示信息
    res.status(404).send('当前访问的页面不存在的')
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
错误处理中间件

在程序执行的过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败,错误处理中间件是一个集中处理错误的地方

捕获同步代码错误

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
app.get('/index', (req, res) => {
    // throw new Error('程序发生了未知错误');
    res.send('程序正常执行')
})
// 错误处理中间件
app.use((err, req, res, next) => {
    res.status(500).send(err.message);
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

捕获异步代码错误

当程序出现错误时,调用next()方法,并且将错误信息通过参数的形式传递给next()方法,即可触发错误处理中间件

// 引入express框架
const express = require('express');
const fs = require('fs')
// 创建网站服务器
const app = express();
app.get('/index', (req, res, next) => {
    // throw new Error('程序发生了未知错误');
    fs.readFile('./demo.txt', 'utf8', (err, result) => {
        if (err != null) {
            next(err)
        } else {
            res.send(result)
        }
    })
    // res.send('程序正常执行')
})
// 错误处理中间件
app.use((err, req, res, next) => {
    res.status(500).send(err.message);
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
捕获异步函数错误

在node.js中,异步API的错误信息都是通过回调函数获取的,支持promise对象的异步API发生错误可以通过catch方法捕获

try cath 可以捕获异步函数以及其它同步代码在执行过程中发生的错误,但是不能捕获其它类型的API发生的错误(比如说回调函数,promise对象)

// 引入express框架
const express = require('express');
const fs = require('fs');
const promisify = require('util').promisify;
const readfile = promisify(fs.readFile);
// 创建网站服务器
const app = express();
app.get('/index', async (req, res, next) => {
    try {
        await readfile('./a.js');
    } catch (ex) {
        next(ex)
    }
})
// 错误处理中间件
app.use((err, req, res, next) => {
    res.status(500).send(err.message);
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

Express请求处理

构建模块化路由基础代码
// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
// 创建路由对象
const home = express.Router();
// 为路由对象匹配请求路径
app.use('/home', home);
// 创建二级路由
home.get('/index', (req, res) => {
    res.send('欢迎来到博客首页页面')
})

// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
构建模块化路由

01.js

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
const home = require('./route/home');
const admin = require('./route/admin');
app.use('/home', home);
app.use('/admin', admin)
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

home.js

const express = require('express');
const home = express.Router();
home.get('/index', (req, res) => {
    res.send('欢迎来到博客首页页面');
});
module.exports = home;

admin.js

const express = require('express');
const admin = express.Router();
admin.get('/index', (req, res) => {
    res.send('欢迎来到博客管理页面');
})
module.exports = admin;
GET参数的获取

Express框架中使用req.query即可获取GET参数,框架内部会将GET参数转化为对象并返回

// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
app.get('/index', (req, res) => {
    // 获取get请求参数
    res.send(req.query);
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
POST参数的获取

Express中接收post请求参数需要借助第三方包body-parser

02.js

// 引入express框架
const express = require('express');
// 引入body-parser模块
const bodyParser = require('body-parser');
// 创建网站服务器
const app = express();
// 拦截所有请求
// extended:false 方法内部使用querystring模块处理请求参数的格式
// extended:true  方法内部使用第三方模块qs处理请求参数的格式
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/add', (req, res) => {
    // 接收post请求参数
    res.send(req.body);
})

// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

post.html

    <form action="http://localhost:3000/add" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" name="">
    </form>
app.use方法传递函数调用语法的解释
// 引入express框架
const express = require('express');
// 引入body-parser模块
const bodyParser = require('body-parser');
// 创建网站服务器
const app = express();

app.use(fn({ a: 1 }));
function fn(obj) {
    return function (req, res, next) {
        if (obj.a == 1) {
            console.log(req.url);

        } else {
            console.log(req.method);
        }
        next();
    }
}
app.get('/', (req, res) => {
    res.send('ok');
})

// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
Express路由参数
// 引入express框架
const express = require('express');
// 引入body-parser模块
const bodyParser = require('body-parser');
// 创建网站服务器
const app = express();
app.get('/index/:id/:name/:age', (req, res) => {
    res.send(req.params);
})

// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
http://localhost:3000/index/123/zhangsan/19

输出

{"id":"123","name":"zhangsan","age":"19"}
静态资源的处理

在这里插入图片描述

// 引入express框架
const express = require('express');
const path = require('path');
// 创建网站服务器
const app = express();
//实现静态资源访问功能
app.use(express.static(path.join(__dirname, 'public')))
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

express-art-template模板引擎

模板引擎
  • 为了使art-template模板引擎能够更好的和Express框架配合,模板引擎官方在原art-template模板引擎的基础上封装了express-art-template
  • 使用npm install art-template express-art-template命令进行安装
// 引入express框架
const express = require('express');
const path = require('path');
// 创建网站服务器
const app = express();
//1.告诉express框架使用什么模板引擎渲染什么后缀的模板文件
//  1.模板后缀
//  2.使用的模板引擎
app.engine('art', require('express-art-template'))
// 2.告诉express框架模板存放的位置是什么
app.set('views', path.join(__dirname, 'views'))
// 3.告诉express框架模板的默认后缀是什么
app.set('view engine', 'art')
app.get('/index', (req, res) => {
    // 1.拼接模板路径
    // 2.拼接模板后缀
    // 3.哪一个模板和哪一个数据进行拼接
    // 4.将拼接结果响应给了客户端
    res.render('index', {
        msg: 'message'
    })
})
app.get('/list', (req, res) => {

    res.render('list', {
        msg: 'list message'
    })
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
app.locals对象

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

// 引入express框架
const express = require('express');
const path = require('path');
// 创建网站服务器
const app = express();
//1.告诉express框架使用什么模板引擎渲染什么后缀的模板文件
//  1.模板后缀
//  2.使用的模板引擎
app.engine('art', require('express-art-template'))
// 2.告诉express框架模板存放的位置是什么
app.set('views', path.join(__dirname, 'views'))
// 3.告诉express框架模板的默认后缀是什么
app.set('view engine', 'art')
app.locals.users = [{
    name: 'zhangsan',
    age: 20
}, {
    name: '李四',
    age: 30
}]
app.get('/index', (req, res) => {
    // 1.拼接模板路径
    // 2.拼接模板后缀
    // 3.哪一个模板和哪一个数据进行拼接
    // 4.将拼接结果响应给了客户端
    res.render('index', {
        msg: '首页'
    })
})
app.get('/list', (req, res) => {

    res.render('list', {
        msg: '列表页'
    })
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');

版权声明:本文为qq_42802170原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。