npm

介绍

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

网站

npmjs.com 网站 是用来搜索npm包的

npm命令行工具

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

升级npm(自己升级自己):

1
npm install --global npm

常用命令

  • npm init(生成package.json说明书文件)

  • npm init -y(可以跳过向导,快速生成)

  • npm install

    • 一次性把dependencies选项中的依赖项全部安装
    • 简写(npm i)
  • npm install包名

    • 只下载
    • 简写(npm i 包名)
  • npm install –save 包名

    • 下载并且保存依赖项(package.json文件中的dependencies选项)
    • 简写(npm i 包名)
    • –save 自动生成说明说文件信息package.json
    • 文件信息自动添加至package.json
    • 若删除掉某些项目使用的模块但有保留package.json,则可在命令行(当前项目文件夹下)执行 npm install 全部自动下回来
  • npm uninstall 包名

    • 只删除,如果有依赖项会依然保存
    • 简写(npm un 包名)
  • npm uninstall –save包名

    • 删除的同时也会把依赖信息全部删除
    • 简写(npm un 包名)
  • npm help

    • 查看使用帮助
  • npm 命令 –help

    • 查看具体命令的使用帮助(npm uninstall –help)

package.json

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

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

对于目前来讲,最有用的是dependencies选项,可以用来帮助我们保存第三方包的依赖信息。

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

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

package.json和package-lock.json

npm 5以前是不会有package-lock.json这个文件

npm5以后才加入这个文件

当你安装包的时候,npm都会生成或者更新package-lock.json这个文件

package.json用处

提升下载速度

  • npm5以后的版本安装都不要加–save参数,它会自动保存依赖信息

  • 当你安装包的时候,会自动创建或者更新package-lock.json文件

package-lock.json

1
2

这个文件会包含

node_modules

1
2
3
4
5
6
7
8
中所有包的信息(版本,下载地址。。。)

- 这样的话重新`npm install`的时候速度就可以提升


#### 锁定版本号

- 从文件来看,有一个

lock

1
2
3
4
5
6
7
8


称之为锁

- 这个`lock`使用来锁版本的
- 如果项目依赖了`1.1.1`版本
- 如果你重新install其实会下载最新版本,而不是`1.1.1`
- `package-lock.json`的另外一个作用就是锁定版本号,防止自动升级

Express

案例:express留言板、学生信息增删改

简介

Express 是一个简洁而灵活的 Node.JS Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

使用 Express 可以快速地搭建一个完整功能的网站。

Express 框架核心特性:

  • 可以设置中间件来响应 HTTP 请求。
  • 定义了路由表用于执行不同的 HTTP 请求动作。
  • 可以通过向模板传递参数来动态渲染 HTML 页面。

安装

1
2
3
npm init //生成说明文件
npm install --save express

简单使用

  1. 引包
  2. 创建服务器应用程序
  3. 接受请求,返回响应
  4. 发布服务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //引包
    var express = require('express')

    //创建服务
    var app = express()

    //开放文件路径
    app.use('/public', express.static('./public/'))

    //接收请求,返回响应
    app.get('/', function(req,res) {
    res.send('成功啦!')
    })

    //发布服务
    app.listen('3000', function() {
    console.log('server is running...')
    })

    ````

    ## art-template
    ### 安装
    ```Shell

    npm install --sava art-template
    npm install -save express-art-template

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

app.engine('html', require('express-art-template'))
app.set('/views', './views/') //可以更改模板引擎默认调用资源文件夹路径,注意书写
````
### 使用
```Javascript

res.render('index.html', {
students: students.students
})
````
## 初识(数据渲染)
```Javascript


var express = require('express')
var fs = require('fs')

var app = express()

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

app.engine('html', require('express-art-template'))
app.set('/views', './views/') //可以更改模板引擎默认调用资源文件夹路径,注意书写

app.get('/', function(req, res) {
fs.readFile('db.json', 'utf8', function(err, data) {
if (err) {
return console.log('数据有误')
}
// console.log(JSON.parse(data))
var students = JSON.parse(data)
res.render('index.html', {
students: students.students
})
})
})

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


express-session

官方文档:https://github.com/expressjs/session

安装

1
npm i express-session

配置

  • 该插件会为req请求对象添加一个成员:req.session默认是一个对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    //配置session中间件
    app.use(session({
    //自定义字符串来对sessionid进行加密处理,避免出现相同的sessionid
    secret: 'dong yi',
    resave: false,
    //无论是否使用了session,设置为true都会默认都会给予钥匙(sessionid)
    saveUninitialized: false
    cookie: { secure: true }
    }))
    ````

    ### 使用
    ```Javascript

    //写
    //添加Session数据
    //session就是一个对象
    req.session.foo = 'bar';

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

    //删
    req.session.foo = null;
    delete req.session.foo //推荐

其它

session:保存登录选项,🔺在内存中存储

cookie:保存不敏感数据

🔺异步编程

得到函数内部异步操作的结果

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

  • 在该情况下无法获得函数内异步操作的结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

    function get(a, b) {
    console.log(a + b)
    setTimeout(function() {
    console.log(2)
    ret = a + b
    return ret
    }, 6000);
    console.log(3)
    }

    console.log(get(20,30))
    ````
    结果:返回`undefined`

    + 若要获得该数据则只能通过回调函数
    ```Javascript

    function get(a, b, c) {
    console.log(a + b)
    setTimeout(function() {
    console.log(2)
    ret = a + b
    return c(ret)
    }, 6000);
    console.log(3)
    }

    // console.log(get(20,30))

    get(20, 30, function(a) {
    console.log(a)
    })

  • 注意:

    凡是需要得到一个函数内部异步操作的结果(setTimeout,readFile,writeFile,ajax,readdir)

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

回调地狱

啥是回调地狱

就是这幅图

1

为什么会有回调地狱

回调地狱的原因是,当人们试图以一种从上到下的视觉方式执行JavaScript的方式编写JavaScript时。期望第1行发生的任何事情都会在第2行的代码开始运行之前完成,但是,在JavaScript上,有时候这并没办法进行,比如,在你通过异步读取文件时,也就是用fs模块读取多个文件时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

var fs = require('fs')

fs.readFile('./a.txt', 'utf8', function(err, data) {
if (err) {
console.log(err)
}
console.log(data)
})

fs.readFile('./b.txt', 'utf8', function(err, data) {
if (err) {
console.log(err)
}
console.log(data)
})

fs.readFile('./c.txt', 'utf8', function(err, data) {
if (err) {
console.log(err)
}
console.log(data)
})



执行多次后,你会发现,有那么几次,也有可能好几次,看人品吧反正是,它是没有规则的读出来的(往往可能你的文件越大,读出来的时间会更久),也就是说,它并不会按照代码书写顺序去执行,这便是异步编程(如果试了没有,那就一直试,反正总会有的)。异步API导致了代码并不是按顺序执行的(可以读读这篇文章 https://www.jianshu.com/p/39adf6ab8ad1 ——然后嘞,就会有上面那种解决方法,但是你会发现,代码非常的丑(别人是这样说的,反正我不是太这么认为,甚至觉得有点好看),还有非常难维护(这点认同)。所以就出现了几种解决方法 —Promise

Promise
Promise:承诺,保证
Promise本身不是异步的,但往往都是内部封装一个异步任务
丢出一张图形容Promise函数,相当于一个容器(下图源于所学教程,pending(悬而未决的))

2

代码如下,较易维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

var fs = require('fs')

//resolved(解决(成功)),rejected(驳回(失败))
var p1 = new Promise(function(resolved, rejected) {
//文件编码!!!!!!
fs.readFile('./a.txt', 'utf8', function(err, data) {
if (err) {
rejected(err)
}
resolved(data)
})
})

var p2 = new Promise(function(resolved, rejected) {
fs.readFile('./b.txt', 'utf8', function(err, data) {
if (err) {
rejected(err)
}
resolved(data)
})
})

var p3 = new Promise(function(resolved, rejected) {
fs.readFile('c.txt', function(err, data) {
if (err) {
rejected(err)
}
resolved(data)
})
})

//链式编程,🔺Promise会默认将then中return的值实例成一个promise对象,所以可以调用then方法,实现链式调用
p1
.then(function(data) {
console.log(data)
return p2
})
.then(function(data) {
console.log(data)
return p3
}).then(function(data) {
console.log(data)
}, function(err) {
console.log(err)
})

then函数(ES6)说明:

3

封装Promise中的readFile方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

var fs = require('fs')

var pReadFile = function(filepath) {
return new Promise(function(resolved, rejected) {
//文件编码!!!!!!
fs.readFile(filepath, 'utf8', function(err, data) {
if (err) {
rejected(err)
}
resolved(data)
})
})
}

pReadFile("a.txt")
.then(function(data) {
console.log(data)
return pReadFile("b.txt")
})
.then(function(data) {
console.log(data)
return pReadFile("c.txt")
}).then(function(data) {
console.log(data)
}, function(err) {
console.log(err)
})

````
## Promise应用场景
### 解决客户端回调嵌套问题
当出现类似于表关联的数据时,这时候就会遇到嵌套问题,当嵌套的数据只有一两个个还好,如果出现三四个甚至五六个,这时候就会出现回调地狱的问题,这里使用promise解决

### 所需知识:
1. npm模块:json-server、http-server
2. 客户端模板引擎art-template
3. Ajax
4. jquery

### 步骤
1. 安装json-server和http-server以及其他必要模块

```Powershell

npm i -g http-server
npm i -g json-server
npm i jquery --save
npm i template --save
  1. 建立html页面

  2. 设计表单,人员信息与工作职业相关联,搭配模板字符串使用

    1
    2

    <form action="" id="user_form"></form>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    <script type="text/template" id="tpl">
    <div>
    <label for="">用户名</label>
    <input type="text" value="{{ user.username }}">
    </div>
    <div>
    <label for="">年龄</label>
    <input type="text" value="{{ user.age }}">
    </div>
    <div>
    <label for="">职业</label>
    <select name="" id="">
    {{ each jobs }} {{ if user.job === $value.id }}
    <option value="{{ $value.id }}" selected>{{ $value.name }}</option>
    {{ else }}
    <option value="{{ $value.id }}">{{ $value.name }}</option>
    {{ /if }} {{ /each }}
    </select>
    </div>
    </script>
  3. 引用相关模板字符串以及JQuery模块

    1
    2
    3

    <script src="node_modules/art-template/lib/template-web.js"></script>
    <script src="node_modules/jquery/dist/jquery.js"></script>
  4. 书写Ajax向服务器发起请求,并封装便于使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      	function get(url, callback) {
    var oReq = new XMLHttpRequest()
    oReq.onload = function () {
    oReq.responseText
    callback(oReq.responseText)
    }
    oReq.open("get", url, true)
    oReq.send()
    }

    ````

    6. 开启json-server服务,使用data.json文件(67步使用cmd)

    ```Powershell
    json-server data.json
  5. 将当前文件所处文件夹开放为服务器

1
http-server
  • 若要禁用缓存,则使用以下命令
1
http-server -c-1
  1. 若采用回调地狱类型
1
2
3
4
5
6
7
8
9
10
11

get("http://127.0.0.1:3000/users/1",function(userData){
get("http://127.0.0.1:3000/jobs",function(jobsData){
var htmlStr = template("tpl", {
user: JSON.parse(userData),
jobs: JSON.parse(jobsData)
})
console.log(htmlStr)
document.querySelector("#user_form").innerHTML = htmlStr
})
})
  1. 使用Jquery版的Promise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

//Jquery的Ajax自带promise
var data = {}
$.get("http://127.0.0.1:3000/users/2")
.then(function(user){
data.user = user
return $.get("http://127.0.0.1:3000/jobs")
})
.then(function(jobs){
data.jobs = jobs
// console.log(data)
var str = template("tpl",{
user: data.user,
jobs: data.jobs
})
document.querySelector('#user_form').innerHTML = str
})


````

10. 封装Promise版本的AJAX方法
```Js

function Rget(url,callback){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
// 当请求加载成功之后要调用指定的函数
xhr.onload = function () {
// 我现在需要得到这里的 xhr.responseText
resolve(JSON.parse(xhr.responseText))
callback && callback(JSON.parse(xhr.responseText))
}
xhr.onerror = function (err){
reject(err)
}
xhr.open("get", url, true)
xhr.send()
})
}
````
+ 使用
```Js

var data = {}
Rget("http://127.0.0.1:3000/users/2")
.then(function(user){
data.user = user
return Rget("http://127.0.0.1:3000/jobs")
})
.then(function(jobs){
data.jobs = jobs
var str = template("tpl", {
user: data.user,
jobs: data.jobs
})
document.querySelector("#user_form").innerHTML = str
})


````

### Promise操作数据库
26-PromisePromise操作数据库)

+ mongoose中所有的API都支持promise
🔺根据查询是否已存在该记录从而决定是否创建新记录

```Javascript

Cat.findOne({ name: "好啊" })
.then(function(cat){
if(cat){
console.log('该cat已存在')
} else{
return new Cat({"name" : "好啊", "age" : 16 }).save()
}
})
.then(function(data){
console.log(data)
})

````
### 注意:
1. 每次改完js或html文件后在浏览器需刷新多次
2. 每次改完json文件后需要重新启动json-server服务

### catch异常处理
在全部then之后添加`.catch(err => {})`即可对任何一个then处理过程抛出的异常进行捕获并中止代码继续执行

例如:读取文件并进行后续相关操作,若处理过程发生一个错误则传递给catch,后面所有的then就不再执行

这里要注意区分,如果是在`then`中自行处理`err`,则代码还是会继续往下执行,这是和`catch`不同的点



```Js

readFile('a.txt', 'utf8')
.then(data => {
console.log(data)
return readFile('a.txt', 'utf8')
})
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})

````


# 中间件
案例:案例5论坛

### 中间件的概念
> 参考文档:http://expressjs.com/en/guide/using-middleware.html

中间件:把很复杂的事情分割成单个,然后依次有条理的执行。就是一个中间处理环节,有输入,有输出。

说的通俗易懂点儿,中间件就是一个(从请求到响应调用的方法)方法。

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

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

## 中间件的分类:
### 应用程序级别的中间件
万能匹配(不关心任何请求路径和请求方法的中间件):

```Javascript

app.use(function(req,res,next){
console.log('Time',Date.now());
next();
});
````
关心请求路径和请求方法的中间件:

```Javascript

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

````
## 路由级别的中间件
严格匹配请求路径和请求方法的中间件

get:
```Javascript

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

post:

1
2
3
4

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

put:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

app.put('/user',function(req,res){
res.send('put');
});
````
delete:

```Javascript

app.delete('/delete',function(req,res){
res.send('delete');
});

````
## 错误处理中间件
```Javascript

app.use(function(err,req,res,next){
console.error(err,stack);
res.status(500).send('Something broke');
});
````
配置使用404中间件:(论坛案例)

```Javascript

app.use(function(req,res){
res.render('404.html');
});

配置全局错误处理中间件: (论坛案例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

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

`````

## 内置中间件
+ express.static(提供静态文件)
* http://expressjs.com/en/starter/static-files.html#serving-static-files-in-express

## 第三方中间件
>参考文档:http://expressjs.com/en/resources/middleware.html

+ body-parser
+ compression
+ cookie-parser
+ mogran
+ response-time
+ server-static
+ session

# art-template搭配dateformat模块
## 简介
art-template搭配dateFormat实现将时间戳格式化为你想要的日期格式

## 步骤
1. npm下载对应模块

```Bash
npm i --save dateformat
  1. 页面代码(注意art-template默认渲染的页面后缀名为art,time为渲染的时间数据,’yyyy-mm-dd’为你要定义的时间格式)
1
2
1
{{ dateFormat(time, 'yyyy-mm-dd')}}
  1. node引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

const template = require('art-template');
const path = require('path');
const dateFormat = require('dateformat');


````

4. 配置模板引擎

```Js

template.defaults.imports.dateFormat = dateFormat;
````

5. 渲染页面并打出在cmd控制台

```Js


const html = template('06.art', {
time: new Date()
});
console.log(html)
````

# 其它
## nodemon
### 作用
修改完代码自动重启

### 安装
```Shell

npm install --global nodemon

使用

1
2

nodemon 文件

ES6函数

find()

方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

实现原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
Array.prototype.findme = function(callback) {
for (var i = 0; i < this.length; i++) {
if(callback(this[i],i)){
return this[i]
}
}
}
console.log(abc.findme(function(item){
return item.id = 3
}))
````
### findIndex()
返回数组中满足提供的测试函数的第一个元素的索引

## 隐藏元素控件
用来放一些不希望被用户看见,但是需要被提交到服务端的数据

```Html

<input type="hidden" name="id" value="{{ student.id }}">