命令行窗口
- 命令行窗口(小黑屏)、CMD窗口、终端、shell
- 打开:
- 开始 —> 运行 —> CMD —>回车
- 左下角搜索 —> CMD —> 回车
- 选中一个文件夹的url —> 输入CMD —> 回车
- 常用指令:
dir
列出当前目录下的所有文件cd
目录名 进入到指定的目录, change directorymd 目录名
新建一个文件夹,make directoryrd 目录名
删除一个文件夹,remove de:
进入E盘
- 目录
.
表示当前目录- 在网络里面可以省略
./
访问当前目录的文件
- 在网络里面可以省略
..
表示上一级目录
- 环境变量(windos系统变量)
path
,更改后要重启shell才能在shell里面成功访问- 当在shell里打开一个文件 或调用一个程序时,首先在当前路径找,若无则在环境变量的path的路径中寻找
- 在path中的文件和程序可以在任意位置访问
进程和线程
- 进程:
- 线程:
- 操作系统学了的,自己记
Node.js
1. 简介
- nodejs 是服务器端的JS运行环境,使得JS可以和系统直接进行交互。原来JS在浏览器执行(本地的)。node底层是使用 C++ 编写的
- node 的中 js 引擎使用的 Chrome 的 V8 引擎
- Node 是对 ES 标准一个实现,Node也是一个JS引擎,在Node中不包含 DOM 和 BOM
- 通过Node可以使 js 代码在服务器端执行
- Node 中可以使用所有的内建对象
- String Number Boolean Math Date RegExp Function Object Array
- 而BOM和DOM都不能使用
- 但是可以使用 console 也可以使用定时器(setTimeout() setInterval())
- Node可以在后台来编写服务器
- Node编写服务器都是单线程的服务器
- 传统的服务器都是多线程的
- 每进来一个请求,就创建一个线程去处理请求
- Node的服务器单线程的
- Node处理请求时是单线程,但是在后台拥有一个I/O线程池
- node的特点:
- 非阻塞、异步的I/O
- 事件和回调函数
- 单线程(主线程单线程,后台I/O线程池)
- 跨平台
2. 模块化
- ES5中没有原生支持模块化,我们只能通过script标签引入js文件来实现模块化
-
模块的引用
使用 require()函数来引入一个模块
- 例子:
- var 变量 = require(“模块的标识”); // 模块标志一般为相对路径,以
.
或..
开头
- var 变量 = require(“模块的标识”); // 模块标志一般为相对路径,以
模块的定义
- 在node中一个js文件就是一个模块
- 默认情况下在js文件中编写的内容,都是运行在一个独立的函数中,外部的模块无法访问
- 在node中有一个全局对象
global
,它的作用和网页的window
类似,在全局中创建的变量、函数都会作为global的属性、方法保存 arguments
对函数的参数进行封装的封装对象- 当node在执行模块(一个js文件)中的代码时,它会在其最顶部添加:
function (exports, require, module, __filename, __dirname) {
module
表示模块本身,且exports
是module
的一个属性,即exports == module.exports
在最底部添加 }
- 当定义变量不用
var
或let
定义 而是直接写a=10
则其为全局变量- 导出变量和函数,将需要暴露给外部的变量或方法设置为exports的属性
- 使用 exports
例子:
exports.属性 = 属性值;<br /> exports.方法 = 函数;
使用module.exports
例子:
module.exports.属性 = 属性值;<br /> module.exports.方法 = 函数;<br /> module.exports = { };<br />对象拷贝:
不能使用exports = { }的形式,因为exports和module.exports相当于上图的obj和obj2,若使用exports={},则是更改了exports变量(栈内存的值) 而不是更改对象
模块的标识
- 模块的标识就是模块的名字或路径,node通过模块的标识来寻找模块
对于核心模块(npm中下载的模块),直接使用模块的名字对其进行引入,首先在当前目录的
node_module
中找,若无则去上一级的node_module
中找,上上一级,直到磁盘根目录var fs = require("fs");<br /> var express = require("express");<br />
对于自定义的文件模块,需要通过文件的路径来对模块进行引入路径可以是绝对路径,如果是相对路径必须以
./
或../
开头var router = require("./router");<br />
包(package)
将多个模块组合为一个完整的功能,就是一个包
包结构
bin,二进制的可执行文件,一般都是一些工具包中才有<br /> lib,js文件,库<br /> doc,文档<br /> test,测试代码<br /> package.json,包的描述文件
package.json
通过npm可以对node中的包进行上传、下载、搜索等操作
- npm会在安装完node以后,自动安装,送的
npm的常用指令
npm -v, 查看自己的npm的版本<br /> npm version, 查看所有模块的版本<br /> npm init, 初始化项目(创建package.json)<br /> npm i/install 包名, 安装指定的包<br /> **npm i/install 包名 --save**, 安装指定的包并添加依赖,即添加到package.json中<br /> npm i/install 包名 -g, 全局安装(一般都是一些工具)<br /> npm i/install, 安装当前项目所依赖的包<br /> npm s/search 包名, 搜索包 <br /> npm r/remove 包名, 删除一个包<br /> npm root -g,显示全局安装的位置
cnpm
npm的淘宝镜像
文件系统(File System)
- Buffer(缓冲区)
- Buffer和数组的结构的非常类似,Buffer是用来存储二进制数据的,存储的都是二进制 以16进制显示,数字在输出时转化为10进制
- buffer中的每一个元素的范围:00 - ff,一个字节。一个汉字占3字节
- Buffer的方法
- var buf = Buffer.from(字符串),将一个字符串中内容保存到一个buffer中
- buf.toString(), 将buffer转换为一个字符串
- Buffer.alloc(size),创建一个指定大小的buffer对象
- Buffer.allocUnsafe(size),创建一个指定大小的buffer对象,可以包含敏感数据,没有清空内存之前的数据
- Buffer和数组的结构的非常类似,Buffer是用来存储二进制数据的,存储的都是二进制 以16进制显示,数字在输出时转化为10进制
- fs模块(file system)
- 在Node通过fs模块来对系统中的文件进行操作,fs模块是node中已经继承好了,不需要在使用npm下载,直接引入即可
- 引入fs,var fs = require(“fs”);
- fs模块中的大部分操作都提供了两种方法,同步方法和异步方法
- 同步方法 带sync
- 异步方法 没有sync,都需要回调函数
写入文件
1.同步写入<br /> 2.异步写入<br /> 3.简单写入<br /> 4.流式写入
读取文件
1.同步读取<br /> 2.异步读取<br /> 3.简单读取<br /> 4.流式读取
方法
打开文件
- flags,打开文件的操作类型,r 只读,w 可写,a 追加 文件不存在则创建
mode,文件的操作权限
fs.open(path, flags[, mode], callback),
无返回值
callback的参数:err 出错信息,fd 文件标志符
fs.openSync(path, flags[, mode]),
返回值为文件的描述符(通过这个对文件进行操作)
读写文件
- fd,文件描述符
string,要写入的字符
fs.write(fd, string[, position[, encoding]], callback),callback的参数 error<br /> fs.writeSync(fd, string[, position[, encoding]])<br /> fs.read(fd, buffer, offset, length, position, callback)<br /> fs.readSync(fd, buffer, offset, length, position)
关闭文件
fs.close(fd,callback)<br /> fs.closeSync(fd);
```javascript // 同步方式 var fs = require(“fs”); var fd = fs.openSync(“hello.txt”,”w”); fs.writeSync(fd, “今天花了150元”); fs.closeSync(fd); // 异步方式 var fs = require(“fs”); fs.open(“hello2.txt”, “w”,function(err, fd){ if(!err){ fs.write(fd, “下周去面试”, function(err){
if(!err)
console.log("写入成功");
}); fs.close(fd,function(err){
if(!err)
console.log("关闭文件");
}) } })
- 简单文件读取和写入
fs.writeFile(file, data[, options], callback),文件路径,要写入的数据,选项,写入完成后的回调函数 参数error<br /> fs.writeFileSync(file, data[, options])<br /> fs.readFile(path[, options], callback)<br /> fs.readFileSync(path[, options])
```javascript
// 写
var fs = require("fs");
fs.writeFile("hello2.txt", "有点紧张",{flag:"a"}, function(err){
if(!err)
console.log("写入成功")
else
console.log(err)
})
// 读
var fs = require("fs");
fs.readFile("hello3.txt", function(err, data){
if(!err){
console.log(data.toString());
}
})
流式文件读取和写入,流式读取和写入适用于一些比较大的文件
fs.createWriteStream(path[, options])<br /> fs.createReadStream(path[, options])
```javascript // 写 var fs = require(“fs”); var ws = fs.createWriteStream(“hello3.txt”); ws.write(“写入内容”); ws.write(“写入内容”); // 只要不关闭就可以一直写入 // 通过监听流的open和close来监听流的打开和关闭 ws.once(“open”, function(){ // 流打开了 } ws.once(“close”, function(){ // 流关闭了 } ws.close(); // 或 ws.end();
// 读 var fs = require(“fs”); var rs = fs.createReadStream(“hello2.txt”); rs.once(“open”, function(){ console.log(“可读流打开”) }) rs.once(“close”, function(){ console.log(“可读流关闭”) }) rs.on(“data”, function(data){ console.log(data.toString()); })
// 复制文件 var fs = require(“fs”); var ws = fs.createWriteStream(“hello3.txt”); var rs = fs.createReadStream(“hello2.txt”); rs.pipe(ws);
- `rs.pipe(ws);` 将可读流中的内容 直接输出到可写流中
- on(事件字符串,回调函数), once(事件字符串,回调函数),one()
- fs.existsSync(path) 检查文件是否存在
- fs.stat(path, callback),callback的参数 err stat 状态信息
- fs.statSync(path)
- 获取文件状态
- 返回一个对象,保存了当前对象的状态信息
- fs.size() 文件大小
- fs.ifFile() 是否是文件
- fs.isDirectory() 是否是一个文件夹
- fs.unlink(path, callback) 删除文件
- fs.unlinkSync(path)
- fs.readdir(path, callback) 列出一个目录的目录结构,callback的参数 err files数组 文件名
- fs.readdirSync(path)
<a name="gVxz1"></a>
# Node中核心模块 path os
<a name="jWlKb"></a>
## path
`path` 处理路径<br />第一步,导入包 `let path = require("path")` <br />`path.extname(str)` 可以得到该路径的扩展名
<a name="HqVQA"></a>
## os
`os` 有关操作系统的信息<br />`os.cous()` 获取操作系统的CPU信息<br />`os.totalmem()` 获取内存大小
看官网
<a name="MJqye"></a>
# HTTP模块
开启一个本地服务器需要Node.js中http核心模块
1. http--模块提供了搭建本地服务器的API,首先我们在项目中引入;
```javascript
let http = require('http')
引入之后我们利用http.createServer()方法得到一个服务器实例。
// createServer()方法返回一个server实例
let server = http.createServer()
- 经过以上两步,我们已经搭建好了一个服务器实例,然后我们给服务器实例绑定接收request的事情处理函数,代码如下: ```javascript server.on(‘request’, (req, res) => { console.log(req.url) // 获取到请求的路径(请求路径永远以“/”开头) res.end(‘hello.’) console.log(req.headers) })
// 给服务器绑定接收请求的处理事件,当服务器接收到客户端发送的请求后,会调用后面的处理函数,处理函数接收两个参数:请求信息对象,响应信息对象。
3. 绑定监听端口号,开启服务器。代码如下:
```javascript
server.listen(3000, () => {
console.log('服务器开启成功,可以通过访问http://127.0.0.1:3000/来获取数据~~')
})
// server.listen()用来绑定监听的端口号,可以传入第二个参数,当服务器开启成功后,触发后面的回调函数
- 最后看到的效果如下图所示:
设置相应头
res.writeHead(200, { 'Content-Type': 'text/plain' });
// 或者
res.setHeader('Content-Type', 'text/html');
写入内容
res.write(fileData);
结束响应
res.end();
接下来我们来实现一个需求:
- 当我们访问“http://127.0.0.1:3000/login”, 服务器返回 “login page”
- 当我们访问“http://127.0.0.1:3000/register”, 服务器返回 “register page”
- 当我们访问“http://127.0.0.1:3000/”, 服务器返回 “index page”
- 当我们访问“http://127.0.0.1:3000/product”, 服务器返回 “产品信息列表”
我们实现这个需求,只需要在绑定服务器监听的事件处理函数中获取到用户的请求路径,然后根据不同路径返回不同数据即可,这个也不难。详情代码看下:
let http = require('http')
let server = http.createServer()
server.on('request', (req, res) => {
let url = req.url //得到请求的路径 (请求的路径永远以‘/’开头)
if (url === '/') {
res.end('index page')
} else if (url === '/login') {
res.end('login page')
} else if (url === '/register') {
res.end('register page')
} else if (url === '/product'){
let arr = [
{
name: 'iphone X',
price: 8888
},
{
name: 'iphone 7',
price: 4320
}
]
// 响应的数据类型必须是字符串或者二进制数据
res.end(JSON.stringify(arr))
} else {
res.end('404 NOT found')
}
})
server.listen(3000, () => {
console.log('服务器启动成功了,,可以访问http://127.0.0.1:3000/啦')
})
这样就可以根据不同的路径访问不同的页面啦~
MongDB数据库
MongDB的数据库中,每一表里面的数据以对象形式存储
对数据库的操作,比如连接、创建、插入等都是异步操作
- 当在一个函数中写与数据库相关的代码时,需要等这些代码都执行完,再继续向下进行,因此需要在相关语句前加
await
,再在该函数的定义前加async
如:
使用流程const loginHandle = async (req, res) => {
...
let user = await User.findOne({email});
...
}
- 官网下载,一个是数据库,一个是可视化软件
- 安装他俩
- 在安装数据库时,可能会显示没有权限打开数据库之类的错误,可以先忽视,再在计算机的服务里面,找到MongoDB服务,打开其属性->登录->本地系统账号,就OK了
- 新建项目,在项目中下载导入mongoose包
- 连接数据库 ```javascript const mongoose = require(‘mongoose’)
// connect返回的是一个Promise对象;连接协议为mongodb // 若不存在plaground数据库,会自动创建 mongoose.connect(‘mongodb://localhost/plaground’) .then(() => {console.log(‘数据库连接成功’);}) .catch(err => {console.log(err,’ 数据库连接失败’);});
5. 创建规则、集合
```javascript
// 创建集合规则
const courseSchema = new mongoose.Schema({
name: String,
author: String,
isPublished: Boolean
})
// 使用规则创建集合, 相当于数据库中的表
// 1. 集合名词,参数首字母大写,但在数据库中创建的集合名字是courses
// 2. 使用的规则
const Course = mongoose.model('Course', courseSchema)
创建文档,并保存数据进数据库
这个不应该叫文档呀,应该是文档中的数据对象嘛
// 创建集合对象(文档)并写入数据
const course = new Course({
name: '深度学习从入门到入土',
author: '一个老师',
isPublished: true
})
// 保存,将创建的文档插入到数据库中
course.save()
另一种创建文档(数据)的方式
Course.create({name: 'JS', author: 'teacher', isPublished: false}, (err, result) => {
console.log(err);
console.log(result);
})
// 或者这样 Promise的方法
Course.create({name: 'JS', author: 'teacher', isPublished: false})
.then(result => {console.log(result)})
.catch(err => {console.log(err)}) // 当插入文档出错时
数据库中的层次关系:数据库 -> 集合 -> 文档
使用mongoimport
导入数据文件到数据库mongoimport -d 数据库名称 -c 集合名称 --file 要导入的数据文件
每一条数据都会自动加上一个_id字段查询文档
find
,输出文档数组 ```javascript // 查找全部文档 Course.find().then(result => console.log(result))
// 指定查找条件 Course.find({_id: ‘6170ccff6de0c89aacd691a5’}).then(result => console.log(result))
`findOne`,返回一个对象,默认返回查找结果中的第一条文档
```javascript
Course.findOne({name: 'JS'}).then(result => console.log(result))
**User.countDocuments({name:'ldf'})**
统计数据条数
条件查询
查询年龄的范围
// age 后面写一个对象,表示起止范围
User.find({age: {$gt: 20, $lt: 40}}).then(result => console.log(result))
包含
// hobbies字段数组中包含指定元素
User.find({hobbies: {$in: ['足球']}}).then(result => console.log(result))
选择要查询的字段,多个字段以 空格 隔开,会默认查询出id。
User.find().select('name email').then(...)
- 想除掉默认的id字段时,在字段名前加一个
-
排序User.find().select('name email -_id').then(...)
skip 跳过多少条数据,limit 限制查询数量(可用在分页中)User.find().sort('age').then(...);
// 降序
User.find().sort('-age').then(...);
删除文档User.find().skip(2).limit(5).then(...);
findOneAndDelete,默认删除查找到的第一条数据,并返回该数据
deleteMany,若查询条件为空,则删除全部数据,返回一个对象 表示是否删除成功Course.findOneAndDelete({name:'名字'}).then(...)
更新文档Course.deleteMany({}).then(...)
updateOne({},{})
,返回一个对象 表示是否更新成功User.updateOne({name: '11'},{name:'22'}).then()
updateMany({},{})
// 更改所有
User.updateMany({},{name:'22'}).then()
设置输入数据的验证条件
在建立规则时,同时设置每一个字段的一些输入条件,比如是否可以为空 ```javascript const postSchema = new mongoose.Schema({ title: {
required: true // 指示该字段必须输入,不能为空 } })type: String,
// 设置自定义错误信息 const postSchema = new mongoose.Schema({ title: { type: String, required: [true, ‘请输入文章标题’], // 指示该字段必须输入,不能为空;出错时的错误信息 minlength: [2, ‘长度要大于2’], maxlength: [5, ‘长度要小于5’], trim: true // 去掉空格 } })
其他验证:
- `min` `max` Number类型的大小范围
- `default` 设置默认值
- `enum`设置取值的范围,`enum: ['a', 'b', 'c']`
自定义验证规则
```javascript
// v 就是用户输入的那个数据值
const postSchema = new mongoose.Schema({
title: {
type: String,
validate: {
validator: v => {
// 对v进行判断,返回一个布尔值
return v && v.length > 4
},
// 自定义错误信息
message: '传入的值不符合验证规则'
}
}
})
集合关联
多个数据集合的数据之间有关系
populate('xx')
指定通过哪个字段关联lean()
将结果转换为普通对象,渲染时防止出错 ```javascript // 建立规则时 const postSchema = new mongoose.Schema({ title: String, author: {
} })type: mongoose.Schema.Types.ObjectId, // 通过id关联
ref: 'User' // 要关联的集合名称
// 关联查找 const posts = Post.find().populate(‘author’).lean();
<a name="NlT6I"></a>
## 修改数据
`update`,根据id修改指定用户数据
```javascript
await User.updateOne({_id: id}, {
username: username,
email: email,
role: role,
state: state
});
为数据库添加不同种类的账号
没做
设置分页
设置参数
- 每页显示的条数
limit(数字)
- 从第几个数据开始显示
skip(数字)
,默认从0开始 ```javascript // 接收客户端传递过来的当前页参数 let page = req.query.page || 1; // 每一页显示的数据条数 let pageSize = 3; // 查询用户数据的总数 let count = await User.countDocuments({}) // 总页数 let total = Math.ceil(count / pageSize) // 当前页码对应的数据查询开始位置 let start = (page - 1) * pageSize;
// 查询并显示数据库中的用户信息 // limit() 每页显示多少条数据 // skip() 跳到第几条数据开始显示 let users = await User.find({}).limit(pageSize).skip(start) res.render(‘admin/user’, { users, page: page, total: total })
<a name="UyN0i"></a>
# Express框架
1. 特性
- 提供了方便的路由定义方式
- 简化处理HTTP的请求
- 对模板引擎支持程度高,方便渲染动态HTML页面
- 提供了中间件机制 有效控制HTTP请求
- 拥有大量第三方中间件对功能进行扩展
对比上面的HTTP协议
```javascript
// 引入框架
const express = require("express")
// 创建服务器对象
const app = express()
// 监听 get 方式的根目录请求
app.get('/', (req, res) => {
/* send() 做出响应, 好处
1. 会内部检测响应内容的类型
2. 会自动设置http状态码
3. 会自动设置响应内容的类型及编码
4. 不用自己写end(),会自动结束
*/
res.send('good night! 中村桑')
})
app.get('/list',(req, res) => {
res.send({name: '悠一', age: 41})
})
// 监听端口
app.listen(3000, ()=>{
console.log("服务端正在执行,请访问 http://localhost:3000/")
})
- 当URL中输入的路径不对时,不会报错,而是在页面中显示
Cannot GET /end
,友好 - 若写的是80端口,则在访问时可以省略不写
中间件
中间件就是一堆方法,可以接收客户端发来的请求、可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理。
组成:中间件方法、请求处理函数
- 中间件方法由Express提供,负责拦截请求
- 请求处理函数由开发人员提供,负责处理请求
如
app.get('请求路径', '处理函数')
get()
就是一个中间件方法next()
一个请求可以设置多个中间件,使用
next()
方法将请求的控制权交给下一个中间件,直到遇到结束请求的中间件。
如:app.get('/list',(req, res, next) => {
req.name = "悠一";
// 把控制权交给下一个中间件,注意 res.send 在一次连接中只能发送一次
next()
})
app.get('/list',(req, res) => {
res.send(req.name)
})
next()
中只能传入一个字符串作为参数,因此当要传递对象时let str = JSON.stringify(对象)
将对象类型转为字符串类型JSON.parse(str)
将字符串转为对象app.use()
app.use 匹配所有的请求方式(当一个路径或多个路径有多种请求方式时),可以直接传入请求处理函数,代表接收后面的所有请求。
需要把它写在所有请求的最前面。
app.use 的第一个参数也可以传入请求地址,指定接收哪个路径的全部请求 ```javascript app.use(‘/admin’, (req, res, next) => { … next(); })app.use((req, res, next) => {
...
next();
})
// 为什么 get 里面不写send会出错404呢? app.use(‘/admin’, (req, res, next) => { console.log(“app.use /admin”) next() }) app.get(‘/admin’, (req, res, next) => { console.log(“app.get /admin”) res.send(“app.get admin”) next() }) app.post(‘/admin’, (req, res) => { console.log(“app.post /admin”) res.send(“晚上好~~”) })
**应用 作用**
1. 路由保护,用于判断用户是否已经登录,是则进入页面 显示内容,否则显示提示错误信息
1. 网站维护公告,阻止用户访问该网站的所有页面,此中间件写在所有中间件函数的最前面
1. 自定义404页面,此中间件写在所有中间件函数的最后面
```javascript
app.use((req, res) => {
res.status(404).send("当前页面不存在...")
})
错误处理中间件
- 获取错误信息,将信息显示在用户页面。这种只适用于同步代码发生的错误 ```javascript app.get(‘/index’, (req,res)=> { // 自己抛出一个异常 throw new Error(“程序发生了未知错误”) }) app.use((err, req, res, next) => { res.status(500).send(err.message) // 显示 程序发生了未知错误 })
// 这种也可以使用第二种的next()来 app.get(‘/index’, (req,res)=> { throw new Error(“程序发生了未知错误”) }) app.use((err, req, res, next) => { next(err.message) // 也显示 程序发生了未知错误。只是和上面显示的字体不一样,应该原理不同 })
2. 当程序出错时,调用next()方法,并且将错误信息通过参数的形式传递给next()方法
```javascript
app.get('/index', (req,res, next)=> {
fs.readFile("./app.js", "utf8", (err, result)=>{
if(err){
// 调用 next方法,将错误信息显示出来
next(err)
}else {
res.send(result)
}
})
})
- 可以使用
try { }catch(){ }
来捕获异步函数错误以及其他同步代码在执行过程中的错误,但是不能捕获其他类型的API发生的错误,比如 Promise
模块化路由
创建访问的二级路径,如下例子可以访问localhost:3000/home/index
const app = express()
// 创建路由对象
const home = express.Router()
// 为路由对象匹配请求路径
app.use('/home', home)
// 创建二级路由
home.get("/index", (req, res) => {
res.send("欢迎 首页")
})
可以把不同模块的设置放在不同的js文件中,通过exports
和require
来使用
// home.js
const express = require("express")
const home = express.Router()
home.get("/index", (req, res)=>{
res.send("welcome to home page")
})
module.exports = home;
// admin.js
const express = require("express")
const admin = express.Router()
admin.get("/index", (req, res)=>{
res.send("welcome to admin page")
})
module.exports = admin;
// app.js
const express = require("express")
const home = require("./home")
const admin = require("./admin")
const app = express()
app.use('/home', home)
app.use('/admin', admin)
app.listen(3000)
请求处理
获取参数
获取get方式的参数,如
localhost:3000/index?name=悠一&gender=男
const app = express()
app.get("/index", (req, res)=>{
res.send(req.query)
})
接收post方式的参数,借用
body-parser
包const express = require("express")
const bodyParser = require("body-parser")
const app = express()
app.use(bodyParser.urlencoded({extended: false}))
app.post("/add", (req, res) => {
// 这个body属性是bodyParser自动添加的
res.send(req.body)
})
app.listen(3000)
设置参数
指定需要传入的地址参数
比如输入loclahost:3000/099&luo
```javascript // 这种方式指定url需要传入的参数 app.get(“/dog/:id&:name”, (req,res) => { res.send(req.params) })
或者是`"/dog/:id/:name"` -> `localhost:3000/007/dogge` <br />`req.params` 则获取输入的参数
<a name="MW2yD"></a>
## 静态资源的处理
通过express.static并指定静态资源文件的所在目录,即可访问img、CSS、html文件等。
```javascript
const express = require("express")
const path = require("path")
const app = express()
app.use(express.static(path.join(__dirname, 'public')))
app.get('/', (req, res) => {
res.send("ni hao")
})
app.listen(3000)
path.join(__dirname, 'public')
指示所在的绝对路径,
就可以通过[http://localhost:3000/static.html](http://localhost:3000/static.html)
直接访问public下面的html文件,
可以通过[http://localhost:3000/css/style.css](http://localhost:3000/css/style.css)
访问其下css文件夹中的css文件
模板引擎
让开发者更加友好的方式拼接字符串,通过{{data}}
来使用数据
- 在命令行工具中使用 npm install art-template 命令进行下载
- 使用
const template = require('art-template')
引入模板引擎 - 告诉模板引擎要拼接的数据和模板在哪
const html = tempalte('模板路径', 数据)
,此处路径为绝对路径 - 如: ```javascript // app.js const template = require(‘art-template’); // html 为拼接后的东西 const html = tempalte(‘模板的绝对路径’, { data: { name: ‘ldf’ } })
// index.art
模板文件默认是以`art`为后缀,其内容为html的东西
<a name="IQo3D"></a>
### 模板语法
**输出**<br />标准语法:`{{ 数据 }}`<br />原始语法:`<%=数据 %>`,输出时必须加上一个等号<br />在模板中可以进行简单的四则运算以及三目运算<br />输出的内容有html的标签时,默认输出时是按字符串输出,想解析出来时,需要加 @ ,如 `{{@ content }}`<br />**条件判断**<br />模板中可以根据条件是否满足来显示响应信息
```javascript
// 标准语法
{{if age > 18}}
内容1
{{else if age < 15}}
内容2
{{else age = 90}}
内容3
{{/if}}
// 原始语法
<% if(age > 18) { %>
内容1
<% } else if(age < 15){ %>
内容2
<% } %>
在原始语法中可以写全部的js代码,大括号必须成对
循环
标准语法:{{ each 数据 }}{{/each}}
原始语法:<% for( ){ %> <% } %>
template(view, {
users: [
{name: 'yuuichi'},
{name: 'sugita'},
{name: 'kamiya'},
{name: 'ono'}
]
})
<ul>
{{each users}}
<li>{{$index}} {{$value.name}} </li>
{{/each}}
</ul>
<ul>
<% for(var i = 0; i < users.length; i++){ %>
<li><%=i %> <%= users[i].name%></li>
<% }%>
</ul>
标准语法中,循环中的下标为{{$index}}
,每一个循环的值为{{$value}}
子模版
把模板中公共的部分抽离出来 放到一个单独的文件中,再通过一些语法进入进来
- 标准语法:
{{include '模板'}}
- 原始语法:
<% include('') %>
- 此处路径为相对路径
模板继承
把html文件的骨架也可以抽离出去。通过继承来使用相同的骨架
在骨架文件中通过挖坑 在具体的子文件中来填坑,显示自己的内容
// layout.art
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>骨架模板</title>
{{block 'head'}} {{/block}}
</head>
<body>
{{block 'main'}} {{/block}}
</body>
</html>
// extend.art
{{extend './layout.art'}}
{{block 'main'}}
<h2>goji sense</h2>
<h2>{{age}}</h2>
{{/block}}
模板配置
- 向模板文件中导入一些方法
template.defaults.imports.名字=方法名
在模板中可以调用这个方法,比如:
let add = function (a, b){
return a+b;
}
template.defaults.imports.add = add;
const view = path.join(__dirname, 'views', 'extend.art')
const html = template(view, {})
console.log(html)
<h2>{{ add(2, 20) }}</h2>
- 设置模板的根目录
访问模板时,就可以直接写模板的名字
template.defaults.root = path.join(__dirname, 'views')
- 设置模板的默认后缀
访问时就不可以不再写.art
,也可以自己定义模板的后缀 比如.html
template.defaults.extname = '.art'
express中的模板引擎
需要install art-template
和express-art-template
大概流程
// 渲染后缀为art的模板,声明使用的是express-art-tenplate引擎
app.engine('art', require('express-art-template'))
// 设置模板存放的目录,前一个views是配置项名字 固定为views;后一个views是目录名
app.set('views', path.join(__dirname, 'views'))
// 设定模板的默认后缀
app.set('view engine', 'art')
app.get('/', (req, res) => {
// 渲染名为indx.art的模板
res.render('index', { }) // 第二个参数 对象,传入模板的数据
}
app.listen(3000)
设置公共属性, 使用app.locals
app.locals.users = [{'name' : 'ldf'}]
app.locals.currentLink= "ldf"
- 在前端中通过
{{currentLink}}
来访问
在模板中使用的外链静态文件 如css文件,链接中的地址为相对路径 相对于浏览器地址栏中的请求路径,而静态文件和模板文件不在同一个目录中,因此需要使用绝对路径来连接静态文件,如'/admin/css/style.css'
则从已设置的静态文件目录public
中找
案例项目中涉及的知识点
密码加密 bcryptjs
- 哈希加密,只能加密不能解密
- 在加密的密码中加入随机字符串 ```javascript const bcrypt = require(“bcryptjs”)
/**
- 生成随机字符串
- 参数为一个整数,越大 加密复杂读越高
- 默认值为10
- 返回生成的随机字符串 */ // await 关键字只能出现在异步函数中,所以需要定义一个函数来执行这语句 // let salt = await bcrypt.genSalt(10)
async function fun(){ let salt = await bcrypt.genSalt(10) /**
* 加密密码
* @param String, 原密码串
* @param String, 盐
* @return String, 加密后的密码
*/
let result = await bcrypt.hash('123456', salt)
console.log(salt, result)
} fun()
3. 密码比对
```javascript
let isValid = await bcrypt.compare("用户输入的明文密码", "数据库中加密的密码")
cookie和session
cookie
:浏览器在电脑硬盘开辟的一块空间,主要供服务器存储数据
- cookie中的数据是以域名的形式进行区分的
- 其中的数据有过期时间,超过过期时间 数据会被浏览器自动删除
- 其中的数据会随着请求被自动发送到服务器端
session
:一个对象,存储在服务器端的内存中,在session对象中也可以存储多条数据,每一条数据都有一个唯一标识sessionId
Joi
用户输入字段的验证
最新Joi的官网
const Joi = require('joi')
const schema = Joi.object({
username: Joi.string().min(2).max(5).error(new Error('用户名未通过验证')),
birth: Joi.number().min(1900).max(2014).error(new Error('生日未通过验证')),
state: Joi.number().valid(0, 1).required().error(new Error('状态值非法'))
});
async function run(){
try {
const result = await schema.validateAsync({ username: 'abc', birth: 1899});
}catch (err) {
console.log(err.message);
return;
}
console.log('验证通过');
}
run();
各方法解释:
error()
自定义错误提示信息valid()
提供可选的字段required()
必填
分页
分页显示数据库中的用户数据
- 在HTML中插入
<% %>
可以写服务器端代码 <%= x %>
是赋值语句- page - 0 + 1 意思是 因为减法可以隐式转换 将字符类型转为数值型,而加法不行
<ul class="pagination">
<li>
<a href="/admin/user?page=<%=page-1%>">
<span>«</span>
</a>
</li>
<% for(var i = 1; i <= total; i++) { %>
<li><a href="/admin/user?page=<%=i%>">{{i}}</a></li>
<% } %>
<li>
<a href="/admin/user?page=<%=page-0+1%>">
<span>»</span>
</a>
</li>
</ul>
使用@进行原文输出
**{{@$value._id}}**
**{{@user && user._id}}**
formidable
解析表单,支持get、post、文件上传。
注意表单中要上传文件时,在 form 中添加一个属性值**enctype="multipart/form-data"**
,指定表单数据的编码类型
enctype
的默认值为application/x-www-form-urlencoded
```javascript const formidable = require(“formidable”) const path = require(“path”)
// 创建表单解析对象 const form = new formidable.IncomingForm() // 配置上传文件的存放位置 form.uploadDir = path.join(__dirname, ‘../‘, ‘public/uploads’); // 设置保留上传文件的后缀 form.keepExtensions = true; // 解析表单 // fields 保存普通表单数据,对象类型 // files 保存上传文件相关的数据,对象类型 form.parse(req, (err, fields, files)=>{
})
**文件读取 FileReader**<br />在页面上显示用户选择的图片
```javascript
<div class="form-group">
<label for="exampleInputFile">文章封面</label>
<input type="file" name="cover" id="file">
<div class="thumbnail-waper">
<img class="img-thumbnail" src="" id="preview" style="max-height: 350px;">
</div>
</div>
// 在用户选择图片后,在页面中显示该图片
let file = document.querySelector('#file');
let preview = document.querySelector('#preview');
file.onchange = function() {
// 文件读取对象
let reader = new FileReader();
reader.readAsDataURL(this.files[0]) // 异步
reader.onload = function () {
preview.src = reader.result;
}
}
开发环境和生产环境
- 在计算机环境变量中添加属性
NODE_ENV
,其值为development
或production
- 使用
process.env
获取计算机的系统环境变量及其值 - 判断
process.env.NODE_ENV
的值是哪一个
morgan模块
npm i morgan
const mrogan = require('morgan')
- 将客户端发送给服务器端的请求信息打印到控制台中
app.use(morgan('dev'))
config模块
系统内部自动判断当前应用的运行环境,并读取对应的配置信息
npm install config
- 在项目的根目录下新建config文件夹
- 在config文件夹下面新建
default.json、development.json、production.json
文件 - 在项目中通过 require方法,将模块导入
**constconfig=require('config')**
- 使用模块内部提供的 get 方法获取配置信息
**config.get('title')**
config 可以将敏感的配置信息存储再环境变量中