1.基础
1.1 Node.js是什么?
- Node.js诞生于2009年,由Joyent的员工 Ryan Dahl开发而成. Node.js不是一门语言也不是框架,它只是基于GooleV8引擎的JavaScript运行时环境,同时结合Libuv扩展了JavaScript功能,使之支持io fs 等语言才有的特性, 是的JavaScript能够同时具有Dom操作 和I/O 文件读写, 操作数据库等能力,是目前最简单的全栈式语言.
- Node.js是一个开源和夸平台的JavaScript运行时环境.它几乎是任何类型项目的流行工具
- Node.js在浏览器之外, 自己运行 V8 JavaScript引擎.这使得Node.js的性能非常好
- Node.js应用程序在单个进程中运行,无需为每个请求创建新的线程.Node.js在其标准库中提供了一组异步的I/O原语,以防止JavaScript代码阻塞,通常Node.js中的库是使用非阻塞范式编写的,使得阻塞行为成为异常而不是常态.
- 当Node.js执行I/O操作时(比如从网络读取,访问数据库或文件系统), Node.js将在响应返回时恢复操作(而不是阻塞线程和浪费CPU周期等待)
- 这允许Node.js使用单个服务器处理数千个并发连接, 而不会引入管理线程并发(这可能是错误的重要来源)的负担.
- Node.js具有独特的优势,因为数百万为浏览器编写JavaScript的前端开发者现在无需学习完全不同的语言,就可以编写除客户端代码之外的服务器端代码.
- 在Node.js中, 可以毫无问题地使用新的ECMAScript标准,因为你不必等待所有用户更新他们的浏览器,你负责通过更改Node.js版本来决定使用哪个ECMAScript版本,你还可以通过运行带有标志的NOde.js来启用特定的实验性功能.
- 大量的库: npm以其简单的结构帮助Node.js生态系统蓬勃发展,现在npm仓库托管了超过1百万个开源包,你可以自由使用.
1.2 简史
-
2009
- Node.js诞生
- 第一版的npm被创建
-
2010
- Express诞生
- Socket.io诞生
-
2011
- npm 发布 1.0 版本
- 较大的公司(LinkedIn、Uber 等)开始采用 Node.js
- hapi 诞生
-
2012
- 普及速度非常快
-
2013
-
2014
- 大分支:io.js 是 Node.js 的一个主要分支,目的是引入 ES6 支持并加快推进速度
-
2015
- Node.js 基金会 诞生
- IO.js 被合并回 Node.js
- npm 引入私有模块
- Node.js 4(以前从未发布过 1、2 和 3 版本)
-
2016
- leftpad 事件
- Yarn 诞生
- Node.js 6
-
2017
- npm 更加注重安全性
- Node.js 8
- HTTP/2
- V8 在其测试套件中引入了 Node.js,除了 Chrome 之外,Node.js 正式成为 JS 引擎的标杆
- 每周 30 亿次 npm 下载
-
2018
- Node.js 10
- ES 模块 .mjs 实验支持
- Node.js 11
-
2019
- Node.js 12
- Node.js 13
-
2020
- Node.js 14
- Node.js 15
-
2021
- Node.js 16
1.3 Node.js特点
- 事件驱动
- 非阻塞I/O模型
- 轻量和高效
- 文件的读写, 进程的管理,网络通信
1.4 命令
- nvm list 当前系统安装了哪些版本的nodejs
- nvm use 16.x.x 来决定当前使用哪个版本
- nvm alias default 16.x.xx 来决定默认使用哪个版本.
1.5 Node.js可以做什么
- 基于Express, Koa2, 构建web应用
- 基于Electron,构建桌面应用
- restify, 构建api接口项目
1.6 怎么学Node
- JavaScript基础语法 + Node.js内置API模块(fs,path,http等) + 第三方模块API(koa, MySQL)
2.模块化理解
2.1 什么是模块化
- 模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程. 对于整个系统来说,模块是可组合 分解和更换的单元.
2.2 Node.js 模块分类
- 内置模块: 由Node.js官方提供的,例如fs,path,http等
- 自定义模块: 用户创建的每个.js文件
- 三方模块: 由第三方开发出来的
2.3 加载模块,导入导出
(1) 导入:
* 加载内置模块 const fs = require('fs')
* 加载自定义模块 const custom = require('./custom.js')
* 加载三方模块 const moment = require('moment')
* 使用require()时可以加省略后缀名.js
*当使用require()方法加载看其他模块时, 被加载的模块会被执行.
* 模块初始化: 导入模块仅在模块第一次使用时执行一次,并且在使用的过程中进行初始化, 之后缓存起来便于后续继续使用.
(2) module.exports 导出对象:
* 在每一个.js自定义模块中都有一个module对象, 它里面存储了和当前模块有关的信息
* module.exports默认值是一个空对象, 用来导出默认对象,没有指定对象名.
常见于修改模块的原始导出对象, 通过module.exports来更改要导出的对象.
(3) exports别名 来导出对象:
* 由于module.exports单词写起来比较复杂, 为了简化,Node提供了exports对象. 默认情况下,exports 和 module.exports指向同一个对象. 最终导出的结果还是以module.exports指向的对象为准. 相当于同一个人的两个名字.
(4) 模块作用域
* 和函数作用域类似,在自定义模块中定义的变量, 方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域.
* 好处: 防止全局变量污染.
2.4 Node.js模块化规范
(1) module 代表当前模块
(2) module变量是一个对象,它的exports属性 (即module.exports)是对外的接口.
(3) 加载某个模块,其实加载的是module.exports属性.
2.5 主模块
- 通过命令行参数传递给NodeJS以启动程序的模块被称为主模块.主模块负责调度组成整个程序的其它模块完成工作. 例如通过以下命令启动程序时,main.js就是主模块.
终端: node main.js // 运行main.js启动程序,main.js称为主模块
npm install -g cnpm --registry=https://registry.npm.taobao.org
Node.js中使用CommonJS模块化机制,通过npm下载的第三方包,我们在项目中引入第三方包都是: let xx = require('第三方包名'). require加载第三方包的原理机制是什么:
(1)优先在加载该包的模块的同级目录node_modules中查找第三方包.
(2) 找到该第三方包中的package.json文件,并且找到里面的main属性对应的入口模块,该入口模块即为加载的第三方模块.
(3)如果没有package.json或main属性,默认加载第三方包中的index.js文件.
(4)如果在同级目录没有找到node_modules文件,则会向父级目录查找node_modules文件夹
(5) 如果一直的磁盘跟目录, 没有找到的话就会报错.
2.6 npm与包
- 什么是包: Node.js中的第三方模块又叫做包
- 包的来源: 第三方提供 Node.js中的包都是免费且开源的, 不需要付费即可免费使用.
- npm,inc: 国外一家公司, 旗下著名网站www.npmjs.com它是全球最大的包共享平台, 你可以从这个网站上搜索到任何你需要的包.
- npm,inc公司还替公司一个地址为registry.npmjs.org的服务器, 来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包.
(1)
如何下包: 用npm包管理工具 (例如moment)
包的语义化版本规范: 点分十进制 例如 2.34.11
第一位数: 大版本
第二位数: 功能版本
第三位数: bug修复版本
注: 只要前边的版本号增长了,后边就需归零.
npm init -y命令: 创建package.json这个文件
(2)
解决下包速度慢: 默认从国外下包,使用淘宝镜像.
当前下包的默认镜像源: npm config get registry
改变默认镜像源: npm config set registry=https://registry.npm.taobao.org/
快速切换: nrm工具
npm i nrm -g
nrm ls 查看所有镜像源
nrm use taobao 切换镜像源
(3) 包的分类
项目包:
* 开发依赖包 -D
* 核心依赖包 -S
全局包: -g 卸载时也用 -g
* 只有工具性质的包,才被安装到全举报
* i5ting_toc是一个可以把 md文档转为html页面的小工具
* i5ting_toc -f 文件 -o 这个-o表示默认打开
package.json中必须包含name,version,main这三个属性, 名 版本号 入口
(4) npm包管理常见的命令:
npm -v
npm init
npm list
npm list -g
npm install
npm --help
npm update
npm uninstall
npm config list
npm 命令 --help
npm info 指定包名
npm config set registry https://registry.npm.taobao.org
npm root 查看当前包的安装路径
npm roo -g 查看全局的包的安装路径
npm ls 包名 查看本地安装的指定包及版本信息,没有显示empty.
npm ls 包名 -g 查看全局安装的指定包及版本信息,没有显示empty
- 开发属于自己的包
(1) 初始化包的结构, 新建文件夹mypack, 作为包的根目录
(2) mypack文件夹,新建三个文件:
* package.json 包管理配置文件
* index.js 包的入口文件
* README.md 包的说明文档
(3)package.json 文件内容
{
"name": "pack", // 真正包名, 不能重复
"version": "1.0.0", // 版本号
"main": "index.js", // 外界使用require来导入,默认导入main属性所指向的文件
"description": "简短的描述信息",
"keywords": ["mypack","format","escape"], // 包的的关键字
"license": "ISC", // 开源许可协议
}
(4)发布npm包
* 注册npm账号.
* 终端登录:npm login 用户名 密码 邮箱.
* npm 下包服务器地址, 更改为npm的官方服务器.
* 将终端切换到包根目录后, 运行npm publish命令,即可发布到npm网站上.
* 删除已发布的包: npm unpublish 包名 --force命令, 即可从npm删除已发布的包.
只能删除72小时之内的包. 删除的包24小时之内不能重复发布.
尽量不要往npm上发布没有意义的包.
*
2.7 模块的加载机制
- 模块在第一次加载后会被缓存. 这也意味着多次调用require()不会导致模块内的代码被执行多次.
- 注意: 不论是内置模块,用户模块,还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率.
- 内置模块的加载机制: 内置模块是由Node.js官方提供的模块, 内置模块的加载优先级最高. 例如require('fs')始终返回内置的fs模块,既是再看node_modules目录下有名字相同的包也叫做fs.
- 自定义模块的加载机制: 用require()时,必须指定以./ 或../ 开头. 如果没有指定./或../这样的路径,则node会把它当作内置模块或第三方模块进行加载.
- 在使用require()时, 如果省略了文件的扩展名:
- 首先按照确切的文件名进行加载
- 补全.js进行加载
- 补全.json进行加载
- 补全.node进行加载
- 都没, 则加载失败
- 第三方模块的加载机制: 首先从当前的父目录开始从/node_modules文件夹中加载.如果没有找到对应的第三方模块,则会逐级向上各层的/node_modules中查找. 一直到根目录. 如果都没有则报错.
- 目录作为模块: 使用require()加载目录时, 有三种加载方式:
- 在被加载的目录查找一个叫做package.json的文件,并寻找main属性,作为加载入口.
- 如果没有package.json 或 有package没main 或这俩都有没有main所指向的.js文件.会加载index.js文件
- 都没有 此时会打印错误消息.
3.文件系统
3.1 介绍
- node,读写文件也有同步和异步的接口,
- fs模块时Node.js官方提供的,用来操作文件的模块.它提供了一系列的方法和属性,用来满足用户对文件的操作需求.
const fs = require('fs')
(1)读
fs.readFile(path,[options],callback) 读取参数
参数说明:路径, 编码格式, 回调函数
fs.readFile('./index.txt', 'utf8', function(err,datastr) {}) 通过err是否为null来判断文件的读取结果.
(2)写
fs.writeFile(file,[options],callback) 向文件写入
参数说明: 第二个参数是 写入的内容
(3)路径动态拼接的问题: 使用完整路径, 不要提供./ ../等相对路径
4.path
4.1 基础
path.join() 方法,用来将多个路径片段拼接成一个完整的路径字符串
../ 可以抵消一层路径
__dirname
以后凡是涉及到路径拼接的操作, 都要使用path.join()方法处理
path.basename() 方法, 用来从路径字符串中, 将文件名解析出来.
path.extname() 方法, 可以获取路径中的扩展名
5.http
5.1 基础
- http模块是Node.js官方提供的,用来创建web服务器的模块.通过http模块提供的http.createServer()方法,就能方便的把一台普通的电脑,变成一台web服务器,从而对外提供web资源服务.
- 服务器和普通电脑的区别在于,服务器上安装了web服务器软件, 例如: IIS,Apache等. 通过安装这些服务器软件,就能把一台普通的电脑变成一台web服务器.
- 在NOde.js中, 我们不需要使用IIS,Apache等这些第三方的web服务器软件.因为我们可以基于Node.js提供的http模块,通过几行简单的代码,就能轻松的手写一个服务器软件,从而对外提供web服务.
5.2 知识拓展
-
ip地址: 就是互联网上每台计算机的唯一地址,因此IP地址具有唯一性.如果把 '个人电脑'比作 '一台电话', 那么"IP地址" 就相当于 "电话号码", 只有在知道对方的IP地址的前提下,才能与对应的电脑之间进行数据通信.
-
互联网中每台web服务器,都有自己的IP地址, 例如: 大家可以在windows的终端中运行 ping www.baidu.com命令,即可查看到百度的IP地址.
-
在开发期间, 自己的电脑既是一台服务器, 也是一台客户端,为了方便测试,可以在自己的浏览器中输入127.0.0.1这个IP地址,就能把自己的电脑当做一台服务器进行访问了.
-
域名:Domain Name, IP和域名是一一对应的关系.. 127.0.0.1对应的域名就是localhost
-
域名服务器:Domain name server
-
端口号: 一台web服务器可以有多个端口号, 只有80端口可以被省略.
5.3 使用http
(1) 导入
const http= require("http")
(2) 创建web服务器的实例
const server = http.createServer()
(3) 为实例对象 绑定request事件
server.on('request', (req, res) =>
console.log() // req请求体 res是响应体
})
(4) 启动
server.listen(80, ()=> {
console.log()
}
- res.end(content)
- res.setHeader('Content-Type','text/html',charset=utf-8')
5.4 根据不同的url来响应 不同的html内容
(1) 获取不同的url地址
(2) 置默认的响应内容
(3) 判断用户请求的是否为/ 或/index.html 首页
(4) 判断用户请求的是否为/about.html关于页面
(5) 设置Content-Type 响应头, 防止中文乱码
(6) 使用res.end(content)把响应内容给客户端
6. EggJs 框架
Egg.js是阿里旗下为数不多的 ,让人放心使用的开源项目。Egg.js为企业级框架和应用而生的Node.js框架,Egg(简写)奉行【约定优于配置】的框架,按照一套同意的约定进行应用开发。适合团队开发,学习成本小,减少维护成本。
6.1 EggJs的特点
- 提供基于Egg定制上层框架的能力
- 高度可扩展的插件机制
- 内置多进程管理(Node是单进程,无法使用多核CPU的能力)
- 基于Koa开发,性能优异
- 框架稳定,测试覆盖率高
- 渐进式开发,逐步模块化模式
6.2 环境搭建
(1)创建项目
npm install -g yarn
yarn create egg --type=simple 或者 npm init egg --type=simple
yarn install
yarn dev
(2)
6.3 Egg.js与Koa/Express对比
- Egg.js相对比Koa和Express框架的学习成本要高,但更适合企业级开发,有成熟的插件机制、扩展机制,还可以使用多进程管理。所以多付出一点学习成本是很划算的事情。我制作了一张图,对Egg.js和Express/Koa框架进行了对比。
6.4 Egg.js 项目结构介绍
- app - 项目开发的主目录,工作中的代码几乎都写在这里面
-- controller -- 控制器目录,所有的控制器都写在这个里面
-- router.js -- 项目的路由文件
- config - 项目配置目录,比如插件相关的配置
-- config.default.js -- 系统默认配置文件
-- plugin.js -- 插件配置文件
- logs -- 项目启动后的日志文件夹
- node_modules - 项目的运行/开发依赖包,都会放到这个文件夹下面
- test - 项目测试/单元测试时使用的目录
- run - 项目启动后生成的临时文件,用于保证项目正确运行
- typings - TypeScript配置目录,说明项目可以使用TS开发
- .eslintignore - ESLint配置文件
- .eslintrc - ESLint配置文件,语法规则的详细配置文件
- .gitignore - git相关配置文件,比如那些文件归于Git管理,那些不需要
- jsconfig.js - js配置文件,可以对所在目录下的所有JS代码个性化支持
- package.json - 项目管理文件,包含包管理文件和命令管理文件
- README.MD - 项目描述文件
6.5 dev和start的区别
- dev : 开发环境中使用,不用重启服务器,只要刷新。修改内容就会更改。
- start:生产环境中使用,也就是开发完成,正式运营之后。以服务的方式运行。修改后要停止和重启后才会发生改变。
6.6 controller控制器的使用
6.6.1
-
HTML URL 渲染页面
-
RESTful Controller
-
代理服务器
-
直接响应数据或渲染模板
-
接受用户的输入
-
与路由建立对应关系
-
this.ctx 可以吃拿到上下文的对象
-
get请求的参数:
- controller里 async index(){} 函数内, let query = this.ctx.request.query
- router里 router.get('/fruits/:id', controller.fruits.getid), 然后在controller里 async getid(){} 函数内 let id = ctx.params
-
post请求: this.ctx.request.body里
CSRF指跨站请求伪造,Egg中对post请求做了一些安全验证, 可以在config.default.js文件中,通过下面的设置验证.
config.security = { // 提交post请求时, 会禁止你提交这个请求
csrf: {
enable: false,
}
}
6.6.2 RESTful风格的定义
- router.resoureces('fruits','/fruits',controller.fruits) // 一个方法同时定义增删改查
6.7 插件
6.7.1 egg-view-nunjucks
* 安装
* 引入: plugin.js, config.default.js
nunjucks: {
enable: true,
package: 'egg-view-nunjucks',
}
config.view = {
defaultViewEngine: 'nunjucks'
}
* 使用
await this.ctx.render("index", { fruits: data })
6.7.2 egg-cors
* npm install --save egg-cors
* 在plugin.js文件中引入插件
cors:{
enable: true,
package: 'egg-cors',
}
config.cors = {
origin: "*",
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
}
* 在config.default.js文件中配置 egg-cors插件
6.8 http 协议
* 请求:
get
post
delete
put
* 响应
状态: 200, 404, 500
* 数据: json格式, js对象,,
* http是无状态的协议
登录,注册,购物车
(1)使用cookie与session识别用户的. cookie是存储在客户端的,session是存储在服务器端的. 这个cookie和session可以让用户保持一个登录状态的功能.
(2)使用JWT(Json Web Token)识别用户. 也可以实现保持用户登录状态的功能.
jwt: {
enable: true,
package: "egg-jwt"
},
config.jwt = {
secret: 'username'
}
6.9 中间件
6.9.1 定义
- 创建目录middleware, 在内部创建.js文件
- 在middleware中验证token和用户
6.10 数据持久化
6.10.1 介绍看
- 应用程序的数据通常存储在数据库中.
- 我们使用MySQL数据库实现数据的持久化.
- 为了方便的操作mysql, 我们使用sequelize(ORM框架) 管理数据层的代码.
6.10.2 ORM框架
-
对象关系映射(Object Relational Mapping)
-
sequelize是一个基于node的orm框架
-
通过egg-sequelize, 可以直接使用sequelize提供的方法操作数据库,而不需要动手写SQL语句
-
安装sequelize
- egg-sequelize 和 mysql2
- 在plugin.js引入插件
- config.default.js 文件中配置数据库连接
- 在app/model文件中创建数据模型
- 添加app.js文件, 初始化数据库