八.node
Node环境 中的 Javascript
node.js:Javascript的服务器端运行环境,可以让程序员使用Javacript来实现服务器端的编程,没有浏览器和HTML的概念,没有 BOM 和 DOM
Node中的js组成:ECMAScript核心 + 全局成员 +核心API模块
全局成员:console、setInterval、setTimeout…
核心API模块:Node平台提供的一些API,是Node独有的
node下载网站http://nodejs.cn/
REPL环境
打开任意终端,输入node并回车,就能进入
按两次ctrl + c 退出REPL环境
npm
全局包
- **如何安装全局包:**运行
npm install 包名 -g
即可;其中-g
参数,表示 把包安装到全局目录中的意思; - 全局包的安装目录:`C:\Users\用户目录\AppData\Roaming\npm
- **如何卸载全局包:**要卸载某个全局的包,直接运行
npm uninstall 包名 -g
即可;其中uninstall
表示卸载的意思;
本地包
- **注意:**如果拿到一个空项目,必须在当前项目根目录中,先运行
npm init
或者npm init -y
命令,初始化一个package.json
的配置文件,否则包无法安装到本地项目中; - **如何安装本地包:**运行
npm i 包名 --save
即可安装本地包;都安装到了当前项目的node_modules
目录下;- 如果大家用的是npm 5.x的版本,可以不指定
--save
命令,如果用的是 npm 3.x 的版本,则需要手动指定--save
;
- 如果大家用的是npm 5.x的版本,可以不指定
package-lock.json
文件中记录了曾经装过的包的下载地址,方便下次直接下载包,能够加快装包的速度,提升装包的体验;- **如何卸载本地包:**使用
npm uninstall/remove 包名 -S/-D
即可卸载指定的本地包;
其它常用命令
--save
的缩写是-S
--save-dev
的缩写是-D
install
的缩写是i
- 注意:
dependencies
节点,表示项目上线部署时候需要的依赖项;devDependencies
节点,表示项目在开发阶段需要的依赖项,但是当项目要部署上线了,devDependencies
节点中的包,就不再需要了! - 注意:当使用
npm i
快速装包的时候,npm会检查package.json
文件中,所有的依赖项,然后都为我们安装到项目中 --production
表示只安装dependencies
节点下,记录的包,不安装devDependencies
节点下的包;当项目要上线了,才会使用--production
命令
解决 npm 下载慢问题
- 如何安装
cnpm
:运行npm i cnpm -g
即可; - 如何使用
cnpm
:在装包的时候,只需要把npm
替换成cnpm
即可,例如:- 使用
npm
安装jquery
:运行npm i jquery -S
- 使用
cnpm
安装jquery
: 运行cnpm i jquery -S
- 使用
安装node_modules里的插件
npm install --save
1.fs文件、path路径、第三方模块、模块查找规则
fs文件系统
const fs = require(‘fs’); //导入fs文件操作模块
文件读取
fs.readFile( __dirname + '/文件路径'[, '编码格式默认为null'] , function(err对象,data读取成功的结果 ){
if(err) return console.log(err.message); //如果err为null 就证明读取成功了 如果为true 则失败
console.log( data ); });
文件写入
fs.writeFile( __dirname + '/文件路径', 写入内容[, '编码格式默认为utf-8'] , function(err){
if(err) return console.log(err.message); //如果文件写入失败则报错
console.log( '文件写入成功' );
} ) //要写入的文件不存在则直接写入,如果已经存在则会覆盖之前的文件
文件内容追加
fs.appendFile(__dirname + '/文件路径', 追加内容[, '编码格式默认为utf-8'] , function(err){
if(err) return console.log(err.message); //如果文件内容追加失败则报错
console.log( '文件内容追加成功' );
})
如果要追加的文件路径不存在,则会先创建这个文件,再向创建的文件中追加内容
fs模块操作文件时路径问题
解决方案:使用node中提供的 __dirname 表示当前这个文件所处的磁盘目录
还有一种 __filename 表示当前这个文件的完整路径,包含了具体的文件名
读取文件信息
fs.stat(__dirname + '/文件路径', function(err, stats){
if(err) return console.log(err.message); //如果文件内容追加失败则报错
console.log( '文件内容读取成功' );
console.log( stats.size ); //文件大小
console.log( stats.birthtime ); //创建时间
console.log( stats.isFile ); //判断是否为文件
console.log( stats.isDirectory); //判断是否为目录
})
复制文件
fs.copyFile( '被拷贝的源文件路径,包含文件名称', '拷贝出的目标文件路径,包含名称'[,拷贝操作修饰符默认0],function(err){
if(err) return console.log(err.message); //如果拷贝失败则报错
console.log( '拷贝成功' );
} ) //如果要拷贝出的目标文件已经存在则会覆盖,如果不存在则会新建
路径操作
path.join([…paths])
const path = require('path')
path.join(__dirname, '相对路径')
其他:
const path = require('path')
// console.log(path.sep) // 路径分隔符
const str = 'c:/a/b/c/1.txt'
console.log(path.basename(str)) // 获取文件名称的
console.log(path.dirname(str)) // 获取文件所在的路径的
console.log(path.extname(str)) // 获取文件的扩展名
Javascript 的单线程和异步
Javascript 的解析和执行一直是单线程的,但是**宿主环境(浏览器或node)**是多线程的;
异步任务是由宿主环境开启子线程完成,并通过事件驱动、回调函数、队列,把完成的任务, 交给主线程执行;
Javascript解析引擎,一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。
CommonJS 规范
- 作用:是一套 Javascript 的模块化规范,规定了 模块的特性 和 各模块之间如何相互依赖;
- 用途:Node.js 中使用了 CommonJS 规范;
- 特点:同步加载模块;不适合在浏览器端使用;
- CommonJS规范都定义了哪些内容:wiki 对于 Modules 的描述
模块作用域 和 全局作用域
在Node.js中有两个作用域,分别是 全局作用域 和 模块作用域;
-
全局作用域使用
global
来访问,类似于浏览器中的window
; -
每个 Javascript 文件,都是一个单独模块,每个模块都有自己独立的作用域,因此:模块中的成员,默认无法被其它模块访问。
-
如果在某个模块内部,想为 全局的 global 作用域挂载一些属性,需要显示的调用
global.***
来挂载; -
注意:在开发中,一般情况下,不推荐使用
global
全局作用域来共享成员,会存在全局变量污染问题;var b = 20 // console.log(global.b) global.b = b global.say = function () { console.log('这是挂载到全局的 say 方法') } console.log(global.b) global.say()
模块作用域
-
module(模块标识)
module 属性是 Common JS 规范中定义的,它是一个对象,表示当前这个具体的 js 模块;
-
require(引用模块)
每一个实现了 CommonJS 规范的模块,必须定义一个 require() 函数,使用这个 require 函数,就能够 很方便的导入其它 模块中的成员,供自己使用;
-
exports(暴露模块成员)
每一个模块中,如果想要把自己的一些私有成员,暴露给别人使用,那么,必须实现一个 exports 对象,通过exports对象,可以方便的把模块内私有的成员,暴露给外界使用;
module.exports 和 exports 的关系
module.exports
和exports
默认引用了同一个空对象;module.exports
和exports
作用一致,都可以向外暴露成员;- 一个模块作用域中,向外暴露私有成员时,永远以
module.exports
为准;
module.exports = {}
模块成员的分类
模块成员,根据一些区别,又可以分为三大类: 核心模块、第三方模块、用户自定义模块
1.核心模块
- 什么是核心模块:
- 随着Node.js的安装包,一同安装到本地的模块,叫做核心模块;
- 例如:
fs
,path
等模块,都是由Node.js官方提供的核心模块; - 只要大家在计算机中,安装了Node这个应用程序,那么,我们的计算机中就已经安装了所有的 核心模块;
- 如何使用核心模块:
require('核心模块标识符')
2.第三方模块
-
什么是第三方模块:
- 一些非官方提供的模块,叫做第三方模块;
- 注意,第三方模块,并不在我们的计算机上;
- 如果大家需要使用某些第三方模块,必须去一个叫做
NPM
的网站上搜索并下载才能使用;
-
如何使用第三方模块:
-
先从 npm 官网上下载指定的第三方模块
-
使用
require('第三方模块的名称标识符')
来导入这个模块 -
根据 第三方模块的 官方文档,尝试使用
-
npm install package => 用npm命令安装(下载)第三方模块 (文件模块)
-
gulp
gulp的安装:
- 定位终端到项目目录 执行 npm install gulp -s 本地安装gulp的库文件
- 执行 npm install gulp-cli -g 全局安装gulp的命令行工具
gulp的插件:
- gulp-htmlmin:html代码压缩 安装 npm install --save gulp-htmlmin
- gulp-file-include:html公共模块提取 npm install gulp-file-include
- gulp-less:less语法转化 npm install gulp-less
- gulp-csso:css压缩 npm install gulp-cssgo
- gulp-babel:JavaScript语法转化 npm install --save-dev gulp-babel @babel/core @babel/preset-env
- gulp-uglify:压缩混淆js代码 npm install --save-dev gulp-uglify
- !!!!!!注意!Gulp 4最大的变化就是你不能像以前那样传递一个依赖任务列表 如果Gulp是4.0的版本需要手动指定版本号 比如 npm install gulp@3.9.1 -D
gulp语法:
1.gulp.task() 建立gulp任务
2.gulp.src() 获取任务要处理的文件
3.gulp.dest() 输出文件
4.gulp.watch() 监控文件的变化
-
-
如何卸载
npm unintall package => 用npm命令卸载包
3.用户自定义模块
- 什么是用户模块:
- 程序员在自己项目中写的 Javascript 文件,就叫做 用户自定义模块;
- 如何使用用户模块:
require('路径标识符')
模块查找规则
- 当require方法接收不带路径的模块名字
- Node.js会假设它是系统模块
- Node.js会去node_modules文件夹中
- 首先看是否有该名字的JS文件
- 再看是否有该名字的文件夹
- 如果是文件夹看里面是否有index.js
- 如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件
- 否则找不到报错
- 当require方法接收带路径的模块名字
- require方法接收相对路径,相对于当前文件
- 先找同名JS文件再找同名JS文件夹
- 找文件夹中index.js否则去package.js中查找main选项确定入口文件
- 否则找不到报错
package.json
项目描述文件,记录当前项目信息,例如项目名称、版本、作者、github地址、当前项目依赖了哪些第三方模块,目的是方便他人了解项目信息,下载项目依赖文件。
该文件一般被放置在项目的根目录下,使用npm init命令生成。
项目依赖
在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖。
使用npm install 包名
命令下载的文件会默认被添加到package.json文件的dependencies字段中。
开发依赖
在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖。
使用npm install 包名 --save-dev
命令将包添加到package.json文件的devDependencies字段中。
只安装项目运行依赖(dependencies)
npm install --production
为什么记录依赖项
- Node.js中下载的第三方包文件拥有非常多的细碎文件,将项目通过移动硬盘传递给别人时传输速度非常慢.
- 使用git工具管理项目时,不希望git管理node_modules文件夹,也不会将其上传到github中.
当其他人获取到项目时,可以在项目根目录下执行npm install
命令,npm工具会自动去package.json文件中查找项目依赖文件并下载.
- 当项目上线以后,可以直接运行
npm install --production
下载项目依赖,避免下载项目开发依赖。
2.服务器、http、静态资源、promise、异步函数
创建web服务器
// 引用系统模块
const http = require('http');
// 创建web服务器
const server = http.createServer();
// 当客户端发送请求的时候
server.on('request', (req, res) => {
// 设置响应头
res.writeHead(200, {
'Content-Type': 'text/html;charset=utf8'
});
// 设置响应体
res.write('<h1>哈哈哈</h1>');
// 结束请求
res.end();
});
// 监听3000端口
server.listen(3000, error => {
if (!error) {
console.log('服务器已启动,监听3000端口,请访问 localhost:3000')
}
});
http协议
req.url //获取请求地址
req.headers //获取请求报文
req.method //获取请求方法
http状态码
- 200请求成功
- 404请求的资源没有找到
- 500服务器端有错误
- 400客户端请求有语法错误
内容类型
- text/plain
- text/html
- text/css
- application/javascript
- image/jpeg
- application/json
GET传参
参数被放置在地址栏中,格式为:name=zhangsan&age=20
// 处理get参数
const url = require('url');
let { query } = url.parse(req.url, true);
POST传参
参数被放置在请求体中,格式和GET参数相同。
// 处理post参数
// 由于post传递的参数数据量比较大,在网络中并不是一次性传递完成的,而是分成了多次传递
// 所以在接收的时候也需要分为多次接收
// 在NodeJs中接收post参数需要使用事件完成
const querystring = require('querystring');
router.post('/add', (req, res) => {
//接收post请求参数
let formData = '';
req.on('data', chunk => formData += chunk);//开始接受
req.on('end', async() => {//接受完
//querystring.parse(formData) 把字符串转换为对象
await Student.create(querystring.parse(formData))
res.writeHead(301, {
Location: '/list'
})
res.end()
})
})
路由
路由是指URL地址与程序的映射关系,更改URL地址可以改变程序的执行结果。简单说就是请求什么响应什么。
// 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
//let { pathname, query } = url.parse(req.url, true) //返回值为对象
// 防止中文乱码
res.writeHeader(200, {
'Content-Type': 'text/html; charset=utf-8'
})
if (method === 'get') {
if (pathname === '/' || pathname === '/index') {
res.end('首页')
} else if (pathname === '/list') {
res.end('列表页')
} else {
res.end('无结果')
}
}
if (method === 'post') {
}
})
app.listen(3000)
console.log('to http://127.0.0.1:3000')
客户端请求方式
- 浏览器地址栏
- Form表单提交
- link标签的href属性
- script标签的src属性
- image标签的src属性
静态资源获取
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const app = http.createServer();
app.on('request', function(req, res) {
//获取用户请求路径
let pathname = url.parse(req.url).pathname
pathname = pathname == '/' ? '/default.html' : pathname
let paths = path.join(__dirname, 'public' + pathname)
let type = mime.getType(paths)
fs.readFile(paths, (err, date) => {
if (err !== null) {
// 防止中文乱码
res.writeHeader(404, { //text/html能识别html标签;text/plain表示普通的文本字符串
'Content-Type': 'text/html; charset=utf-8'
})
res.writeHeader(200, {
'content-Type': type
})
return res.end('报错')
}
res.end(date)
})
})
app.listen(3000, '127.0.0.1', function() {
console.log('server running at http://127.0.0.1:3000');
})
promise
Promise出现的目的是解决Node.js异步编程中回调地狱的问题
//下面是用promise 读取静态资源文件
const promisify = require('util').promisify;
const fs = require('fs');
/* let promise = new Promise((dome1, dome2) => {
fs.readFile('./views/1.text', 'utf-8', (err, date) => {
if (err !== null) {
dome2(err)
} else {
dome1(date)
}
})
})
promise.then((date) => {
console.log(date);
})
.catch((err) => {
console.log(err);
}) */
function p1() {
return new Promise((dome1, dome2) => {
fs.readFile('./views/1.text', 'utf-8', (err, date) => {
if (err !== null) {
dome2(err)
} else {
dome1(date)
}
})
})
}
function p2() {
return new Promise((dome1, dome2) => {
fs.readFile('./views/2.text', 'utf-8', (err, date) => {
if (err !== null) {
dome2(err)
} else {
dome1(date)
}
})
})
}
p1().then((date) => {
console.log(date);
return p2()
})
.then((date) => {
console.log(date);
})
异步函数
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了
const fn = async () => {};
async function fn () {}
async关键字
- 普通函数定义前加async关键字 普通函数变成异步函数
- 异步函数默认返回promise对象
- 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
- 在异步函数内部使用throw关键字抛出程序异常
- 调用异步函数再链式调用then方法获取异步函数执行结果
- 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
await关键字
- await关键字只能出现在异步函数中
- await promise await后面只能写promise对象 写其他类型的API是不不可以的
- await关键字可是暂停异步函数向下执行直到promise返回结果
promisify改造函数
//下面是异步函数读取文件代码示例:
const fs = require('fs');
// 改造现有异步函数api 让其返回promise对象 从而支持异步函数语法
const promisify = require('util').promisify;
// 调用promisify方法改造现有异步API 让其返回promise对象
const readFile = promisify(fs.readFile);
async function run() {
let n1 = await readFile('./views/1.text', 'utf-8')
let n2 = await readFile('./views/2.text', 'utf-8')
console.log(n1);
console.log(n2);
}
run()
异步函数示例代码如下:
// 1.在普通函数定义的前面加上async关键字 普通函数就变成了异步函数
// 2.异步函数默认的返回值是promise对象
// 3.在异步函数内部使用throw关键字进行错误的抛出
// await关键字
// 1.它只能出现在异步函数中
// 2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果后再向下执行函数
// async function fn() {
// throw '发生了错误'
// return 123
// }
// fn().then((date) => {
// console.log(date);
// })
// .catch((err) => {
// console.log(err);
// })
async function p1() {
return 'p1'
}
async function p2() {
return 'p2'
}
async function p3() {
return 'p3'
}
async function run() {
console.log(await p1());
console.log(await p2());
console.log(await p3());
}
run()
3.数据库mongodb
MongoDB数据库安装
Node.js通常使用MongoDB作为其数据库,具有高性能,易使用,存储数据方便等特点,完全使用JavaScript语法即可操作。下载
MongoDB可视化软件
MongoDB可视化操作软件,使用图形界面操作数据库的一种方式。下载
Mongoose第三方包
使用Node.js操作MongoDB数据库需要依赖Node.js第三方包mongoose,使用npm install mongoose
命令下载
数据库连接
const mongoose = require('mongoose');
// 数据库连接 27017是mongodb数据库的默认端口
// mongoose.connect('mongodb://账号名:密码@localhost:27017/数据库名字', { useNewUrlParser: true })
mongoose.connect('mongodb://likai:550@localhost:27017/blog', { useNewUrlParser: true })
.then(() => console.log('数据库连接成功'))
.catch(() => console.log('数据库连接失败'));
创建集合
//连接数据库
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true })
.then(() => {
console.log('数据库连接成功');
})
.catch((err) => {
console.log(err, '数据库连接失败');
})
//创建集合规则(类似于设计数据库表结构)
const courseSchema = new mongoose.Schema({
name: String,
author: String,
ispub: Boolean
})
//使用规则创建集合,第一个参数是集合名称(第一个字母要大写) 第二个参数是集合规则
const Course = mongoose.model('Course', courseSchema)
添加数据
//第一种插入数据方式
// const dome1 = new Course({
// name: 'kk',
// author: 'student',
// ispub: true
// })
// dome1.save()
//第二种插入数据方式
Course.create({
name: 'qiuqiu',
author: 'student',
ispub: true
})
.then(doc => {
console.log(doc);
})
.catch(err => {
console.log(err);
})
查找
//1.全部查询
// User.find().then(date => {
// console.log(date);
// })
User.find({
_id: '5c09f267aeb04b22f8460968'
}).then(date => {
console.log(date); //输出值是个数据
})
User.findOne({
// _id: '5c09f236aeb04b22f8460967'
}).then(date => {
console.log(date); //输出值是个集合,并且是集合中的第一条
})
//2.范围查询
User.find({
age: { $gt: 20, $lt: 50 }
}).then(date => {
console.log(date);
})
//3.包含查询
User.find({
hobbies: { $in: ['足球'] }
}).then(date => {
console.log(date);
console.log('-----------');
})
//4.字段查询,多个字段中间以空格隔开,前面加 - 表示不查询该字段
User.find().select('name age -_id').then(date => {
console.log(date); //返回值为数组包含数条集合
console.log('-----------');
})
//5.将查找的数据进行升序排序,若降序排列,在字段名前面加 - ,下面以年龄为示例
User.find().sort('age').then(date => {
console.log(date); //返回值为数组包含数条集合
})
//6.skip()跳过多少条数据, llimit()限制查询结果的数量
User.find().skip(2).limit(2).then(date => {
console.log(date); //返回值为数组包含数条集合
})
删除
// 查找到一条文档并且删除
// 返回删除的文档
// 如何查询条件匹配了多个文档 那么将会删除第一个匹配的文档
User.findOneAndDelete({ _id: '5c09f267aeb04b22f8460968' })
.then(date => {
console.log(date);
})
// 删除多条文档
//返回值为{ n:4, ok:1 } n为删除的数量,ok为1表示删除成功
// User.deleteMany({ _id: '5c09f267aeb04b22f8460968' })
// .then(date => {
// console.log(date);
// })
修改
//修改单个,返回值{ n: 1, nModified: 1, ok: 1 } n代表受影响的数据个数, nModifie表示修改的数据个数, ok为1表示成功
User.updateOne({ name: '赵六' }, { name: '赵云' })
.then(date => {
console.log(date);
})
//修改多个
User.updateMany({}, { age: 88 })
.then(date => {
console.log(date);
})
验证规则
// 创建集合规则,并添加规则验证
const pot = new mongoose.Schema({
title: {
type: String,
required: [true, '请传入文章标题'], //必选字段,第二参数为报错时提示信息
maxlength: 10, //传入字符串最大长度
minlength: 2, //传入字符串最小长度
trim: true //去除字符串两端的空格
},
age: {
type: Number,
min: 2, //数值最小值
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 验证失败
// v 要验证的值
return v && v.length > 4
},
message: '传入的值不符合验证规则' // 自定义错误信息
}
}
});
多集合联合查询(集合关联)
通常不同集合的数据之间是有关系的,例如文章信息和用户信息存储在不同集合中,但文章是某个用户发表的,要查询文章的所有信息包括发表用户,就需要用到集合关联。
// 用户集合
const User = mongoose.model('User', new mongoose.Schema({ name: { type: String } }));
// 文章集合
const Post = mongoose.model('Post', new mongoose.Schema({
title: { type: String },
// 使用ID将文章集合和作者集合进行关联
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
}));
//联合查询
Post.find()
.populate('author')
.then((err, result) => console.log(result));
在catch中获取错误信息
Post.create({title:'aa', age: 60, category: 'java', author: 'bd'})
.then(result => console.log(result))
.catch(error => {
// 获取错误信息对象
const err = error.errors;
// 循环错误信息对象
for (var attr in err) {
// 将错误信息打印到控制台中
console.log(err[attr]['message']);
}
})
数据库导入文档数据
mongoimport -d 数据库名称 -c 集合名称 --file '要导入的数据文件名(包含路径)'
mongoDB数据库添加账号
- 以系统管理员的方式运行powershell
- 连接数据库 mongo
- 查看数据库 show dbs
- 切换到admin数据库 use admin
- 创建超级管理员账户 db.createUser()
- 切换到blog数据 use blog
- 创建普通账号 db.createUser()
- 卸载mongodb服务
停止服务 net stop mongodb
mongod --remove
9.创建mongodb服务
mongod --logpath=“C:\Program
Files\MongoDB\Server\4.1\log\mongod.log” --dbpath=“C:\Program
Files\MongoDB\Server\4.1\data” --install –-auth
10.启动mongodb服务 net start mongodb
11.在项目中使用账号连接数据库
mongoose.connect(‘mongodb://likai:550@localhost:27017/blog’, { useNewUrlParser: true })
mongoose.connect(‘mongodb://账号名:密码@localhost:27017/数据库名字’, { useNewUrlParser: true })
创建项目数据库账号
1.mongo
2.use admin
3.db.auth(‘root’,‘root’)
4.use 项目使用的数据库名
5.db.createUser({user:‘likai’,pwd:‘550’,roles:[‘readWrite’]})
6.exit
4.模板引擎、Express
art-template下载
高性能 JavaScript 模板引擎,使用npm install art-template
命令下载。
通过调用模板引擎提供的template函数,告知模板引擎将特定模板和特定数据进行拼接,最终返回拼接结果
//导入模板引擎
const template = require('art-template');
const path = require('path')
// 设置模板的根目录
template.defaults.root = path.join(__dirname, 'views');
// 配置模板的默认后缀
template.defaults.extname = '.html';
// template方法是用来拼接字符串的
// 参数1. 模板路径 绝对路径
// 参数2. 要在模板中显示的数据 对象类型
// 返回拼接好的字符串
const html = template('index.art'), {
name: '张三',
age: 20
})
console.log(html);
模板语法
标准语法: {{ 数据 }}
原始语法:<%= 数据 %>
原文输出
如果数据中携带HTML标签,默认不会解析标签,会将其转义后输出。使用以下方式可以解析标签。
标准语法: {{ @数据 }}
原始语法:<%- 数据 %>
条件判断
<!-- 标准语法 -->
{{if 条件}} ... {{/if}}
{{if v1}} ... {{else if v2}} ... {{/if}}
<!-- 原始语法 -->
<% if (value) { %> ... <% } %>
<% if (v1) { %> ... <% } else if (v2) { %> ... <% } %>
数据循环
<!-- 标准语法 -->
{{each 数据}}
{{$index}} {{$value}}
{{/each}}
<!-- 原始语法 -->
<% for(var i = 0; i < target.length; i++){ %>
<%= i %> <%= target[i] %> //带等号表示输出
<% } %>
子模板
使用子模板可以将网站公共区块(头部、底部)抽离到单独的文件中。
<!-- 标准语法 -->
{{include '模板路径'}}
<!-- 原始语法 -->
<% include('模板路径') %>
模板继承
使用模板继承可以将网站HTML骨架抽离到单独的文件中,其他页面模板可以继承骨架文件。
{{extend './layout.html'}}//要继承的html文件
{{block 'head'}} ... {{/block}} //内容填充
模板配置
下载: npm install dateformat
代码示例:
const dateformat = require('dateformat');
// 导入模板变量 用于处理日期格式的方法
template.defaults.imports.dateformat = dateformat;
//日期格式 {{dateformat($value.name名,'yyyy-mm-dd')}}
第三方模块 router
功能:实现路由
使用步骤:
-
获取路由对象
-
调用路由对象提供的方法创建路由
-
启用路由,使路由生效
const getRouter = require('router') const router = getRouter();//创建路由 router.get('/list', async(req, res) => { let student = await Student.find() //查询学生信息 let html = template('list.art', { students: student }) res.end(html)//最后要有一个返回值 }) app.on('request', (req, res) => { router(req, res, () => {} ) })
实现静态资源访问
-
第三方模块serve-static
-
引入serve-static模块获取创建静态资源(比如css,js等)服务功能的方法
-
调用方法创建静态资源服务并指定静态资源服务目录
-
启用静态资源服务功能
const serveStatic = require('serve-static') const serve = serveStatic('静态资源目录') app.on('request', () => { serve(req, res) }) app.listen(3000)
-
Express
Express是一个基于Node平台的web应用开发框架,提供一系列强大特性,帮助你创建各种 Web应用。
使用npm install express
进行本地安装。
中间件
使用app.use()
方法定义中间件。
-
该方法可以传递一个函数作为参数,表示任何任何请求都会经过该中间件,都会执行该参数内部的代码。
app.use((req, res, next) => { console.log(req.url); next(); });
- 中间件函数有三个参数,分别为请求对象req、响应对象res、释放控制权方法next。
- 中间件函数中的代码执行完成之后需要调用next()方法,才能开始执行下一个中间件,否则请求将挂起。
-
该方法的第一个参数也可以是请求路径,表示只有该请求路径才会经过该中间件。
app.use('/user', (req, res, next) => { console.log(req.method); next(); });
中间件错误处理
app.get('/index', (req, res, next) => {
// throw new Error('程序发生了未知错误')
fs.readFile('./01.js', 'utf8', (err, result) => {
if (err != null) {
next(err)
}else {
res.send(result)
}
})
})
// 下面是错误处理
app.use((err, req, res, next) => {
res.status(500).send(err.message);
})
异步函数错误捕捉
const express = require('express');
const fs = require('fs');
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);
app.get('/index', async (req, res, next) => {
try {
await readFile('./aaa.js')
}catch (ex) {
next(ex);
}
})
构建模块化路由
第一种:在同一个页面
const express = require('express')
// 创建路由对象
const home = express.Router();
// 将路由和请求路径进行匹配
app.use('/home', home);
// 在home路由下继续创建路由
home.get('/index', () => {
// /home/index
res.send('欢迎来到博客展示页面');
});
第二种:在多个页面
// home.js
const express = require('express')
// 创建路由对象
const home = express.Router();
home.get('/index', () => {
res.send('欢迎来到博客展示页面');
});
module.exports = home;
// admin.js
const express = require('express')
// 创建路由对象
const admin = express.Router();
admin.get('/index', () => {
res.send('欢迎来到博客管理页面');
});
module.exports = admin;
// app.js
const express = require('express');
// 创建网站服务器
const app = express();
const home = require('./route/home.js');
const admin = require('./route/admin.js');
app.use('/home', home);
app.use('/admin', admin);
// 端口监听
app.listen(3000);
console.log('to http://127.0.0.1:3000')
获取get请求参数
// 引入express框架
const express = require('express');
// 创建网站服务器
const app = express();
app.get('/index', (req, res) => {
// 获取get请求参数
res.send(req.query)
})
// 端口监听
app.listen(3000);
console.log('to http://127.0.0.1:3000')
获取post请求参数
Express中接收post请求参数需要借助第三方包 body-parser
// 引入express框架
const express = require('express');
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('to http://127.0.0.1:3000')
app.use方法
// 引入express框架
const express = require('express');
const bodyParser = require('body-parser');
// 创建网站服务器
const app = express();
app.use(fn({ a: 2 }))
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) => {
// 接收post请求参数
res.send('ok')
})
// 端口监听
app.listen(3000);
console.log('to http://127.0.0.1:3000')
路由参数
// 引入express框架
const express = require('express');
const bodyParser = require('body-parser');
// 创建网站服务器
const app = express();
app.get('/index/:id/:name/:age', (req, res) => {
// 客户端请求时地址栏应该写成这样
// http://localhost:3000/list/12/zhangsan /20
// 接收post请求参数
res.send(req.params)
})
// 端口监听
app.listen(3000);
console.log('to http://127.0.0.1:3000')
express静态资源
通过 Express 内置的 express.static
可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。
app.use(express.static('public'));
现在,public
目录下面的文件就可以访问了。
http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/images/bg.png
http://localhost:3000/hello.html
express模板引擎
使用npm install art-template express-art-template
命令进行安装。
const express = require('express');
const path = require('path');
const app = express();
// 1.当渲染后缀为art的模板时 所使用的模板引擎是什么
// 参数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.listen(3000);
console.log('to http://127.0.0.1:3000')
app.locals
const express = require('express');
const path = require('path');
const app = express();
// 模板配置
app.engine('art', require('express-art-template'))
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'art');
//将变量设置到app.locals对象中,这个数据在所有的模板中到可以获得到
app.locals.users = [{
name: 'zhangsan',
age: 20
}, {
name: '李四',
age: 30
}]
app.get('/index', (req, res) => {
res.render('index', {
msg: '首页'
})
});
// 端口监听
app.listen(3000);
console.log('to http://127.0.0.1:3000')
5.其他
密码加密bcrypt
哈希斯加密是单程加密方式
在加密的密码中加入随机字符串可以增加密码被破解的难度
//导入bcrypt模板
const bcrypt = require('bcrypt')
//生成随机字符串 gen => generate生成 salt盐
let salt = await bcrypt.genSalt(10)
//使用随机字符串对密码进行加密
let pass = await bcrypt.hash('明文密码',salt)
//密码比对,返回布尔值
let isEqual = await bcrypt.compare('明文密码','加密密码')
bcrypt依赖的其他环境
1.python 2.x
2.node-gyp 安装命令:npm install -g node-gyp
3.windows-build-tools(window系统需要安装)
安装命令:npm install --global --production windows-build-tools
cookie与session
cookie:浏览器在电脑硬盘中开辟的一块空间,在客户端,主要供服务器端存储数据。
- cookie中的数据是以域名的形式进行区分的。
- cookie中的数据是有过期时间的,超过时间数据会被浏览器自动删除。
- cookie中的数据会随着请求被自动发送到服务器端。
session:实际上就是一个对象,存储在服务器端的内存中,在session对象中也可以存储多条数据,每一条数据都有一个sessionid做为唯一标识。
在node中使用express-session模块实现session功能
const session = require('express-session');
app.use(session({ secret: 'secret key' }));
在app.js中引入模块
// 导入express-session模块
const session = require('express-session');
// 配置session
app.use(session({
secret: 'secret key',
saveUninitialized: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000
}
}));
在用户页面从session中获取数据
// 创建用户列表路由
admin.get('/user',(req,res)=>{
res.render('admin/user',{
msg:req.session.username
})
})
实现退出功能
1.删除session
2.删除cookie
3.重定向到用户登录页面
module.exports = (req, res) => {
// 删除session
req.session.destroy(function () {
// 删除cookie
res.clearCookie('connect.sid');
// 重定向到用户登录页面
res.redirect('/admin/login');
});
}
express的页面重定向
res.redirect('/admin/user')
joi
JavaScript对象的规则描述语言和验证器
安装 npm install joi
const Joi = require('joi');
const schema = {//alphanum()字母字符串
username: Joi.string().alphanum().min(3).max(30).required().error(new Error(‘错误信息’)),
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
access_token: [Joi.string(), Joi.number()],//表示两种类型都可以
birthyear: Joi.number().integer().min(1900).max(2013),//integer()整数
email: Joi.string().email()//邮箱格式
//valid(0, 1)值必须为0或1
static: Joi.number().valid(0, 1).error(new Error('状态值非法'))
};
async function run () {
try {
// 实施验证
await Joi.validate({ username: 'abc', birthyear: 1994 }, schema);
}catch (ex) {
console.log(ex.message);
return;
}
console.log('验证通过')
formidable
作用:解析表单,支持get请求参数,post请求参数、文件上传
安装: npm install formidable
// 引入formidable模块
const formidable = require('formidable');
// 创建表单解析对象
const form = new formidable.IncomingForm();
// 设置文件上传路径
form.uploadDir = "/my/dir";
// 是否保留表单上传文件的扩展名
form.keepExtensions = true;
// 对表单进行解析
form.parse(req, (err, fields, files) => {
// fields 存储普通请求参数(普通表单数据)
// files 存储上传的文件信息
});
FileReader
//文件读取
var reader = new FileReader();
reader.readAsDataURL('文件');
reader.onload = function () {
console.log(reader.result);
}
数据分页 mongoose-sex-page
const pagination = require('mongoose-sex-page');
// page 指定当前页
// size 指定每页显示的数据条数
// display 指定客户端要显示的最多页码数
// exec 向数据库中发送查询请求
// 查询所有文章数据
pagination(集合构造函数).page(1) .size(20) .display(8) .exec();
config
作用:允许开发人员将不同运行环境下的应用配置信息抽离到单独的文件中,模块内部自动判断当前应用的运行环境,
并读取对应的配置信息,极大提供应用配置信息的维护成本,避免了当运行环境重复的多次切换时,手动到项目代码
中修改配置信息
使用步骤:
- 使用npm install config命令下载模块
- 在项目的根目录下新建config文件夹
- 在config文件夹下面新建default.json、development.json、production.json文件
- 在项目中通过require方法,将模块进行导入
- 使用模块内部提供的get方法获取配置信息
{
"db": {
"user": "itcast",
"host": "localhost",
"port": "27017",
"name": "blog"
}
}
mongoose.connect(`mongodb://${config.get('db.user')}:${config.get('db.pwd')}@${config.get('db.host')}:${config.get('db.port')}/${config.get('db.name')}`, {useNewUrlParser: true })
.then(() => console.log('数据库连接成功'))
.catch(() => console.log('数据库连接失败'))
将敏感配置信息存储在环境变量中
- 在config文件夹中建立custom-environment-variables.json文件
- 配置项属性的值填写系统环境变量的名字
- 项目运行时config模块查找系统环境变量,并读取其值作为当前配置项属于的值
{
"db": {
"pwd": "APP_PWD"
}
}