nodejs笔记
Node.js是什么
- Node.js是JavaScript 运行时
- 通俗易懂的讲,Node.js是JavaScript的运行平台
- Node.js既不是语言,也不是框架,它是一个平台
- 浏览器中的JavaScript
- EcmaScript
- 基本语法
- if
- var
- function
- Object
- Array
- Bom
- Dom
- EcmaScript
- 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
- 查看node的版本号:
- 配置环境变量
解析执行JavaScript
- 创建编写JavaScript脚本文件
- 打开终端,定位脚本文件的所属目录
- 输入
node 文件名
执行对应的文件
注意:文件名不要用node.js
来命名,也就是说除了node
这个名字随便起,最好不要使用中文。
文件的读写
文件读取:
1 | //浏览器中的JavaScript是没有文件操作能力的 |
文件写入:
1 | // 1.使用fs核心模块 |
http
服务器:
1 | // 1.加载http核心模块 |
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
8exports.a = 123;
exports.b = function(){
console.log('bbb')
};
exports.c = {
foo:"bar"
};
exports.d = 'hello';导出单个成员(拿到的就是函数,字符串):
1
module.exports = 'hello';
以下情况会覆盖:
1
2
3
4
5module.exports = 'hello';
//后者会覆盖前者
module.exports = function add(x,y) {
return x+y;
}也可以通过以下方法来导出多个成员:
1
2
3
4
5
6module.exports = {
foo = 'hello',
add:function(){
return x+y;
}
};
模块原理
exports和module.exports
的一个引用:
1 | console.log(exports === module.exports); //true |
当给exports重新赋值后,exports!= module.exports.
最终return的是module.exports,无论exports中的成员是什么都没用。
1 | 真正去使用的时候: |
总结
1 | // 引用服务 |
1 | 1.jQuery中的each 和 原生JavaScript方法forEach的区别: |
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 | // 如果非路径形式的标识 |
模块标识符中的/
和文件操作路径中的/
文件操作路径:
1 | // 咱们所使用的所有文件操作的API都是异步的 |
模块操作路径:
1 | // 在模块加载中,相对路径中的./不能省略 |
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 | 在任意目录执行都可以 |
安装包的时候把以前的npm
替换成cnpm
。
1 | 走国外的npm服务器下载jQuery包,速度比较慢 |
如果不想安装cnpm
又想使用淘宝的服务器来下载:
1 | npm install jquery --registry=https://npm.taobao.org; |
但是每次手动加参数就很麻烦,所以我们可以把这个选项加入到配置文件中:
1 | npm config set registry https://npm.taobao.org; |
只要经过上面的配置命令,则以后所有的npm install
都会通过淘宝的服务器来下载
package.json
每一个项目都要有一个package.json
文件(包描述文件,就像产品的说明书一样)
这个文件可以通过npm init
自动初始化出来
1 | D:\code\node中的模块系统>npm init |
对于目前来讲,最有用的是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路径操作模块
- path.basename:获取路径的文件名,默认包含扩展名
- path.dirname:获取路径中的目录部分
- path.extname:获取一个路径中的扩展名部分
- path.parse:把路径转换为对象
- root:根路径
- dir:目录
- base:包含后缀名的文件名
- ext:后缀名
- name:不包含后缀名的文件名
- path.join:拼接路径
- path.isAbsolute:判断一个路径是否为绝对路径
Node中的其它成员(__dirname,__filename)
在每个模块中,除了require
,exports
等模块相关的API之外,还有两个特殊的成员:
__dirname
,是一个成员,可以用来动态获取当前文件模块所属目录的绝对路径__filename
,可以用来动态获取当前文件的绝对路径(包含文件名)__dirname
和filename
是不受执行node命令所属路径影响的
在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。
所以为了解决这个问题,只需要把相对路径变为绝对路径(绝对路径不受任何影响)就可以了。
就可以使用__dirname
或者__filename
来帮助我们解决这个问题
在拼接路径的过程中,为了避免手动拼接带来的一些低级错误,推荐使用path.join()
来辅助拼接
1 | var fs = require('fs'); |
补充:模块中的路径标识和这里的路径没关系,不受影响(就是相对于文件模块)
注意:
模块中的路径标识和文件操作中的相对路径标识不一致
模块中的路径标识就是相对于当前文件模块,不受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.listen
app.listen(3000,function(){console.log('app is runing at port 3000');
})
1
2
3
4
5
6
7
8
9
10
### 学习Express
#### 起步
##### 安装:{% image C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200310123723079.png %}
```javascript
cnpm install express
hello world:
1 | // 引入express |
基本路由
路由:
请求方法
请求路径
请求处理函数
get:
1 | //当你以get方法请求/的时候,执行对应的处理函数 |
post:
1 | //当你以post方法请求/的时候,执行对应的处理函数 |
Express静态服务API
1 | // app.use不仅仅是用来处理静态资源的,还可以做很多工作(body-parser的配置) |
1 | // 引入express |
在Express中配置使用art-templete
模板引擎
- art-template官方文档
- 在node中,有很多第三方模板引擎都可以使用,不是只有
art-template
- 还有ejs,jade(pug),handlebars,nunjucks
安装:
1 | npm install --save art-template |
配置:
1 | app.engine('html', require('express-art-template')); |
使用:
1 | app.get('/',function(req,res){ |
如果希望修改默认的views
视图渲染存储目录,可以:
1 | // 第一个参数views千万不要写错 |
在Express中获取表单请求数据
获取get请求数据:
Express内置了一个api,可以直接通过req.query
来获取数据
1 | // 通过requery方法获取用户输入的数据 |
获取post请求数据:
在Express中没有内置获取表单post请求体的api,这里我们需要使用一个第三方包body-parser
来获取数据。
安装:
1 | npm install --save body-parser; |
配置:
// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )
1 | var express = require('express') |
使用:
1 | app.use(function (req, res) { |
在Express中配置使用express-session
插件操作
安装:
1 | npm install express-session |
配置:
1 | //该插件会为req请求对象添加一个成员:req.session默认是一个对象 |
使用:
1 | // 读 |
提示:
默认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 | /** |
app.js:
1 | var router = require('./router'); |
设计操作数据的API文件模块
es6中的find和findIndex:
find接受一个方法作为参数,方法内部返回一个条件
find会便利所有的元素,执行你给定的带有条件返回值的函数
符合该条件的元素会作为find方法的返回值
如果遍历结束还没有符合该条件的元素,则返回undefined
1 | /** |
步骤
处理模板
配置静态开放资源
配置模板引擎
简单的路由,/studens渲染静态页出来
路由设计
提取路由模块
由于接下来的一系列业务操作都需要处理文件数据,所以我们需要封装Student.js’
先写好student.js文件结构
- 查询所有学生列别哦的API
- findById
- save
- updateById
- deleteById
实现具体功能
- 通过路由收到请求
- 接受请求中的参数(get,post)
- req.query
- req.body
- 调用数据操作API处理数据
- 根据操作结果给客户端发送请求
业务功能顺序
- 列表
- 添加
- 编辑
- 删除
子模板和模板的继承(模板引擎高级语法)【include,extend,block】
注意:
模板页:
1 | <!DOCTYPE html> |
模板的继承:
header页面:
1 | <div id=""> |
footer页面:
1 | <div id=""> |
模板页的使用:
1 | <!-- 继承(extend:延伸,扩展)模板也layout.html --> |
最终的显示效果:
MongoDB
关系型和非关系型数据库
关系型数据库(表就是关系,或者说表与表之间存在关系)。
- 所有的关系型数据库都需要通过
sql
语言来操作 - 所有的关系型数据库在操作之前都需要设计表结构
- 而且数据表还支持约束
- 唯一的
- 主键
- 默认值
- 非空
非关系型数据库
- 非关系型数据库非常的灵活
- 有的关系型数据库就是key-value对儿
- 但MongDB是长得最像关系型数据库的非关系型数据库
- 数据库 -》 数据库
- 数据表 -》 集合(数组)
- 表记录 -》文档对象
一个数据库中可以有多个数据库,一个数据库中可以有多个集合(数组),一个集合中可以有多个文档(表记录)
1 | { |
- 也就是说你可以任意的往里面存数据,没有结构性这么一说
安装
下载
安装
1
npm i mongoose
配置环境变量
最后输入
mongod --version
测试是否安装成功
启动和关闭数据库
启动:
1 | mongodb 默认使用执行mongod 命令所处盼复根目录下的/data/db作为自己的数据存储目录 |
如果想要修改默认的数据存储目录,可以:
1 | mongod --dbpath = 数据存储目录路径 |
停止:
1 | 在开启服务的控制台,直接Ctrl+C; |
连接数据库
连接:
1 | # 该命令默认连接本机的 MongoDB 服务 |
退出:
1 | # 在连接状态输入 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项目团队开发的。
1 | npm i mongoose |
https://mongoosejs.com/
学习指南(步骤)
官方学习文档:https://mongoosejs.com/docs/index.html
设计Scheme 发布Model (创建表)
1 | // 1.引包 |
添加数据(增)
1 | // 5.通过模型构造函数对User中的数据进行操作 |
删除(删)
根据条件删除所有:
1 | User.remove({ |
根据条件删除一个:
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来修改表数据 |
查询(查)
查询所有:
1 | // 查询所有 |
条件查询所有:
1 | // 根据条件查询 |
条件查询单个:
1 | // 按照条件查询单个,查询出来的数据是一个对象({}) |
使用Node操作MySQL数据库
文档:https://www.npmjs.com/package/mysql
安装:
1 | npm install --save mysql |
1 | // 引入mysql包 |
异步编程
回调函数
不成立的情况下:
1 | function add(x,y){ |
使用回调函数解决:
回调函数:通过一个函数,获取函数内部的操作。(根据输入得到输出结果)
1 | var ret; |
注意:
凡是需要得到一个函数内部异步操作的结果(setTimeout,readFile,writeFile,ajax,readdir)
这种情况必须通过 回调函数 (异步API都会伴随着一个回调函数)
ajax:
基于原生XMLHttpRequest封装get方法:
1 | var oReq = new XMLHttpRequest(); |
1 | function get(url,callback){ |
Promise
callback hell(回调地狱):
文件的读取无法判断执行顺序(文件的执行顺序是依据文件的大小来决定的)(异步api无法保证文件的执行顺序)
1 | var fs = require('fs'); |
通过回调嵌套的方式来保证顺序:
1 | var fs = require('fs'); |
- Promise:承诺,保证
- Promise本身不是异步的,但往往都是内部封装一个异步任务
基本语法:
1 | // 在EcmaScript 6中新增了一个API Promise |
链式循环:
封装Promise的readFile
:
1 | var fs = require('fs'); |
mongoose所有的API都支持Promise:
1 | // 查询所有 |
注册:
1 | User.findOne({username:'admin'},function(user){ |
1 | User.findOne({ |
Generator
async函数
其他
修改完代码自动重启
我们在这里可以使用一个第三方命名行工具:nodemon
来帮助我们解决频繁修改代码重启服务器的问题。
nodemon
是一个基于Node.js开发的一个第三方命令行工具,我们使用的时候需要独立安装:
1 | #在任意目录执行该命令都可以 |
安装完毕之后使用:
1 | node app.js |
只要是通过nodemon
启动的服务,则他会监视你的文件变化,当文件发生变化的时候,会自动帮你重启服务器。
封装异步API
回调函数:获取异步操作的结果
1 | function fn(callback){ |
数组的遍历方法,都是对函数作为一种参数
EcmaScript 6
项目案例
目录结构
1 | . |
模板页
- 子模板
- 模板继承
路由设计
路由 | 方法 | get参数 | post参数 | 是否需要登录 | 备注 |
---|---|---|---|---|---|
/ | get | 渲染首页 | |||
/register(登录) | get | 渲染注册页面 | |||
/register | post | email,nickname,password | 处理注册请求 | ||
/login | get | 渲染登陆界面 | |||
/login | post | email,password | 处理登录请求 | ||
/loginout | get | 处理退出请求 | |||
模型设计
功能实现
步骤
- 创建目录结构
- 整合静态也-模板页
- include
- block
- extend
- 设计用户登陆,退出,注册的路由
- 用户注册
- 先处理客户端页面的内容(表单控件的name,收集表单数据,发起请求)
- 服务端
- 获取从客户端收到的数据
- 操作数据库
- 如果有错,发送500告诉客户端服务器错了‘
- 其他的根据业务发送不同的响应数据
- 登录
- 退出
Express中间件
中间件的概念
中间件:把很复杂的事情分割成单个,然后依次有条理的执行。就是一个中间处理环节,有输入,有输出。
说的通俗易懂点儿,中间件就是一个(从请求到响应调用的方法)方法。
把数据从请求到响应分步骤来处理,每一个步骤都是一个中间处理环节。
1 | var http = require('http'); |
同一个请求对象所经过的中间件都是同一个请求对象和响应对象。
1 | var express = require('express'); |
中间件的分类:
应用程序级别的中间件
万能匹配(不关心任何请求路径和请求方法的中间件):
1 | app.use(function(req,res,next){ |
关心请求路径和请求方法的中间件:
1 | app.use('/a',function(req,res,next){ |
路由级别的中间件
严格匹配请求路径和请求方法的中间件
get:
1 | app.get('/',function(req,res){ |
post:
1 | app.post('/a',function(req,res){ |
put:
1 | app.put('/user',function(req,res){ |
delete:
1 | app.delete('/delete',function(req,res){ |
总
1 | var express = require('express'); |
错误处理中间件
1 | app.use(function(err,req,res,next){ |
配置使用404中间件:
1 | app.use(function(req,res){ |
配置全局错误处理中间件:
1 | app.get('/a', function(req, res, next) { |
内置中间件
- express.static(提供静态文件)
第三方中间件
- body-parser
- compression
- cookie-parser
- mogran
- response-time
- server-static
- session