Node大纲

2019年11月24日 阅读数:31
这篇文章主要向大家介绍Node大纲,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

Node大纲

目录

  • 1 day

    • 基本介绍
    • 环境配置(node nvm,cnpm)
    • REPL 环境(命令行) 运行js代码
    • js文件执行
    • nodemon实时监听
    • 模块/包与commonjs 规范
      • 内置
      • 第三方
      • 自定义
    • 内置模块详解
      • Url
        1. url 介绍
        2. parse
        3. format
      • Query String
        1. parse
        2. stringfly
        3. escape
        4. unescape
      • Http(爬虫)
        1. get
        2. requst
        3. cheerio
      • Event
      • Fs 文件操做
      • Stream
  • 2dayjavascript

    • 服务器代理跨域原理
    • nodejs建立后端路由
    • nodejs参数
    • node中的异步处理( 前端异步流程工具 )
    • npmscript
    • scocket 实时通讯
      • net
      • websocket
      • socket.io
  • 做业:php

    1. 实现聊天室
  • 3dayhtml

    • express基本使用
    • express 路由
    • express 经常使用插件
    • ejs 模板使用
    • 搭建web服务器(静态路径)
    • 搭建api服务
    • api测试工具 postman
    • 做业:
      • 实现文件系统版注册登陆
  • 4day前端

    • mongodb 环境配置安装vue

    • mongoose 操做mongod数据库java

    • 经常使用的服务器缓存redisnode

    • 自动化测试react

  • 5daylinux

    • token 验证
    • cookie+session 用户鉴权
    • 图片上传实现
    • jwt+验证码实现注册登陆

Node 简介

客户端的JavaScript是怎样的

  • 什么是 JavaScript?
    +是一个脚本语言
    +运行在浏览器(浏览器的js解析内核 v8)
    +实现用户的交互 (interactive)
    • 变量 赋值 循环 逻辑 判断 分支 对象 函数。。。。
    • dom 操做
    • bom 操做
    • ajax
  • JavaScript 的运行环境?
    +浏览器内核解析内核 es6
  • 浏览器中的 JavaScript 能够作什么?git

  • 浏览器中的 JavaScript 不能够作什么?(不安全)
    +访问数据库
    +不能对文件进行操做
    +对os 进行操做
    +缘由 是不安全 和浏览器运行机制有关

  • 在开发人员能力相同的状况下编程语言的能力取决于什么?

    +cordova hbuilder 平台 platform
    +java java虚拟机 (运行平台)
    +php php虚拟机
    +c# .net framework mono
    +js 解析内核 chrome v8

  • JavaScript 只能够运行在浏览器中吗?
    +不是

为何是JavaScript

  • node js 不是由于js 产生的
  • node 选择了js
  • Ryan dahl
  • 2009 2 月份 node有想法
  • 2009 5 月份 githup 开源
  • 2009 11月份 jsconf 讲解推广node
  • 2010年末 被xxx公司收购
  • 2018 发布有重大bug
  • npm ( npm 最大的开源 , 包管理器 ) https://www.npmjs.com/
  • github 世界上最大的同性交友网站( github.com) 码云( gitee.com)

what is node ?

  • Node.js 是一个基于Chrome V8 引擎的JavaScript运行环境 王者

  • Node.js使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效

    • 事件驱动: 任务执行,发布者,订阅者,事件驱动 ( on emit )
    • 非阻塞: 执行某一个任务的同时也能够执行其余任务
    • 阻塞: 执行某一个任务,这个任务若是没有执行完成,其余任务必须等待
      • 同步:
      • 异步
    • I/O: 输入/输出( 数据库操做,文件系统操做等 )
      • 非阻塞I/O模型: 当咱们使用Node.js来实现数据库操做、文件系统等操做时,要进行的异步操做,异步操做的核心传统实现方式就是回调函数
  • Node.js的包管理工具npm,是全球最大的开源库生态系统

    • 第三方: 国外的

    • 建议: 切换国内的, 淘宝国内镜像源

      • nrm

      • 安装cnpm

        $ npm install -g cnpm --registry=https://registry.npm.taobao.org

        查看是否cnpm安装成功

        cnpm -v

  • 官网 http://nodejs.cn/

  • npm 插件官网:https://www.npmjs.com/

环境配置

Node的安装

  • 安装包安装

    • 官网下载对应的安装包
    • 一路next
  • nvm安装(有一个相似的工具:nvm)

    • Node Version Manager(Node版本管理工具)

    • 因为之后的开发工做可能会在多个Node版本中测试,并且Node的版本也比较多,因此须要这么款工具来管理
    • 默认安装结束以后,默认路径: C:\Users\xxx\AppData\Roaming\nvm

      nvm安装教程博客:https://blog.csdn.net/qq_32682137/article/details/82684898

    • 问题: 若是有的同窗,任意目录不能使用nvm,只有在nvm安装目录下才能使用,那么就是环境变量有问题?

      • 在高级环境变量中的系统变量中建立两个变量

        1. NVM_HOME
          变量名: NVM_HOME   变量值: nvm安装目录路径
        2. NVM_SYMLINK
          变量名: NVM_SYMLINK 变量值: Node.js的安装目录
        
        
      • 在系统变量的path路径中将上面建立的变量引用进来

        ;%NVM_HOME%%NVM_SYMLINK%添加到系统变量  pth路径的后面

        切记: 不要将path路径删除了

      • nvm使用

        nvm list  列出当前电脑中全部的Node.js的版本
        nvm install version  安装某一个版本的Node.js , 
          举例: 安装10.12.0版本        nvm install 10.12.0
        nvm use version 切换某一个Node.js版本
        
          切换后切记: 要确认Node运行十分正常
              node -v 
              npm -v
              若是以上二者输出正常,那么久切换成功了
              若是不正常呢?换版本下载安装( 必须在8.9+ 以上)

相关版本

  • node版本常识
    • 偶数版本为稳定版 (0.6.x ,0.8.x ,0.10.x)
    • 奇数版本为非稳定版(0.7.x ,0.9.x ,0.11.x)
    • LTS(Long Term Support)
    • LTS和Current区别
  • 操做方式:
    • 从新下载最新的安装包;
    • 覆盖安装便可;
  • 问题:
    • 之前版本安装的不少全局的工具包须要从新安装
    • 没法回滚到以前的版本
    • 没法在多个版本之间切换(不少时候咱们要使用特定版本)

Windows下经常使用的命令行操做

  • 切换当前目录(change directory):cd
  • 建立目录(make directory):mkdir
  • 查看当前目录列表(directory):dir
    • 别名:ls(list)
  • 清空当前控制台:cls
    • 别名:clear
  • 删除文件:del
    • 别名:rm

注意:全部别名必须在新版本的 PowerShell (linux系统)中使用

电脑:

​ cmd里面用的叫作 DOS命令

​ cmd终端唤醒: win键 + R , 输入cmd 回车

​ mkdir 建立目录

​ dir 列出当前目录的列表

​ cls 清空终端命令

​ del 删除某一个文件

​ git / powershell 用的是linux命令

​ mkdir 建立目录

​ ls 查看当前目录列表

​ clear 清空当前控制台

​ rm -rf 文件名称 删除某一个文件或是目录

​ 两种类型的命令中, cd是一个意思

常见问题

  • 环境变量丢失

  • 部分电脑安装完毕以后没有环境变量须要手动配置
  • Windows中环境变量分为系统变量和用户变量
  • 环境变量的变量名是不区分大小写的
  • PATH 变量:只要添加到 PATH 变量中的路径,均可以在任何目录下
  • 目的能够在任何地方调起node命令

Node.js实时监听( 自动刷新 )

借助第三方工具实现:

​ nodemon 【 推荐

​ cnpm i nodemon -g

​ nodemon 文件名称

​ supervisor

​ cnpm i supervisor -g

​ supervisor 文件名称

模块,包 commonjs

commonjs规范

前端模块化:AMD,CMD,Commonjs

Node 应用由模块组成,采用 CommonJS 模块规范。

定义module

每一个文件就是一个模块,有本身的做用域。在一个文件里面定义的变量、函数、类,都是私有的,对其余文件不可见。

暴露接口

CommonJS规范规定,每一个模块内部,module变量表明当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,实际上是加载该模块的module.exports属性。

    var x = 5;
    var addX = function (value) {
      return value + x;
    };
    module.exports.x = x;
    module.exports.addX = addX;
引用

require方法用于加载模块。

    var example = require('./example.js');
    console.log(example.x); // 5
    console.log(example.addX(1)); // 6

模块的分类

  • 内置模块
    const process = require('process')
    const path = require('path')
    console.log(process.version)
    console.log(path.resolve('../'))
  • 第三方模块
    const request=require("request");
    console.log(request)
    request.get('http://api.douban.com/v2/movie/in_theaters', (err, response, body) => {
      if (!err) {
        // console.log(body);
        console.log(JSON.parse(body))
      } else {
        console.log(err);
      }
    })
  • 自定义模块

npm 使用入门

官网:https://www.npmjs.com/

安装:无需安装

查看当前版本:

 $ npm -v

更新:

 $ npm install npm@latest -g

初始化工程

 $ npm init

 $ npm init --yes 默认配置

安装包

使用npm install会读取package.json文件来安装模块。安装的模块分为两类
dependencies和devDependencies,分别对应生产环境须要的安装包和开发环境须要的安装包。

 $ npm install

 $ npm install <package_name> 

 $ npm install <package_name> --save

 $ npm install <package_name> --save-dev

更新模块

 $ npm update

卸载模块

 $ npm uninstall <package_name>

 $ npm uninstall --save lodash

配置npm源

  • 临时使用, 安装包的时候经过--registry参数便可

    $ npm install express --registry https://registry.npm.taobao.org

  • 全局使用

      $ npm config set registry https://registry.npm.taobao.org
      // 配置后可经过下面方式来验证是否成功
      npm config get registry
      // 或
      npm info express
  • cnpm 使用

       // 安装cnpm
        npm install -g cnpm --registry=https://registry.npm.taobao.org
    
        // 使用cnpm安装包
        cnpm install express

经常使用的内置模块

node 经常使用内置api

(1) URL 网址解析
解析URL相关网址信息
url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
url.format(urlObject)
url.resolve(from, to)

(2) QueryString 参数处理
querystring.escape(str)
querystring.unescape(str)
querystring.parse(str[, sep[, eq[, options]]])
querystring.stringify(obj[, sep[, eq[, options]]])

(3) HTTP 模块概要
http.createServer([options][, requestListener])
http.get(options[, callback])
简易的爬虫
代理跨域处理

(4) 事件 events 模块

(5) 文件fs模块
打印目录树

(6) Stream 流模块
歌词播放
音乐下载

(8) request 方法

二、Node.js 基础应用
一、应用 HTTP 模块编写一个小爬虫工具

(1) 利用爬虫获取“拉勾网”首页列表数据

(2) 经过 npm 安装 cheerio 模块得到数据
二、后端表单的提交
要求:

(1) 应用 request post 模拟提交表单

文件读取

Node中文件读取的方式主要有:

fs.readFile(file[, options], callback(error, data))

fs.readFile('c:\\demo\1.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

fs.readFileSync(file[, options])

try {
  const data = fs.readFileSync('c:\\demo\1.txt', 'utf8');
  console.log(data);
} catch(e) {
  // 文件不存在,或者权限错误
  throw e;
}

fs.createReadStream(path[, options])

const stream = fs.createReadStream('c:\\demo\1.txt');
let data = ''
stream.on('data', (trunk) => {
  data += trunk;
});
stream.on('end', () => {
  console.log(data);
});

因为Windows平台下默认文件编码是GBK,在Node中不支持,能够经过iconv-lite解决

Readline模块逐行读取文本内容

const readline = require('readline');
const fs = require('fs');

const rl = readline.createInterface({
  input: fs.createReadStream('sample.txt')
});

rl.on('line', (line) => {
  console.log('Line from file:', line);
});

文件写入

Node中文件写入的方式主要有:

fs.writeFile(file, data[, options], callback(error))

fs.writeFile('c:\\demo\a.txt', new Date(), (error) => {
  console.log(error);
});

fs.writeFileSync(file, data[, options])

try {
  fs.writeFileSync('c:\\demo\a.txt', new Date());
} catch (error) {
  // 文件夹不存在,或者权限错误
  console.log(error);
}

fs.createWriteStream(path[,option])

var streamWriter = fs.createWriteStream('c:\\demo\a.txt');
setInterval(() => {
  streamWriter.write(`${new Date}\n`, (error) => {
    console.log(error);
  });
}, 1000);

node中的异步操做

  • fs模块对文件的几乎全部操做都有同步和异步两种形式
  • 例如:readFile() 和 readFileSync()
  • 区别:
    • 同步调用会阻塞代码的执行,异步则不会
    • 异步调用会将读取任务下达到任务队列,直到任务执行完成才会回调
    • 异常处理方面,同步必须使用 try catch 方式,异步能够经过回调函数的第一个参数
console.time('sync');
try {
  var data = fs.readFileSync(path.join('C:\\Users\\iceStone\\Downloads', 'H.mp4'));
  // console.log(data);
} catch (error) {
  throw error;
}
console.timeEnd('sync');

console.time('async');
fs.readFile(path.join('C:\\Users\\iceStone\\Downloads', 'H.mp4'), (error, data) => {
  if (error) throw error;
  // console.log(data);
});
console.timeEnd('async');
promise 对象的使用

参考资料:JavaScript Promise迷你书

  • what is Promise *
    Promise是抽象异步处理对象以及对其进行各类操做的组件。Promise并非从JavaScript中发祥的概念。
    Promise最初被提出是在 E语言中, 它是基于并列/并行处理设计的一种编程语言。
    如今JavaScript也拥有了这种特性,这就是JavaScript Promise

使用了回调函数的异步处理

----
getAsync("fileA.txt", function(error, result){
    if(error){// 取得失败时的处理
        throw error;
    }
    // 取得成功时的处理
});
----
<1> 传给回调函数的参数为(error对象, 执行结果)错误优先处理

使用了回调函数的异步处理

----
var promise = getAsyncPromise("fileA.txt"); 
promise.then(function(result){
    // 获取文件内容成功时的处理
}).catch(function(error){
    // 获取文件内容失败时的处理
});
----
<1> 返回promise对象
  • 建立Promise对象 *
var promise = new Promise(function(resolve, reject) {
    // 异步处理
    // 处理结束后、调用resolve 或 reject
    resolve('成功处理')
    reject('错误处理')
});
  • 使用实例 *
  1. 建立一个priomise 对象并返回new Promise(fn)
  2. 在fn 中指定异步等处理
    • 处理结果正常的话,调用resolve(处理结果值)
    • 处理结果错误的话,调用 reject(Error对象)
function asyncFunction() {
    
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('Async Hello world');
        }, 16);
    });
}

asyncFunction().then(function (value) {
    console.log(value);    // => 'Async Hello world'
}).catch(function (error) {
    console.log(error);
});
  • Promise的状态

用new Promise 实例化的promise对象有如下三个状态。

  • "has-resolution" - Fulfilled resolve(成功)时。
  • "has-rejection" - Rejected reject(失败)时
  • "unresolved" - Pending 既不是resolve也不是reject的状态。也就是promise对象刚被建立后的初始化状态等

promise对象的状态,从Pending转换为Fulfilled或Rejected以后, 这个promise对象的状态就不会再发生任何变化。

也就是说,Promise与Event等不一样,在.then 后执行的函数能够确定地说只会被调用一次。

另外,Fulfilled和Rejected这两个中的任一状态均可以表示为Settled(不变的)。

Settled
resolve(成功) 或 reject(失败)。

从Pending和Settled的对称关系来看,Promise状态的种类/迁移是很是简单易懂的。

当promise的对象状态发生变化时,用.then 来定义只会被调用一次的函数。

路径模块

在文件操做的过程当中,都必须使用物理路径(绝对路径),path模块提供了一系列与路径相关的 API

console.log('join用于拼接多个路径部分,并转化为正常格式');
const temp = path.join(__dirname, '..', 'lyrics', './友谊之光.lrc');
console.log(temp);

console.log('获取路径中的文件名');
console.log(path.basename(temp));

console.log('获取路径中的文件名并排除扩展名');
console.log(path.basename(temp, '.lrc'));

console.log('====================================');

console.log('获取不一样操做系统的路径分隔符');
console.log(process.platform + '的分隔符为 ' + path.delimiter);

console.log('通常用于分割环境变量');
console.log(process.env.PATH.split(path.delimiter));

console.log('====================================');

console.log('获取一个路径中的目录部分');
console.log(path.dirname(temp));

console.log('====================================');

console.log('获取一个路径中最后的扩展名');
console.log(path.extname(temp));

console.log('====================================');

console.log('将一个路径解析成一个对象的形式');
const pathObject = path.parse(temp);
console.log(pathObject);

console.log('====================================');

console.log('将一个路径对象再转换为一个字符串的形式');
// pathObject.name = '我终于失去了你';
pathObject.base = '我终于失去了你.lrc';
console.log(pathObject);

console.log(path.format(pathObject));

console.log('====================================');

console.log('获取一个路径是否是绝对路径');
console.log(path.isAbsolute(temp));
console.log(path.isAbsolute('../lyrics/爱的代价.lrc'));

console.log('====================================');

console.log('将一个路径转换为当前系统默认的标准格式,并解析其中的./和../');
console.log(path.normalize('c:/develop/demo\\hello/../world/./a.txt'));

console.log('====================================');

console.log('获取第二个路径相对第一个路径的相对路径');
console.log(path.relative(__dirname, temp));

console.log('====================================');

console.log('以相似命令行cd命令的方式拼接路径');
console.log(path.resolve(temp, 'c:/', './develop', '../application'));

console.log('====================================');

console.log('获取不一样平台中路径的分隔符(默认)');
console.log(path.sep);

console.log('====================================');

console.log('容许在任意平台下以WIN32的方法调用PATH对象');
// console.log(path.win32);
console.log(path === path.win32);

console.log('====================================');

console.log('容许在任意平台下以POSIX的方法调用PATH对象');
console.log(path === path.posix);

express


官网:http://www.expressjs.com.cn/

express 环境搭建

安装

    $ npm install express --save

快速开始

    const express = require('express')
    const app = express()
    app.get('/', (req, res) => res.send('Hello World!'))
    app.listen(3000, () => console.log('Example app listening on port 3000!'))

express 路由配置

let express=require('express')
let router=express.Router()
// 该路由使用的中间件
router.use((req,res,next)=>{
     next()
});
// 定义网站主页的路由
router.post('/addFood', function(req, res) {
    console.log('hahaha')
  // res.send('这里是admin的登陆');
});
// 定义 about 页面的路由
router.post('/regist', function(req, res) {
  res.send('这里是admin的注册侧');
});

module.exports = router;

app.use('/admin',admin)

传递数据的获取

get
req.query
post
req.body
body-parser

设置中文格式
res.set('Content-Type','text/plain,charset=utf8')

请求模拟工具 insomina

静态资源配置

app.use(express.static('public'))

app.use('/static', express.static('public'))

app.use('/static', express.static(path.join(__dirname, 'public')))

mongod

安装配置

Mongodb官网下载最新版本的Mongodb下载地址

下载msiwindow安装包,能够装到C盘或者D盘目录下

配置

因为我是安装在D盘的环境下

D:\Program Files (x86)\MongoDB\Server\3.2\bin

因此在bin文件夹下找到mongod.exe命令,而后经过管理员执行mongod --dbpath x路径x,路径能够是任何地方,我这里选择在D盘的MongoDB目录下,固然路径不要包含特殊的字符串,好比Program Files (x86)也不行

mongod --dbpath D:\mongodb\data\db

image

命令行

通过上面的配置以后,就能够返回bin目录下找到mongo.exe命令,并管理员下执行,就能够出现mongodb的命令行模式

D:\Program Files (x86)\MongoDB\Server\3.2\bin

image

而后就可使用下面的命令来测试了

mongod

db.help()//帮助
db.stats()//统计

显示数据库

show dbs

检查当前选择的数据库

db

添加数据库

数据库名为数据库建立的名字,使用该命令后会默认切换到对应的数据库,而且在数据库中添加选项,数据库信息才显示,若是默认就有该数据库,那就是切换到对应的数据库里面

use 数据库名

删除数据库

先切换到对应的数据库,而后再执行db.dropDatabase()删除该数据库

use 数据库名
//switched to db 数据库名
db.dropDatabase()

显示集合

用一下命令能够检查建立的集合

show collections

添加集合

在建立完数据库以后,咱们就能够建立集合

db.createCollection(集合名字name,设置参数options[对象类型])

name是要建立的集合的名称。 options是一个文档,用于指定集合的配置

参数 类型 描述
name String 要建立的集合的名称
options Document (可选)指定有关内存大小和索引的选项
options参数是可选的,所以只须要指定集合的名称。 如下是可使用的选项列表:

字段 类型 描述
capped Boolean (可选)若是为true,则启用封闭的集合。上限集合是固定大小的集合,它在达到其最大大小时自动覆盖其最旧的条目。 若是指定true,则还须要指定size参数。
autoIndexId Boolean (可选)若是为true,则在_id字段上自动建立索引。默认值为false。
size 数字 (可选)指定上限集合的最大大小(以字节为单位)。 若是capped为true,那么还须要指定此字段的值。
max 数字 (可选)指定上限集合中容许的最大文档数。
因为option是可选,咱们也能够不带配置项建立集合

db.createCollection("mycollection")

删除集合

db.collection.drop()用于从数据库中删除集合

db.集合名.drop()

好比咱们能够测试如下操做

db.createCollection("wscats")//建立名为wscats的集合
show collections//显示该数据库全部集合   wscats
db.wscats.drop()//删除名为wscats的集合

查看文档

最简单查看文档的方法就是find(),会检索集合中全部的文档结果

db.集合名.find()

要以格式化的方式显示结果,可使用pretty()方法。

db.集合名.find().pretty()

1.固值寻找

寻找age集合里面全部含有属性值为wscats的文档结果,至关于where name = 'wscats'

db.age.find({name:"wscats"})

2.范值寻找

操做 语法 示例 等效语句
相等 {:} db.age.find({"name":"wscats"}).pretty() where name = 'wscats'
小于 {:{$lt:}} db.age.find({"likes":{$lt:50}}).pretty() where likes < 50
小于等于 {:{$lte:}} db.age.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {:{$gt:}} db.age.find({"likes":{$gt:50}}).pretty() where likes > 50
大于等于 {:{$gte:}} db.age.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {:{$ne:}} db.age.find({"likes":{$ne:50}}).pretty() where likes != 50

3.AND和OR寻找

AND

在find()方法中,若是经过使用将它们分开传递多个键,则mongodb将其视为AND条件。 如下是AND的基本语法

寻找_id为1而且name为wscats的全部结果集

db.age.find(
{
   $and: [
      {"_id": 1}, {"name": "wscats"}
   ]
}
)

OR

在要根据OR条件查询文档,须要使用$or关键字。如下是OR条件的基本语法

寻找name为corrine或者name为wscats的全部结果集

db.age.find(
{
   $or: [
      {"name": "corrine"}, {“name“: "wscats"}
   ]
}
)

AND和OR等结合

至关于语句where title = "wscats" OR ( title = "corrine" AND _id < 5)

db.age.find({
$or: [{
 "title": "wscats"
}, {
 $and: [{
   "title": "corrine"
 }, {
   "_id": {
     $lte: 5
   }
 }]
}]
})

插入文档

文档的数据结构和JSON基本同样。
全部存储在集合中的数据都是BSON格式。
BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON

要将数据插入到mongodb集合中,须要使用mongodb的insert()save()方法。

db.集合名.insert(document)

好比咱们能够插入如下数据

db.wscats.insert({
_id: 100,
title: 'MongoDB Tutorials', 
description: 'node_tutorials',
by: 'Oaoafly',
url: 'https://github.com/Wscats/node-tutorial',
tags: ['wscat','MongoDB', 'database', 'NoSQL','node'],
num: 100,
})

也能够支持插入多个,注意传入的是数组形式

db.wscats.insert([{
_id: 100,
title: ‘Hello’
},{
_id: 101,
title: ‘World’
}])

在插入的文档中,若是不指定_id参数,那么mongodb会为此文档分配一个惟一的ObjectId
要插入文档,也可使用db.post.save(document)。若是不在文档中指定_id,那么save()方法将与insert()方法同样自动分配ID的值。若是指定_id,则将以save()方法的形式替换包含**_id**的文档的所有数据。

db.wscats.save({
_id: 111,
title: 'Oaoafly Wscats', 
})

更新文档

1.update()方法

寻找第一条title为wscats的值,而且更新值title为corrine和age为12

db.age.update({
'title': 'wscats'
}, {
$set: {
 'title': 'corrine',
 'age': 12
}
})

默认状况下,mongodb只会更新一个文档。要更新多个文档,须要将参数multi设置为true,还能够配合find方法里面的各类复杂条件判断来筛选结果,而后更新多个文档

寻找全部title为wscats的值,而且更新值title为corrine和age为12

db.age.update({
'title': 'wscats'
}, {
$set: {
 'title': 'corrine',
 'age': 12
}
}, {
multi: true
})

2.save()方法

_id主键为3的文档,覆盖新的值,注意_id为必传

db.age.save({
'_id':3,
'title': 'wscats'
})

删除文档

删除主键_id为3的文档,默认是删除多条

db.age.remove({
'_id':3
})

建议在执行remove()函数前先执行find()命令来判断执行的条件是否正确

若是你只想删除第一条找到的记录能够设置justOne为1,以下所示

db.age.remove({...},1)

所有删除

db.age.remove({})

Limit与Skip方法

Limit

若是你须要在mongodb中读取指定数量的数据记录,可使用mongodb的Limit方法,limit()方法接受一个数字参数,该参数指定从mongodb中读取的记录条数。

db.age.find().limit(数量)

Skip

咱们除了可使用limit()方法来读取指定数量的数据外,还可使用skip()方法来跳过指定数量的数据,skip方法一样接受一个数字参数做为跳过的记录条数。

db.age.find().limit(数量).skip(数量)
//skip()方法默认值为0

因此咱们在实现分页的时候就能够用limit来限制每页多少条数据(通常固定一个值),用skip来决定显示第几页(一个有规律变更的值)

排序

在mongodb中使用使用sort()方法对数据进行排序,sort()方法能够经过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列。

1 升序排列
-1 降序排列

db.集合名.find().sort({键值(属性值):1})

age集合表从新根据_id主键进行降序排列

db.age.find().sort({
"_id": -1
})

Node.js链接

安装mongodb的模块

npm install mongodb

1.链接数据库

var MongoClient = require('mongodb').MongoClient;
//结尾是选择数据库名
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';
MongoClient.connect(DB_CONN_STR, function(err, db) {
console.log("链接成功!");
});

2.查询数据

注意查询回来的结果须要toArray来遍历处理

var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
console.log("链接成功!");
//选中age集合,并用find方法把结果集拿回来进行处理
db.collection("age").find({title: "cba"}).toArray(function(err, result) {
 if (err) {
   console.log('Error:' + err);
   return;
 }
 console.log(result);
});
});

通过测试,读取大于100条的时候会出现报错官网解释,能够尝试用forEach代替

db.collection('pokemon').find({})
.forEach(function(item){
   console.log(item)
})

查询ID

查询自动生成的ObjectId

var ObjectId = require('mongodb').ObjectId;
let _id = ObjectId("5bcae50ed1f2c2f5e4e1a76a");
db.collection('xxx').find({
 "_id": _id
}).forEach(function (item) {
 console.log(item)
})

3.插入数据

insert函数第一个参数是须要插入的值(能够一个也能够多个),第二个参数是接受一个回调函数,当值插入成功后回返回插入值得一些关键信息,好比_id

var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
console.log("链接成功!");
  const db = client.db("demo");
db.collection("age").insert([
 { 
   title: "插入的值A"
 }, {
   title: "插入的值B"
 }
], function(err, result) {
 if (err) {
   console.log('Error:' + err);
   return;
 }
 console.log(result)
})
});

4.更新数据

注意若是不加$set就是彻底替换原来的那份(没有设置的属性值将会丢失),加上$set则只是更新对应的属性值,其他不作改变

var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
console.log("链接成功!");
db.collection("age").update({
 "_id": 1
}, {
 $set: {
   title: "你好,世界",
   skill: "js"
 }
}, function(err, result) {
 if (err) {
   console.log('Error:' + err);
   return;
 }
 //console.log(result);
});
});

5.删除数据

var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
console.log("链接成功!");
db.collection("age").remove({
 "_id": 1
}, function(err, result) {
 if (err) {
   console.log('Error:' + err);
   return;
 }
 //console.log(result);
 //关闭数据库
 db.close();
});
});

6.关闭数据库

db.close();

封装自定义模块

新建mongo.js写入如下代码,封装自定义模块,方便其余路由复用,注意assert是node自带的断言模块,用于测试代码

参考

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const url = 'mongodb://localhost:27017';
const dbName = 'shop';
function query(callback) {
  MongoClient.connect(url, function(err, client) {
      assert.equal(null, err);
      console.log("Connected successfully to server");
      const db = client.db(dbName);
      callback(db);
      client.close();
  });
}
module.exports = {
  query
}

在路由文件中引入和使用

var mongo = require('./mongo.js')
router.post('/addproduct', function(req, res, next) {
  mongo.query(function(db) {
      db.collection("product").insertMany([req.body], function(err, result) {
          console.log("Inserted 1 document into the collection");
          res.send('respond with a resource');
      });
  })
});

mongoose

  1. 下载mongoose
npm install mongoose --save
  1. 链接数据库
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/1823');
var db = mongoose.connection;// 获取链接对象进行监听
db.on('error',(err)=>{
    console.log('链接错误')
});
db.on('open', function() {
  console.log('链接ok')
});
  1. 建立schema对象
var UserSchema = new mongoose.Schema({
    name: String,
    pass: String,
    test:String
  });
  1. 将schema转化为数据模型
let user = mongoose.model('user', UserSchema); //参数1 是集合的名字 与数据模型关联的schema对象
  1. 经过数据模型执行查询操做

mongoose

可视化

Socket

  • 实时刷新(蜡烛图)
  • 推送服务

socket.io

<!DOCTYPE html>
<html lang="en">
<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>socket.io</title>
  <script src="socket.io.js" charset="utf-8"></script>
</head>
<body>
  <h1>gp6 交流区</h1>
  <div id="content" name="name" style="overflow-y: scroll; width: 400px; height: 300px; border: solid 1px #000"></div>
  <br />
  <div>
    <input type="text" id="msg" style="width: 200px;">
  </div>
  <button id="submit">提交</button>
  <script>
    var socket = io.connect('http://10.9.164.98:8081');
    const content = document.getElementById('content')
    document.querySelector('#submit')
      .addEventListener('click', function () {
        var msg2 = msg.value
        socket.emit('receive', msg2)
        msg.value = ''
        content.innerHTML += msg2 + '<br/>'
      }, false)

      socket.on('message', function(msg){
        content.innerHTML += msg + '<br/>'
      })
  </script>
</body>
</html>

server.js

var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

app.use(express.static(__dirname + '/client'))

io.on('connection', function (socket) {
  setInterval(function () {
    socket.emit('list', 'abc')
  }, 1000)
  socket.broadcast.emit('list', 'test');
  socket.on('backend', (msg) => {
    console.log(msg);
  })

  socket.on('receive', (msg) => {
    socket.broadcast.emit('message', msg);
  })
});

server.listen(8081, '10.9.164.98');

net模块

serverCode

const net = require('net')

const server = new net.createServer()

let clients = {}
let clientName = 0

server.on('connection', (client) => {
  client.name = ++clientName
  clients[client.name] = client

  client.on('data', (msg) => {
    // console.log('客户端传来:' + msg);
    broadcast(client, msg.toString())
  })

  client.on('error', (e) => {
    console.log('client error' + e);
    client.end()
  })

  client.on('close', (data) => {
    delete clients[client.name]
    console.log(client.name + ' 下线了');
  })
})

function broadcast(client, msg) {
  for (var key in clients) {
    clients[key].write(client.name + ' 说:' + msg)
  }
}

server.listen(9000)

clientCode

var net = require('net')
const readline = require('readline')

var port = 9000
var host = '127.0.0.1'

var socket = new net.Socket()

socket.setEncoding = 'UTF-8'

socket.connect(port, host, () => {
  socket.write('hello.')
})

socket.on('data', (msg) => {
  console.log(msg.toString())
  say()
})

socket.on('error', function (err) {
  console.log('error' + err);
})

socket.on('close', function () {
  console.log('connection closeed');
})

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})

function say() {
  r1.question('请输入:', (inputMsg) => {
    if (inputMsg != 'bye') {
      socket.write(inputMsg + '\n')
    } else {
      socket.destroy()
      r1.close()
    }
  })
}

websocket

const ws = new WebSocket('ws://localhost:8080/')

ws.onopen = () => {
  ws.send('你们好')
}

ws.onmessage = (msg) => {
  const content = document.getElementById('content')
  content.innerHTML += msg.data + '<br/>'
}

ws.onerror = (err) => {
  console.log(err);
}

ws.onclose = () => {
  console.log('closed~');
}
ws.send(msg2)

server.js

const WebSocket = require('ws')
const ws = new WebSocket.Server({ port: 8080 })

let clients = {}
let clientName = 0

ws.on('connection', (client) => {
  client.name = ++clientName
  clients[client.name] = client

  client.on('message', (msg) => {
    broadcast(client, msg)
  })

  client.on('close', () => {
    delete clients[client.name]
    console.log(client.name + ' 离开了~')
  })
})

function broadcast(client, msg) {
  for (var key in clients) {
    clients[key].send(client.name + ' 说:' + msg)
  }
}

SSR 与 SEO

vue 和 react 介绍

项目实战

api接口

  • RestfulApi 规范
  • 接口文档的生成(apidoc)
  • 接口请求方式区别

跨域解决

  • cors
  • jsonp
  • proxy

Hui 基本使用

身份验证

http 请求的无状态性

JWT

  • 用户登陆 服务器端产生一个token (加密字符串) 发送给前端
  • 前端将token 进行保存
  • 前端发起数据请求的时候携带token
  • 服务端 验证token 是否合法 若是合法继续操做 不合法终止操做
  • token 的使用场景 无状态请求 保持用户的登陆状态 第三方登陆(token+auth2.0)
非对称加密 经过私钥产生token 经过公钥解密token
// 1.产生公钥和私钥
// 产生私钥  openssl genrsa -out ./private_key.pem 1024    1024 表明私钥长度
// 产生公钥  openssl rsa -in ./private_key.pem -pubout -out ./public_key.pem

 let private_key=fs.readFileSync(path.join(__dirname,'./private_key.pem'))
 let public_key=fs.readFileSync(path.join(__dirname,'./public_key.pem'))
 var token = jwt.sign(palyload, private_key,{ algorithm: 'RS256'});
 console.log(token)
 let  token='eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IueUqOaIt2lkIiwiaWF0IjoxNTUxMTUyNzk1fQ.TI_xDBvObHGAH7EV40WWpQemm5nx077Gdjq-pzDx0NWN5YFd40S7XcLmgoDdYscLM7vMOP0c7z1l83JUixqk7IBjBCU-tMNo_G5_-LGkQjV3vDYq_3TkXTl42lgmFA-EBey7W6W1PgPfYlowyHAyp-07hXaMRevgVkXm2lPEFXo'

  var decoded = jwt.verify(token, public_key);
const jwt=require('jsonwebtoken')
const scrict='sdjfksdjflajflasjflasjflksf'

function creatToken(palyload){
    // 产生token
    palyload.ctime=Date.now()
    return jwt.sign(palyload,scrict)
}
function checkToken(token){
    return  new Promise((resovle,reject)=>{
        jwt.verify(token,scrict,(err,data)=>{
           if(err){ reject('token 验证失败')}
           resovle(data)
           })
    })
    
}
module.exports={
    creatToken,checkToken
}

Cookie+Session

const  cookieParse=require('cookie-parser')
const  session = require('express-session')

app.use(session({
    secret: 'hubwizApp', //为了安全性的考虑设置secret属性
    cookie: {maxAge: 60 * 1000 * 60 * 24 }, //设置过时时间
    resave: true, // 即便 session 没有被修改,也保存 session 值,默认为 true
    saveUninitialized: false, //不管有没有session cookie,每次请求都设置个session cookie ,默认给个标示为 connect.sid
}));

登陆成功

req.session.sign = true;
req.session.name = us;

须要验证的接口判断是否存在

注销session

app.get('/out', function(req, res){
    req.session.destroy();
    res.redirect('/');
})

图片上传

  1. 安装multer模块
npm install multer
  1. 引用模块
    它是依赖于express的一个模块
//引用express并配置
var express = require("express");
var app = express();
app.listen(3000);
var multer = require('multer');
/*var upload = multer({
    //若是用这种方法上传,要手动添加文明名后缀
        //若是用下面配置的代码,则能够省略这一句
    dest: 'uploads/'
})*/
  1. 配置
    设置保存文件的地方,并根据上传的文件名对应文件添加后缀
    能够经过filename属性定制文件保存的格式
属性值 用途
destination 设置资源的保存路径。注意,若是没有这个配置项,默认会保存在/tmp/uploads下。此外,路径须要本身建立
filename 设置资源保存在本地的文件名
var storage = multer.diskStorage({
    //设置上传后文件路径,uploads文件夹会自动建立。
    destination: function(req, file, cb) {
        cb(null, './uploads')
    },
    //给上传文件重命名,获取添加后缀名
    filename: function(req, file, cb) {
        var fileFormat = (file.originalname).split(".");
        //给图片加上时间戳格式防止重名名
        //好比把 abc.jpg图片切割为数组[abc,jpg],而后用数组长度-1来获取后缀名
        cb(null, file.fieldname + '-' + Date.now() + "." + fileFormat[fileFormat.length - 1]);
    }
});
var upload = multer({
    storage: storage
});
  1. 接受文件
    upload.single('xxx'),xxx与表单中的name属性的值对应
    这里虽然用到post请求,但实际上不须要bodyParser模块处理
app.post('/upload-single', upload.single('logo'), function(req, res, next) {
    console.log(req.file)
    console.log('文件类型:%s', req.file.mimetype);
    console.log('原始文件名:%s', req.file.originalname);
    console.log((req.file.originalname).split("."))
    console.log('文件大小:%s', req.file.size);
    console.log('文件保存路径:%s', req.file.path);
    res.send({
        ret_code: '0'
    });
});
  1. 多图上传
    多图上传只要更改一下地方,前端往file输入框加多一个multiple="multiple"属性值,此时就能够在选图的时候多选了,固然也能够并列多个file输入框(不推荐多个上传图片输入框),这样体验会很差
<input type="file" name="logo" multiple="multiple" />

后端也须要相应的改变

app.post('/upload-single', upload.single('logo'), function(req, res, next) {
//upload.single('logo')变为upload.array('logo', 2),数字表明能够接受多少张图片
app.post('/upload-single', upload.array('logo', 2), function(req, res, next) {

若是不想有图片数量上传限制,咱们能够用upload.any()方法

app.post('/upload-single', upload.any(), function(req, res, next) { 
    res.append("Access-Control-Allow-Origin","*");
    res.send({
        wscats_code: '0'
    });
});
  1. 前端部分
  • formData表单提交
<form action="http://localhost:3000/upload-single" method="post" enctype="multipart/form-data">
    <h2>单图上传</h2>
    <input type="file" name="logo">
    <input type="submit" value="提交">
</form>
  • formData表单+ajax提交
<form id="uploadForm">
    <p>指定文件名: <input type="text" name="filename" value="" /></p>
    <p>上传文件: <input type="file" name="logo" /></ p>
    <input type="button" value="上传" onclick="doUpload()" />
</form>

FormData对象,是可使用一系列的键值对来模拟一个完整的表单,而后使用XMLHttpRequest发送这个"表单"

注意点

  • processData设置为false。由于data值是FormData对象,不须要对数据作处理。
  • <form>标签添加enctype="multipart/form-data"属性。
  • cache设置为false,上传文件不须要缓存。
  • contentType设置为false。由于是由<form>表单构造的FormData对象,且已经声明了属性enctype="multipart/form-data",因此这里设置为false

上传后,服务器端代码须要使用从查询参数名为logo获取文件输入流对象,由于<input>中声明的是name="logo"

function doUpload() {
    $.ajax({
        url: 'http://localhost:3000/upload-single',
        type: 'POST',
        cache: false, //没必要须
        data: new FormData($('#uploadForm')[0]),
        processData: false,//必须
        contentType: false,//必须
        success: function(data) {
            console.log(data)
        }
    })
}

参考文档
Github MyDemo
Github Multer
MDN FormData对象的使用

自动化测试 mocha

Mocha('摩卡'),诞生于2011年,如今比较流行的JavaScript测试框架之一,能够运行于Node环境和浏览器环境

测试框架:能够运行测试的工具。经过他,能够为JavaScript应用 添加测试,从而保证代码质量

参考文档
mochajs
mocha中文文档

安装配置

使用npm 全局安装

$ npm install --global mocha

项目依赖 局部安装

$ npm isntall mocha

基本语法

assert 断言

  • 断言库:chai
  • should 风格断言
  • expect 风格断言

全局安装chai

npm install chai -g 

案例使用

递归执行

$ mocha test --recursive