前端

node 学习笔记.md

2018-09-07  本文已影响743人  再见天才

Node.js第一天

1. 初识Node.js

1.1 Node.js是什么

1.2 ==node的API参考文档的使用:==

1.3 Node.js能做什么

1.4 达到目标

2. 起步

2.1. 安装Node

2.2 node练习

  1. 创建编写js文件
  2. 打开终端(命令行),定位到脚本文件所属目录
  3. 输入node 文件名执行对应的文件,显示结果

==注意:文件名不能使用node,最好也不要使用中文==

==Node.js中常常使用回调函数作为参数,因为是异步的==

3. Node中的JavaScript

什么是模块化

3.1 Node中使用模块

3.2 核心模块

3.3 用户自定义模块

​ Node.js不能同时执行多个js文件,需要使用模块化编程

​ 每个文件模块中都提供了一个对象:exports

​ exports默认是一个空对象,是require方法的返回值

4. web服务器开发

4.1端口的概念

计算机中只有一个物理网卡,而且同一个局域网中,网卡的地址必须是唯一的。

var http = require('http')

//1.  创建server
var server = http.createServer()

//2.  监听Server的request请求事件,设置请求处理函数
        // 请求
            // 处理
        // 响应
        // 一个请求对应一个响应,如果一个请求过程中,已经结束响应了,则不能重发发送响应。
        // 没有请求就没有响应

server.on('request', function (req, res) {
    console.log(req.url)
})

// 3.绑定端口号,启动服务

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

4.2 Content-Type

==网址http://tool.oschina.net能够查询文件对应的Content-Type==

==URL==:统一资源定位符,一个url对应一个资源

5. 注意事项

Node.js第二天

1. 代码规范问题

2. 目录斜杠问题

3. Node 安装第三方库

4. 服务器端渲染

5. 浏览器解析静态资源


6. 开发文件规范问题(处理网站中的静态资源)

7. url模块

==网页中的路径都是url路径,不是文件路径==

8. ==服务器让客户端重定向==

9. Node中使用Console测试

10.Node 常用指令

11. 注意:

Node.js 第三天

知识点

软件版本

each和forEach解析

伪数组转化为数组

node和PHP

1.Node中的模块系统

使用node编写应用程序主要就是在使用:

1.1 什么是模块化

1.2 CommonJs模块规范

在Node中的js还有一个重要的概念:模块系统

如果一个模块需要直接导出某个成员,而非挂载的方式,需要使用下面的方式

module.exports = 'hello'//这样另一个模块加载这个模块就会返回'hello',而不是exports对象
1.2.1 加载 require

语法:

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

两个作用:

1.2.2 导出exports

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

exports.a = 123
exports.b = {
    foo : 123
}

导出单个成员(拿到的就是:函数、字符串):

module.exports = 'hello'
module.exports = function(){//后者会覆盖前者
    
}

因此,也可以如下方式导出多个成员:

module.exports = {
    //加成员
}
1.2.3 原理解析
var module = {
    exports : {
        
    }
}
//谁require这个模块,就会得到 module.exports
//为了简化操作,在模块中还有一句代码
var exports = module.exports  //exports和module.exports挂载是一样的。这行代码应该是在第一行
console.log(exports === module.exports)//结果为true
//默认在代码的最后又一句:
return module.exports
//一定要记住return的是module.exports,不能通过直接修改exports影响返回的对象,因此导出单个成员需要修改module.exports对象
1.2.4 require方法加载规则

1.3 npm

1.3.1 npm网站

npmjs.com npm官方网站,可以查询包和发包

1.3.2 npm命令行工具

npm第二层含义就是一个命令行工具,只要你安装node就已经安装好了npm。

npm也有版本的概念(独立的软件)

npm --version 
npm -v
npm install --global npm//自己升级自己
npm init
npm init -y//可以跳过向导,快速生成
1.3.3 解决npm被墙问题
# 在任意目录下执行都可以
# --global表示安装到全局,而非当前目录,因此在任意目录都可以执行
# --global不可以省略
npm install --global cnpm

​ 接下来你安装包的时候吧之前的npm替换成 cnpm

​ 举个例子:

# 这里走国外的npm服务器,速度较慢
npm install jquery

#使用淘宝的服务器下载:jquery
cnpm install jquery

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

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

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

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

只要经过了上面命令的配置,则你以后所有的npm install都会默认通过淘宝的服务器来下载。

1.4 package.json

==补充:==

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

npm 5 之后才加入这个文件

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


2. Express

原生的==http==在某些方面表现不足以应对我们的开发需求,所以我们就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码更高度统一。

//先建一个app.js
// 安装依赖
// 初始化应用
// 引包开发

var express = require('express')

// 2.创建你的服务器应用程序
// 也就是原来的 http.createServer

var app = express()   //相当于原来的server
//当服务器收到get请求/的时候,执行回调处理函数。必须为get请求
app.get('/', function (req, res) {
    res.send('hello world')
})
//不需要一个一个再判断了
app.get('/about', function (req, res) {
    res.send('你好') //此处的编码问题框架已经帮我们处理好了,不需要res.setHeader()
})
//相当于server.listen
app.listen(3000, function () {
    console.log('app is running at port 3000.')
})

express的request对象

//req对象中直接包含了 原生 url 模块中返回的方法
req.query   //返回参数对象

express公开指定的目录(处理静态资源)

// 公开指定目录
//只要这样做了,就可以直接通过 /public/xx 的方式访问 public 目录中的所有资源了
app.use('/public/', express.static('./public/'))  //相当于拼接的'. + 路径'

总结:

伪数组

var fakeArr = {
    0 : 'abc',
    1 : 'efg',
    2 : 'aaa',
    length : 3
}

Node.js 第四天

知识点

文件路径和模块标识

1. 修改完代码自动重启

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

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

npm install --global nodemon

安装完毕之后,使用:

node aap.js

# 使用 nodemon启动app
nodemon app.js

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

2. express 基本路由(router)

路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程。

路由器(映射关系):

get:

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

post:

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

3. express静态服务

  1. 当以/public/开头的时候,添加../public/目录中寻找对应的资源
app.use('/public/', express.static('./public/'))
  1. 当省略第一个参数的时候,则可以通过省略 /public 的方式来访问。直接写该目录下的资源路径即可,例如public目录下的index.html,url为 127.0.0.1:3000/inde.html
app.use(express.static('./public/'))
  1. 当指定第一个参数的时候,必须是/a/为开头取代 /public/ 访问public中的资源,是为了方便指定定制访问路径。例:1207.0.0.1:3000/a/index.html 才能访问如下的开放目录。
app.use('/a/', express.static('./public/'))

4 第四种还没学

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

web浏览器中的路径为 URL,不是传统的路径,是一个定位符

4. 在express中配置使用art-template模板引擎

安装

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

配置:

app.engine('art', require('express-art-template'))
// 第一个参数标识,当前以 .art 结尾的文件的时候,使用art-template模板引擎
// 也可以把第一个参数改写为html
app.engine('html', require('express-art-template'))

使用:

// Express 为 Response 响应对象提供了一个方法 : render
// render 方法默认是不可以使用的,但是如果配置了模板引擎就可以使用了
// res.render('html模板名', {模板数据})
// render方法中第一个参数不能写路径,默认会去项目中的 views 目录查找该模板文件
// 也就是说 Express 有一个约定:开发人员把所有的视图文件都放到 views 目录中 
// Express 默认查找目录就是 views 目录
// 呈现视图并将呈现的HTML字符串发送到客户端
app.get('/', function (req, res) {
    res.render('404.art')
})

// 如果想要修改默认的views 为别的目录
// app.set('views', 'render函数的默认路径')
app.set('views', './views')


// 查找views 目录下的文件
app.get('/admin', function (req, res) {
    res.render('admin/index.html', {
        title : '管理系统'
    })
})

==当需要直接返回页面的时候,也可以使用render方法==

//当需要返回路径的时候
app.get('/', function (req, res) {
    res.render('index.html')
})

5. express 中重定向的使用

//原生的重定向
res.statusCode = 302
res.setHeader('Location', '/')

//express 中封装的重定向
res.redirect('/')

GET和POST请求获取数据

  1. get请求数据在请求行中,post需要设置请求行
  2. GET没有请求体,post的请求数据在请求体中

在express 中获取表单 POST 请求体数据

安装:

$ npm install body-parser

配置:

var express = require('express')
//0.先引包
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 来获取表单 POST 请求数据
  res.end(JSON.stringify(req.body, null, 2))
})

fs.readFile()方法补充

//fs.readFile()方法读取到的文件为二进制数据,如果想要解析该二进制数据,可以使用toString()方法,也可以使用readFile()方法第二个参数传值为'utf-8'

==SyntaxError表示语法错误==

6. express 中设置状态码

res.status(500).send('hello')

==数据库中的性别gender一般使用0或1代替==

7. express注意

8. Express-crud 路由设计

请求方式 请求路径 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 处理删除请求

路由的文件采用模块化开发,需要单独写一个模块。

express 提供了一种==包装路由==的方法:

//1. 创建一个路由容器
var router = express.Router()
//2. 把路由都挂载到 router 路由容器中
router.get('/', function(){})
......
//3. 导出router容器
module.exports = router

在app文件中使用router容器:

//1. 导入路由容器文件
var router = require('./router')
//2. 把路由容器挂载到app服务中
app.use(router)

==模块的职责要单一,不要混写。划分模块的目的就是为了增强项目代码的可维护性,提升开发效率==

注意:

==配置模板引擎和body-parser 一定要在 app.use(router) 挂载路由之前,才能正常执行==

9. 获取函数异步操作的结果(封装异步API)

例如:

function fn (callback) {
    setTimeout(function () {
        var data = 'ddd'
        callback(data)
    }, 1000)
}
fn(function (data) {
    console.log(data)
})

只要是获取函数中的异步函数的数据(函数中还有执行的函数),都可以采用上述方法。通过引用类型可以获取到异步操作的结果。(试一下对象行不行) 实际运行的过程中是上面的fn函数中代用了回调函数。

注意:

==callback的运行一定要写在函数的最后一行,要不然会阻碍后边代码的执行==

10. ES6 初识

ECMAscript 6 中的一个数组方法:find

该方法需要接收一个函数作为参数

当某个遍历项符合回调函数中的遍历条件时,find会终止遍历,同时返回遍历的成员。只要对比项和数组中的某一项相同时,就会返回数组中的某一项。

var stu = students.find(function (item) {
    return item.name === student.name
})

ECMAscript 6 中的一个数组方法:findIndex(),同上

11. 数字和字符串之间的转换

==浏览器端渲染和服务器端渲染==

表单请求的post不需要设置请求头。

浏览器脚本请求需要设置请求头。

post只是一种请求!不是响应

请求方式

1. 表单
    2. a 标签,href 属性
    3. 脚本(ajax,http)

1和2属于采用服务器端渲染的方式,3属于浏览器端渲染的方式。

服务器端使用 template 和浏览器端使用 template 的区别

Node.js 第五天

JavaScript的异步操作是因为其单线程、事件循环的底层机制。

1. 异步编程

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

这种情况必须通过:回调函数来获取数据

2. 浏览器中的模块化

浏览器中可以使用第三方库实现模块化开发,js天生不支持模块化

PHP直接支持requireinclude,因为PHP当初设计时加入了这个功能

node.js中的模块化 CommonJS

学vue 的时候会去学习,例如:

目前前端的情况就是使用很多新技术,然后利用编译器工具打包可以在低版本浏览器中运行。

使用新技术的目的就是为了提高效率,增加可维护性

3. app.use()

4. 新版本的npm特性(npm5 以上)

  1. 新版本的的npm不需要加 --save 命令
  2. uninstall 不加--save 会删除依赖
  3. 当安装包的时候,会自动创建或者是更新package-lock.json这个文件
  4. package-lock.json文件中保存着所有依赖树(所有包)的信息,下载地址,删除node_modules文件之后能够根据该文件快速下载依赖。
    • 这样重新npm install的速度就会提升
  5. 从文件来看有lock,是用来锁定版本的
    • 如果项目依赖了某第三方库的 1.1.1 版本,如果重新安装有可能升级这个库,而package-lock.json会锁住依赖的版本,不会出现升级造成项目不可用的情况

5.条件函数的使用(封装数组 find 方法)

例:

//条件函数的使用
//封装数组的 find 和 findIndex 方法
Array.prototype.myFind = function (conditionFunc) {
    for (var i = 0; i < this.length; i++) {
        if (conditionFunc(this[i], i)) {
            return this[i]
        }
    }
}

var ret = [1,2,3].myFind(function (item, index) {
    return item === 2
})

数组中的遍历方法,都是对函数作为参数的一种应用。数组中遍历的方法都有:

6. sublime 中设置 js 代码高亮

7. Express 定制 404 页面

MongoDB

1. 关系型数据库和非关系型数据库

表就是关系

或者说表与表之间存在关系

2. 启动和关闭数据库

启动:

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

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

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

停止:

#在开启服务的控制台,直接 Ctrl + c 即可停止
# 或者直接关闭开启服务的控制台也可以

3. 连接数据库

连接:

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

退出:

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

4. 基本命令

5. 在Node中如何操作 MongoDB 数据库

5.1 使用官方的mongodb包来操作

5.2 使用第三方 mongoose 来操作 MongoDB 数据库

第三方包:mongoose基于MongoDB 官方的 mongodb包再做的一次封装.

安装: npm i mongoose

开启mongodb 数据库,然后执行例程代码

var mongoose = require('mongoose');

//连接MongoDB数据库,引号中的第一个为本机,第二个为本机的test数据库。
//指定连接的数据库不需要存在,当你插入第一条数据之后就会自动被创建出来
mongoose.connect('mongodb://localhost/test', { useMongoClient: true });

mongoose.Promise = global.Promise;

// 创建一个模型
// 就是在设计数据库
// MongoDB 是动态的,非常灵活,只需要在代码中设计你的数据库就可以了
// mongoose 这个包就可以让你的设计编写过程变得非常的简单
var Cat = mongoose.model('Cat', { name: String });
// Cat 为表名(最终生成名称为 cats),第二个参数为数据结构

// 实例化一个Cat 
var kitty = new Cat({ name: '喵喵'});

// 持久化保存 kitty 实例
kitty.save(function (err) {
  if (err) {
    console.log(err);
  } else {
    console.log('meow');
  }
});

5.3 MongoDB 数据库的基本概念

按开发顺序:

理解:

{
    qq : {//数据库
        users : [//集合
            {name:"zzz"}//文档对象 
        ]
    },
    taobao : {
            
    }
}

mongoose 官方指南

1. 设计Schema 发布 model

// 此模块是为了新建一个数据库
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

// 连接数据库
mongoose.connect('mongodb://localhost/zbb')

// 相当于设计集合结构(表结构)
// 字段名称就是表结构中的属性名称
// 每个属性的值做了强制要求,都为js代码
// 约束的目的是为了保证数据的完整性,不要有脏数据
// var blogSchema = new Schema({
//   title:  String,
//   author: String,
//   body:   String,
//   comments: [{ body: String, date: Date }],
//   date: { type: Date, default: Date.now },
//   hidden: Boolean,
//   meta: {
//     votes: Number,
//     favs:  Number
//   }
// });


// 设计约束案例:
var userSchema = new Schema({
  username: {
    type: String,
    required: true //必须要有不能为空
  },
  password: {
    type: String,
    required: true
  },
  // email: {
  //   type: String
  // }
  email: String
})

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

// 4. 当我们游了模型构造函数之后就可以使用构造函数对users 集合中的数据操作了(增删改查)

2. 增加数据

// 4. 当我们有了模型构造函数之后就可以使用构造函数对users 集合中的数据操作了(增删改查)
// new 模型构造函数 参数为对象
var admin = new User({
  username: 'admin',
  password: 'aaa1'
})

// 5. 数据持久化
// 使用 .save() 方法
admin.save(function (err, ret) {
  if (err) {
    console.log('保存失败');
  } else {
    console.log('保存成功');
    console.log(ret);  //就是刚刚保存的数据,id自动生成
  }
})

3. 查询

查询所有:

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

按条件查询所有:

User.find({
  username: 'zs'
}, function (err, ret) {
  if(err) {
    console.log('查询失败')
  } else {
    console.log(ret);
  }
})

按条件查询单个:

User.findOne({
  username: 'zs',
  password: '123'
}, function (err, ret) {
  if(err) {
    console.log('查询失败')
  } else {
    console.log(ret);
  }
})

4. 删除数据

按条件删除:

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

根据条件删除一个:

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

根据id删除一个:

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

5. 更新数据

根据id更新一个:

User.findByIdAndUpdate('数据的id', {
  更新的数据
  password: '1234'
}, function (err, ret) {
  if(err) {
    console.log('更新失败');
  } else {
    console.log('更新成功');
  }
})

剩余的更新方法查看文档。

Node 操作 MySQL数据库

安装:

npm install --save mysql

未完待续。。。

异步编程

回调函数

Promise

callback hell:

[图片上传失败...(image-3fee8d-1536318692721)]

无法保证顺序的代码:

var fs = require('fs')

fs.readFile('a.text', 'utf8', function(err, data) {
    if (err) {
        throw err
    }
    console.log(data)
    fs.readFile('b.text', 'utf8', function(err, data) {
        if (err) {
            throw err
        }
        console.log(data)
        fs.readFile('c.text', 'utf8', function(err, data) {
            if (err) {
                throw err
            }
            console.log(data)
        })
    })
})
注意:

之前的读取文件 return 结束函数的方式,在开发中常常使用 throw err 的形式抛出异常

多个异步编程的情况,无法保证回调函数的执行顺序(多线程调度机制),只能通过异步嵌套异步的形式保证顺序。代码太繁琐,不好修改。

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

Promise 容器:

Promise 的具体使用:

[图片上传失败...(image-d65b7a-1536318692721)]

var p1 = new Promise(function (resolve, reject) {
    fs.readFile('a.txt', function (err, data) {  //异步任务
        if (err) {
            //容器中的任务失败
            //把容器中的 Pending 状态变为 Rejected
            reject(err)
        } else {
            //承诺容器中的任务成功了
            //把容器中的 Pending 状态改为成功 Resolved
            //也就是说这里调用的resolve 方法实际上就是then 方法传递的那个 function
            resolve(data)
        }
    })
})
//p1 就是那个承诺
//当p1 成功了 然后(then) 做指定的操作
//then 方法接收的第一个 function 就是容器中的 resolve 函数中的数据
//调用了reject 就相当于调用了 then 方法的第二个参数函数
p1
   .then(function (data) {
    console.log(data)
    //return 123
    // 当前成功函数 return 的结果就可以在后面的 then 中function 接收到
    //当你 return 123 后面就会接收到 123
    //return hello 后面就会接收到 'hello'
    // 没有 return 后面收到的就是哦undefined
    // 上面这些事原理
    // 真正使用的是:我们可以 return 一个 Promise 对象
    //当 return 一个 Promise 对象的时候,后续的 then 中的方法的第一个参数会作为 p2 的 resolve函数的结果
    return p2
}, function (err) {
    console.log('读取文件失败', err)
})

以上情况称为异步调用链式编程。

注意:

==1. 要使用 .then() 方法,必须要支持 Promise==

==2. mongoose 的查询操作都是异步操作,都可以使用 .then 方法==

==3. .then() 方法的执行,必须要等到 Promise 的状态改变==

中间件概念

 当一个请求进入一个中间件之后,如果不调用 next 则会停留在当前中间件。所以next 是一个方法,用来调用下一个中间件的。
  1. 关心请求路径的中间件(以 /xxx 开头的路径中间件)。

    app.use('/a', function (req, res, next) {
        console.log(req.url) //此处打印的 url 是出去 /a 后面的url
    })
    

    当请求路径是以 a开头的情况,执行function 里面的代码

当请求进来,会从第一个中间件开始进行匹配。(代码顺序)

​ 如果匹配,则进来。

​ 如果请求进入中间件之后,没有调用next() 方法,则代码会停留在这个中间件中。

​ 如果调用了next 则向后查找匹配的中间件

​ 如果不匹配,则继续判断匹配下一个中间件。

2. 路由级别的中间件
  1. 除了以上中间件之外,还有一种最常用的

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

    app.get 和 app.post

    app.get('/a', function (req, res, next) {
        console.log('aaa')
        next()
    })
    app.get('/a', function (req, res, next) {
        console.log('bbb')
    })
    

    上述例子中下面的不会覆盖上面的中间件

总结


  1. ==```` 是ES6中新增的一种字符串包裹方式,叫做:模板字符串。它支持换行```和非常方便的拼接变量==

上一篇 下一篇

猜你喜欢

热点阅读