【前端】尚硅谷Node.js零基础视频教程笔记

文章目录


【前端目录贴】
参考视频: 尚硅谷Node.js零基础视频教程,nodejs新手到高手

1.基础

1.1. 基础命令

  • node -v 查看版本号
  • node hello.js 运行js文件

1.2. 注意事项

Node.js中不能使用BOMDOM的API

console.log('hello world');
console.log(window);//报错
console.log(document);//报错

说明:浏览器中的javascript语法包括如下内容组成部分
在这里插入图片描述
node.js语法的组成部分
在这里插入图片描述

2. Buffer(缓冲器)介绍与创建

2.1 概念

  1. Buffer 是一个类似于数组的 对象 ,用于表示固定长度的字节序列
  2. Buffer 本质是一段内存空间,专门用来处理 二进制数据

在这里插入图片描述

2.2 特点

  1. Buffer 大小固定且无法调整
  2. Buffer 性能较好,可以直接对计算机内存进行操作
  3. 每个元素的大小为 1 字节(byte)

在这里插入图片描述

2.3 使用

  • 创建 Buffer

Node.js 中创建 Buffer 的方式主要如下几种

  1. Buffer.alloc
  2. Buffer.allocUnsafe
  3. Buffer.from
// 创建了一个长度为 10 字节的 Buffer,相当于申请了 10 字节的内存空间,每个字节的值为 0
let buf_1 = Buffer.alloc(10) //=>结果为<Buffer 00 00 00 00 00 00 00 00 00 00>

//创建了一个长度为 10 字节的 Buffer,buffer 中可能存在旧的数据, 可能会影响执行结果,所以叫unsafe
let buf_2 = Buffer.allocUnsafe(10);

//通过字符串创建 Buffer
let buf_3 = Buffer.from('hello');
//通过数组创建 Buffer
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
  • Buffer 与字符串的转化

toString 方法将 Buffer 转为字符串 (toString 默认是按照 utf-8 编码方式进行转换的。)

let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
console.log(buf_4.toString())
  • Buffer 的读写

Buffer 可以直接通过 [] 的方式对数据进行处理。
注意:
如果修改的数值超过 255 ,则超过 8 位数据会被舍弃
一个 utf-8 的中文字符 一般 占 3 个字节

//读取
console.log(buf_3[1]);
//修改
buf_3[1] = 97;
//查看字符串结果
console.log(buf_3.toString());

3. fs模块(file system)

fs 全称为 file system ,称之为 文件系统 ,是 Node.js 中的 内置模块 ,可以对计算机中的磁盘进行操
作。

3.1 文件写入

文件写入就是将 数据 保存到 文件 中,我们可以使用如下几个方法来实现该效果

方法说明
writeFile异步写入
writeFileSync同步写入
appendFile / appendFileSync追加写入
createWriteStream流式写入
  • writeFile 异步写入

语法: fs.writeFile(file, data[, options], callback)
参数说明:

  • file 文件名
  • data 待写入的数据
  • options 选项设置 (可选)
  • callback 写入回调

返回值: undefined

// require 是 Node.js 环境中的'全局'变量,用来导入模块
var fs = require('fs');
// 参数说明:
// file 文件名
// data 待写入的数据
// options 选项设置 (可选)
// callback 写入回调
fs.writeFile('./fileBak/aa.txt','三人行',err => {//不存在,则创建
    //如果写入失败,则调用回调函数,会传入错误对象,如写入成功,会传入null
    if (err) {
        console.log(err);
        return;
    }
    console.log('写入成功');
});
  • writeFileSync同步写入

语法: fs.writeFileSync(file, data[, options])
参数与 fs.writeFile 大体一致,只是没有 callback 参数
返回值: undefined

// require 是 Node.js 环境中的'全局'变量,用来导入模块
var fs = require('fs');
// 参数说明:
// file 文件名
// data 待写入的数据
// options 选项设置 (可选)
fs.writeFileSync('./fileBak/bb.txt','三人行');
console.log('写入完成')

Node.js 中的磁盘操作是由其他 线程 完成的,结果的处理有两种模式:
同步处理 JavaScript 主线程 会等待 其他线程的执行结果,然后再继续执行主线程的代码,
效率较低
异步处理 JavaScript 主线程 不会等待 其他线程的执行结果,直接执行后续的主线程代码,
效率较好

  • appendFile / appendFileSync追加写入

appendFile 作用是在文件尾部追加内容,appendFile 语法与writeFile语法完全相同
语法:
fs.appendFile(file, data[, options], callback)
fs.appendFileSync(file, data[, options])
返回值: 二者都为 undefined

// require 是 Node.js 环境中的'全局'变量,用来导入模块
// require 是 Node.js 环境中的'全局'变量,用来导入模块
var fs = require('fs');
// 参数说明:
// file 文件名
// data 待写入的数据
// options 选项设置 (可选)
// callback 写入回调
for (let i = 0; i < 10; i++) {
    // fs.appendFile('./fileBak/aa.txt','三人行',err => {//不存在,则创建,异步追加
    fs.appendFileSync('./fileBak/aa.txt','三人行');
}
console.log(1)
  • createWriteStream流式写入

语法: fs.createWriteStream(path[, options])
参数说明:

  • path 文件路径
  • options 选项配置( 可选
  • 返回值: Object
// require 是 Node.js 环境中的'全局'变量,用来导入模块
var fs = require('fs');
// 参数说明:
// file 文件名
// options 选项设置 (可选)
var ws = fs.createWriteStream('./fileBak/cc.txt');
ws.write('11');
ws.write('22');
ws.end();
console.log(1)

程序打开一个文件是需要消耗资源的 ,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于 大文件写入或者频繁写入 的场景, writeFile 适合于 写入频率较低的场景

3.2 文件读取

文件读取顾名思义,就是通过程序从文件中取出其中的数据,我们可以使用如下几种方式

方法说明
readFile异步读取
readFileSync同步读取
createReadStream流式读取
  • readFile 异步读取

语法: fs.readFile(path[, options], callback)
参数说明:

  • path 文件路径
  • options 选项配置
  • callback 回调函数
  • 返回值:undefined
var fs = require('fs');
fs.readFile('./fileBak/aa.txt',(err,data) => {
    if (err) {
        throw err
    }
    console.log(data);
})

fs.readFile('./fileBak/aa.txt','utf-8',(err,data) => {
    if (err) {
        throw err
    }
    console.log(data);
})
  • readFileSync 同步读取

语法: fs.readFileSync(path[, options])
参数说明:
path 文件路径
options 选项配置
返回值: string | Buffer

var fs = require('fs');
var data1 = fs.readFileSync('./fileBak/aa.txt');
console.log(data1)// data1.toString()会将buffer转换; toString 方法将 Buffer 转为字符串 (toString 默认是按照 utf-8 编码方式进行转换的。)
var data2 = fs.readFileSync('./fileBak/aa.txt','utf-8');
console.log(data2)
  • createReadStream 流式读取

语法: fs.createReadStream(path[, options])
参数说明:

  • path 文件路径
  • options 选项配置( 可选
  • 返回值: Object
var fs = require('fs');
var readStream = fs.createReadStream('./fileBak/aa.txt','utf-8');
readStream.on('data',data3=>{
    console.log(data3)
    console.log(data3.length)
});
//读取完毕后,执行end回调
readStream.on('end',()=>{
    console.log('读取完后');
})

3.3 文件移动与重命名

在 Node.js 中,我们可以使用 rename 或 renameSync` 来移动或重命名 文件或文件夹
语法:

  • fs.rename(oldPath, newPath, callback)
  • fs.renameSync(oldPath, newPath)

参数说明:

  • oldPath 文件当前的路径
  • newPath 文件新的路径
  • callback 操作后的回调(同步)
var fs = require('fs');
fs.rename('./fileBak/aa.txt','./fileBak/aa2.txt',err => {
    if(err) throw err;
    console.log('移动完成');
});
var fs = require('fs');
fs.renameSync('./fileBak/aa2.txt','./fileBak/aa.txt');

3.4 文件删除

在 Node.js 中,我们可以使用 unlink 或 unlinkSync 来删除文件
语法:

  • fs.unlink(path, callback)
  • fs.unlinkSync(path)

参数说明:

  • path 文件路径
  • callback 操作后的回调
var fs = require('fs');
fs.unlink('./fileBak/aa.txt',err => {
    if(err) throw err;
    console.log('删除完成');
});
var fs = require('fs');
fs.unlinkSync('./fileBak/aa.txt');

3.5 文件夹操作

借助 Node.js 的能力,我们可以对文件夹进行 创建读取删除 等操作

方法说明
mkdir / mkdirSync创建文件夹
readdir / readdirSync读取文件夹
rmdir / rmdirSync删除文件夹
  • mkdir / mkdirSync 创建文件夹

在 Node.js 中,我们可以使用 mkdirmkdirSync 来创建文件夹
语法:

  • fs.mkdir(path[, options], callback)
  • fs.mkdirSync(path[, options])

参数说明:

  • path 文件夹路径
  • options 选项配置( 可选 )
  • callback 操作后的回调
var fs = require('fs');
fs.mkdir('./fileBak/cc',err => {//如果存在会报错
    if(err) throw err;
    console.log('创建成功');
})
var fs = require('fs');
fs.mkdirSync('./fileBak/bbD');
var fs = require('fs');
//异步递归创建文件夹
fs.mkdir('./fileBak/1/2/3',{recursive:true},err => {
    if (err) {
        throw err;
    }
    console.log('同步递归创建文件夹');
});
var fs = require('fs');
//同步递归创建文件夹
fs.mkdirSync('./fileBak/1/2/3',{recursive:true});
  • readdir / mkdirSync 读取文件夹

在 Node.js 中,我们可以使用 readdir 或 readdirSync 来读取文件夹
语法:

  • fs.readdir(path[, options], callback)
  • fs.readdirSync(path[, options])

参数说明:

  • path 文件夹路径
  • options 选项配置( 可选 )
  • callback 操作后的回调
var fs = require('fs');
fs.readdir('./fileBak/1',(err,data) => {
    if (err) {
        throw err;
    }
    console.log(data);
});
var fs = require('fs');
var data = fs.readdirSync('./fileBak/1');
console.log(data)
  • rmdir / mkdirSync 删除文件夹

在 Node.js 中,我们可以使用 rmdir 或 rmdirSync 来删除文件夹
语法:

  • fs.rmdir(path[, options], callback)
  • fs.rmdirSync(path[, options])

参数说明:

  • path 文件夹路径
  • options 选项配置( 可选 )
  • callback 操作后的回调
var fs = require('fs');
//异步删除文件夹
// fs.rmdir('./fileBak/cc', err => {
//     if(err) throw err;
//     console.log('删除成功');
// });

//异步递归删除文件夹
fs.rmdir('./fileBak/1', {recursive: true}, err => {
    if(err) {
        console.log(err);
    }
    console.log('递归删除')
});

3.6 查看资源状态

在 Node.js 中,我们可以使用 stat 或 statSync 来查看资源的详细信息
语法:

  • fs.stat(path[, options], callback)
  • fs.statSync(path[, options])

参数说明:

  • path 文件夹路径
  • options 选项配置( 可选 )
  • callback 操作后的回调
var fs = require('fs');
//异步获取状态
// fs.stat('./fileBak/aa.txt', (err, data) => {
//     if(err) throw err;
//     console.log(data);
// });


//同步获取状态
let data = fs.statSync('./fileBak/aa.txt');
console.log(data)
console.log(data.isFile())//是否是文件 true
console.log(data.isDirectory())//是否是目录 false

3.7 相对路径问题

fs 模块对资源进行操作时,路径的写法有两种:

  • 相对路径
    * ./座右铭.txt 当前目录下的座右铭.txt
    * 座右铭.txt 等效于上面的写法
    * ../座右铭.txt 当前目录的上一级目录中的座右铭.txt
  • 绝对路径
    * D:/Program Files windows 系统下的绝对路径
    * /usr/bin Linux 系统下的绝对路径

相对路径中所谓的 当前目录 ,指的是 命令行的工作目录 ,而并非是文件的所在目录
所以当命令行的工作目录与文件所在目录不一致时,会出现一些 BUG

3.8 __dirname

__dirnamerequire 类似,都是 Node.js 环境中的’全局’变量,__dirname 保存着 当前文件所在目录的绝对路径 ,可以使用 __dirname 与文件名拼接成绝对路径
let data = fs.readFileSync(__dirname + '/data.txt');

3.9 练习复制文件脚本

var fs = require('fs');
fs.readFile('./fileBak/1.png', (err, data) => {
    if (err) {
        throw  err;
        return;
    }
    console.log('开始读取....')

    fs.writeFile('./fileBak/2.png', data, err => {
        if (err) {
            throw  err;
            return;
        }
        console.log('开始导出...');
        console.log('结束导出...');
    })


    console.log('结束读取....')
});
// fs.writeFile('./fileBak/2.png', 'f', err => {
//     if (err) {
//         throw  err;
//         return;
//     }
//     console.log('开始导出...');
//     console.log('结束导出...');
// })

4. path 模块

path 模块提供了 操作路径 的功能,我们将介绍如下几个较为常用的几个 API:

API说明
path.resolve拼接规范的绝对路径 常用
path.sep获取操作系统的路径分隔符
path.parse解析路径并返回对象
path.basename获取路径的基础名称
path.dirname获取路径的目录名
path.extname获得路径的扩展名
var path = require('path');
//获取路径分隔符
console.log(path.sep);
//拼接绝对路径
console.log(path.resolve(__dirname,'test'));
//解析路径
var pathname = 'F:\\front-end\\[B站]尚硅谷Nodejs\\fileBak\\aa.txt';
console.log('解析路径',path.parse(pathname));//{root:'F:\\',   dir: 'F:\\front-end\\[B站]尚硅谷Nodejs\\fileBak',
//  base: 'aa.txt',   ext: '.txt',   name: 'aa' }
//获取路径基础名称
console.log('获取路径基础名称',path.basename(pathname));//aa.txt
//获取路径的目录名
console.log('获取路径的目录名',path.dirname(pathname));// F:\front-end\[B站]尚硅谷Nodejs\fileBak
//获取路径的扩展名
console.log('获取路径的扩展名',path.extname(pathname));//.txt
console.log('文件的绝对路径',__filename);//全局变量

5. http模块

5.1 使用 nodejs 创建 HTTP 服务

//1. 导入 http 模块
var http = require('http');
//2. 创建服务对象 create 创建 server 服务
// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据
// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文
var server=http.createServer((request, response)=>{
  response.setHeader('content-type','text/html;charset=utf-8');//解决中文乱码
  response.end('Hello HTTP server,锅子')
});
//3. 监听端口, 启动服务
server.listen(9000,()=>{
    console.log('服务已经启动,端口9000监听中...');
});
// http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行

http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行
注意事项:

  • 命令行 ctrl + c 停止服务
  • 当服务启动后,更新代码 必须重启服务才能生效
  • 响应内容中文乱码的解决办法response.setHeader('content-type','text/html;charset=utf-8');

5.2 HTTP 请求报文

在这里插入图片描述

注意事项:

  1. request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容
  2. request.headers 将请求信息转化成一个对象,并将属性名都转化成了『小写』
  3. 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为『 / 』
  4. 关于 favicon.ico:这个请求是属于浏览器自动发送的请求
//1. 导入 http 模块
var http = require('http');
//2. 创建服务对象 create 创建 server 服务
// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据
// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文
var server = http.createServer((request, response) => {
    // console.log('request',request);//request  对象
    // console.log('request.method',request.method);//request  对象
    let {url, method} = request; //对象的解构赋值
    console.log(url);
    console.log(method);


    response.setHeader('content-type', 'text/html;charset=utf-8');//解决中文乱码
    if (url === '/register' && method === 'GET') {
        response.write("<h1>注册页面</h1>");
    } else if (url === "/login" && method === "GET") {
        response.write("<h1>登录页面</h1>");
    } else {
        response.write("<h1>404 Not Found</h1>")
    }
    response.end('Hello HTTP server,锅子');
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
    console.log('服务已经启动,端口9000监听中...');
});
// http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行

5.3 HTTP 响应报文

在这里插入图片描述

write 和 end 的两种使用情况:
//1. write 和 end 的结合使用 响应体相对分散
response.write('xx');
response.write('xx');
response.write('xx');
response.end(); //每一个请求,在处理的时候必须要执行 end 方法的
//2. 单独使用 end 方法 响应体相对集中
response.end('xxx');
//1. 导入 http 模块
var http = require('http');
//2. 创建服务对象 create 创建 server 服务
// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据
// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文
var server = http.createServer((request, response) => {
    response.setHeader('Content-Type','application/octet-stream');
    response.end('<!DOCTYPE html>\n' +
        '<html lang="en">\n' +
        '<head>\n' +
        '    <meta charset="UTF-8">\n' +
        '    <title>Document</title>\n' +
        '    <style>\n' +
        '        td {\n' +
        '            padding: 20px 40px;\n' +
        '        }\n' +
        '\n' +
        '        table tr:nth-child(odd) {\n' +
        '            background: #aef;\n' +
        '        }\n' +
        '\n' +
        '        table tr:nth-child(even) {\n' +
        '            background: #fcb;\n' +
        '        }\n' +
        '\n' +
        '        table, td {\n' +
        '            border-collapse: collapse;\n' +
        '        }\n' +
        '    </style>\n' +
        '</head>\n' +
        '<body>\n' +
        '<table border="1">\n' +
        '    <tr>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '    </tr>\n' +
        '    <tr>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '    </tr>\n' +
        '    <tr>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '    </tr>\n' +
        '    <tr>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '        <td></td>\n' +
        '    </tr>\n' +
        '</table>\n' +
        '    <script>\n' +
        '        var listOf = document.querySelectorAll(\'td\');\n' +
        '        for (let i = 0; i < listOf.length; i++) {\n' +
        '            var element = listOf[i];\n' +
        '            // element.onclick = function () {\n' +
        '            //     console.log(\'你好啊\');\n' +
        '            // };\n' +
        '            element.addEventListener(\'click\', function () {\n' +
        '                this.style.background=\'red\'\n' +
        '            });\n' +
        '\n' +
        '        }\n' +
        '    </script>\n' +
        '</body>\n' +
        '</html>');
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
    console.log('服务已经启动,端口9000监听中...');
});
// http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行

5.4 静态资源服务

静态资源是指 内容长时间不发生改变的资源 ,例如图片,视频,CSS文件,JS文件,HTML文件,字体文件等
动态资源是指 内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等

//1. 导入 http 模块
var http = require('http');
var fs = require('fs');
//2. 创建服务对象 create 创建 server 服务
// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据
// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文
var server = http.createServer((request, response) => {
    response.setHeader('Content-Type','text/html;charset=utf-8');
    //获取请求的方法已经路径
    let {url,method} = request;
//文件夹路径
    let rootDir = __dirname + '/fileBak';
//拼接文件路径
    let filePath = rootDir + url;
    fs.readFile(filePath,(err,data)=>{
        //判断
        if(err){
//如果出现错误,响应404状态码
            response.statusCode = 404;
            response.end('<h1>404 Not Found</h1>');
        }else{
//响应文件内容
            response.end(data);
        }
    })
});
//3. 监听端口, 启动服务
server.listen(9000, () => {
    console.log('服务已经启动,端口9000监听中...');
});
// http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行

5.5 设置资源类型(mime类型)

HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源:
mime 类型结构: [type]/[subType]
常见: text/html text/css image/jpeg image/png application/json

对于未知的资源类型,可以选择 application/octet-stream 类型,浏览器在遇到该类型的响应
时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果

5.6 GET 和 POST 请求场景小结

GET 请求的情况:

  • 在地址栏直接输入 url 访问
  • 点击 a 链接
  • link 标签引入 css
  • script 标签引入 js
  • img 标签引入图片
  • form 标签中的 method 为 get (不区分大小写)
  • ajax 中的 get 请求

POST 请求的情况:

  • form 标签中的 method 为 post(不区分大小写)
  • AJAX 的 post 请求

6. Node.js 模块化

6.1 介绍

  • 什么是模块化与模块 ?

将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化,其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用

  • 什么是模块化项目 ?

编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目

  • 模块化好处
  1. 防止命名冲突
  2. 高复用性
  3. 高维护性

6.2 初体验

*******************me.js
function tiemo() {
    console.log('贴膜...');
}
//暴露数据
module.exports = tiemo;

*******************index.js
var tiemo2 = require('./me');
tiemo2();
console.log(tiemo2);

在这里插入图片描述

6.3 暴漏数据

模块暴露数据的方式有两种:

  1. module.exports = value
  2. exports.name = value

在这里插入图片描述

*******************me.js
function tiemo() {
    console.log('贴膜...');
}
function niejiao() {
    console.log('捏脚...');
}
//暴露数据
module.exports = {
    niejiao,
    tiemo
};

*******************index.js
var me = require('./me');
console.log(me)
me.niejiao();

在这里插入图片描述

6.4 导入模块

在模块中使用 require 传入文件路径即可引入文件
require 使用的一些注意事项:

  1. 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./ 和../
    2. jsjson 文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到
  2. 如果导入其他类型的文件,会以js文件进行处理
  3. 如果导入的路径是个文件夹,则会 首先 检测该文件夹下package.json 文件中 main 属性对应的文件,如果存在则导入,反之如果文件不存在会报错。如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的index.js 和index.json,如果还是没找到,就会报错
  4. 导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./ 和 ../

6.5 导入自定义模块的基本流程

这里我们介绍一下 require 导入 自定义模块 的基本流程

  1. 将相对路径转为绝对路径,定位目标文件
  2. 缓存检测
  3. 读取目标文件代码
  4. 包裹为一个函数并执行(自执行函数)。通过 arguments.callee.toString() 查看自执行函数
  5. 缓存模块的值
  6. 返回 module.exports 的值

6.6 CommonJS 规范

module.exports 、 exports 以及 require 这些都是 CommonJS 模块化规范中的内容。而 Node.js 是实现了 CommonJS 模块化规范,二者关系有点像 JavaScript 与 ECMAScript

7 包管理

7.1 介绍

  • 包是什么

『包』英文单词是 package ,代表了一组特定功能的源码集合

  • 包管理工具

管理『包』的应用软件,可以对「包」进行 下载安装更新 删除上传 等操作借助包管理>工具,可以快速开发项目,提升开发效率包管理工具是一个通用的概念,很多编程语言都有包管理工具,所以 掌握好包管理工具非常重要

  • 常用的包管理工具

下面列举了前端常用的包管理工具
npm
yarn
cnpm

7.2 npm

npm 全称 Node Package Manager,翻译为中文意思是『Node 的包管理工具』
npm 是 node.js 官方内置的包管理工具,是 必须要掌握住的工具

7.2.1 npm的安装 npm -v

node.js 在安装时会 自动安装 npm ,所以如果你已经安装了 node.js,可以直接使用 npm
可以通过 npm -v 查看版本号测试,如果显示版本号说明安装成功,反之安装失败

7.2.2 初始化: npm init (下载包前要初始化)

创建一个空目录,然后以此目录作为工作目录 启动命令行工具 ,执行 npm init

npm init 命令的作用是将文件夹初始化为一个『包』, 交互式创建 package.json 文件
package.json 是包的配置文件,每个包都必须要有 package.json
package.json 内容示例:

{
		"name": "1-npm", #包的名字 
		"version": "1.0.0", #包的版本
		 "description": "", #包的描述
		"main": "index.js", #包的入口文件
		"scripts": { #脚本配置
			"test": "echo \"Error: no test specified\" && exit 1"
		},
		"author": "", #作者
		"license": "ISC" #开源证书
}

初始化的过程中还有一些注意事项:

  1. package name ( 包名 ) 不能使用中文、大写,默认值是 文件夹的名称 ,所以文件夹名称也不能使用中文和大写
  2. version ( 版本号 )要求 x.x.x 的形式定义,x必须是数字,默认值是 1.0.0
  3. ISC 证书与 MIT 证书功能上是相同的,关于开源证书扩展阅读 http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html
  4. package.json 可以手动创建与修改
  5. 使用 npm init -y 或者 npm init --yes 极速创建 package.json

7.2.3 搜索包

  • 搜索包的方式有两种
  1. 命令行 『npm s/search 关键字』 eg:npm s uniq
  2. 网站搜索 网址是 https://www.npmjs.com/

7.2.4 下载&删除安装包

# 格式
npm install <包名> `(多个包可以用空格分隔开)`
npm i <包名> `(多个包可以用空格分隔开)`
# 示例
npm install uniq
npm i uniq

# 指定版本号
## 格式
npm i <包名@版本号>
## 示例
npm i jquery@1.11.2

## 局部删除
npm remove uniq
npm r uniq
## 全局删除
npm remove -g nodemon

运行之后文件夹下会增加两个资源

  • node_modules 文件夹 存放下载的包
  • package-lock.json 包的锁文件 ,用来锁定包的版本

安装 uniq 之后, uniq 就是当前这个包的一个 依赖包 ,有时会简称为 依赖
比如我们创建一个包名字为 A,A 中安装了包名字是 B,我们就说 B 是 A 的一个依赖包 ,也会说
A 依赖 B

7.2.5 require 导入 npm 包基本流程

  1. 在当前文件夹下 node_modules 中寻找同名的文件夹
  2. 在上级目录中下的 node_modules 中寻找同名的文件夹,直至找到磁盘根目录

总结

  • require导入内置模块 require('http')
  • require导入自定义模块require('./aa.js')
  • require导入npm包 require('uniq')

7.2.6 生产环境与开发环境

开发环境是程序员 专门用来写代码 的环境,一般是指程序员的电脑,开发环境的项目一般 只能程序员自己访问
生产环境是项目 代码正式运行 的环境,一般是指正式的服务器电脑,生产环境的项目一般 每个客户都可以访问

7.2.7 生产依赖与开发依赖

我们可以在安装时设置选项来区分 依赖的类型 ,目前分为两类:

类型命令补充
生产依赖npm i -S uniq
npm i --save uniq
-S 等效于 --save, -S 是默认选项
包信息保存在 package.json 中 dependencies 属性
开发依赖npm i -D less
npm i --save-dev less
-D 等效于 --save-dev
包信息保存在 package.json 中 devDependencies 属性

举个例子方便大家理解,比如说做蛋炒饭需要 大米 , 油 , 葱 , 鸡蛋 , 锅 , 煤气 , 铲子
其中 锅 , 煤气 , 铲子 属于开发依赖,只在制作阶段使用
大米 , 油 , 葱 , 鸡蛋 属于生产依赖,在制作与最终食用都会用到
所以 开发依赖 是只在开发阶段使用的依赖包,而 生产依赖 是开发阶段和最终上线运行阶段都用到
的依赖包

7.2.8 全局安装

  • 我们可以执行安装选项 -g 进行全局安装
npm i -g nodemon # nodemon  http.js 热启动命令,

注意,也可以局部安装,但是使用nodemon需要使用 npx nodemon http.js临时运行安装在项目中的 nodemon,而不需要全局安装

全局安装完成之后就可以在命令行的任何位置运行 nodemon 命令,该命令的作用是 自动重启 node 应用程序
说明:
全局安装的命令不受工作目录位置影响
可以通过 npm root -g 可以查看全局安装包的位置
不是所有的包都适合全局安装 , 只有全局类的工具才适合,可以通过 查看包的官方文档来确定
安装方式 ,这里先不必太纠结

7.2.9 安装包依赖

在项目协作中有一个常用的命令就是 npm i ,通过该命令可以依据 package.jsonpackage-lock.json 的依赖声明安装项目依赖
node_modules 文件夹大多数情况都不会存入版本库

npm i
npm install

7.2.10 安装指定版本的包

项目中可能会遇到版本不匹配的情况,有时就需要安装指定版本的包,可以使用下面的命令的

npm i <包名@版本号>
npm i jquery@1.11.2

7.2.11 配置命令别名

通过配置命令别名可以更简单的执行命令
配置 package.json 中的 scripts 属性

{
.
.
.
"scripts": {
"server": "node server.js",
"start": "node index.js",
},
.
.
}
  • 配置完成之后,可以使用别名执行命令

npm run server
npm run start
不过 start 别名比较特别,使用时可以省略 run
npm start
补充说明:
npm start 是项目中常用的一个命令,一般用来启动项目
npm run 有自动向上级目录查找的特性,跟 require 函数也一样
对于陌生的项目,我们可以通过查看 scripts 属性来参考项目的一些操作

7.2.10 安装cnpm

  • 方式一:通过 npm 来安装 cnpm 工具
# 直接安装cnpm命令, 原来npm命令并不变,使用cnpm才会去淘宝进行加载
npm install -g cnpm --registry=https://registry.npmmirror.com
命令和npm一样,  
# 初始化:cnpm init; 
# 安装: cnpm i uniq; 
# 删除: cnpm r uniq;
  • 方式二:npm 配置淘宝镜像 (npm配置淘宝镜像比cnpm命令快一些)
# 修改npm的镜像配置--直接配置(使用npm即用的淘宝镜像)
npm config set registry https://registry.npmmirror.com/

# 修改npm的镜像配置--工具配置(使用npm即用的淘宝镜像)
npm i -g nrm #安装 nrm
nrm use taobao #修改镜像
npm config list #检查是否配置成功(选做) 检查 registry 地址是否为 https://registry.npmmirror.com/ , 如果 是 则表明成功
nrm ls #也可查看, *号的是当前镜像链接
  • 方式二:npm 使用工具进行切换(nrm)
npm install -g nrm

nrm ls   //查看镜像源, *表示正在使用的

  npm ---------- https://registry.npmjs.org/
  yarn --------- https://registry.yarnpkg.com/
  tencent ------ https://mirrors.cloud.tencent.com/npm/
  cnpm --------- https://r.cnpmjs.org/
* taobao ------- https://registry.npmmirror.com/
  npmMirror ---- https://skimdb.npmjs.com/registry/
     

nrm use taobao //切换

补充说明:

  1. 建议使用第二种方式 进行镜像配置,因为后续修改起来会比较方便
  2. 虽然 cnpm 可以提高速度,但是 npm 也可以通过淘宝镜像进行加速,所以 npm 的使用率还 是高于 cnpm

7.3 yarn

7.3.1 yarn介绍

yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具,官方网址: https://yarnpkg.com/

  • yarn特点

yarn 官方宣称的一些特点

  • 速度超快:yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快
  • 超级安全:在执行代码之前,yarn 会通过算法校验每个安装包的完整性
  • 超级可靠:使用详细、简洁的锁文件格式和明确的安装算法,yarn 能够保证在不同系统上无差异的工作

7.3.2 yarn安装

我们可以使用 npm 安装 yarn
npm i -g yarn

7.3.3 yarn常用命令

功能命令
初始化yarn init / yarn init -y
安装包yarn add uniq 生产依赖
yarn add less --dev 开发依赖
yarn global add nodemon 全局安装
删除包yarn remove uniq 删除项目依赖包
yarn global remove nodemon 全局删除包
安装项目依赖yarn
运行命令别名yarn <别名> # 不需要添加 run

7.3.4 yarn 配置淘宝镜像

yarn config set registry https://registry.npmmirror.com/

#可以通过 yarn config list 查看 yarn 的配置项

7.4 npm 和 yarn 选择

大家可以根据不同的场景进行选择

  1. 个人项目
    如果是个人项目, 哪个工具都可以 ,可以根据自己的喜好来选择
  2. 公司项目
    如果是公司要根据项目代码来选择,可以 通过锁文件判断 项目的包管理工具
    npm 的锁文件为 package-lock.json
    yarn 的锁文件为 yarn.lock
    包管理工具 不要混着用,切记,切记,切记

7.6 发布包

我们可以将自己开发的工具包发布到 npm 服务上,方便自己和其他开发者使用,操作步骤如下:

  1. 创建文件夹,并创建文件 index.js, 在文件中声明函数,使用 module.exports 暴露
  2. npm 初始化工具包,package.json 填写包的信息 (包的名字是唯一的)
  3. 注册账号 https://www.npmjs.com/signup
  4. 激活账号 ( 一定要激活账号 )(chenyj123)
  5. 修改为官方的官方镜像 (命令行中运行 nrm use npm )
  6. 命令行下 npm login 填写相关用户信息
  7. 命令行下 npm publish 提交包 👌 (chenyj_math)

7.7 更新包

后续可以对自己发布的包进行更新,操作步骤如下

  1. 更新包中的代码
  2. 测试代码是否可用
  3. 修改 package.json 中的版本号 (一定要修改,远端已经存在之前版本,也可删除重新发布)
  4. 发布更新 (npm publish)

7.8 删除包

执行如下命令删除包

npm unpublish --force

删除包需要满足一定的条件,
https://docs.npmjs.com/policies/unpublish
你是包的作者
发布小于 24 小时
大于 24 小时后,没有其他包依赖,并且每周小于 300 下载量,并且只有一个维护者 (写的哪个版本,就会删除哪个版本)

7.7 扩展内容

在很多语言中都有包管理工具,比如:
在这里插入图片描述
除了编程语言领域有包管理工具之外,操作系统层面也存在包管理工具,不过这个包指的是『 软件包 』
在这里插入图片描述

8. nvm

8.1 介绍&安装使用

  • nvm 全称 Node Version Manager 顾名思义它是用来管理 node 版本的工具,方便切换不同版本的Node.js
  • nvm 的使用非常的简单,跟 npm 的使用方法类似
  • 下载安装

首先先下载 nvm,下载地址 https://github.com/coreybutler/nvm-windows/releases
选择 nvm-setup.exe 下载即可(网络异常的小朋友可以在资料文件夹中获取)
在这里插入图片描述

8.2 常用命令

在这里插入图片描述

9. express

9.1 express 介绍

express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,官方网址: https://www.expressjs.com.cn/
简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用(HTTP 服务)

9.2 express 使用

9.2.1 初体验

  • 下载

express 本身是一个 npm 包,所以可以通过 npm 安装
npm init
npm i express

  • js代码
var express = require('express');
//创建应用
var app = express();
//创建路由规则
app.get('/home',(req,res)=>{
    console.log('有人请求服务器1了');
    res.end('hello express server');
});
// 监听端口 启动服务
app.listen(3000,()=>{
    console.log('服务已经启动, 端口监听为 3000...')
})
  • 启动
`node <文件名>`
# 或者
`nodemon <文件名>`
  • 然后在浏览器就可以访问 http://127.0.0.1:3000/home 👌

9.2.2 express 路由

官方定义: 路由确定了应用程序如何响应客户端对特定端点的请求
一个路由的组成有 请求方法 , 路径 和 回调函数 组成
express 中提供了一系列方法,可以很方便的使用路由,使用格式如下:app.<method>(pathcallback)

var express = require('express');
//创建应用
var app = express();
//创建路由规则
app.get('/home',(req,res)=>{
    console.log('有人请求home页了');
    res.send('hello express server');
});
app.get('/',(req,res)=>{
    console.log('有人请求shou页了');
    res.send('hello express server');
});
//创建 post 路由
app.post('/login',(req,res)=>{
    res.send('登录成功');
});
//匹配所有的请求方法
 app.get('/search',(req,res)=>{
     console.log('有人请求search页了');
     res.send('hello express server');
 });

//匹配所有的请求方法
app.all('/search2', (req, res) => {
    res.send('1 秒钟为您找到相关结果约 100,000,000 个');
});
//自定义 404 路由 ,应该放在最下面,路由会从上到下匹配
 app.get('*',(req,res)=>{
     res.send('<h1>404 Not Found</h1>');
 }); 


// 监听端口 启动服务
app.listen(3000,()=>{
    console.log('服务已经启动, 端口监听为 3000...')
})
  • 注意:验证post的方法,目前已知有ajax表单,验证html如下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>home</h1>
    <form action="http://localhost:3000/login" method="post">
        <input type="submit">
    </form>
</body>
</html>

9.2.3 获取请求参数

express 框架封装了一些 API 来方便获取请求报文中的数据,并且兼容原生 HTTP 模块的获取方式

app.get('/login',(req,res)=>{
    console.log('有人请求login页了');
    // 1. 获取报文的方式与原生 HTTP 获取方式是兼容的 【原生操作】
    console.log(req.method);//GET
    console.log(req.url);// //login?username=sadasd
    console.log(req.httpVersion);// 1.1
    console.log(req.headers);

    // 2. express 独有的获取报文的方式 【express独有操作】
    //获取查询字符串
    console.log(req.path)//『相对重要』 /login 等价于:require('url').parse(request.url).pathname
    console.log(req.query)//『相对重要』{ username: 'sadasd' } 等价于:require('url').parse(request.url,true).query
    console.log(req.params.username)//『相对重要』{ username: 'sadasd' }
    // 获取指定的请求头
    console.log(req.get('host'));
    res.send('请求报文的获取');
});

9.2.4 获取路由参数

eg:https://npcitem.jd.hk/10051796795661.html , 取得10051796795661参数

var express = require('express');
//创建应用
var app = express();
// eg: https://npcitem.jd.hk/10051796795661.html
//创建路由规则
app.get('/:id.html',(req,res)=>{
    console.log('有人请求home页了');
    res.send('商品详情, 商品 id 为'+req.params.id);
});

// 监听端口 启动服务
app.listen(3000,()=>{
    console.log('服务已经启动, 端口监听为 3000...')
})

9.2.5 相应设置参数

//获取请求的路由规则
app.get("/response", (req, res) => {
//1. express 中设置响应的方式兼容 HTTP 模块的方式
res.statusCode = 404;
res.statusMessage = 'xxx';
res.setHeader('abc','xyz');
res.write('响应体');
res.end('xxx');
//2. express 的响应方法
res.status(500); //设置响应状态码
res.set('xxx','yyy');//设置响应头
res.send('中文响应不乱码');//设置响应体
//连贯操作
res.status(404).set('xxx','yyy').send('你好朋友')
//3. 其他响应
res.redirect('http://atguigu.com')//重定向
res.download('./package.json');//下载响应
res.json();//响应 JSON
res.sendFile(__dirname + '/home.html') //响应文件内容 只接受绝对路径作为参数,因此不能直接使用相对路径。
});

9.3 express 中间件

中间件(Middleware)本质是一个回调函数
中间件函数 可以像路由回调一样访问 请求对象(request)响应对象(response)
中间件的作用 就是 使用函数封装公共操作,简化代码

9.3.1 定义全局中间件

每一个请求 到达服务端之后 都会执行全局中间件函数

let recordMiddleware = function(request,response,next){
//实现功能代码
//.....
//执行next函数(当如果希望执行完中间件函数之后,仍然继续执行路由中的回调函数,必须调用next)
next();
}
#应用中间件
app.use(recordMiddleware);

在这里插入图片描述

9.3.2 多个全局中间件

app.use(function (request, response, next) {
console.log('定义第一个中间件');
next();
})
app.use(function (request, response, next) {
console.log('定义第二个中间件');
next();
})

9.3.4 定义路由中间件

如果 只需要对某一些路由进行功能封装 ,则就需要路由中间件

app.get('/路径',`中间件函数`,(request,response)=>{
});
app.get('/路径',`中间件函数1`,`中间件函数2`,(request,response)=>{
});

9.4 静态资源中间件

express 内置处理静态资源的中间件

//引入express框架
const express = require('express');
//创建服务对象
const app = express();
//静态资源中间件的设置,将当前文件夹下的public目录作为网站的根目录
app.use(express.static('./public')); //当然这个目录中都是一些静态资源
//如果访问的内容经常变化,还是需要设置路由
//但是,在这里有一个问题,如果public目录下有index.html文件,单独也有index.html的路由,
//则谁书写在前,优先执行谁
app.get('/index.html',(request,response)=>{
respsonse.send('首页');
});
//监听端口
app.listen(3000,()=>{
console.log('3000 端口启动....');
});

注意事项:

  1. index.html 文件为默认打开的资源
  2. 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
  3. 路由响应动态资源,静态资源中间件响应静态资源

9.5 获取请求体数据body-parser

获取请求体数据 body-parser

  • 安装&导包

npm i body-parser
const bodyParser = require('body-parser');

  • 获取中间件函数

//处理 querystring 格式的请求体
let urlParser = bodyParser.urlencoded({extended:false});
//处理 JSON 格式的请求体
let jsonParser = bodyParser.json();

  • 设置路由中间件,然后使用 request.body 来获取请求体数据
app.post('/login', urlParser, (request,response)=>{
//获取请求体数据
//console.log(request.body);
//用户名
console.log(request.body.username);
//密码
console.log(request.body.userpass);
response.send('获取请求体数据');
});

9.6 图片防盗链

var express = require('express');
//创建应用
var app = express();
//声明中间函数
function recordMiddleware(req,res,next){
    var referer = req.get('referer');// 获取指定的请求头
    if (referer) {
        var url = new URL(referer);
        var hostname = url.hostname;
        console.log(hostname);
        console.log(hostname !== 'localhost');
        if (hostname !== 'localhost') {
            console.log('f')
            res.status(404).send(`<h1>404 Not found</h1>`);
            return;
        }
    }
    console.log('继续执行')
    next();

}
app.use(recordMiddleware);
app.use(express.static(__dirname+'/public')); //要放在后面

// 监听端口 启动服务
app.listen(3000,()=>{
    console.log('服务已经启动, 端口监听为 3000...')
})

9.7 Router

9.7.2 Router 介绍

express 中的 Router 是一个完整的中间件和路由系统,可以看做是一个小型的 app 对象。对路由进行模块化,更好的管理路由

9.7.1 Router 使用

  • 创建独立的 JS 文件(homeRouter.js)
//1. 导入 express
const express = require('express');
//2. 创建路由器对象
const router = express.Router();
//3. 在 router 对象身上添加路由
router.get('/', (req, res) => {
res.send('首页');
})
router.get('/cart', (req, res) => {
res.send('购物车');
});
//4. 暴露
module.exports = router;

*主文件

const express = require('express');
const app = express();
//5.引入子路由文件
const homeRouter = require('./routes/homeRouter');
//6.设置和使用中间件
app.use(homeRouter);
//app.use('/', indexRouter);//路由(默认)
//app.use('/users', usersRouter);//路由前缀
app.listen(3000,()=>{
console.log('3000 端口启动....');
})

9.8 EJS模板引擎

模板引擎是分离 用户界面和业务数据 的一种技术 (分离htmljs)

9.8.1 什么是 EJS

EJS 是一个高效的 Javascript 的模板引擎
官网: https://ejs.co/
中文站: https://ejs.bootcss.com/

//1.安装ejs
//2.导入ejs
var ejs = require('ejs');
var fs = require('fs');
//字符串
var china = '中国';
var weather = '大太阳';

var demo = fs.readFileSync('./13_模板引擎/demo.html','utf-8');
// //使用ejs渲染
// // var result = ejs.render('我爱你 <%= china %>',{china:china});
// // var result = ejs.render('我爱你 <%= china %>',{china});
var result = ejs.render(demo,{china,weather});
console.log(result);

9.8.2 EJS常用语法

执行JS代码
<% code %>
输出转义的数据到模板上
<%= code %>
输出非转义的数据到模板上
<%- code %>

9.8.3 express中使用EJS

npm i ejs

  • home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 <h1>我爱你 <%= china %></h1>
<%=weather%>
</body>
</html>
  • 15_模板引擎_express中使用ejs.js
var express = require('express');
var path = require('path');
var app = express();
var china='中国';
var weather = '大太阳';
//1.设置模板引擎
app.set('view engine','ejs');
//2.设置模板文件存放位置
app.set('views',path.resolve(__dirname,'./13_模板引擎'));
app.get('/home2',(req,res)=>{
    res.setHeader("Content-Type","text/html;charset=utf-8")
    //3.render响应,直接渲染到页面返回
    res.render('home',{china,weather});
})

// 监听端口 启动服务
app.listen(3000,()=>{
    console.log('服务已经启动, 端口监听为 3000...')
})

9.8.4 express-generator

express-generator中文官网

npm install -g express-generator 全局安装命令
express -h 查看帮助
express -e <目录名> --view =ejs <目录名> 当前目录或指定目录初始化可直接创建)
express i 安装依赖
npm start 启动

9.8.5 文件上传

<form method="post" action="./fileDeal" enctype="multipart/form-data">
        请输入姓名:<input type="text" name="username">
        请上传文件:<input type="file" name="myfile">
        <button>提交</button>
    </form>

9.8.6 文件处理

  • 引入包npm i formidable@2.1.2 (高版本会报错)
  • formidable

    在这里插入图片描述
var express = require('express');
var formidable = require('formidable');
var path = require('path');
var router = express.Router();
router.post('/fileDeal', (req, res) => {
    const form = formidable({
        //设置文件保存路径
        uploadDir: path.resolve(__dirname , './public'),
        //保持文件后缀
        keepExtensions: true

    });

    form.parse(req, (err, fields, files) => {
        if (err) {
            next(err);
            return;
        }
        // console.log(fields)//属性集合
        // console.log(files)//文件集合

        //服务器保存该图片的访问 URL
        // /images/8ad3d5e36012212ba7642c000.jpg
        // let url = '/public/' + files.myfile.newFilename;// 将来将此数据保存在数据库中
        let url = files.myfile.newFilename;// 将来将此数据保存在数据库中
        // res.json({fields, files});
        res.end(url);
    });
})

10. Mongodb

10.1 介绍

MongoDB 是一个基于分布式文件存储的数据库(并不是单纯的内存数据库),官方地址 https://www.mongodb.com/
操作语法与 JavaScript 类似,容易上手,学习成本低

Mongodb 中有三个重要概念需要掌握
数据库(database) 数据库是一个数据仓库,数据库服务下可以创建很多数据库,数据库中可以存放很多集合
集合(collection) 集合类似于 JS 中的数组,在集合中可以存放很多文档
文档(document) 文档是数据库中的最小单位,类似于 JS 中的对象

在这里插入图片描述

10.2 下载与启动

下载地址: https://www.mongodb.com/try/download/community
建议选择 zip 类型, 通用性更强配置步骤如下:

  1. 将压缩包移动到 C:\Program Files 下,然后解压
  2. 创建 C:\data\db 目录,mongodb 会将数据默认保存在这个文件夹
  3. 以 mongodb 中 bin 目录作为工作目录,启动命令行
  4. 运行命令 mongod
  5. 访问http://localhost:27017 测试 或启动客户端工具mongo.exe工具

自定义配置启动:
1.新建data log etc 3个目录
2.在etc下新建mongodb.conf配置文件
3. # db存放的目录
dbpath=D:\mongodb-windows-x86_64-5.0.24\mongodb-win32-x86_64-windows-5.0.24\data
# 后台启动需要配置日志输出
logpath=D:\mongodb-windows-x86_64-5.0.24\mongodb-win32-x86_64-windows-5.0.24\log\mongodb.log
4.运行命令启动
mongod --config D:\mongodb-windows-x86_64-5.0.24\mongodb-win32-x86_64-windows-5.0.24\etc\mongodb.conf

linunx安装参考

在这里插入图片描述
在这里插入图片描述

10.3 命令行交互

  • 数据库操作

显示所有的数据库
show dbs
切换到指定的数据库,如果数据库不存在会自动创建数据库(刚创建不会显示,只有加入数据,才会创建)
use 数据库名
显示当前所在的数据库
db
删除当前数据库
use 库名
db.dropDatabase()

  • 集合操作

创建集合
db.createCollection('集合名称')
显示当前数据库中的所有集合
show collections
删除某个集合
db.集合名.drop()
重命名集合
db.集合名.renameCollection('newName')

  • 文档操作

插入文档
db.集合名.insert(文档对象);
查询文档
db.集合名.find(查询条件)
_id 是 mongodb 自动生成的唯一编号,用来唯一标识文档
更新文档
db.集合名.update(查询条件,新的文档) 默认全部子弹更新
db.集合名.update({name:'张三'},{$set:{age:19}}) 指定字段更新
删除文档
db.集合名.remove(查询条件)

update 方法默认只会更新符合查询条件的第一条文档。这是因为在进行更新操作时,MongoDB默认只会更新匹配的第一条文档,除非你显式指定 multi: true 选项,这样才会更新所有匹配的文档。例如,你可以这样使用 update 方法来更新所有匹配的文档:
db.集合名.update(查询条件, 更新操作, { multi: true })

在这里插入图片描述
在这里插入图片描述

10.4 mongoose

Mongoose 是一个对象文档模型库,官网 http://www.mongoosejs.net/
方便使用代码操作 mongodb 数据库
npm i mongoose@5.13.15

10.4.1 初步使用

//1. 安装 mongoose
//2. 导入 mongoose
const mongoose = require('mongoose');
//设置 strictQuery 为 true
mongoose.set('strictQuery', true);
//3. 连接 mongodb 服务                        数据库的名称,没有会自动创建
mongoose.connect('mongodb://127.0.0.1:27017/bilibili');

//4. 设置回调
// 设置连接成功的回调  once 一次   事件回调函数只执行一次
mongoose.connection.once('open', () => {
        console.log('连接成功');
        //5. 创建文档的结构对象
        //设置集合中文档的属性以及属性值的类型
        let BookSchema = new mongoose.Schema({
            name: String,
            author: String,
            price: Number
        });
        //6. 创建模型对象  对文档操作的封装对象
        let BookModel = mongoose.model('book', BookSchema);
        //7. 新增文档
        //7. 新增
        BookModel.create({
            name: '西游记',
            author: '吴承恩',
            price: 19.9
        }, (err, data) => {
            //判断是否有错误
            if (err) {
                console.log(err);
                return;
            }
            //如果没有出错, 则输出插入后的文档对象
            console.log(data);
            //8. 关闭数据库连接 (项目运行过程中, 不会添加该代码)
            mongoose.disconnect();
        });
    }
);

// 设置连接错误的回调
mongoose.connection.on('error', () => {
    console.log('连接失败');
});

//设置连接关闭的回调
mongoose.connection.on('close', () => {
    console.log('连接关闭');
});

// 关闭 mongodb 的连接
// setTimeout(() => {
//   mongoose.disconnect();
// }, 2000)

10.4.2 字段类型

在这里插入图片描述

10.4.3 字段值验证

Mongoose 有一些内建验证器,可以对字段值进行验证

  • 必填项
title: {
type: String,
required: true // 设置必填项
},
  • 默认值
author: {
type: String,
default: '匿名' //默认值
},
  • 枚举值
gender: {
type: String,
enum: ['男','女'] //设置的值必须是数组中的
},
  • 唯一值
username: {
type: String,
unique: true
},
## unique 需要 重建集合 才能有效果
 let BookSchema = new mongoose.Schema({
            name: {
                type: String,
                required: true, // 表明该属性必须不为空
                unique: true// 设置为独一无二的
            },
            author: {
                type: String,
                default: '匿名'
            },
            //类型
            style: {
                type: String,
                //枚举
                enum: ['言情','城市','志怪','恐怖']
            },
            price: Number
        });

10.4.4 CURD

数据库的基本操作包括四个,增加(create),删除(delete),修改(update),查(read)

  • 增加
插入一条
SongModel.create({
title:'给我一首歌的时间',
author: 'Jay'
}, function(err, data){
//错误
console.log(err);
//插入后的数据对象
console.log(data);
});

批量插入
PhoneModel.insertMany([
{
brand:'华为',
color:'灰色',
price:2399,
tags:['电量大','屏幕大','信号好']
},
{
brand:'小米',
color:'白色',
price:2099,
tags:['电量大','屏幕大','信号好']
}
],(err,data)=>{
if(err) throw err;
console.log('写入成功');
mongoose.connection.close();
})
  • 删除
删除一条数据
SongModel.deleteOne({_id:'5dd65f32be6401035cb5b1ed'}, function(err){
if(err) throw err;
console.log('删除成功');
mongoose.connection.close();
});

批量删除
SongModel.deleteMany({author:'Jay'}, function(err){
if(err) throw err;
console.log('删除成功');
mongoose.connection.close();
});
  • 更新
更新一条数据
SongModel.updateOne({author: 'JJ Lin'}, {author: '林俊杰'}, function (err) {
if(err) throw err;
mongoose.connection.close();
});


批量更新数据
SongModel.updateMany({author: 'Leehom Wang'}, {author: '王力宏'}, function (err) {
if(err) throw err;
mongoose.connection.close();
});
  • 查询
查询一条数据
SongModel.findOne({author: '王力宏'}, function(err, data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});
//根据 id 查询数据
SongModel.findById('5dd662b5381fc316b44ce167',function(err, data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});

批量查询数据
//不加条件查询
SongModel.find(function(err, data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});
//加条件查询
SongModel.find({author: '王力宏'}, function(err, data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});

10.4.5 条件控制

  • 运算符

在 mongodb 不能 > < >= <= !== 等运算符,需要使用替代符号
> 使用 $gt
< 使用 $lt
>= 使用 $gte
<= 使用 $lte
!== 使用 $ne
db.students.find({id:{$gt:3}}); id号比3大的所有的记录

  • 逻辑运算

$or 逻辑或的情况
db.students.find({KaTeX parse error: Expected 'EOF', got '}' at position 23: …e:18},{age:24}]}̲); `and 逻辑与的情况db.students.find({$and: [{age: {$lt:20}}, {age: {$gt: 15}}]});正则匹配条件中可以直接使用 JS 的正则语法,通过正则可以进行模糊查询db.students.find({name:/imissyou/});`

10.4.6 个性化读取

  • 字段筛选
//0:不要的字段
//1:要的字段
SongModel.find().select({_id:0,title:1}).exec(function(err,data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});
  • 数据排序
//sort 排序
//1:升序
//-1:倒序
SongModel.find().sort({hot:1}).exec(function(err,data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});
  • 数据截取
//skip 跳过 limit 限定
SongModel.find().skip(10).limit(10).exec(function(err,data){
if(err) throw err;
console.log(data);
mongoose.connection.close();
});

10.4.7 图形化管理工具

我们可以使用图形化的管理工具来对 Mongodb 进行交互,这里演示两个图形化工具

Robo 3T 免费 https://github.com/Studio3T/robomongo/releases
Navicat 收费 https://www.navicat.com.cn/

11. 接口

11.1 RESTful API

RESTful API 是一种特殊风格的接口,主要特点有如下几个:

  • URL 中的路径表示 资源 ,路径中不能有 动词 ,例如 create , delete , update 等这些都不能有
  • 操作资源要与 HTTP 请求方法 对应
  • 操作结果要与 HTTP 响应状态码 对应
    在这里插入图片描述

扩展阅读: https://www.ruanyifeng.com/blog/2014/05/restful_api.html

11.2 json-server

json-server 本身是一个 JS 编写的工具包,可以快速搭建 RESTful API 服务

官方地址: https://github.com/typicode/json-server

操作步骤:

  1. 全局安装 json-server
    npm i -g json-server
  2. 创建 JSON 文件(db.json),编写基本结构
  3. 以 JSON 文件所在文件夹作为工作目录 ,执行如下命令
    json-server --watch db.json
{
"song": [
{ "id": 1, "name": "干杯", "singer": "五月天" },
{ "id": 2, "name": "当", "singer": "动力火车" },
{ "id": 3, "name": "不能说的秘密", "singer": "周杰伦" }
]
}

12. 会话控制

所谓会话控制就是 对会话进行控制
HTTP 是一种无状态的协议,它没有办法区分多次的请求是否来自于同一个客户端, 无法区分用户
而产品中又大量存在的这样的需求,所以我们需要通过 会话控制 来解决该问题.
常见的会话控制技术有三种:

  • cookie
  • session
  • token

12.1 cookie

  • cookie 是什么

cookie 是 HTTP 服务器发送到用户浏览器并保存在本地(浏览器)的一小块数据
cookie 是保存在浏览器端的一小块数据
cookie 是按照域名划分保存的
浏览器向服务器发送请求时,会自动将 当前域名下 可用的 cookie 设置在请求头中,然后传递给服务器
这个请求头的名字也叫 cookie ,所以将cookie 理解为一个 HTTP 的请求头也是可以的
不同浏览器中的 cookie 是相互独立的,不共享

在这里插入图片描述
在这里插入图片描述

  • express中使用cookie
  • 设置
//设置 cookie
app.get('/settt-cookie', (req, res) => {
    console.log('settt-cookie');
    // 不带时效性,会在浏览器关闭的时候,销毁
    res.cookie('username', 'wangwu');

    // 带时效性
    res.cookie('email', '23123456@qq.com', {maxAge: 30 * 1000});
    res.end('hello express server');
});
  • 删除
//删除cookie
app.get('/removee-cookie', (req, res) => {
    res.clearCookie('email');
    res.send('Cookie的删除');
});
  • 获取
## npm -i cookie-parser
const cookieParser = require('cookie-parser');
//3. 设置 cookieParser 中间件
app.use(cookieParser());


//读取 cookie
app.get('/gettt-cookie', (req, res) => {
    console.log(req.cookies);
    res.send('Cookie的读取');
});

12.2 session

session 是保存在 服务器端的一块儿数据 ,保存当前访问用户的相关信息
实现会话控制,可以识别用户的身份,快速获取当前用户的相关信息

在这里插入图片描述
在这里插入图片描述

var express = require('express');
//2. 引入 express-session connect-mongo(如果存入mongodb中的话)
const session = require("express-session");
//2. 引入 cookieParser 包
const cookieParser = require('cookie-parser');
const MongoStore = require('connect-mongo');
var app = express();
app.use(cookieParser());
//3. 设置 session 的中间件
app.use(session({
    name: 'sid', //设置cookie的name,默认值是:connect.sid
    secret: 'atguigu', //参与加密的字符串(又称签名)
    saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的id
    resave: true, //是否在每次请求时重新保存session,重新延长session存活周期
    store: MongoStore.create({
        mongoUrl: 'mongodb://114.115.185.179:27017/bilibili' //数据库的连接配置
    }),
    cookie: {
        httpOnly: true, // 开启后前端无法通过 JS 操作
        maxAge: 1000 * 30 // 这一条 是控制 sessionID 的过期时间的!!! (浏览器和服务器session都过期)
    },
}))

//创建 session
app.get('/login', (req, res) => {
//设置session
    req.session.username = 'zhangsan';
    req.session.email = 'zhangsan@qq.com'
    res.send('登录成功');
})
//获取 session
app.get('/home', (req, res) => {
    console.log('session的信息');
    console.log(req.session.username);
    console.log(req.cookies);
    if (req.session.username) {
        res.send(`你好 ${req.session.username}`);
    }else{
        res.send('登录 注册');
    }
})
//销毁 session
app.get('/logout', (req, res) => {
//销毁session
// res.send('设置session');
    req.session.destroy(() => {
        res.send('成功退出');
    });
});
// 监听端口 启动服务
app.listen(3000, () => {
    console.log('服务已经启动, 端口监听为 3000...')
})

12.3 session和cookie的区别

cookie 和 session 的区别主要有如下几点

  1. 存在的位置
    • cookie:浏览器端
    • session:服务端
  2. 安全性
    • cookie 是以明文的方式存放在客户端的,安全性相对较低
    • session 存放于服务器中,所以安全性 相对 较好
  3. 网络传输量
    • cookie 设置内容过多会增大报文体积, 会影响传输效率
    • session 数据存储在服务器,只是通过 cookie 传递 id,所以不影响传输效率
  4. 存储限制
    • 浏览器限制单个 cookie 保存的数据不能超过 4K ,且单个域名下的存储数量也有限制
    • session 数据存储在服务器中,所以没有这些限制

12.4 token

token 是服务端生成并返回给 HTTP 客户端的一串加密字符串, token 中保存着 用户信息
实现会话控制,可以识别用户的身份,主要用于移动端 APP
在这里插入图片描述
在这里插入图片描述

  • token 的特点
  1. 服务端压力更小
    • 数据存储在客户端
  2. 相对更安全
    • 数据加密
    • 可以避免 CSRF(跨站请求伪造)
  3. 扩展性更强
    • 服务间可以共享
    • 增加服务节点更简单

12.4.1 JWT

JWT(JSON Web Token )是目前最流行的跨域认证解决方案,可用于基于 token 的身份验证 , JWT 使token的生成与校验更规范
我们可以使用 jsonwebtoken 包 来操作 token
扩展阅读: https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值