NodeJs

By 大Van家 on 2021-07-22
阅读时间 63 分钟
文章共 15.1k
阅读量

nodejs基础知识笔记

Node介绍

为什么要学习Node.js

  • 企业需求
    • 具有服务端开发经验更改
    • front-end
    • back-end
    • 全栈开发工程师
    • 基本的网站开发能力
      • 服务端
      • 前端
      • 运维部署
    • 多人社区

image-20200317114503403

Node.js是什么

  • Node.js是JavaScript 运行时
  • 通俗易懂的讲,Node.js是JavaScript的运行平台
  • Node.js既不是语言,也不是框架,它是一个平台
  • 浏览器中的JavaScript
    • EcmaScript
      • 基本语法
      • if
      • var
      • function
      • Object
      • Array
    • Bom
    • Dom
  • Node.js中的JavaScript
    • 没有Bom,Dom
    • EcmaScript
    • 在Node中这个JavaScript执行环境为JavaScript提供了一些服务器级别的API
      • 例如文件的读写
      • 网络服务的构建
      • 网络通信
      • http服务器
  • 构建与Chrome的V8引擎之上
    • 代码只是具有特定格式的字符串
    • 引擎可以认识它,帮你解析和执行
    • Google Chrome的V8引擎是目前公认的解析执行JavaScript代码最快的
    • Node.js的作者把Google Chrome中的V8引擎移植出来,开发了一个独立的JavaScript运行时环境
  • Node.js uses an envent-driven,non-blocking I/O mode that makes it lightweight and efficent.
    • envent-driven 事件驱动
    • non-blocking I/O mode 非阻塞I/O模型(异步)
    • ightweight and efficent. 轻量和高效
  • Node.js package ecosystem,npm,is the larget scosystem of open sourcr libraries in the world
    • npm 是世界上最大的开源生态系统
    • 绝大多数JavaScript相关的包都存放在npm上,这样做的目的是为了让开发人员更方便的去下载使用
    • npm install jquery

Node能做什么

  • web服务器后台
  • 命令行工具
    • npm(node)
    • git(c语言)
    • hexo(node)
  • 对于前端工程师来讲,接触最多的是它的命令行工具
    • 自己写的很少,主要是用别人第三方的
    • webpack
    • gulp
    • npm

起步

安装Node环境

  • 查看Node环境的版本号
  • 下载:https://nodejs.org/en/
  • 安装:
    • 傻瓜式安装,一路next
    • 安装过再次安装会升级
  • 确认Node环境是否安装成功
    • 查看node的版本号:node --version
    • 或者node -v
  • 配置环境变量

解析执行JavaScript

  1. 创建编写JavaScript脚本文件
  2. 打开终端,定位脚本文件的所属目录
  3. 输入node 文件名执行对应的文件

注意:文件名不要用node.js来命名,也就是说除了node这个名字随便起,最好不要使用中文。

文件的读写

文件读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//浏览器中的JavaScript是没有文件操作能力的
//但是Node中的JavaScript具有文件操作能力
//fs是file-system的简写,就是文件系统的意思
//在Node中如果想要进行文件的操作就必须引用fs这个核心模块
//在fs这个和兴模块中,就提供了人所有文件操作相关的API
//例如 fs.readFile就是用来读取文件的

// 1.使用fs核心模块
var fs = require('fs');

// 2.读取文件
fs.readFile('./data/a.txt',function(err,data){
if(err){
console.log('文件读取失败');
}
else{
console.log(data.toString());
}
})

文件写入:

1
2
3
4
5
6
7
8
9
10
11
12
//  1.使用fs核心模块
var fs = require('fs');

// 2.将数据写入文件
fs.writeFile('./data/a.txt','我是文件写入的信息',function(err,data){
if(err){
console.log('文件写入失败');
}
else{
console.log(data.toString());
}
})

http

服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1.加载http核心模块
var http = require('http');

// 2.使用http.createServer()创建一个web服务器
var server = http.createServer();

// 3.服务器要做的事儿
// 提供服务:对数据服务
// 发请求
// 接收请求
// 处理请求
// 反馈(发送响应)
// 当客户端请求过来,就会自动触发服务器的request请求事件,然后执行第二个参数:回调处理函数
server.on('request',function(){
console.log('收到客户的请求了')
})

// 4.绑定端口号,启动服务
server.listen(3000,function(){
console.log('runing...')
})

Node中的模块系统

使用Node编写应用程序主要就是在使用:

  • EcmaScript语言

    • 和浏览器一样,在Node中没有Bom和Dom
  • 核心模块

    • 文件操作的fs
    • http服务操作的http
    • url路径操作模块
    • path路径处理模块
    • os操作系统信息
  • 第三方模块
    • art-template
    • 必须通过npm来下载才可以使用
  • 自己写的模块
    • 自己创建的文件

什么是模块化

  • 文件作用域(模块是独立的,在不同的文件使用必须要重新引用)【在node中没有全局作用域,它是文件模块作用域】
  • 通信规则
    • 加载require
    • 导出exports

CommonJS模块规范

在Node中的JavaScript还有一个重要的概念,模块系统。

  • 模块作用域

  • 使用require方法来加载模块

  • 使用exports接口对象来导出模板中的成员

    加载require

    语法:

    1
    var 自定义变量名 = require('模块')

    作用:

    • 执行被加载模块中的代码
    • 得到被加载模块中的exports导出接口对象

    导出exports

    • Node中是模块作用域,默认文件中所有的成员只在当前模块有效

    • 对于希望可以被其他模块访问到的成员,我们需要把这些公开的成员都挂载到exports接口对象中就可以了

      导出多个成员(必须在对象中):

      1
      2
      3
      4
      5
      6
      7
      8
      exports.a = 123;
      exports.b = function(){
      console.log('bbb')
      };
      exports.c = {
      foo:"bar"
      };
      exports.d = 'hello';
导出单个成员(拿到的就是函数,字符串):

1
module.exports = 'hello';
以下情况会覆盖:
1
2
3
4
5
module.exports = 'hello';
//后者会覆盖前者
module.exports = function add(x,y) {
return x+y;
}
也可以通过以下方法来导出多个成员:
1
2
3
4
5
6
module.exports = {
foo = 'hello',
add:function(){
return x+y;
}
};

模块原理

exports和module.exports的一个引用:

1
console.log(exports === module.exports);	//trueexports.foo = 'bar';//等价于module.exports.foo = 'bar';

当给exports重新赋值后,exports!= module.exports.

最终return的是module.exports,无论exports中的成员是什么都没用。

1
真正去使用的时候:	导出单个成员:exports.xxx = xxx 或者 modeule.exports = xxx;	导出多个成员:module.exports = {}

总结

1
// 引用服务var http = require('http');var fs = require('fs');// 引用模板var template = require('art-template');// 创建服务var server = http.createServer();// 公共路径var wwwDir = 'D:/app/www';server.on('request', function (req, res) {    var url = req.url;    // 读取文件    fs.readFile('./template-apche.html', function (err, data) {        if (err) {            return res.end('404 Not Found');        }        fs.readdir(wwwDir, function (err, files) {            if (err) {                return res.end('Can not find www Dir.')            }            // 使用模板引擎解析替换data中的模板字符串            // 去xmpTempleteList.html中编写模板语法            var htmlStr = template.render(data.toString(), {                 title: 'D:/app/www/ 的索引',                files:files             });            // 发送响应数据            res.end(htmlStr);        })    })});server.listen(3000, function () {    console.log('running....');})
1
1.jQuery中的each 和 原生JavaScript方法forEach的区别:	提供源头:    	原生js是es5提供的(不兼容IE8),        jQuery的each是jQuery第三方库提供的(如果要使用需要用2以下的版本也就是1.版本),它的each方法主要用来遍历jQuery实例对象(伪数组),同时也可以做低版本forEach的替代品,jQuery的实例对象不能使用forEach方法,如果想要使用必须转为数组([].slice.call(jQuery实例对象))才能使用2.模块中导出多个成员和导出单个成员3.301302的区别:	301永久重定向,浏览器会记住    302临时重定向4.exportsmodule.exports的区别:	每个模块中都有一个module对象    module对象中有一个exports对象    我们可以把需要导出的成员都挂载到module.exports接口对象中	也就是`module.exports.xxx = xxx`的方式    但是每次写太多了就很麻烦,所以Node为了简化代码,就在每一个模块中都提供了一个成员叫`exports`    `exports === module.exports`结果为true,所以完全可以`exports.xxx = xxx`    当一个模块需要导出单个成员的时候必须使用`module.exports = xxx`的方式,=,使用`exports = xxx`不管用,因为每个模块最终return的是module.exports,而exports只是module.exports的一个引用,所以`exports`即使重新赋值,也不会影响`module.exports`。    有一种赋值方式比较特殊:`exports = module.exports`这个用来新建立引用关系的。    

require的加载规则

  • 核心模块

    • 模块名
  • 第三方模块

    • 模块名
  • 用户自己写的

    • 路径

require的加载规则:

  • 优先从缓存加载

  • 判断模块标识符

    • 核心模块
    • 自己写的模块(路径形式的模块)
    • 第三方模块(node_modules)
      • 第三方模块的标识就是第三方模块的名称(不可能有第三方模块和核心模块的名字一致)
      • npm
        • 开发人员可以把写好的框架库发布到npm上
        • 使用者通过npm命令来下载
      • 使用方式:var 名称 = require('npm install【下载包】 的包名')
        • node_modules/express/package.json main
        • 如果package.json或者main不成立,则查找被选择项:index.js
        • 如果以上条件都不满足,则继续进入上一级目录中的node_modules按照上面的规则依次查找,直到当前文件所属此盘根目录都找不到最后报错
1
// 如果非路径形式的标识// 路径形式的标识:    // ./  当前目录 不可省略    // ../  上一级目录  不可省略    //  /xxx也就是D:/xxx    // 带有绝对路径几乎不用(D:/a/foo.js)// 首位表示的是当前文件模块所属磁盘根目录// require('./a'); // 核心模块// 核心模块本质也是文件,核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了require('fs'); // 第三方模块// 凡是第三方模块都必须通过npm下载(npm i node_modules),使用的时候就可以通过require('包名')来加载才可以使用// 第三方包的名字不可能和核心模块的名字是一样的// 既不是核心模块,也不是路径形式的模块//      先找到当前文所述目录的node_modules//      然后找node_modules/art-template目录//      node_modules/art-template/package.json//      node_modules/art-template/package.json中的main属性//      main属性记录了art-template的入口模块//      然后加载使用这个第三方包//      实际上最终加载的还是文件//      如果package.json不存在或者mian指定的入口模块不存在//      则node会自动找该目录下的index.js//      也就是说index.js是一个备选项,如果main没有指定,则加载index.js文件//              // 如果条件都不满足则会进入上一级目录进行查找// 注意:一个项目只有一个node_modules,放在项目根目录中,子目录可以直接调用根目录的文件var template = require('art-template');

模块标识符中的/和文件操作路径中的/

文件操作路径:

1
// 咱们所使用的所有文件操作的API都是异步的// 就像ajax请求一样// 读取文件// 文件操作中 ./ 相当于当前模块所处磁盘根目录// ./index.txt    相对于当前目录// /index.txt    相对于当前目录// /index.txt   绝对路径,当前文件模块所处根目录// d:express/index.txt   绝对路径fs.readFile('./index.txt',function(err,data){    if(err){       return  console.log('读取失败');    }    console.log(data.toString());})

模块操作路径:

1
// 在模块加载中,相对路径中的./不能省略// 这里省略了.也是磁盘根目录require('./index')('hello')

npm

  • node package manage(node包管理器)
  • 通过npm命令安装jQuery包(npm install —save jquery),在安装时加上—save会主动生成说明书文件信息(将安装文件的信息添加到package.json里面)

npm网站

​ npmjs.com 网站 是用来搜索npm包的

npm命令行工具

npm是一个命令行工具,只要安装了node就已经安装了npm。

npm也有版本概念,可以通过npm --version来查看npm的版本

升级npm(自己升级自己):

1
npm install --global npm

常用命令

  • npm init(生成package.json说明书文件)
    • npm init -y(可以跳过向导,快速生成)
  • npm install
    • 一次性把dependencies选项中的依赖项全部安装
    • 简写(npm i)
  • npm install 包名
    • 只下载
    • 简写(npm i 包名)
  • npm install —save 包名
    • 下载并且保存依赖项(package.json文件中的dependencies选项)
    • 简写(npm i 包名)
  • npm uninstall 包名
    • 只删除,如果有依赖项会依然保存
    • 简写(npm un 包名)
  • npm uninstall —save 包名
    • 删除的同时也会把依赖信息全部删除
    • 简写(npm un 包名)
  • npm help
    • 查看使用帮助
  • npm 命令 —help
    • 查看具体命令的使用帮助(npm uninstall —help)

解决npm被墙问题

npm存储包文件的服务器在国外,有时候会被墙,速度很慢,所以需要解决这个问题。

https://developer.aliyun.com/mirror/NPM?from=tnpm淘宝的开发团队把npm在国内做了一个镜像(也就是一个备份)。

安装淘宝的cnpm:

1
npm install -g cnpm --registry=https://registry.npm.taobao.org;
1
#在任意目录执行都可以#--global表示安装到全局,而非当前目录#--global不能省略,否则不管用npm install --global cnpm

安装包的时候把以前的npm替换成cnpm

1
#走国外的npm服务器下载jQuery包,速度比较慢npm install jQuery;#使用cnpm就会通过淘宝的服务器来下载jQuerycnpm install jQuery;

如果不想安装cnpm又想使用淘宝的服务器来下载:

1
npm install jquery --registry=https://npm.taobao.org;

但是每次手动加参数就很麻烦,所以我们可以把这个选项加入到配置文件中:

1
npm config set registry https://npm.taobao.org;#查看npm配置信息npm config list;

只要经过上面的配置命令,则以后所有的npm install都会通过淘宝的服务器来下载

package.json

每一个项目都要有一个package.json文件(包描述文件,就像产品的说明书一样)

这个文件可以通过npm init自动初始化出来

1
D:\code\node中的模块系统>npm initThis utility will walk you through creating a package.json file.It only covers the most common items, and tries to guess sensible defaults.See `npm help json` for definitive documentation on these fieldsand exactly what they do.Use `npm install <pkg>` afterwards to install a package andsave it as a dependency in the package.json file.Press ^C at any time to quit.package name: (node中的模块系统)Sorry, name can only contain URL-friendly characters.package name: (node中的模块系统) clsversion: (1.0.0)description: 这是一个测试项目entry point: (main.js)test command:git repository:keywords:author: xiaochenlicense: (ISC)About to write to D:\code\node中的模块系统\package.json:{  "name": "cls",  "version": "1.0.0",  "description": "这是一个测试项目",  "main": "main.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "xiaochen",  "license": "ISC"}Is this OK? (yes) yes

对于目前来讲,最有用的是dependencies选项,可以用来帮助我们保存第三方包的依赖信息。

如果node_modules删除了也不用担心,只需要在控制面板中npm install就会自动把package.json中的dependencies中所有的依赖项全部都下载回来。

  • 建议每个项目的根目录下都有一个package.json文件
  • 建议执行npm install 包名的时候都加上--save选项,目的是用来保存依赖信息

package.json和package-lock.json

npm 5以前是不会有package-lock.json这个文件

npm5以后才加入这个文件

当你安装包的时候,npm都会生成或者更新package-lock.json这个文件

  • npm5以后的版本安装都不要加--save参数,它会自动保存依赖信息
  • 当你安装包的时候,会自动创建或者更新package-lock.json文件
  • package-lock.json这个文件会包含node_modules中所有包的信息(版本,下载地址。。。)
    • 这样的话重新npm install的时候速度就可以提升
  • 从文件来看,有一个lock称之为锁
    • 这个lock使用来锁版本的
    • 如果项目依赖了1.1.1版本
    • 如果你重新install其实会下载最细版本,而不是1.1.1
    • package-lock.json的另外一个作用就是锁定版本号,防止自动升级

path路径操作模块

参考文档:https://nodejs.org/docs/latest-v13.x/api/path.html

  • path.basename:获取路径的文件名,默认包含扩展名
  • path.dirname:获取路径中的目录部分
  • path.extname:获取一个路径中的扩展名部分
  • path.parse:把路径转换为对象
    • root:根路径
    • dir:目录
    • base:包含后缀名的文件名
    • ext:后缀名
    • name:不包含后缀名的文件名
  • path.join:拼接路径
  • path.isAbsolute:判断一个路径是否为绝对路径image-20200315150610001

Node中的其它成员(dirname,filename)

在每个模块中,除了require,exports等模块相关的API之外,还有两个特殊的成员:

  • __dirname,是一个成员,可以用来动态获取当前文件模块所属目录的绝对路径

  • __filename,可以用来动态获取当前文件的绝对路径(包含文件名)

  • __dirnamefilename是不受执行node命令所属路径影响的

==在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。==(node xxx.js)

所以为了解决这个问题,只需要把相对路径变为==绝对路径==(绝对路径不受任何影响)就可以了。

就可以使用==__dirname==或者==__filename==来帮助我们解决这个问题

在拼接路径的过程中,为了避免手动拼接带来的一些低级错误,推荐使用path.join()来辅助拼接

1
var fs = require('fs');var path = require('path');// console.log(__dirname + 'a.txt');// path.join方法会将文件操作中的相对路径都统一的转为动态的绝对路径fs.readFile(path.join(__dirname + '/a.txt'),'utf8',function(err,data){	if(err){		throw err	}	console.log(data);});

补充:模块中的路径标识和这里的路径没关系,不受影响(就是相对于文件模块)

注意:

模块中的路径标识和文件操作中的相对路径标识不一致

模块中的路径标识就是相对于当前文件模块,不受node命令所处路径影响

Express(快速的)

作者:Tj

原生的http在某些方面表现不足以应对我们的开发需求,所以就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码高度统一。

在node中有很多web开发框架。主要学习express

  • http://expressjs.com/,其中主要封装的是http。

  • ```javascript
    // 1 安装// 2 引包var express = require(‘express’);// 3 创建服务器应用程序// 也就是原来的http.createServer();var app = express();// 公开指定目录// 只要通过这样做了,就可以通过/public/xx的方式来访问public目录中的所有资源// 在Express中开放资源就是一个API的事app.use(‘/public/‘,express.static(‘/public/‘));//模板引擎在Express中开放模板也是一个API的事// 当服务器收到get请求 / 的时候,执行回调处理函数app.get(‘/‘,function(req,res){ res.send(‘hello express’);})// 相当于server.listenapp.listen(3000,function(){ console.log(‘app is runing at port 3000’);})

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10


    ### 学习Express

    #### 起步

    ##### 安装:![image-20200310123723079](C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200310123723079.png)

    ```javascript
    cnpm install express
hello world:image-20200310124850557
1
// 引入expressvar express = require('express');// 1. 创建appvar app = express();//  2. app.get('/',function(req,res){    // 1    // res.write('Hello');    // res.write('World');    // res.end()    // 2    // res.end('hello world');    // 3    res.send('hello world');})app.listen(3000,function(){    console.log('express app is runing...');})
基本路由

路由:

  • 请求方法

  • 请求路径

  • 请求处理函数

get:

1
//当你以get方法请求/的时候,执行对应的处理函数app.get('/',function(req,res){    res.send('hello world');})

post:

1
//当你以post方法请求/的时候,执行对应的处理函数app.post('/',function(req,res){    res.send('hello world');})
Express静态服务API
1
// app.use不仅仅是用来处理静态资源的,还可以做很多工作(body-parser的配置)app.use(express.static('public'));app.use(express.static('files'));app.use('/stataic',express.static('public'));
1
// 引入expressvar express = require('express');// 创建appvar app = express();// 开放静态资源// 1.当以/public/开头的时候,去./public/目录中找对应资源// 访问:http://127.0.0.1:3000/public/login.htmlapp.use('/public/',express.static('./public/')); // 2.当省略第一个参数的时候,可以通过省略/public的方式来访问// 访问:http://127.0.0.1:3000/login.html// app.use(express.static('./public/'));   // 3.访问:http://127.0.0.1:3000/a/login.html// a相当于public的别名// app.use('/a/',express.static('./public/')); //  app.get('/',function(req,res){    res.end('hello world');});app.listen(3000,function(){    console.log('express app is runing...');});
在Express中配置使用art-templete模板引擎
  • art-template官方文档
  • 在node中,有很多第三方模板引擎都可以使用,不是只有art-template
    • 还有ejs,jade(pug),handlebars,nunjucks

安装:

1
npm install --save art-templatenpm install --save express-art-template//两个一起安装npm i --save art-template express-art-template

配置:

1
app.engine('html', require('express-art-template'));

使用:

1
app.get('/',function(req,res){    // express默认会去views目录找index.html    res.render('index.html',{           title:'hello world'         });})

如果希望修改默认的views视图渲染存储目录,可以:

1
// 第一个参数views千万不要写错app.set('views',目录路径);
在Express中获取表单请求数据
获取get请求数据:

Express内置了一个api,可以直接通过req.query来获取数据

1
// 通过requery方法获取用户输入的数据// req.query只能拿到get请求的数据 var comment = req.query;
获取post请求数据:

在Express中没有内置获取表单post请求体的api,这里我们需要使用一个第三方包body-parser来获取数据。

安装:

1
npm install --save body-parser;

配置:

// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )

1
var express = require('express')// 引包var bodyParser = require('body-parser')var app = express()// 配置body-parser// 只要加入这个配置,则在req请求对象上会多出来一个属性:body// 也就是说可以直接通过req.body来获取表单post请求数据// parse application/x-www-form-urlencodedapp.use(bodyParser.urlencoded({ extended: false }))// parse application/jsonapp.use(bodyParser.json())

使用:

1
app.use(function (req, res) {  res.setHeader('Content-Type', 'text/plain')  res.write('you posted:\n')  // 可以通过req.body来获取表单请求数据  res.end(JSON.stringify(req.body, null, 2))})

在Express中配置使用express-session插件操作

参考文档:https://github.com/expressjs/session

安装:

1
npm install express-session

配置:

1
//该插件会为req请求对象添加一个成员:req.session默认是一个对象//这是最简单的配置方式//Session是基于Cookie实现的app.use(session({  //配置加密字符串,他会在原有的基础上和字符串拼接起来去加密  //目的是为了增加安全性,防止客户端恶意伪造  secret: 'keyboard cat',  resave: false,  saveUninitialized: true,//无论是否适用Session,都默认直接分配一把钥匙  cookie: { secure: true }}))

使用:

1
// 读//添加Session数据//session就是一个对象req.session.foo = 'bar';//写//获取session数据req.session.foo//删req.session.foo = null;delete req.session.foo

提示:

默认Session数据时内存储数据,服务器一旦重启,真正的生产环境会把Session进行持久化存储。

利用Express实现ADUS项目

模块化思想

模块如何划分:

  • 模块职责要单一

javascript模块化:

  • Node 中的 CommonJS
  • 浏览器中的:
    • AMD require.js
    • CMD sea.js
  • es6中增加了官方支持

起步

  • 初始化
  • 模板处理

路由设计

请求方法 请求路径 get参数 post参数 备注
GET /students 渲染首页
GET /students/new 渲染添加学生页面
POST /students/new name,age,gender,hobbies 处理添加学生请求
GET /students/edit id 渲染编辑页面
POST /students/edit id,name,age,gender,hobbies 处理编辑请求
GET /students/delete id 处理删除请求

提取路由模块

router.js:

1
/** * router.js路由模块 * 职责: *      处理路由 *      根据不同的请求方法+请求路径设置具体的请求函数 * 模块职责要单一,我们划分模块的目的就是增强代码的可维护性,提升开发效率 */var fs = require('fs');// Express专门提供了一种更好的方式// 专门用来提供路由的var express = require('express');// 1 创建一个路由容器var router = express.Router();// 2 把路由都挂载到路由容器中router.get('/students', function(req, res) {    // res.send('hello world');    // readFile的第二个参数是可选的,传入utf8就是告诉他把读取到的文件直接按照utf8编码,直接转成我们认识的字符    // 除了这样来转换,也可以通过data.toString()来转换    fs.readFile('./db.json', 'utf8', function(err, data) {        if (err) {            return res.status(500).send('Server error.')        }        // 读取到的文件数据是string类型的数据        // console.log(data);        // 从文件中读取到的数据一定是字符串,所以一定要手动转换成对象        var students = JSON.parse(data).students;        res.render('index.html', {            // 读取文件数据            students:students        })    })});router.get('/students/new',function(req,res){    res.render('new.html')});router.get('/students/edit',function(req,res){    });router.post('/students/edit',function(req,res){    });router.get('/students/delete',function(req,res){    });// 3 把router导出module.exports = router;

app.js:

1
var router = require('./router');// router(app);// 把路由容器挂载到app服务中// 挂载路由app.use(router);

设计操作数据的API文件模块

es6中的find和findIndex:

find接受一个方法作为参数,方法内部返回一个条件

find会便利所有的元素,执行你给定的带有条件返回值的函数

符合该条件的元素会作为find方法的返回值

如果遍历结束还没有符合该条件的元素,则返回undefinedimage-20200313103810731

1
/** * student.js * 数据操作文件模块 * 职责:操作文件中的数据,只处理数据,不关心业务 */var fs = require('fs'); /**  * 获取所有学生列表  * return []  */exports.find = function(){    } /**  * 获取添加保存学生  */exports.save = function(){        }/** * 更新学生 */exports.update = function(){        } /** * 删除学生 */exports.delete = function(){        }

步骤

  • 处理模板
  • 配置静态开放资源
  • 配置模板引擎
  • 简单的路由,/studens渲染静态页出来
  • 路由设计
  • 提取路由模块
  • 由于接下来的一系列业务操作都需要处理文件数据,所以我们需要封装Student.js’
  • 先写好student.js文件结构
    • 查询所有学生列别哦的API
    • findById
    • save
    • updateById
    • deleteById
  • 实现具体功能

    • 通过路由收到请求
    • 接受请求中的参数(get,post)
      • req.query
      • req.body
    • 调用数据操作API处理数据
    • 根据操作结果给客户端发送请求
  • 业务功能顺序

    • 列表
    • 添加
    • 编辑
    • 删除

子模板和模板的继承(模板引擎高级语法)【include,extend,block】

注意:

模板页:

1
<!DOCTYPE html><html lang="zh"><head>	<meta charset="UTF-8">	<meta name="viewport" content="width=device-width, initial-scale=1.0">	<meta http-equiv="X-UA-Compatible" content="ie=edge">	<title>模板页</title>	<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"/>	{{ block 'head' }}{{ /block }}</head><body>	<!-- 通过include导入公共部分 -->	{{include './header.html'}}		<!-- 留一个位置 让别的内容去填充 -->	{{ block  'content' }}		<h1>默认内容</h1>	{{ /block }}		<!-- 通过include导入公共部分 -->	{{include './footer.html'}}		<!-- 公共样式 -->	<script src="/node_modules/jquery/dist/jquery.js" ></script>	<script src="/node_modules/bootstrap/dist/js/bootstrap.js" ></script>	{{ block 'script' }}{{ /block }}</body></html>

模板的继承:

​ header页面:

1
<div id="">	<h1>公共的头部</h1></div>

​ footer页面:

1
<div id="">	<h1>公共的底部</h1></div>

模板页的使用:

1
<!-- 继承(extend:延伸,扩展)模板也layout.html --><!-- 把layout.html页面的内容都拿进来作为index.html页面的内容 -->{{extend './layout.html'}}<!-- 向模板页面填充新的数据 --><!-- 填充后就会替换掉layout页面content中的数据 --><!-- style样式方面的内容 -->{{ block 'head' }}	<style type="text/css">		body{			background-color: skyblue;		}	</style>{{ /block }}{{ block 'content' }}	<div id="">		<h1>Index页面的内容</h1>	</div>{{ /block }}<!-- js部分的内容 -->{{ block 'script' }}	<script type="text/javascript">			</script>{{ /block }}

最终的显示效果:

image-20200316134759517

MongoDB

关系型和非关系型数据库

关系型数据库(表就是关系,或者说表与表之间存在关系)。

  • 所有的关系型数据库都需要通过sql语言来操作
  • 所有的关系型数据库在操作之前都需要设计表结构
  • 而且数据表还支持约束
    • 唯一的
    • 主键
    • 默认值
    • 非空

非关系型数据库

  • 非关系型数据库非常的灵活
  • 有的关系型数据库就是key-value对儿
  • 但MongDB是长得最像关系型数据库的非关系型数据库
    • 数据库 -》 数据库
    • 数据表 -》 集合(数组)
    • 表记录 -》文档对象

一个数据库中可以有多个数据库,一个数据库中可以有多个集合(数组),一个集合中可以有多个文档(表记录)

1
{    qq:{       user:[           {},{},{}...       ]    }}
  • 也就是说你可以任意的往里面存数据,没有结构性这么一说

安装

启动和关闭数据库

启动:

1
# mongodb 默认使用执行mongod 命令所处盘符根目录下的/data/db作为自己的数据存储目录# 所以在第一次执行该命令之前先自己手动新建一个 /data/dbmongod# 开启服务net start mongodb

如果想要修改默认的数据存储目录,可以:

1
mongod --dbpath = 数据存储目录路径

停止:

1
在开启服务的控制台,直接Ctrl+C;或者直接关闭开启服务的控制台。

image-20200314101047100

连接数据库

连接:

1
# 该命令默认连接本机的 MongoDB 服务mongo

退出:

1
# 在连接状态输入 exit 退出连接exit

基本命令

  • show dbs
    • 查看数据库列表(数据库中的所有数据库)
  • db
    • 查看当前连接的数据库
  • use 数据库名称
    • 切换到指定的数据库,(如果没有会新建)
  • show collections
    • 查看当前目录下的所有数据表
  • db.表名.find()
    • 查看表中的详细信息

在Node中如何操作MongoDB数据库

使用官方的MongoDB包来操作

http://mongodb.github.io/node-mongodb-native/

使用第三方包mongoose来操作MongoDB数据库

​ 第三方包:mongoose基于MongoDB官方的mongodb包再一次做了封装,名字叫mongoose,是WordPress项目团队开发的。

学习指南(步骤)

官方学习文档:https://mongoosejs.com/docs/index.html

设计Scheme 发布Model (创建表)

1
// 1.引包// 注意:按照后才能require使用var mongoose = require('mongoose');// 拿到schema图表var Schema = mongoose.Schema;// 2.连接数据库// 指定连接数据库后不需要存在,当你插入第一条数据库后会自动创建数据库mongoose.connect('mongodb://localhost/test');// 3.设计集合结构(表结构)// 用户表var userSchema = new Schema({	username: { //姓名		type: String,		require: true //添加约束,保证数据的完整性,让数据按规矩统一	},	password: {		type: String,		require: true	},	email: {		type: String	}});// 4.将文档结构发布为模型// mongoose.model方法就是用来将一个架构发布为 model// 		第一个参数:传入一个大写名词单数字符串用来表示你的数据库的名称// 					mongoose 会自动将大写名词的字符串生成 小写复数 的集合名称// 					例如 这里会变成users集合名称// 		第二个参数:架构// 	返回值:模型构造函数var User = mongoose.model('User', userSchema);

添加数据(增)

1
// 5.通过模型构造函数对User中的数据进行操作var user = new User({	username: 'admin',	password: '123456',	email: 'xiaochen@qq.com'});user.save(function(err, ret) {	if (err) {		console.log('保存失败');	} else {		console.log('保存成功');		console.log(ret);	}});

删除(删)

根据条件删除所有:

1
User.remove({	username: 'xiaoxiao'}, function(err, ret) {	if (err) {		console.log('删除失败');	} else {		console.log('删除成功');		console.log(ret);	}});

根据条件删除一个:

1
Model.findOneAndRemove(conditions,[options],[callback]);

根据id删除一个:

1
User.findByIdAndRemove(id,[options],[callback]);

更新(改)

更新所有:

1
User.remove(conditions,doc,[options],[callback]);

根据指定条件更新一个:

1
User.FindOneAndUpdate([conditions],[update],[options],[callback]);

根据id更新一个:

1
// 更新	根据id来修改表数据User.findByIdAndUpdate('5e6c5264fada77438c45dfcd', {	username: 'junjun'}, function(err, ret) {	if (err) {		console.log('更新失败');	} else {		console.log('更新成功');	}});

查询(查)

查询所有:

1
// 查询所有User.find(function(err,ret){	if(err){		console.log('查询失败');	}else{		console.log(ret);	}});

条件查询所有:

1
// 根据条件查询User.find({ username:'xiaoxiao' },function(err,ret){	if(err){		console.log('查询失败');	}else{		console.log(ret);	}});

条件查询单个:

1
// 按照条件查询单个,查询出来的数据是一个对象({})// 没有条件查询使用findOne方法,查询的是表中的第一条数据User.findOne({	username: 'xiaoxiao'}, function(err, ret) {	if (err) {		console.log('查询失败');	} else {		console.log(ret);	}});

使用Node操作MySQL数据库

文档:https://www.npmjs.com/package/mysql

安装:

1
npm install --save  mysql
1
// 引入mysql包var mysql      = require('mysql');// 创建连接var connection = mysql.createConnection({  host     : 'localhost',	//本机  user     : 'me',		//账号root  password : 'secret',	//密码12345  database : 'my_db'	//数据库名}); // 连接数据库	(打开冰箱门)connection.connect(); //执行数据操作	(把大象放到冰箱)connection.query('SELECT * FROM `users` ', function (error, results, fields) {  if (error) throw error;//抛出异常阻止代码往下执行  // 没有异常打印输出结果  console.log('The solution is: ',results);});//关闭连接	(关闭冰箱门)connection.end();

异步编程

回调函数

不成立的情况下:

1
function add(x,y){    console.log(1);    setTimeout(function(){        console.log(2);        var ret = x + y;        return ret;    },1000);    console.log(3);    //到这里执行就结束了,不会i等到前面的定时器,所以直接返回了默认值 undefined}console.log(add(2,2));// 结果是 1 3 undefined 4

使用回调函数解决:

回调函数:通过一个函数,获取函数内部的操作。(根据输入得到输出结果)

1
var ret;function add(x,y,callback){    // callback就是回调函数    // var x = 10;    // var y = 20;    // var callback = function(ret){console.log(ret);}    console.log(1);    setTimeout(function(){        var ret = x + y;        callback(ret);    },1000);    console.log(3);}add(10,20,function(ret){    console.log(ret);});

注意:

​ 凡是需要得到一个函数内部异步操作的结果(setTimeout,readFile,writeFile,ajax,readdir)

​ 这种情况必须通过 回调函数 (异步API都会伴随着一个回调函数)

ajax:

基于原生XMLHttpRequest封装get方法:

1
var oReq = new XMLHttpRequest();// 当请求加载成功要调用指定的函数oReq.onload = function(){    console.log(oReq.responseText);}oReq.open("GET", "请求路径",true);oReq.send();
1
function get(url,callback){    var oReq = new XMLHttpRequest();    // 当请求加载成功要调用指定的函数    oReq.onload = function(){        //console.log(oReq.responseText);        callback(oReq.responseText);    }    oReq.open("GET", url,true);    oReq.send();}get('data.json',function(data){    console.log(data);});

Promise

callback hell(回调地狱):

文件的读取无法判断执行顺序(文件的执行顺序是依据文件的大小来决定的)(异步api无法保证文件的执行顺序)

1
var fs = require('fs');fs.readFile('./data/a.text','utf8',function(err,data){	if(err){		// 1 读取失败直接打印输出读取失败		return console.log('读取失败');		// 2 抛出异常		// 		阻止程序的执行		// 		把错误信息打印到控制台		throw err;	}	console.log(data);});fs.readFile('./data/b.text','utf8',function(err,data){	if(err){		// 1 读取失败直接打印输出读取失败		return console.log('读取失败');		// 2 抛出异常		// 		阻止程序的执行		// 		把错误信息打印到控制台		throw err;	}	console.log(data);});

通过回调嵌套的方式来保证顺序:

1
var fs = require('fs');fs.readFile('./data/a.text','utf8',function(err,data){	if(err){		// 1 读取失败直接打印输出读取失败		return console.log('读取失败');		// 2 抛出异常		// 		阻止程序的执行		// 		把错误信息打印到控制台		throw err;	}	console.log(data);	fs.readFile('./data/b.text','utf8',function(err,data){		if(err){			// 1 读取失败直接打印输出读取失败			return console.log('读取失败');			// 2 抛出异常			// 		阻止程序的执行			// 		把错误信息打印到控制台			throw err;		}		console.log(data);		fs.readFile('./data/a.text','utf8',function(err,data){			if(err){				// 1 读取失败直接打印输出读取失败				return console.log('读取失败');				// 2 抛出异常				// 		阻止程序的执行				// 		把错误信息打印到控制台				throw err;			}			console.log(data);		});	});});

为了解决以上编码方式带来的问题(回调地狱嵌套),所以在EcmaScript6新增了一个API:

  • Promise:承诺,保证 (深度变长度)
  • Promise本身不是异步的,但往往都是内部封装一个异步任务

  • Promise 容器:存放了一个异步任务,有两种状态(Pending):

    • Resolved:解决

    • Rejected:失败

    • 两种状态只能变成其中一种
    • 容器一旦创建,就开始执行里面的代码

基本语法:

1
// 在EcmaScript 6中新增了一个API Promise// Promise 是一个构造函数var fs = require('fs');// 1 创建Promise容器		resolve:解决   reject:失败var p1 = new Promise(function(resolve, reject) {	fs.readFile('./a.text', 'utf8', function(err, data) {		if (err) {			// console.log(err);			// 把容器的Pending状态变为rejected			reject(err);		} else {			// console.log(data);			// 把容器的Pending状态变为resolve			resolve(1234);		}	});});// 当p1成功了,然后就(then)做指定的操作// then方法接收的function就是容器中的resolve函数// 当你 return 123 后面就接收到 123,没有 return 后面收到的就是 undefined// 真正有用的是:我们可以 return 一个 Promise 对象p1	.then(function(data) {		console.log(data);	}, function(err) {		console.log('读取文件失败了', err);	});

链式循环:png

封装Promise的readFile

1
var fs = require('fs');function pReadFile(filePath) {	return new Promise(function(resolve, reject) {		fs.readFile(filePath, 'utf8', function(err, data) {			if (err) {				reject(err);			} else {				resolve(data);			}		});	});}pReadFile('./a.txt')	.then(function(data) {		console.log(data);		return pReadFile('./b.txt');	})	.then(function(data) {		console.log(data);		return pReadFile('./a.txt');	})	.then(function(data) {		console.log(data);	})

mongoose所有的API都支持Promise:

1
// 查询所有User.find()	.then(function(data){        console.log(data)    })

注册:

1
User.findOne({username:'admin'},function(user){    if(user){        console.log('用户已存在')    } else {        new User({             username:'aaa',             password:'123',             email:'fffff'        }).save(function(){            console.log('注册成功');        })    }})
1
User.findOne({    username:'admin'})    .then(function(user){        if(user){            // 用户已经存在不能注册            console.log('用户已存在');        }        else{            // 用户不存在可以注册            return new User({                username:'aaa',                password:'123',                email:'fffff'            }).save();        }    })    .then(funciton(ret){		console.log('注册成功');    })

Generator

async函数

其他

修改完代码自动重启

我们在这里可以使用一个第三方命名行工具:nodemon来帮助我们解决频繁修改代码重启服务器的问题。

nodemon是一个基于Node.js开发的一个第三方命令行工具,我们使用的时候需要独立安装:

1
#在任意目录执行该命令都可以#也就是说,所有需要 --global安装的包都可以在任意目录执行npm install --global nodemonnpm install -g nodemon#如果安装不成功的话,可以使用cnpm安装cnpm install -g nodemon

安装完毕之后使用:

1
node app.js#使用nodemonnodemon app.js

只要是通过nodemon启动的服务,则他会监视你的文件变化,当文件发生变化的时候,会自动帮你重启服务器。

封装异步API

回调函数:获取异步操作的结果

1
function fn(callback){    // var callback = funtion(data){ console.log(data); }	setTimeout(function(){        var data = 'hello';        callback(data);    },1000);}// 如果需要获取一个函数中异步操作的结果,则必须通过回调函数的方式来获取fn(function(data){    console.log(data);})

数组的遍历方法,都是对函数作为一种参数

EcmaScript 6

参考文档:https://es6.ruanyifeng.com/

项目案例

目录结构

1
.├- app.js				项目入口├- controllers/├- models/				存储数据模型(mongoose)	﹂ comment.js			评论数据模型	﹂ topic.js				话题数据模型	﹂ user.js				用户数据模型├- node_modules/		第三方包├- package.json		包描述文件├- package-lock.json	第三方包版本锁定文件├- public/				公共静态资源	﹂ css/					样式资源        · login.css		  		· main.css 		· markdown-github.css 		· setting.css	﹂ img/					媒体资源    ﹂ js/					脚本├- routes/				路由文件    ﹂ session.js			﹂ topic.js┖- views/				存储视图目录    ﹂ _layouts/        · home.html				主页面    ﹂ _partials/		公共模板        · header.html			公共头部模板		· footer.html			公共页脚模板		· settings-nav.html		公共设置导航条模板    ﹂ settings/        · admin.html		· profile.html    ﹂ topic/			操作页面        · edit.html				 编辑		· new.html				 添加        · show.html				 展示    ﹂ index.html		内容主体	﹂ login.html		登陆页面	﹂ register.html	注册页面

模板页

  • 子模板
  • 模板继承

路由设计

路由 方法 get参数 post参数 是否需要登录 备注
/ get 渲染首页
/register(登录) get 渲染注册页面
/register post email,nickname,password 处理注册请求
/login get 渲染登陆界面
/login post email,password 处理登录请求
/loginout get 处理退出请求

模型设计

功能实现

步骤

  • 创建目录结构
  • 整合静态页-模板页
    • include
    • block
    • extend
  • 设计用户登陆,退出,注册的路由
  • 用户注册
    • 先处理客户端页面的内容(表单控件的name,收集表单数据,发起请求)
    • 服务端
      • 获取从客户端收到的数据
      • 操作数据库
        • 如果有错,发送500告诉客户端服务器错了‘
        • 其他的根据业务发送不同的响应数据
  • 登录
  • 退出

Express中间件

中间件的概念

参考文档:http://expressjs.com/en/guide/using-middleware.html

中间件:把很复杂的事情分割成单个,然后依次有条理的执行。就是一个中间处理环节,有输入,有输出。

说的通俗易懂点儿,中间件就是一个(从请求到响应调用的方法)方法。

把数据从请求到响应分步骤来处理,每一个步骤都是一个中间处理环节。

1
var http = require('http');var url = require('url');var cookie = require('./expressPtoject/cookie');var query = require('./expressPtoject/query');var postBody = require('./expressPtoject/post-body');var server = http.createServer(function(){	// 解析请求地址中的get参数	// var obj = url.parse(req.url,true);	// req.query = obj.query;	query(req,res);	//中间件		// 解析请求地址中的post参数	req.body = {		foo:'bar'	}});if(req.url === 'xxx'){	// 处理请求	...}server.listen(3000,function(){	console.log('3000 runing...');});

同一个请求对象所经过的中间件都是同一个请求对象和响应对象。

1
var express = require('express');var app = express();app.get('/abc',function(req,res,next){	// 同一个请求的req和res是一样的,	// 可以前面存储下面调用	console.log('/abc');	// req.foo = 'bar';	req.body = {		name:'xiaoxiao',		age:18	}	next();});app.get('/abc',function(req,res,next){	// console.log(req.foo);	console.log(req.body);	console.log('/abc');});app.listen(3000, function() {	console.log('app is running at port 3000.');});

中间件的分类:

1.1 应用程序级别的中间件

万能匹配(不关心任何请求路径和请求方法的中间件):

1
app.use(function(req,res,next){    console.log('Time',Date.now());    next();});

关心请求路径和请求方法的中间件:

1
app.use('/a',function(req,res,next){    console.log('Time',Date.now());    next();});

路由级别的中间件

严格匹配请求路径和请求方法的中间件

get:

1
app.get('/',function(req,res){	res.send('get');});

post:

1
app.post('/a',function(req,res){	res.send('post');});

put:

1
app.put('/user',function(req,res){	res.send('put');});

delete:

1
app.delete('/delete',function(req,res){	res.send('delete');});

1
var express = require('express');var app = express();// 中间件:处理请求,本质就是个函数// 在express中,对中间件有几种分类// 1 不关心任何请求路径和请求方法的中间件// 也就是说任何请求都会进入这个中间件// 中间件本身是一个方法,该方法接收三个参数// Request 请求对象// Response 响应对象// next 下一个中间件// // 全局匹配中间件// app.use(function(req, res, next) {// 	console.log('1');// 	// 当一个请求进入中间件后// 	// 如果需要请求另外一个方法则需要使用next()方法// 	next();// 	// next是一个方法,用来调用下一个中间件//  // 注意:next()方法调用下一个方法的时候,也会匹配(不是调用紧挨着的哪一个)// });// app.use(function(req, res, next) {// 	console.log('2');// });// // 2 关心请求路径的中间件// // 以/xxx开头的中间件// app.use('/a',function(req, res, next) {// 	console.log(req.url);// });// 3 严格匹配请求方法和请求路径的中间件app.get('/',function(){	console.log('/');});app.post('/a',function(){	console.log('/a');});app.listen(3000, function() {	console.log('app is running at port 3000.');});

错误处理中间件

1
app.use(function(err,req,res,next){    console.error(err,stack);    res.status(500).send('Something broke');});

配置使用404中间件:

1
// 放在最后,如果前面没有任何中间件能匹配处理,则进入这个404app.use(function(req,res){    res.render('404.html');});

配置全局错误处理中间件:

1
app.get('/a', function(req, res, next) {	fs.readFile('.a/bc', funtion() {		if (err) {        	// 当调用next()传参后,则直接进入到全局错误处理中间件方法中(4个参数)        	// 当发生全局错误的时候,我们可以调用next传递错误对象        	// 然后被全局错误处理中间件匹配到并进行处理        	// 就不需要每处理一次业务都要判断err			next(err);		}	})});//全局错误处理中间件app.use(function(err,req,res,next){    res.status(500).json({        err_code:500,        message:err.message    });});

内置中间件

第三方中间件

参考文档:http://expressjs.com/en/resources/middleware.html

  • body-parser
  • compression
  • cookie-parser
  • mogran
  • response-time
  • server-static
  • session

项目2.0

目录结构

1
.├- app.js	项目入口├- controllers/├- models/	存储数据模型(mongoose)	﹂ comment.js	评论数据模型	﹂ topic.js		话题数据模型	﹂ user.js		用户数据模型├- node_modules/ 第三方包├- package.json	 包描述文件├- package-lock.json	第三方包版本锁定文件├- public/	公共静态资源	﹂ css/	样式资源        · login.css		  		· main.css 		· markdown-github.css 		· setting.css	﹂ img/	媒体资源    ﹂ js/	脚本资源├- routes/	路由文件    ﹂ session.js			﹂ topic.js┖- views/	存储视图目录    ﹂ _layouts/        · home.html	主页面    ﹂ _partials/		公共模板        · header.html	公共头部模板		· footer.html	公共页脚模板		· settings-nav.html	共设置导航条模板    ﹂ settings/        · admin.html		· profile.html    ﹂ topic/			操作页面        · edit.html	编辑		· new.html	添加        · show.html	展示    ﹂ index.html 内容主体	﹂ login.html 登陆页面	﹂ register.html	注册页面

模板页

  • 子模板
  • 模板继承

路由设计

路由 方法 get参数 post参数 是否需要登录 备注
/ get 渲染首页
/register(登录) get 渲染注册页面
/register post email,nickname,password 处理注册请求
/login get 渲染登陆界面
/login post email,password 处理登录请求
/loginout get 处理退出请求

模型设计

阶段一

  • 页面设计

    • 一体页

功能实现

NodeJS2.0

补充

命令行窗口

  • cmd、终端、shell、dos
  • 常用指令

    • dir

      • 列出当前文件夹下所有文件
    • cd

    • md 目录名

      • 创建文件夹
    • rd 目录名

      • 删除文件夹
    • 文件名

      • 打开文件
  • 环境变量

    • 即windows系统的变量
    • path

      • 系统寻找文件的顺序(dos)

        • 当前目录下
        • →环境变量path
      • 类似作用域链

    • 修改环境变量后需要重启dos

进程与线程

  • 进程

    • 负责为程序的运行提供必备的环境

      • 相当于工厂车间
  • 线程

    • 计算机中最小的计算单位,负责执行进程中的程序

      • 相当于车间里的工人
    • 单线程

    • 多线程

      • 并发问题

关于性能

  • 客户端
  • 服务器端

    • 多线程
    • nodejs

      • 单线程
  • 数据库

    • 通过磁盘调用操作数据库

简介

定义

  • nodejs是能在服务器端运行js的开放源代码、跨平台的js运行环境

特点

  • 采用Google开发的V8引擎运行js代码
  • 单线程服务器
  • 使用事件驱动
  • 非阻塞
  • 异步I/O模型

作用

  • web服务API

    • REST
  • 实时多人游戏

关于node版本

  • 奇数为开发版,偶数为稳定版

操作

使用node执行js文件

  • 进入当前文件所在目录

    • 快捷方法

      • 在文件地址栏输入 cmd
      • node 文件名
  • 在编译器终端

    • node 文件名
  • 编译器直接run

模块化

简介

  • 特点

    • 大化小
    • 避免耦合
    • 增加复用
  • ES标准缺陷

    • 没有模块系统(ES5)
    • 标准库较少
    • 没有标准接口
    • 缺乏管理系统

CommonJS规范

  • 简介

    • 弥补当前js没有标准的缺陷
    • 对模块的定义

      • 模块引用
      • 模块定义
      • 模块标识
  • 定义模块

    • 创建js文件
    • 在node中一个js文件就是一个模块
  • 模块标识

    • 定义

      • 找到模块的方式
      • 使用require引入外部模块时,使用的就是模块标识,通过模块标识来找到指定的标识
    • 模块分类

      • 核心模块

        • 由node引擎提供的模块
        • 模块标识

          • 模块名
      • 文件模块

        • 由用户自己创建的模块
        • 模块标识

          • 文件路径
  • 引用模块

    • require()

      • 参数

        • 文件路径
      • 注意

        • 相对路径必须以 ./或 ../开头
      • 返回值

        • 该函数会返回一个对象,该对象代表引入的模块
    • 在node中,每一个文件中的js代码都是独立运行在一个函数中且传入了5个实参,而不是全局作用域

      • 所以一个模块中的变量和函数在其他模块中无法访问
      • console.log(arguments)//证明一个模块的确是运行在函数里
      • arguments.callee

        • 该属性保存的是当前正在执行的函数对象
    • node执行模块中的代码时

      • 最顶部添加

        • function (exports, require, module, filename, dirname) {
      • 最底部添加

        • }
    • 五个参数

      • exports

        • 该对象用来向外部暴露属性或方法
        • 语法

          • exports . x
      • require

        • 函数,用来引入外部的模块
      • module

        • 代表的是当前模块本身
        • exports就是module的属性

          • 即module . exports = exports,本质相同
      • __filename

        • 当前文件完整路径
      • __dirname

        • 当前模块所在文件夹的完整路径
    • 关于

      • module .exports

        • 批量导入

          • module.exports = {
            }
        • 可以通过 . 的形式,也可以直接赋值

          • module.exports = { }
          • model.exports . xxx = xxx
      • exports

        • 通过exports只能使用 . 的方式向外暴露内部变量

          • exports . xxx = xxx
        • 改变量,不是改对象

  • global

    • 定义

      • node的全局对象,与window类似
      • 在全局中创建的变量/函数都会作为global的属性/方法保存
    • 创建

      • 去掉var

包package

简介

  • 规范的模块

    • 将一组相关的模块组合到一起
  • 本质:文件夹

CommonJS的包规范组成

  • 包结构

    • 用于组织包中的各种文件(压缩文件)
    • 规范目录组成

      • package.json

        • 描述文件(说明书)
      • bin

        • 可执行二进制文件
      • lib

        • js代码
      • doc

        • 文档
      • test

        • 单元测试
  • 包描述文件

    • 表达非代码的相关信息,放在包的根目录下
    • package.json

      • name、version、description…
      • 不能写注释

npm

  • 简介

    • Node Package Manager包管理工具
    • CommonJS包规范是理论,npm是其实践之一
  • 功能

    • 帮助node完成第三方模块的发布、安装和依赖等
  • 命令

    • npm -v

      • 查npm版本
    • npm -version

      • 所有模块版本
    • npm search 包名

      • 搜索包
    • npm init

      • 初始化,创建package.json文件
    • npm remove / r 包名

      • 删除包
    • npm install / i 包名

      • 安装包
    • npm install 包名 —save

      • 安装包并添加到依赖
      • 将包版本直接添加到package.json文件的dependencies中
    • npm install

      • 下载当前项目所依赖的包
      • 通过package.josn文件的dependencies寻找
    • npm install 包名 -g

      • 全局安装包
      • 一般是一些工具

        • 用于计算机中,而不是项目中
    • npm install 包名 -registry=地址

      • 从镜像原安装
    • npm config set registry 地址

      • 设置镜像源

配置cnpm

补充

  • 通过npm下载的包都放到node_module文件夹中

    • 直接通过包名引入即可
  • node搜索包的流程

    • 通过包名引入时,首先在当前目录的node_module中寻找
    • 如果没有则去上一级的node_module寻找
    • 直到找到磁盘的根目录,如果依然没有,报错

Buffer缓冲区

简介

  • 概念

    • buffer结构与数组相似,操作也类似
    • 专门用来存储二进制文件的数组,以16进制显示

      • 元素范围:00 ~ ff
      • 实际范围:00000000~11111111

        • 0~255(十进制)
  • 作用

    • 接收用户发送的数据,暂存
  • 本质

    • 直接操作内存空间

      • 开辟连续的内存空间

操作

  • 创建

    • Buffer.from

      • 保存字符串到buffer中
      • var buf = Buffer.from(str)
    • new Buffer(1024)

      • 创建一个指定长度的buffer
      • 尽量不用此构造函数
    • Buffer.alloc(1024)

      • 创建指定字节的buffer
    • Buffer.allocUnsafe(10)

      • 特性

        • 可能含有敏感数据
        • 分配空间时,不会清空之前的数据
      • 缺点

        • 可能泄露数据
      • 优点

        • 性能好
  • 添加

    • buf [0] = 254
  • 读取

    • buf[0]
    • 只要数字在控制台输出或页面中输出一定是10进制

      • 若需要转换成其他

        • toString(16)
  • 遍历

    • 同数组

属性

  • length

    • 占用内存的大小(字节数)
    • 区别字符串length

      • 表示字符串的长度

fs 文件系统

简介

  • 文件系统简单来说就是通过Node操作系统中的文件
  • 该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互

特点

  • fs模块所有操作都有同步与异步可选择

使用

  • 引入

    • var fs = require(‘fs’)

分类

  • 文件写入

    • 同步文件写入

      • 同步文件系统会阻塞程序的执行

        • 除非操作完毕,否则不会向下执行代码
      • 步骤

        • 打开文件

          • fs.openSync()

            • 参数

              • path:要打开文件的路径
              • flags:打开文件要做的操作的类型(w,r)

                • w

                  • 覆盖写
                • a

                  • 追加写
                • r+

                  • 读写文件,追加,文件不存在则报错
              • [mode]:设置文件的操作权限,windows一般不写

            • 返回值

              • 该方法会返回一个文件的描述符
              • 可通过该描述符对文件进行操作
        • 写入内容

          • fs.writeSync()

            • 参数

              • fd

                • 文件描述符
              • string

                • 要写入的内容
              • mode

                • 写入的起始位置
              • encoding

                • 写入的编码
        • 保存并关闭文件

          • fs.closeSync(3)

            • 参数

              • fd

                • 文件描述符
    • 异步文件写入

      • 异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回
      • 步骤

        • 打开文件

          • open

            • 参数

              • path:路径
              • flags:操作类型
              • callback:回调函数

                • 参数

                  • err:错误对象

                    • 没有错误则为null(错误优先思想)
                  • fd:文件描述符

            • 返回值

              • 异步方法没有返回值
              • 异步调用的方法,结果都是通过回调函数返回的
            • open时,会将该任务交给后台线程池,继续异步执行open之后的代码,当open操作完成后,通过回调函数返回

        • 写入文件

          • write

            • 在回调函数里面操作
            • 参数

              • fd
              • string:内容
              • callback:回调

                • 参数

                  • err
                  • written:被写入的字符串的字节数
                  • string:写入的内容
        • 关闭文件

          • close

            • 参数

              • fd
              • callback

                • 参数

                  • err
    • 简单文件写入

      • 步骤

        • 异步

          • fs.writeFile()

            • 参数

              • file:操作文件路径
              • data:指定要写入的数据
              • 【options】

                • 选项,配置信息,对写入进行一些设置
                • 一般需要一个对象作为参数

                  • encoding:编码
                  • mode:0o666
                  • flag:‘w’
              • callback

                • 写入完成以后执行的操作
                • 参数

                  • err

                    • 错误对象
        • 同步

          • fs.writeFileSync()

            • 参数

              • file:操作文件路径
              • data:指定要写入的数据
              • 【options】

                • 选项,配置信息,对写入进行一些设置
    • 流式文件写入

      • 概念

        • 特点

          • 非一次性写入文件
          • 避免占用内存
          • 适用大型文件
        • 对比其他文件写入

          • 容易内存溢出
          • 性能差
      • 步骤

        • 创建一个可写流(接通水管)

          • fs.createWriteStream()

            • 参数

              • path
              • 【option】
            • 返回值

        • 写入内容

          • write()

            • 参数

              • 字符串
        • 关闭流

          • 通过监听流的open和close事件来监听流的打开和关闭
          • 补充

            • once(事件字符串,回调函数)

              • 为对象绑定一个一次性的事件,触发后自动失效
            • on(事件字符串,回调函数)

              • 绑定一个事件
          • end()

            • 输送完后断“流”
          • close()

            • 可能没送完就断“流”(旧版本)
  • 文件读取

    • 同步文件读取
    • 异步文件读取
    • 简单文件读取

      • 同步

        • fs.readFile()

          • 参数

            • path,【options,】callback(err,data)
          • 返回值

            • 返回一个buffer

              • 提高通用性
              • 比如读取一张图片
      • 异步

        • fs.readFileSync()

          • 参数

            • path,【options,】callback
    • 流式文件读取

      • 特点

        • 适用于大文件
        • 分多次读取文件
      • 步骤

        • 创建一个可读流

          • var rs = fs.createReadStream(“hello2.txt”)
        • 监听流的开启和关闭

        • 读取

          • 如果要读取一个可读流中的数据,必须为可读流绑定一个data事件,data事件绑定完毕,会自动开始读取数据
      • pipe()

        • 将可读流中的内容直接输出到可写流中
        • 简写(复制文件)

          • var fs = require(‘fs’)
            //创建一个可读流
            var rs = fs.createReadStream(“hello2.txt”)
            //创建一个可写流
            var ws = fs.createWriteStream(‘a.txt’)
            rs.pipe(ws)

其他方法

  • existsSync()

    • 验证文件路径是否存在
    • 只有同步(立即获取结果)
  • stat( path, callback(err,stat))

    • 获取文件状态
    • 返回一个对象,保存当前对象状态的信息
    • 属性

      • size
  • unlink(path,callback)/unlinkSync(path)

    • 删除文件
  • readdir(path[, option], callback(err, files))

    • 读取一个目录的 目录结构
    • 回调参数

      • files

        • 是一个字符串数组,每一个元素就是一个文件夹或文件
  • truncate(path , len, callback)

    • 截断文件
    • len:截断后的文件大小
  • mkdir(path[,mode],callback)

    • 创建目录
  • rmdir(path)

    • 删除文件夹
  • rename(oldpath,newpath,callback)

    • 重命名文件
    • 改变路径
  • watchFile(filename[,optin], listener)

    • 监视文件的修改,耗性能
    • 参数

      • 文件名
      • 配置选项

        • interval:1000

          • 监听频率(1s刷新一次)
      • 回调函数

        • 当文件发送变化时执行
        • 参数

          • curr

            • 当前文件状态stat
          • prev

            • 修改前文件状态stat

XMind: ZEN - Trial Version


Tips: Please indicate the source and original author when reprinting or quoting this article.