笔记视频

node.js介绍

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 来命名,最好不要使用中文。

模块系统

  • 核心模块
    • fs 文件操作模块
    • http 网络服务构建模块
    • os 操作系统信息模块
    • path 路径处理模块
  • 用户自定义模块
    • 通过require(‘路径’) 加载模块,例如:require(‘./b.js’), 相对路径 ./ 不能省略
    • 可以省略文件后缀名,默认 .js
  • 第三方模块
    • art-template
    • 必须通过npm来下载才可以使用

CommonJS模块规范

  • 模块作用域
  • 使用 require 方法来加载模块
  • 使用 exports 接口对象来导出模板中的成员

加载(require)

语法:

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

作用:

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

导出(exports)

  • Node 中是模块作用域,默认文件中所有的成员只在当前模块有效
  • 对于希望可以被其他模块访问到的成员,我们需要把这些公开的成员都挂载到 exports 接口对象中就可以了

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

exports.a = 123; 
exports.b = 'hello'; 
exports.c = function(){ 
    console.log('bbb') 
}; 
exports.d = { 
    foo:"bar" 
};

导出单个成员:

module.exports = 'hello'; 
// 后者会覆盖前者 
module.exports = function add(x,y) { 
    return x+y; 
}

也可以通过以下方法来导出多个成员:

module.exports = { 
    foo = 'hello', 
    add:function(){ 
        return x+y; 
    } 
};

原理

在node中,每个模块内部都有一个自已的 module 对象

在 module 对象中,有一个成员 exports 也是一个对象

module = { 
     exports = { 
     } 
}

当给exports重新赋值后,exports!= module.exports.
最终return的是module.exports,无论exports中的成员是什么都没用。

exports是 module.exports 的一个引用:

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

真正去使用的时候:

  • 导出单个成员:module.exports = xxx; // 获取的是
  • 导出多个成员:module.exports.xxx 或者 modeule.exports = {};

exports == module.exports 用来重新建立关系

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 文件不存在或者 main 指定的入口模块也没有
      • 则 node 会默认找该目录下的 Index.js
      • 如果以上任何一个条件都不成立,则会进入上一级目录按以上规则查找
      • 直到磁盘根目录,最后报错

        npm

node package manage(node包管理器)

npm 官网

npm 命令行工具

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

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

升级npm: 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 速度慢

方法1

淘宝的开发团队把npm在国内做了一个 镜像

安装淘宝的cnpm:

npm install --global cnpm

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

#在任意目录执行都可以 
#--global表示安装到全局,而非当前目录 
#--global不能省略,否则不管用 
npm install --global cnpm

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

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

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

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

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

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

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

使用 码云(gitee)作为中转站

  1. 从 GitHub / GitLab 导入仓库

1.png

  1. 复制 GitHub 地址

2.png

  1. 导入 gitee 仓库

3.png

  1. 下载

4.png

方法3

在 GitHub 地址后面添加 cnpmjs.org

例如: https://github.com/jackfrued/Python-100-Days.git

修改后: https://github.com.cnpmjs.org/jackfrued/Python-100-Days.git

然后 clone 下载

方法4

适合下载单个文件

使用 jsdelivr提供的免费 cdn 加速

例如: https://github.com/stevenjoezhang/live2d-widget/blob/master/README.md

修改后: https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@master/README.md

package.json

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

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

dependencies 选项,可以用来帮助我们保存第三方包的依赖信息。

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

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

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 的另外一个作用就是锁定版本号,防止自动升级

核心模块使用

文件的读写

//  1.使用fs核心模块 
var fs = require('fs'); 
// 2.读取文件 
//     第一个参数就是要读取的文件路径 
//     第二个参数是一个回调函数 
//     成功 
//       data 数据 
//       err null 
//     失败 
//       data undefined 没有数据 
//       err 错误对象 
fs.readFile('./data/a.txt',function(err,data){ 
   if(err){ 
        console.log('文件读取失败'); 
   }else{ 
        // 文件中存储的都是二进制数据 
        // 通过toString()方法转换为我们能认识的字符 
        // 可以结合服务器通过读取文件来实现发送页面 
         console.log(data.toString()); 
    } 
}) 
// 3.将数据写入文件 
//     第一个参数:文件路径 
//     第二个参数:文件内容 
//     第三个参数:回调函数 
fs.writeFile('./data/a.txt','写入文件的内容',function(err){ 
   if(err){ 
        console.log('写入失败'); 
   } 
    else{ 
         console.log('写入成功'); 
    } 
})

HTTP服务

  • 提供服务:对数据服务
  • 发送请求
  • 接收请求
  • 处理请求
  • 反馈(发送响应)
  • 当客户端请求过来,就会自动触发服务器的request请求事件,然后执行第二个参数:回调处理函数
// 1.加载http核心模块 
var http = require('http'); 
var fs = require('fs'); 
// 2.使用http.createServer()创建一个web服务器 
var server = http.createServer(); 
// 读取文件时的根目录 
var wwwDir = 'D:/app/www' 
// 3.触发服务器的request请求事件 
//     request 请求事件处理函数,需要接收两个参数: 
//         request 请求对象 
//             请求对象可以用来获取客户端的一些请求信息,例如请求地址 
//         response 响应对象 
//             响应对象可以用来给客户端发送响应消息 
server.on('request', function (req, res) { 
    // 获取当前 url(端口号后面的路径) 
    var url = req.url 
    // 默认为 /index.html 
    var filePath = '/index.html' 
    // 如果不是默认 url,则获取当前用户输入的 url 
    if (url !== '/') { 
        filePath = url 
    } 
    // 读取文件 
    fs.readFile(wwwDir + filePath, function (err, data) { 
        if (err) { 
            // 响应内容只能是字符串或二进制数据 
            // 告诉浏览器发送的是什么类型的内容 
            // 图片不需要指定编码 
            response.setHeader('Content-Type', 'text/html; charset=utf-8') 
            return res.end('404 NOT Found') 
        } 
        res.end(data) 
    }) 
}) 
// 4.绑定端口号,启动服务 
server.listen(3000, function () { 
    console.log('running') 
})

path路径操作模块

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

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

模版使用

浏览器中使用模板

安装

npm install --save art-template

使用

<script src="./node_modules/art-template/lib/template-web.js"></script> 
<script type="text/template" id="tpl"> 
     大家好,我叫:{{ name }} 
     我今年 {{ age }} 岁了 
     我来自 {{ province }} 
     我喜欢:{{ each hobbies }} {{ $value }} {{ /each }} 
</script> 
<script> 
     var ret = template('tpl', { 
          name: 'Jack', 
          age: 18, 
          province: '北京市', 
          hobbies: [ 
               '写代码', 
               '唱歌', 
               '打游戏' 
          ] 
     }) 
     // ret 只是渲染后的内容,需要写进页面才会显示 
     console.log(ret); 
</script>

node中使用模板

安装

npm install --save art-template

使用

// 引用模板模块 
var template = require('art-template') 
var fs = require('fs') 
fs.readFile('./tpl.html', function (err, data) { 
    if (err) { 
        console.log('读取失败') 
    } 
    // 转换为字符串 
    var data = data.toString() 
    // 模板渲染 
    var ret = template.render(data, { 
        name: 'Jack', 
        age: 18, 
        province: '北京市', 
        hobbies: [ 
            '写代码', 
            '唱歌', 
            '打游戏' 
        ] 
    }) 

    console.log(ret) 
})

tpl.html

<p>大家好,我叫:{{ name }}</p> 
<p>我今年 {{ age }} 岁了</p> 
<p>我来自 {{ province }}</p> 
<p>我喜欢:{{ each hobbies }} {{ $value }} {{ /each }}</p>

Apche目录案例

// 1.加载http核心模块 
var http = require('http'); 
var fs = require('fs'); 
// 引入模板模块 
var template = require('art-template') 
// 2.使用http.createServer()创建一个web服务器 
var server = http.createServer(); 
// 读取文件时的根目录 
var wwwDir = 'D:/app/www' 
// 3.触发服务器的request请求事件 
server.on('request', function (req, res) { 
    // 读取模版页面 
    fs.readFile('./template-Apache.html', function (err, data) { 
        if (err) { 
            return res.end('404 NOt Fount.') 
        } 
        // 读取要显示的目录 
        fs.readdir(wwwDir, function (err, files) { 
            if (err) { 
                return res.end('Can not find www dir.') 
            } 
            // 使用模板引擎 
            var htmlStr = template.render(data.toString(), { 
                title: '哈哈', 
                files: files 
            }) 
            // 发送解析过后的响应数据 
            res.end(htmlStr) 
        }) 
    }) 
}) 
// 4.绑定端口号,启动服务 
server.listen(3000, function () { 
    console.log('running') 
})

template-Apache.html

<tbody id="tbody"> 
  {{each files}} 
    <tr> 
      <td data-value="img/"><a class="icon dir" href="/D:/app/www/img/">{{$value}}</a></td> 
      <td class="detailsColumn" data-value="0"></td> 
      <td class="detailsColumn" data-value="1592877672">2020/6/23 上午10:01:12</td> 
    </tr> 
  {{/each}} 
</tbody>

留言板案例

var http = require('http'); 
var fs = require('fs') 
var url = require('url') 
var template = require('art-template') 

// 用来存放评论数据 
var comments = [ 
    { 
        name: '张三1', 
        message: '今天天气真好', 
        dateTime: '2020-6-24' 
    }, 
    { 
        name: '张三2', 
        message: '今天天气真不错', 
        dateTime: '2020-6-24' 
    }, 
    { 
        name: '张三3', 
        message: '今天天气真不错', 
        dateTime: '2020-6-24' 
    }, 
    { 
        name: '张三4', 
        message: '今天天气真不错', 
        dateTime: '2020-6-24' 
    }, 
    { 
        name: '张三5', 
        message: '今天天气真不错', 
        dateTime: '2020-6-24' 
    } 
] 

http 
    .createServer(function (req, res) { 
        var parseObj = url.parse(req.url, true) 
        // 获取路径名 
        var parsename = parseObj.pathname 
        if (parsename === '/') { 
            fs.readFile('./views/index.html', function (err, data) { 
                if (err) { 
                    return res.end('404 Not Found.') 
                } 
                // 模板渲染 
                var data = template.render(data.toString(), { 
                    comments: comments 
                }) 
                res.end(data) 
            }) 
        } else if (parsename === '/post') { 
            fs.readFile('./views/post.html', function (err, data) { 
                if (err) { 
                    return res.end('404 Not Found.') 
                } 
                res.end(data) 
            }) 
            // 表单属性 action="/pinglun" 
        } else if (parsename === '/pinglun') { 
            // 获取 get 请求数据 
            var comment = parseObj.query 
            comment.dateTime = '2020-7-11 17:27:14' 
            // 添加到评论数组对象中 
            comments.unshift(comment) 
            // 重定向 
            // 状态码设置为 301 永久重定向 
            // 状态码设置为 302 临时重定向 
            // 在响应头中通过 location 告诉客户端往哪儿重定向 
            res.statusCode = 302 
            res.setHeader('Location', '/') 
            // 结束响应 
            res.end() 
            // 开放目录 
        } else if (parsename.indexOf('/public/') == 0) { 
            fs.readFile('.' + parsename, function (err, data) { 
                if (err) { 
                    return res.end('404 Not Found.') 
                } 
                res.end(data) 
            }) 
        } else { 
            fs.readFile('./views/404.html', function (err, data) { 
                if (err) { 
                    return res.end('404 Not Found.'); 
                } 
                res.end(data); 
            }) 
        } 
    }) 
    // 4.绑定端口号,启动服务 
    .listen(1314, function () { 
        console.log('服务器启动成,通过localhost:3000/来进行访问') 
    })

index.html

<ul class="list-group"> 
  <!-- 遍历对象数组并渲染 --> 
  {{each comments}} 
    <li class="list-group-item">{{ $value.name }}说:{{ $value.message }}</li> 
  {{/each}} 
</ul>

开放目录之后页面中的引入文件(如:link 标签和 script 标签)将不需要用相对路径引用,例如:
<link rel="stylesheet" href="/public/lib/bootstrap/css/bootstrap.min.css">

/ 表示的是服务器根目录

Node中的其它成员

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

  • __dirname ,可以用来 动态 获取当前文件模块所属目录的绝对路径(不包含文件名)
  • __filename ,可以用来 动态 获取当前文件的绝对路径(包含文件名)
  • __dirnamefilename 是不受执行 node 命令所属路径影响的

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

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

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

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

模块中的路径标识和这里的路径没关系,不受 node 命令执行所处目录影响(相对于文件模块)

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(快速的)

http://expressjs.com

其中主要封装的是http

// 0. 安装 
// 1. 引包 
var express = require('express'); 
// 2 创建服务器应用程序 
//   也就是原来的http.createServer(); 
var app = express(); 

// 公开指定目录 
// 这样就能通过 /public/xx 的方式来访问 public 目录中的所有资源 
// 在 Express中开放资源就是一个 API 的事 
app.use('/public/',express.static('./public/')); 

//模板引擎在 Express 中开放模板也是一个 API 的事 

// 当服务器收到 get 请求 / 的时候,执行回调处理函数 
// 使用 res.send 就不需要考虑编码问题 
// 在 Express 中可以直接使用 req.query 来获取查询字符串参数 
app.get('/',function(req,res){ 
res.send('hello express'); 
}) 

app.get('/about',function(req,res){ 
res.send('你好,我是Express!'); 
}) 

// 相当于 server.listen 
app.listen(3000,function(){ 
console.log('app is runing at port 3000'); 
})

起步

安装

npm install express --save

hello world

var express = require('express'); 
var app = express(); 
app.get('/',function(req,res){ 

res.send('hello world'); 
}) 
app.listen(3000,function(){ 
console.log('express app is runing...'); 
})

基本路由

路由

  • 请求方法
  • 请求路径
  • 请求处理函数

get

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

post

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

静态服务

// 开放目录 
app.use('/public/',express.static('./public/')); 
// 当省略第一个参数的时候,则 url 可以省略 /public 来访问 
app.use(express.static('./public/')); 
// a 相当于 public 的别名,通过 /a/文件名 来访问 public 目录下的文件 
app.use('/a/',express.static('./public/'));

使用art-template模板引擎

  • art-template官方中文文档
  • 在node中,有很多第三方模板引擎都可以使用,不是只有 art-template
    • 还有ejs,jade(pug),handlebars,nunjucks

安装

c npm install --save art-template 
c npm install --save express-art-template 
// 同时安装两个时用空格隔开 
c npm i --save art-template express-art-template

配置

// 表示当渲染 .art 文件时,使用 art-template 模板引擎,使用 .art 只是为了更好辨别 
// express-art-template 是专门用来在 express 中使用的 art-template 
// 因为 express-art-template 依赖了 art-template ,所以不加载也必须安装 
// app.engine('art', require('express-art-template')); 
app.engine('html', require('express-art-template'))

使用

app.get('/',function(req,res){ 
// 第一个参数不能写路径,默认会去 views 目录找 index.html 
res.render('admin/index.html',{ 
title:'hello world' 
}); 
})

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

// 第一个参数 views千万不要写错 
app.set('views', 默认路径);

日期格式化

https://blog.csdn.net/qincidong/article/details/82252298 https://www.cnblogs.com/HJ412/p/11495588.html

获取表单请求体数据

获取GET请求数据

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

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

获取POST请求数据

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

安装

c npm install --save body-parser

配置

http://expressjs.com/en/resources/middleware/body-parser.html

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

var express = require('express') 
// 引包 
var bodyParser = require('body-parser') 
var app = express() 
// 配置 body-parser 
// 只要加入这个配置,则在 req 请求对象上会多出来一个属性:body 
// 也就是说可以直接通过 req.body 来获取表单 post 请求体数据 
// 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') 
// 可以通过req.body来获取表单请求数据 
res.end(JSON.stringify(req.body, null, 2)) 
})

session

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

安装

c npm i express-session  --save

配置

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

使用

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

提示:

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

CRUD 案例

实现步骤

  • 处理模板
  • 配置静态开放资源
  • 配置模板引擎
  • 简单的路由,/studens渲染静态页出来
  • 路由设计
  • 提取路由模块
  • 由于接下来的一系列业务操作都需要处理文件数据,所以我们需要封装Student.js’
  • 先写好 student.js 文件结构
    • 查询所有学生列表的API
    • findById
    • save
    • updateById
    • deleteById
  • 实现具体功能
    • 通过路由收到请求
    • 接受请求中的参数(get,post)
      • req.query
      • req.body
    • 调用数据操作API处理数据
    • 根据操作结果给客户端发送响应
  • 业务功能顺序
    • 列表
    • 添加
    • 编辑
    • 删除

模块化

模块如何划分:

  • 模块职责要单一

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:

注:当封闭了学生信息操作模块时,以下代码需要进行整改

/** 
* router.js路由模块 
* 职责: 
*      处理路由 
*      根据不同的请求方法+请求路径设置具体的请求函数 
* 模块职责要单一,我们划分模块的目的就是增强代码的可维护性,提升开发效率 
*/ 
var   fs   =   require('fs') 
var Students = require('./students') 
//   Express   提供了一种专门用来 包装 路由的 方式 
var   express   =   require('express') 
//   1 .  创建一个路由容器 
var   router   =   express.Router() 
//   2 .  把路由都挂载到  router  路由容器中 
/** 
 * 渲染学生列表页面 
 */ 
router.get('/students', function(req, res) { 
     //   readFile   第二个参数可选,传入   utf8  会 按照   utf8   转 换 
     //   也 能 通过   data.toString () 的方式 转换 
fs.readFile('./db.json', 'utf8', function(err, data) { 
if (err) { 
return res.status(500).send('Server error.') 
} 
// 读取到的文件数据是   string   类型的数据 
// 需要转换成对象 
var students = JSON.parse(data).students; 
res.render('index.html', { 
students:students 
}) 
}) 
}); 
/** 
 * 渲染学生添加页面 
 */ 
router.get('/students/new',function(req,res){ 
res.render('new.html') 
}); 
/** 
 * 处理添加学生 
 */ 
router. pos t('/students/ n e w ',   function   (req,   res)   { 

}); 
/** 
 * 渲染编辑学生页面 
 */ 
router. ge t('/students/edit',   function   (req,   res)   { 

}); 
/** 
 * 处理编辑学生 
 */ 
router. pos t('/students/e di t',   function   (req,   res)   { 

}); 
/** 
 * 处理删除学生 
 */ 
router.get('/students/delete', function (req, res) { 

}); 
// 3 把   router   导出 
module.exports = router;

app.js:

/** 
 * app.js 入口模块 
 * 职责: 
 *      创建服务 
 *      做一些服务相关配置 
 *          模板引擎 
 *          body-parser 解析表单 Post 请求体 
 *          提供静态资源服务 
 *      挂载路由 
 *      监听端口启动服务 
 */ 
var  express = require('express') 
var  router   =   require('./router .js ') 
var bodyParser = require('body-parser') 

var app = express() 
// 开放目录 
app.use(' / node_modules / ', exp r ess.static('./n o de_mod u les/')) 
app.use('/public/', express.s t atic('./public/')) 
// 配置模板引擎和 body-parser 一定要在 app.user(router) 挂载路由之前 
app. e ngine('html',  r equire ( 'express-art-template')) 
app.use(bodyParser.urlencoded({ extended: false })) 
app.use(bodyParser.json()) 
//  router(app) 
//  把路由容器挂载到   app   服务中 
app.use(router) 

app.listen(1314, function (req, res) { 
    console.log('running 1314...') 
})

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

es6中的find和findIndex:

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

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

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

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

/** 
* student.js 
* 数据操作文件模块 
* 职责:操作文件中的数据,只处理数据,不关心业务 
*/ 
var fs = require('fs') 
/** 
* 获取所有学生列表 
* return [] 
*/ 
exports.find = function(){ 

} 
/** 
 * 根据 id 获取学生信息对象(单个学生) 
 */ 
exports.findById = function (id, callback) { 
} 
/** 
* 获取添加保存学生 
*/ 
exports.save = function(){ 

} 
/** 
* 更新学生 
*/ 
exports.updat aById = function(){ 

} 
/** 
* 删除学生 
*/ 
exports.delete ById  = function(){ 

}

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

注意:

模板页:

<!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页面:
<div id=""> 
<h1>公共的头部</h1> 
</div>
footer页面:
<div id=""> 
<h1>公共的底部</h1> 
</div>

模板页的使用:

<!-- 继承(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 }}

MongoDB

关系型和非关系型数据库

关系型数据库。

表就是关系,或者说表与表之间存在关系

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

非关系型数据库

  • 非关系型数据库非常的灵活
  • 有的关系型数据库就是 key-value(键值) 对儿
  • 但 MongDB 是长得最像关系型数据库的非关系型数据库
    • 数据库 -》 数据库
    • 数据表 -》 集合(数组)
    • 表记录 -》文档(对象)
  • MongoDB 不需要设计表结构
  • 可以任意的往里面存数据,没有结构性这么一说

基本结构

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

{ 
qq:{ 
user:[ 
{},{},{}... 
] 
    ... 
     } 
    .... 
}

安装

下载安装

下载地址

配置环境变量

和配置其它环境变量一样,配置 bin 目录

最后输入 mongod --version 测试是否安装成功(注意是 mongod)

教程和报错

启动和关闭数据库

启动

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

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

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

停止

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

连接和退出数据库

连接

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

退出

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

基本命令

  • show dbs
    • 查看所有数据库列表
  • db
    • 查看当前连接的数据库
  • use 数据库名称
    • 切换到指定的数据库(如果没有会新建)
    • 只有插入数据后才会真正创建数据库
  • db.集合名.inserOne(对象)
    • 插入数据
  • show collections
    • 查看当前数据库下的所有集合(表)
  • db.集合名.find()
    • 查看集合中的详细信息

在 Node 中操作 MongoDB 数据库

使用官方的 MongoDB 包

官方的比较原始,麻烦,所以不使用它

https://mongodb.github.io/node-mongodb-native https://github.com/mongodb/node-mongodb-native

使用 mongoose 包

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

安装

cnpm i mongoose --save

hello word

const mongoose = require('mongoose'); 
// 连接 mongoDB 数据库 
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true }); 
// 创建一个模型(设计数据库) 
const Cat = mongoose.model('Cat', { name: String }); 
// 实例化一个 Cat(会生成一个小写复数的集合) 
const kitty = new Cat({ name: 'Zildjian' }); 
// 持久化保存 Kitty 实例 
kitty.save().then(() => console.log('meow'));

设计 Scheme 发布 Model

des:相当于创建表结构

注意:设计集合结构时,对象名不能有大写(例如 userName )

// 1.引包 
var mongoose = require('mongoose'); 
// 拿到  S chema   图表 
var Schema = mongoose.Schema; 
// 2.连接数据库 
// 指定连接数据库后不需要存在,当你插入第一条数据库后会自动创建数据库 
mongoose.connect('mongodb://localhost :27017 /test' , { useNewUrlParser: true, useUnifiedTopology: true } ); 
// 3.设计集合结构(表结构) 
// 用户表 
var userSchema = new Schema({ 
username: { 
type: String, 
require d : true //添加约束,保证数据的完整性,让数据按规矩统一 
}, 
password: { 
type: String, 
require d : true 
}, 
email: { 
type: String 
} 
}); 
// 4.将文档结构发布为模型 
//   第一个参数:大写单数 集合 名 (最终 会 转换为 小写复数集合名 ,表 名 ) 
//   第二个参数:架构  Schema(集合结构, 表结构) 
//   返回值:模型构造函数 
var User = mongoose.model('User', userSchema);

添加(增)

注 :版本不同可能 API 不同

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

删除(删)

注 :版本不同可能 API 不同

根据条件删除所有:

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

根据条件删除一个:

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

根据id 删除一个:

Model .(id,[options],[callback]);

更新(改)

注 :版本不同可能 API 不同

更新所有:

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

根据指定条件更新一个:

Mod e l . updateOn e([conditions],   [update],   [options],[callback]);

根据 id 更新一个:

// 更新    根据   id   来修改表数据 
User.findByIdAndUpdate('5 f19 4f 80f a4 659521 8 e 5 7110 ', { 
 p a ssword : ' 123 ' 
}, function(err, ret) { 
if (err) { 
console.log('更新失败'); 
} else { 
console.log('更新成功'); 
} 
});

查询(查)

注 :版本不同可能 API 不同

查询所有:

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

条件查询所有:

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

条件查询单个:

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

通过 id 查询单个:

Model.findById(id,[options],[callback]);

使用Node操作MySQL数据库

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

安装:

c npm i  mysql --save
// 引入mysql包 
var mysql      = require('mysql'); 
//  1.  创建连接 
var connection = mysql.createConnection({ 
host     : 'localhost',    //   本机 
user     : 'me',      //   账号root 
password : 'secret',      //   密码12345 
database : 'my_db'      //   数据库名 
}); 

//  2.  连接数据库 
connection.connect(); 

//  3.  执行数据操作 
// 第一个参数写 sql 语句 
connection.query('SELECT * FROM `users`', function (error, results, fields) { 
if (error) throw error;//   抛出异常阻止代码往下执行 
// 没有异常打印输出结果 
console.log('The solution is: ',results); 
}); 
//  4.  关闭连接 
connection.end();

异步编程

回调函数

不成立的情况

function add(x,y){ 
    console.log(1); 
  // setTimeout 是个异步 API 
  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 2

使用回调函数

也可以通过定义全局变量来获取异部数据,但那样函数封装就没有意义了

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

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

注意

凡是需要得到一个函数内部异步操作的结果

  • setTimeout
  • readFile
  • writeFile
  • ajax
  • readdir

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

ajax

基于原生 XMLHttpRequest 封装 get 方法:

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

Promise

参考文档: https://es6.ruanyifeng.com/#docs/promise

callback hell(回调地狱)

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

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

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

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

5.png

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

6.png

  • Promise:承诺,保证
  • Promise 是一个构造函数
  • Pending、Resolved、Rejected 是容器 Promise 的状态
  • Promise 本身不是异步的,但往往都是内部封装一个异步任务

Promise 基本语法

var fs = require('fs');
// 在EcmaScript 6中新增了一个API Promise 
// Promise 是一个构造函数 
// 1 . 创建 Promise 容器 
//        resolve: 解决 
//        reject:失败 
var p1 = new Promise(function (resolve, reject) {
  fs.readFile('./ data/ a.txt', 'utf8', function (err, data) {
    if (err) {
      // console.log(err); 
      // 把容器的 Pending 状态变为 rejected 
      // 调用的 reject 方法实际上就是 then 方法的第二个参数函数 
      reject(err);
    } else {
      // console.log(data); 
      // 把容器的 Pending 状态变为 resolve 
      // 调用的 resolve 方法实际上就是 then 方法传递的那个 function 
      resolve(data);
    }
  });
});
// 当   p1   成功了,然后(then)做指定的操作 
// then   方法接收的   function   就是容器中的   resolve   函数 
p1.
  then(function (data) {
    console.log(data);
  }, function (err) {
    cconsole.log('读取文件失败了', err);
  })

图解

7.png
8.png

封装 Promise 的 readFile

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)
    return pReadFile('./data/c.txt')
  })
  .then(function (data) {
    console.log(data)
  })

封装 Promise 的 ajax

var data = {}
function pGet(url, callback) {
  return new Promise(function (resolve, reject) {
    var oReq = new XMLHttpRequest();
    oReq.onload = function () {
      callback && callback(JSON.parse(oReq.responseText))
      resolve(JSON.parse(oReq.responseText))
    }
    oReq.onerror(function (err) {
      reject(err)
    })
    oReq.open("get", url, true)
    oReq.send()
  })
}

mongoose 中的 Promise

  • 凡是操作数据库都是异步的
  • mongoose 所有的 API 都支持 Promise
// 查询所有 
User.find() 
  .then(function(data){
  console.log(data) 
})

注册:

User.findOne({ username: 'admin' }, function (user) {
  if (user) {
    console.log('用户已存在')
  } else {
    new User({
      username: 'aaa',
      password: '123',
      email: 'fffff'
    }).save(function () {
      console.log('注册成功');
    })
  }
})
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('注册成功');
  })

其他

修改完代码自动重启

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

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

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

安装完毕之后使用:

node app.js 
#使用nodemon 
nodemon app.js

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

封装异步API

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

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

数组常用的遍历方法

  • forEach
  • find
  • findIndex
  • every
  • some
  • includes
  • indexOf
  • map
  • reduce

加密密码

MD5

https://github.com/blueimp/JavaScript-MD5

安装

cnpm i blueimp-md5 --save
var md5 = require('blueimp-md5') 
// 重复加密安全性更高 
body.password = md5(md5(body.password))

规范

当一行代码是以:

(
[
`
开头的时候,则在前面补上一个分号以避免一些语法解析错误

在控制台使用 node 可以用来调试代码(API)

项目案例

目录结构

. 
app.js              项目的入口文件 
controllers 
models              存储使用   mongoose   设计的数据模型 
node_modules      第三方包 
package.json      包描述文件 
package-lock.json    第三方包版本锁定文件(npm5之后才有) 
public              公共静态资源 
README.md           项目说明文档 
routes                          
routes.js                                 
views              存储视图目录

模板页

路由设计

路径 方法 get参数 post参数 是否需要登录 备注
/ GET 渲染首页
/register GET 渲染注册页
/register 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

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

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

var http = require('http'); 
var url = require('url'); 
// 模拟第三方包(中间件) 
var cookie = require('./ middleware /cookie'); 
var query = require('./ middl e wa re/query'); 
var postBody = require('./ middl e wa re/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...'); 
});

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

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.'); 
});

中间件的分类

应用程序级别的中间件

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

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

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

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

路由级别中间件

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

get:

app.get('/',function(req,res){ 
res.send(' H e llo World! '); 
});

post:

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

put:

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

delete:

app.delete('/delete',function(req,res){ 
res.send(' Got a DELETE request at /user '); 
});

执行顺序

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() 方法调用下一个方法的时候,也需要匹配(不一定是调用紧挨着的哪一个)
    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 (req, res, next) {
    console.log('/');
});
app.post('/a', function (req, res, next) {
    console.log('/a');
});

app.listen(3000, function () {
    console.log('app is running at port 3000.');
});

错误处理中间件

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

配置使用404中间件:

// 放在最后 
app.use(function(req,   res){ 
res.render('404.html'); 
});

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

app.get('/a', function(req, res, next) { 
fs.readFile('.a/bc', funtion() { 
if (err) { 
// 当调用   next()   传参后,则直接进入到全局错误处理中间件方法中 
// 当发生全局错误的时候,我们可以调用next传递错误对象 
// 然后被全局错误处理中间件匹配到并进行处理 
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

报错

npm 安装报错

  1. npm ERR! Refusing to install package with name “xxxx” under a packagexxxx

解决方法

检查package.json下的name字段是不是项目名字和你安装的那个包名字相同了,如果一样就会报错,把项目名字改成和你安装不冲突的名字。
9.png

Mongodb 报错

  1. (node:6608) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.

(Use node --trace-deprecation ... to show where the warning was created)

(node:6608) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.ion. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.

10.png

解决方法

虽然保存成功了但是 current URL string parser is deprecated, and will be removed in a future version.

在 mongoose.connect(‘mongodb://localhost:27017/itcast’); 中添加 { useNewUrlParser: true, useUnifiedTopology: true }

mongoose.connect('mongodb://localhost:27017/itcast', { useNewUrlParser: true, useUnifiedTopology: true });