Node.js 笔记

Node.js 笔记

1.Node.js 简介

  • Node.js官网

  • node 不是一门语言,不是库,不是框架,它是 JavaScript 运行时环境,以前只有浏览器能解析 js 代码,现在 node 也可以

  • npm 是 世界最大的开源库生态系统,绝大多数 js 相关的包都放在了这个上面,这样做的目的是为了开发人员方便去下载

2.Node创建 http 服务

// 使用 node 构建一个 node 服务器

// 在 node 中专门提供了一个核心模块 : http

// 1.加载 http 核心模块
var http = require('http');

// 2.使用 http.createServer() 方法创建一个 http 服务器
// 返回一个 Server 实例
var server = http.createServer();

// req 请求事件处理函数,需要接受两个参数
/* 
    Request 请求对象
      请求对象可以用来获取客户端的一些请求信息,例如请求路径
    Response 响应对象
      响应对象可以用来给客户端发送响应消息
*/
// 3.提供服务
// 发请求  接受请求 处理请求 发送响应
// 注册 request 请求, 当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理
server.on('request', function(req, res) {
  // console.log('收到请求了,请求路径为', req.url);
  // res 对象有一个方法: write 可以用来给客户端发送响应;
  // write 可以使用多次,但是最后一次要使用 end 来结束响应.否则客户端会一直等待
  var url = req.url;
  // 1.
  // if (url == '/') {
  //   res.end('index page')
  // } else if (url == '/login') {
  //   res.write('login')
  // } else if (url == '/regist') {
  //   res.write('regist')
  // } else {
  //   res.end('404 not found')
  // }

  // 2.仿商品数据
  if (url === '/product') {
    var product = [
      {
        name: '苹果1',
        price: '8888'
      },
      {
        name: '苹果2',
        price: '8888'
      },
      {
        name: '苹果3',
        price: '8888'
      }
    ]
    // 注:发送响应只能是二进制数或字符串
    res.end(JSON.stringify(product));
  }
})

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

// 运行
node 文件名

3.Node 中的模块

  • 核心模块

    • 如 fs、http、path、os等
    // fs 模块的使用
    var fs = require('fs')
    
    // 读文件
    fs.readFile('文件路径', function(error, data) {})
    
    // 写文件
    fs.writeFile('文件路径', '写的内容', function(error) {})
    
    // 读文件目录
    fs.readdir('文件路径', function(error, files) {
    	// files 是一个文件数组
    })
    
    // url 模块 解析 url
    // url.parse('/1111')
    // url.parse() 是将路径解析成一个对象 第二个参数为 true 表示直接 将 "?" 后的地址(查询字符串)解析成一个对象, 在 query 中
    url.parse(req.url, true)
    
  • 第三方模块

  • 自定义模块

3.1Path 路径操作模块
  • path.basename
    • 获取一个路径的文件名(默认包含扩展名)
  • path.dirname
    • 获取一个路径中的目录部分
  • path.extname
    • 获取一个路径的扩展名部分
  • path.parse
    • 把一个路径转为对象
      • root根路径
      • dir 目录
      • base 包含后缀名的文件名
      • ext 后缀名
      • name 不包含后缀名的文件名
  • path.join
    • 进行路径拼接
  • path.isAbsolute
    • 判断一个路径是否事绝对路径
3.2 Node中的其它成员

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

  • __dirname 动态获取 可以用来获取当前文件的绝对路径
  • __filename 动态获取 可以用来获取当前文件的绝对路径
  • __dirname__filename 是不受执行 Node 命令所属路径影响的

补充:
__dirname: 总是返回被执行的 js 所在文件夹的绝对路径
__filename: 总是返回被执行的 js 的绝对路径

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

所以,为了解决这个问题,只需要把相对路径变为绝对路径。

这里就可以使用 __dirname__filename 变成绝对路径

在拼接的过程中,为了避免拼接错误,所以推荐多使用 path.join() 来辅助拼接

补充:模块操作路径不受影响,模块中的路径标识和文件操作中的相对路径标识不一致,模块路径就是相对于当前文件路径

4.Node 中的模板引擎

  • art-template
npm install art-template -S

var template = require('art-template')

var ret = template('hello {{name}}', {
    name: '你好'
})

console.log(ret); // hello 你好

5.Node 模块加载规则

  • require方法加载规则
    • 优先从缓存加载(不会加载重复的模块代码)
    • 核心模块
    • 路径形式的模块
    • 第三方模块
      • node_modules
      • 第三方模块加载,首先会去找到这个文件下的 是否有 package.json 这个文件夹,然后再到 package.json 里面找到 main , main 就是指向的 index.js 文件,如果 没有 package.json 文件,就会默认去找 index.js. 如果都不成立,就会去上一级找 node_modules ,一直找到磁盘根目录,否则报错。
  • package.json 包描述文件
    • dependencies 选项的作用

6.Node 中 exports 和 module.exports 的区别

exports 和 module.exports 的区别

  • 每个模块中都有一个 module 对象
  • module 对象中有一个 exports 对象
  • 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
  • 也就是:moudle.exports.xxx = xxx 的方式
  • 但是每次都 moudle.exports.xxx = xxx 很麻烦,点儿的太多了
  • 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports
  • exports === module.exports 结果为 trues
  • 所以对于:moudle.exports.xxx = xxx 的方式 完全可以:expots.xxx = xxx
  • 当一个模块需要导出单个成员的时候,这个时候必须使用:module.exports = xxx 的方式
  • 不要使用 exports = xxx 不管用
  • 因为每个模块最终向外 return 的是 module.exports
  • exports 只是 module.exports 的一个引用
  • 所以即便你为 exports = xx 重新赋值,也不会影响 module.exports
  • 但是有一种赋值方式比较特殊:exports = module.exports 这个用来重新建立引用关系的
  • 之所以让大家明白这个道理,是希望可以更灵活的去用它

7.npm

// 安装
npm install xxx 简写 npm i xxx

// 全局安装
npm i xxx -g

// 生产环境安装(发布之后需要用到的 express、jQuery等)
npm i xxx --save 简写 npm i xxx -S

// 开发环境安装(开发要用到的 如 webpack、gulp)
npm i xxx --save--dev 简写 npm i xxx -D

// 卸载就是 把 install 改成 uninstall 或 i 改成 un
npm uninstall xxx

npm help
	查看使用帮助
npm 命令 --help
	查看指定命令的帮助
	例如 uninstall 命令的简写,可以输入 npm unintsall --help 查看

// npm 自己升级自己
npm i npm -g

# 使用淘宝镜像
npm i cnpm -g
# 如
npm i jquery
cnpm i jquery

# 如果不想安装 cnpm 又想使用淘宝服务器来下载
npm i jquery --registry=https://registry.npm.taobao.org

# 但是每次手动这样加参数很麻烦,所以可以把这个选项加入配置文件中
npm config set registry https://registry.npm.taobao.org (淘宝)
				registry  http://r.cnpmjs.org/ (官方)

# 查看 npm 配置信息
npm config list
7.1 package 和 package-lock.json
npm 5以前是不会有 package-lock.json 这个文件的
npm 5 以后会生成这个文件
当你安装包的时候,npm 都会 生成或者关系 package-lock.json 这个文件
   npm 5 以后的版本不需要加 --save 参数,它会自动保存依赖信息
   当你安装包的时候,会自动创建或者更新 package-lock.json 这个文件
   package-lock.json 这个文件会保存 node_modules 中所有 包的信息(版本,下载地址)
   重新 npm 的时候会比以前快
 从文件来个,有一个lock称之为锁,
 	这个lock用来锁定版本的
    如果这个项目依赖了 1.11.1
    如果你重新下载会下载最新版本,而不是1.11.1
    我们目的就是希望锁住1.11.1这个版本
    所以这个package-lock.json这个文件的另一个作用就是锁定版本号,防止自动升级

8.渲染区别

服务端渲染和客户端渲染的区别

  • 客户端渲染不利于 SEO 搜索引擎优化
  • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
  • 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的
  • 而是两者结合来做的
  • 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化
  • 而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染

9.Express

// 安装
npm i express -S
// 起步 简单的服务 hello
var express = require('express')

var app = express()

app.get('/', function(req, res) {
    res.send('hello express')
})

app.listen(3000, function() {
    console.log('running...')
})

10.修改完代码自动重启

// 安装
npm i nodemon -g

// 使用
nodemon xxx(app.js)

11.在 Express 中使用 art-template 模板引擎

安装

# 安装
npm install --save art-template
npm install --save express-art-template

配置

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

使用

app.get('/', function (req, res) {
    res.render('index.html', {
        user: {
            name: 'aui',
            tags: ['art', 'template', 'nodejs']
        }
    });
});

如果希望改变默认的 views视图渲染存储目录,所以

# 第一个参数 views 一定不要写错
app.set('views', path.join(__dirname, 'views'));

12.在 Express 中 获取表单 get 请求参数

在 Express 中内置了一个 API,可以直接通过 req.query 来获取

app.get('/item', function(req, res){
    res.send(req.query);
});
// req.query => /item?name=1(浏览器) => {"name":"1"}
req.query

// Restful 形式的路由
// 号分隔路径,这些路径将被以对象的形式保存到req.params中
app.get('/item/:name', function(req, res){
    res.send(req.params);
});
// req.params => /item/1(浏览器) => {"name":"1"}

13.在 Express 中获取表单 post 请求体数据

在 Express 中没有设置内置表单 post 请求的 API,这里我们需要使用一个第三方插件 body-parse

安装

npm install body-parser -S

配置

var express = require('express')
// 0 引包
var bodyParser = require('body-parser')

var app = express()

// 配置 body-parse
// 只要 加入这个配置。则在 req 请求对象上会多出一个属性: body
// 就可以用 req.body 来获取表单请求的数据了
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use(bodyParser.json())

使用

app.use(function (req, res) {
  res.setHeader('Content-Type', 'text/plain')
  res.write('you posted:\n')
  res.end(JSON.stringify(req.body, null, 2))
})

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

参考文档:https://www.npmjs.com/package/express-session

安装:

npm install express-session

配置

app.use(session({
  secret: 'itcast', // 配置加密字符串, 它会在原有加密基础之上和这个字符串拼接去加密 增加安全性
  resave: false, 
  saveUninitialized: true // 无论是否使用 Session ,默认给你一把钥匙。 false 时 往 Session 存储数据时在发一把钥匙 
}))

使用

// 添加 Session 数据
req.session.foo = 'bar'

// 获取 Session 数据
req.session.foo

提示: 默认 Session 数据时内存存储的,服务器一旦重启就会丢失,真正的生产环境会将 Session 持久化处理

14.异步编程

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

console.log(add(10, 20)) ==> undefined

// 回调函数
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)
}

add(10, 20, function (ret) {
    console.log(ret)
})
  • 基于原生 XMLHttpRequest 封装 get 方法
function get(url, callback) {
    var oReq = new XMLHttpRequest();

    oReq.onload = function(e) {
      var arraybuffer = oReq.response; // not responseText
      /* ... */
      callback(oReq.responseText)
    }
    oReq.open("GET", url, true);
    oReq.send();
}

get('url', function() {})

15.MongoDB

  • 可以有多个数据库

  • 一个数据库中可以有多个集合(表)(数组)

  • 一个集合中可以有多个文档(表纪录)(对象)

  • 文档结构灵活,没有任何限制

  • 关系型数据库和非关系型数据库

    • 表就是关系
    • 表与表之间存在关系
      • 所有的关系型数据库都需要通过 sql 语言来操作
      • 所有的关系型数据库都需要在操作之前设计表结构
      • 而且数据表还支持约束
        • 唯一的
        • 主键
        • 默认值
        • 非空
      • 非关系型数据库非常的灵活
      • 有的非关系型数据库就是 key-value 对儿
      • 在 MongoDB 是长的最像关系型数据库的非关系型数据库
        • 数据库 -》数据库
        • 数据表 -》集合(数组)
        • 表纪录 -》(文档对象)
      • MongoDB 不需要设计表结构
      • 也就是可以任意往里面存数据,没有结构性这么一说
  • 安装

  • 配置环境变量

    • 小朱变量 加入 bin
查看是否成功
mongo --version
  • 1.启动和关闭数据库
  • # mongodb 默认使用执行 mongod 命令所处盘目录下的 /data/db 作为自己的数据存储目录
    # 所以在第一次执行该命令之前先自己手动建一个 /data/db
    mongod
    

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

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

    停止

    ctrl + c
    或者点 关闭
    
  • 2.连接数据库
#默认连接本机的服务
mongo
  • 退出
#在连接状态输入 exit 
exit
  • 3.基本命令
    • show dbs
    • 查看显示所有数据库
    • db
      • 查看当前操作的数据库
    • use 数据库名称
    • 切换到指定的数据库(如果没有会新建)
    • 插入数据
  • 4.在 Node 中如何操作 MongoDB 数据
    • 使用官方包来操作
  • 5.使用 mongoose 来操作 MongoDB 数据库
    • 第三方包 mongoose 基于 MongoDB

16.mongoose

官方指南
设计 Schema 发布 Model
var mongoose = require('mongoose');

var Schema = mongoose.Schema;

// 1. 连接数据库
// 指定连接的数据库不需要存在,当你插入第一条数据后会自动创建
mongoose.connect('mongodb://localhost/test')

// 2.设计文档结构(表结构)
// 字段名称就是表结构中的属性名称
// 约束是为了保证数据的完整性,不要有脏数据
var userSchema = new Schema({
  username: {
    type: String,
    require: true // 必须有
  },
  password: {
    type: String,
    require: true
  },
  email: {
    type: String,
    require: true
  }
});

// 3.将文档结构发布为模型
// mongoose.model 方法就是同来将一个架构发布为 model
//   第一个参数:传入一个大写名词单数字符串用来表示你的数据库名称
//              mongoose 会自动将大写的名词字符串生成 小写复数 的集合名称
//                例如 User 会变成 users 集合名称
//   第二个参数: 架构 Schema
//   返回值:模型构造函数
var User = mongoose.model('User', userSchema)

添加数据
// 4.当我们有了模型架构函数之后,就可以用这个构造函数对 users 集合中的数据去操作
var admin = new User({
  username: 'admin',
  // password: '123456',
  email: 'admin@admin.com'
})

admin.save(function (err, res) {
  if (err) {
    console.log('保存失败');
  } else {
    console.log('保存成功');
  }
})
查询数据

查询所有

// 按条件查 find 返回的是一个数组
User.find(function (err, res) {
    if (err) {
   	 	console.log('查询失败');
    } else {
        console.log(res);
    }
}) 

按条件查询所有

// 按条件查 find 返回的是一个数组
User.find({
  username: 'zs'
}, function (err, res) {
  if (err) {
    console.log('查询失败');
  } else {
    console.log(res);
  }
})

查询单个

// 按条件 查单个 findOne 返回的是一个对象
User.findOne({
  username: 'zs'
}, function (err, res) {
  if (err) {
    console.log('查询失败');
  } else {
    console.log(res);
  }
})

// 没接条件默认返回 第一个
User.findOne(function (err, res) {
  if (err) {
    console.log('查询失败');
  } else {
    console.log(res);
  }
})


删除数据

根据条件删所有

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

根据条件删一个

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

根据 id 删一个

Model.findByIdAndRemove(id, [options], [callback])
更新数据

更新

Model.update(conditions, doc, [options], [callback])

根据指定条件更新一个

// 根据指定条件更新一个
Model.findOneAndUpdate([conditions], [update], [options], [callback])

根据 id 更新一个

User.findByIdAndUpdate('5e51cb6a9bcc0044c8d66e35', {
  password: 123
}, function (err, res) {
  if (err) {
    console.log('更新失败');
  } else {
    console.log('更新成功');
  }
})

17.node 操作 MySQL 数据库

安装

npm install mysql

模板

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});
 
connection.connect();
 
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});
 
connection.end();

18.Promise 回调

callback hell 回调地狱

无法保证代码执行的顺序

通过回调保证代码执行的顺序

为了解决以上编码方式带来的问题(回调地狱),ES6 新增一个 API(Promise)

  • ES6Promise
  • callback hell 回调地狱
  • 回调函数中套了回调函数
  • Promise(EcmaScript 6 中新增了一个语法 API)
  • 容器
    • 异步任务(pending)
    • resolve
    • reject
  • then 方法获取容器的结果(成功的,失败的)
  • then 方法支持链式调用
  • 可以在 then 方法中返回一个 promise 对象,然后在后面的 then 方法中获取上一个 then 返回的 promise 对象的状态结果
Promise 基本语法
// 在 ES6 中新增了一个 API Promise
// Promise 是一个构造函数

var fs = require('fs')

// console.log(1)

// 创建一个 Promise 容器
// 1. 给别人一个承诺
// 		Promise 容器一旦创建,就开始执行里面的代码
// 承诺本身不是异步,往往都是封装一个异步任务
var p1 = new Promise(function (resolve, reject) {
	// console.log(2)
	fs.readFile('./data/a.txt', 'utf8', function (err, data) {
		if (err) {
			// 失败了, 承诺容器中的任务失败了
			// console.log(err)
			// 把容器的 pending状态变为 rejected

			// 调用 reject 就相对于调用了 then 方法的第二个参数函数
			reject(err)
		} else {
			// console.log(3)
			// 承诺容器中的任务失败了
			// console.log(data)
			// 把容器的 pending 状态改为成功
			// 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function
			resolve(data)
		}
	})
})

// p1就是那个承诺,
// 当 p1 成功了 然后 then() 做指定的操作
// then 接收的 function 就是容器中的 resolve 函数
p1
  .then(function (data) {
    console.log(data)
  }, function (err) {
    console.log('读取文件失败了', err)
  })

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

封装 Promise readFile API
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('./data/a.txt')
  .then(function (data) {
    console.log(data);
    return pReadFile('./data/b.txt')
  })
  .then(function (data) {
    console.log(data);
  })

开启数据接口

json-server --watch data.json

19.Express 中间件

1.中间件

中间件

中间件的本质就是一个请求处理方法,我们把用户从请求到响应的整个过程分发到多个中间件中去处理,这样的目的是提高代码的灵活性,动态可扩展的。

中间也是一个方法,它有三个参数 req, res, next

  • 同一个请求所经过的中间件都是同一个请求对象和响应对象
1.1 应用程序级别中间件

万能匹配(不关心任何请求路径和请求方法)

app.use(function (req, res, next) {
    console.log('time', Date.now())
    next() // 执行下一个中间件,也是有匹配规则的
})

只是以 '/xxx/'开头的

app.use('/a', function (req, res, next) {
    console.log('time', Date.now())
    next()
})
1.2 路由级别中间件

get:

app.get('/', function (req, res, next) {
    res.send('hello world')
})

post:

app.post('/', function (req, res, next) {
    res.send('Got a post request')
})

put:

app.put('/user', function (req, res, next) {
    res.send('Got a put request')
})

delete:

app.delete('/user', function (req, res, next) {
    res.send('Got a delete request')
})
1.3 错误处理中间件
app.use(function (err, req, res, next) {
    console.log(err.stack)
    res.status(500).send('Something broke!')
})
1.4 内置中间件
  • express.static
  • express.json
  • express-urlencoded
1.5 第三方中间件

第三方中间件

  • body-parser
  • compression
  • cookie-parser
  • morgan
  • response-time
  • serve-static
  • session
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值