Nodejs DforDream

开班

1、自我介绍

2、三阶段学习内容(9周)

  • node.js、数据库开发(1周)
  • vue(3周)
  • react、跨平台APP开发(3周)
  • 微信开发:小程序、公众号开发(1周)
  • 就业周(1周)

技术重点:CSS、JavaScript/DOM/BOM、ES6、Vue/React、小程序开发。 目标:管理系统 webapp 小程序 跨平台APP

前端框架的发展历史

学习笔记建议

  • 随堂笔记,当天的内容当天消化。
  • 每天整理所学内容(Markdown 文档与语法、xmind 思维导图、有道云笔记或印象笔记)

三阶段学习建议

  • 官方文档就是最好的成长资料
    • 框架是为了解决问题,它为什么存在?为什么要学习它?
    • 学习内容多,API多,死记硬背是不可能的
    • 多翻官方文档,从中找到解决方案,学习到更多的知识
  • 培养项目能力
    • 前端工程师与产品、UI、后端、测试等配合
    • 工程搭建、项目架构、业务开发、项目部署,都要懂
    • 初级阶段更重要的业务开发能力,如UI稿还原、交互功能开发
  • 动手
    • 只要动手敲代码,就会有Bug出现
    • 解决Bug,小组式地交流学习,社区网站,写写博客,在Bug中成长
    • 工作中遇到的很多需求,都要新的,甚至是从来没有做过的,敢于动手去实现

知识点讲解


MarkDown语法介绍

0、介绍

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档。 Markdown 语言在 2004 由约翰·格鲁伯(英语:John Gruber)创建。 Markdown 编写的文档可以导出 HTML 、Word、图像、PDF、Epub 等多种格式的文档。 Markdown 编写的文档后缀为 .md, .markdown。

HbuilderX的优化:轻巧、快速、流畅 文档结构图

1、标题

快捷键:h1~h6

2、列表

  1. 第一行
  2. 第二行

快捷键:li

  • 第一行
  • 第二行

3、引用

欲穷千里目,更上一楼层。

4、链接

快捷键:a img

百度

图片

5、文字样式

快捷键:b

aaaa aaaa aaaa

90909304

6、表格

快捷键:table3*3

     
     
     

7、代码

快捷键:code ctrl + /

console.log(1)

注释:

8、其它

快捷键:hr

快捷键:day time

2019-12-16 2019-12-16 10:27:59

Node.js基础

1、Node.js介绍

编译型 解释型

为什么要学习node.js?

  • 辅助前端开发,搭建前端工程
  • 编写服务端业务、数据库开发

什么是Node.js?

Node.js 是一个基于 Chrome V8 引擎的 JavaScript运行环境。 Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。

  • Chrome V8
  • 事件驱动
  • 非阻塞I/O

Node.js 可以解析JS代码(没有浏览器安全级别的限制),提供很多系统级别的API,如fs/net/process/os等。

什么是NPM?

Node.js 的包管理器 npm,成为世界上最大的开放源代码的生态系统。

体验 npm

npm -v   // 查看npm版本
npm install nodemon -g  // 全局安装nodemon

学习资源推荐

  • node.js官网:文档查询
  • npm官网:模块搜索
  • GitHub:大量的开源工具可供学习和使用
  • StackOverflow:学习中遇到的问题查询
  • SegmentFault:问题查询

2、Node.js安装

版本识别:

12.3.2

  • LTS 稳定版本,建议安装
  • Current 最新版本
  • 主版本号.子版本号.修订版本号 v6.8.x
  • 子版本号为偶数的版本是稳定版本,为奇数的是非稳定版本
  • 建议安装LTS的偶数版本

下载安装:

  • 在官网下载.msi安装包
  • 验证安装是否成功:在Git Bash中
    node -v
    npm -v
    

3、Node.js代码初体验

  • Node.js和谷歌浏览器一样,都是通过V8引擎来解析JS代码的。
  • 宿主环境(Node.js和谷歌浏览器)的差异对比,分别在浏览器控制台和 Node.js交互模式(REPL)下实验如下代码:
// 函数
function add(x,y) {return x+y}
add(2,3)
// 全局对象的差异
window
document
global
process
  • 使用 node 命令执行.js脚本
    node test.js [arg1 arg2 arg3]
    

4、Hello World 第一个Node.js程序

Nodejs官方介绍

  • 实现步骤:
    • require(‘http’) 导入http模块
    • http.createServer()创建服务器对象,同时会自动地绑定request事件,该事件需要传递一个事件处理函数
    • 我们在request事件处理函数中处理客户端请求、并进行响应
      • 响应体对象:res.writeHeader() / Content-Type
    • listen()设置监听端口号
  • 处理浏览器请求一直转圈的问题
    • 添加 res.end()
  • 处理favicon.ico的请求问题
    • 打开浏览器中的network,查看favicon.ico的请求状态
    • 这是因为浏览会默认访问服务端根目录下的favicon图标导致的
    • 解决 “/favicon.ico” 的问题

5、Node.js内置模块

  • http / https
  • path
  • fs
  • stream .pipe()管道流
  • querystring
  • url

NPM 入门使用

1、认识

  • npm 是基于CommonJS规范的包管理工具
  • Node.js安装完成后,npm同步安装
    node -v
    npm -v
    

仓库源切换,建议使用淘宝镜像

npm install nrm -g
nrm ls
nrm use taobao
nrm ls

2、管理模块:package.json

  • package.json初始化
    npm init
    
  • 作用:
    • 便于模块管理
    • 便于代码转移
    • package.json 文件属性讲解
      • 项目依赖
      • 开发时依赖

3、模块安装

  • 全局安装:
    npm install name -g/--global
    npm i name -g
    
  • 本地安装:
    npm install react -S/--save
    npm install gulp -D/--save-dev
    
  • 安装指定版本:
    npm info react
    npm install react@16.9.0 -S
    npm list
    rm -rf node_modules
    npm install
    
  • 三个标识:
    npm outdated
    
    1. ^ 保留主版本号不变,后面最新
    2. ~ 保留主版本和次版本号不变,后面取最新
      • 安装最新版本

4、模块卸载

npm uninstall name -g
npm uninstall react -S
npm uninstall gulp -D

5、nrm 源管理

npm install nrm -g
nrm list  查看源
nrm use taobao  切换源
nrm test  测试源的速度

6、如果安装某个模块报错,重新安装又没有覆盖怎么办?

  • 先卸载,再清除缓存,最后重新安装
    npm uninstall nodemon -g
    npm cache clear
    
  • 或者,删除node_modules后,再npm install
  • NPM官网

7、yarn 另一个包管理器推荐

  • yarn中文网
  • 安装yarn:npm install yarn -g
  • yarn和npm二选一,即可

Node.js内置模块

1、URL模块

url.parse('http://www.baidu.com:8080/api?user=geekxia&pwd=123#100')
url.parse('http://www.baidu.com:8080/api?user=geekxia&pwd=123#100', true)

// 把URL解析成对象
url.parse('://www.baidu.com:8080/api?user=geekxia&pwd=123#100', true, true)
	{
		protocol: 'http:',
		slashes: true,
		auth: null,
		host: 'www.baidu.com:8080',
		port: '8080',
		hostname: 'www.baidu.com',
		hash: '#100',
		search: '?user=geekxia&pwd=123',
		query: 'user=geekxia&pwd=123',
		pathname: '/api',
		path: '/api?user=geekxia&pwd=123',
		href: 'http://www.baidu.com:8080/api?user=geekxia&pwd=123#100'
	}

// 把对象格式化成URL
url.format({
	protocol: 'http:',
	slashes: true,
	auth: null,
	host: 'www.baidu.com:8080',
	port: '8080',
	hostname: 'www.baidu.com',
	hash: '#100',
	search: '?user=geekxia&pwd=123',
	query: 'user=geekxia&pwd=123',
	pathname: '/api',
	path: '/api?user=geekxia&pwd=123',
})

// 把两段URL片段,组合成一个完整的URL
url.resolve('http://www.baidu.com', '/api/getList')

2、QueryString模块

  • 对查询字符串执行更加强大的解析 ``` querystring.string()

// 默认使用 & 进行分隔,键值用 = 连接 querystring.stringify({ name: ‘qf’, course: [‘nodejs’, ‘vue’, ‘react’] })

// 自定义分隔符 querystring.stringify({ name: ‘qf’, course: [‘nodejs’, ‘vue’, ‘react’] }, ‘,’)

// 自定义键值对之间的分隔符 querystring.stringify({ name: ‘qf’, course: [‘nodejs’, ‘vue’, ‘react’] }, ‘,’, ‘:’)

querystring.parse() // querystring.string()的逆方法,也可以接受后面的两参数

querystring.escape(‘北京’)

querystring.unescape() // 与querystring.escape互逆


#### 3、HTTP/HTTPS 模块 - get()

目标:实现一个HTTP爬虫

需求讲解:
	打开拉勾网 https://www.lagou.com
	我们的目标是抓取到了一级品类和二级品类的内容
	审查元素,分析其源码结构

	第一步:抓取首页源码字符串
		https.get(url, fn)
		用于抓取页面中的静态内容和数据
	
	第二步:使用 cheerio 进一步处理源码字符串,获取到品类名称
		```
		npm install cheerio -D
		```
	第三步:在控制台上打印出我们获取到的品类名称

#### 4、HTTP/HTTPS 模块 - request()

目标:获取cnode开放数据

	https.request() 报错“Error: getaddrinfo ENOTFOUND https://cnodejs.org”
		解决办法:把 hostname 中的"https://wwww"去掉
	
	GET 的请求方式
		demo:从cnode开放平台获取文章列表
	
	POST 的请求方式
		demo:执行cnode开放平台上的文件收藏功能


#### 5、events模块、事件触发器

如何创建一个事件触发器?

var EventEmitter = require(‘events’) var myEvent = new EventEmitter()

如何定义事件监听器?
	myEvent.on('xxx', fn)
	myEvent.once('xxx', fn)

如何触发事件?如何传递事件参数?
	myEvent.emit('xxx', args)

#### 6、fs模块,文件(夹)的增删改查

与文件夹相关的操作:创建、改名称、读、删

与文件相关的操作:写、读、删、改名称

#### 7、stream模块

为什么需要使用流?
	当文件较大时,避免一次性把数据读入到内存,所以使用流批量读取文件数据。

	.pipe() 管道流的使用

--------------------------------------------------


## Node.js原生Api 搭建服务器


#### 1、什么是服务器?

> 提供服务的程序或设备,它的功能有接收并处理请求,处理并响应数据信息。

* 接收客户请求
* 处理请求
* 响应请求

#### 2、Node.js原生路由实现WebServer

原理,就是根据 req.url 来区分客户的请求路径,根据不同的访问路径响应不同内容。

nodejs代码实时编辑工具(进程守护):
	nodemon / supervisor

res.writeHead(200, {'Content-Type':'text/plain;charset=utf-8;'})
	参见:HTTP媒体类型/MIME_Types

项目需求描述:
	使用node.js原生代码,实现图片、HTML/CSS/JS文件的访问

```js
var http = require('http')
var fs = require('fs')
var path = require('path')

var server = http.createServer(function(req, res) {
	var url = req.url
	// favicon.ico
	if (url != '/favicon.ico') {
		// url路径处理
		// 当用户直接访问 根路径 时
		url = url === '/' ? '/index.html' : url
		var filePath = path.join(__dirname, '/public' + url)
		// 判断是不是文件
		fs.stat(filePath, function(err, stats) {
			// 报错、或文件不存在时
			if (err || !stats) {
				res.writeHead(404, {'Content-Type':'text/plain;charset=utf-8;'})
				res.end('文件不存在 ')
			}
			// 如果是一个文件
			if (stats && stats.isFile()) {
				res.statusCode = 200
				res.setHeader('Content-text', 'text/plain;')
				// 读取文件,响应给客户端
				fs.createReadStream(filePath).pipe(res)
			}
		})
	}
})

// 端口监听
server.listen(8000, function() {
	console.log('server is running on 8000')
})

用Express 重构WebServer

Express安装:npm install express -S

实现静态资源服务器static

var express = require('express')
var app = express()

// 静态服务,在根目录创建 public 目录,把静态资源放进去
app.use(express.static('public'))

// 路由
app.get('/', (req, res) => {
  res.send('hello world')
})
// 端口监听
app.listen(8000, ()=>{
  console.log('server in running on 8000')
})

MongoDB/Robo3T安装

1、MongoDB安装

1、MongoDB安装 下载.msi文件 安装时,取消勾选“MongoDB Compass”,它是MongoDB官方的图形化工具,无须安装。 2、配置环境变量 系统设置->环境变量->添加PATH:D:\mongo\bin 3、启动mongodb服务 mongod –dbpath “D:\mongo\data”

4、使用 mongo shell 连接mongodb服务 连接服务:mongo 查看数据库列表:show dbs;

2、Robo3T安装

  • Robo3T
  • adminMongo 以Robo3T为例,使用步骤如下: 官网下载 robo3t-1.3.1-windows-x86_64-7419c406.exe 点击安装 填写相关信息,启动 Robo3T GitBash启动 MongoDB服务 在Robo3T中创建连接、完成
  • 使用Robo3T
    • 连接本地MongoDB服务
    • 查看集合,切换三种显示方式——json视图、表格视图、对象视图
    • 使用Robo3T Shell:输入mongo shell命令,点击“执行”按钮

CommonJS 模块规范

1、问题:如果不使用模块化开发,会怎么样?

  • 同一个文件中的函数定义与调用(demo演示)
  • 跨文件无法复用

2、模块化开发,有哪些好处?

  • 最重要是解决了命名空间的问题,避免了命名冲突、全局变量的污染
  • 清晰的依赖关系
  • 清晰的代码组织,避免代码臃肿
  • 代码复用

3、Node.js使用CommonJS模块规范

  • Node.js、Webpack、小程序原生开发都采用了CommonJS模块规范
  • 通过 npm install 安装的第三方模块,都实现了CommonJS规范
  • 通过这种模块化的方式,我们可以开发出功能强大的程序应用

3、ES6模块

export function() {}
import {} from './xxx'
  • 在后面学习Vue、React时,会进一步学习。

4、CommonJS模块的定义与引用

var util = {
	eat: function() {},
	sayHello: function() {}
}
module.exports = util
exports.eat = util.eat  // 推荐这种写法,更清晰
exports.sayHello = util.sayHello  // 推荐这种写法,更清晰
// 模块的引用与调用:
require('./util')   // 整体引入
require('./util').sayHello  // 引入单个方法
  • 在后续Node.js开发时,我都将采用CommonJS进行模块编写,大家要熟练使用

5、关于路径

  • 相对路径: ./ ../
  • 绝对路径: D:\code\img\boy.jpg

6、模块的加载优先级

两种模块:内置核心模块、文件模块

  • 优先加载node.js内置模块
  • 其次node_modules第三方模块
  • 常用的内置模块:process/util/url/fs/path/http

7、省略后缀

  • 后缀优先级:.js -> .json -> .node
    var Test = require('./test')   // .js后缀可以省略
    

资源推荐:


MongoDB 数据库

1、MongoDB vs. MySQL 及其概念讲解

MongoDB是一个基于分布式文件存储的数据库,由C++编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。
特点:高性能、易部署、易使用、存储数据非常方便。

相关术语:
	database  数据库
	collection  集合
	document  文档
	field  域
	index  索引
	primary_key  自动地使用_id字段作为主键



文档:即键值对(BSON,二进制的JSON),文档中不需要设置对等的字段,并且相同字段的值可以是不同的数据类型,这是与关系型数据库有很大的区别。
示例:{"_id": ObjectId{"s232323232"}, name: geekxia, age: 20}

集合:集合是文档的组,集合存在于数据库中,集合中没有固定的数据结构。但同一个集合中的文档通常有一定的关联性。

MongoDB的数据类型:
	String
	Integer
	Boolean
	Double
	Min/Max keys
	Array
	Timestamp
	Object
	Null
	Symbol
	Date
	Object ID
	Binary Data
	Code
	Regular expression

2、使用 mongo shell,常用命令

1、帮助命令
	help
	db.help()
	db.test.help()
	db.test.find().help()

2、数据库操作命令
	show dbs
	use dbname  切换数据库
	db / db.getName()  查看当前数据库名称
	db.stats()  显示当前DB的状态
	db.version()  查看当前DB的版本
	db.getMongo()  查看当前DB的连接的主机地址
	db.dropDatabase()  删除当前DB

3、创建数据库和集合
	use project  不存在就创建,存在就切换至
	db.createCollection('user')  // 创建user集合
	db.createCollection('music', {size:20,capped:true,max:100})  创建固定容量的集合
	show dbs
	show collections / db.getCollectionNames()
	db.user.isCapped()  判断集合是否为定容量
	db.getCollection('music')  获取指定集合
	db.printCollectionStats()  打印指定集合的状态

4、集合中的文档操作:
	db.user.insertOne({})  向集合中插入文档
	db.user.insertMany([{},{}])
	db.user.save({})

	db.user.updateOne({"name":"geekxia"}, {$set:{"age":100}})
	db.user.updateMany({},{$set:{}})

	db.user.deleteOne({"name":"jiaming"})
	db.user.deleteMany()
	db.user.remove({})   // 要指出删除的条件

	db.user.find()

5、文档操作的综合示例
	db.user.findAndModify({
		query: {age: {$gte: 25}},
		sort: {age: -1},
		update: {$set:{name:'a'},$inc:{age:2}},
		remove: true
	});
	等价于下面这个命令:
	db.runCommand({
		findandmodify: 'user',
		query: {age: {$gte: 25}},
		sort: {age: -1},
		update: {$set:{name:'a'},$inc:{age:2}},
		remove: true
	});

6、聚集集合查询
	db.user.find()     查询所有记录
	db.user.distinct('name')    以name字段去重查询
	db.user.find({age:22})     查询age=22的记录
	db.user.find({age:{$gt: 22}})   查询age>22的记录
	db.user.find({age:{$lt: 22}})   查询age<22的记录
	db.user.find({age:{$gte: 22}})   查询age>=22的记录
	db.user.find({age:{$lte: 22}})   查询age<=22的记录
	db.user.find({age:{$gte:20, $lte:30}})  查询age>=20 && age<=30的记录
	db.user.find({name:/geek/})  查询name中包含'geek'的记录
	db.user.find({name:/^geek/})  查询name以'geek'开头的记录
	db.user.find({},{name:1,age:1})  查询所有记录,只返回name和age字段(1-显示 0-不显示)
	db.user.find({age:{$gt:20}},{name:1,age:1})  查询age>20的记录,只返回name和age字段
	db.user.find().sort({age:1})  按age进行升序排列
	db.user.find().sort({age:-1})  按age进行降序排列
	db.user.find({},{name:1,age:1,_id:0}).sort({age:1})
	db.user.find({name:'geek',age:22})  查询name='geek' && age=22的记录
	db.user.find().limit(5)  只查询前5条记录
	db.user.find().skip(10)  查询10条以后的所有数据
	db.user.find().skip(5).limit(5)  查询第6~10条记录
	db.user.find({$or:[{age:20},{age:25}]})  查询age=20或者age=25的记录
	db.user.findOne()  查询满足条件的第一条记录
	db.user.find({age:{$gte:25}}).count()  查询满足条件的记录的总条数
	db.user.find({grade:{$exists:true}})  查询含有grade字段的记录
	db.user.find({sex:{$exists:true}}).count()  查询存在sex字段的记录的总条数

7、实战mongo shell
	构造一批假数据,使用insertMany()入库
	然后就可以使用上述命令进行实战练习了

3、mongoose 模块连接数据库

  • 安装:npm install mongoose -S
  • 文档:
  • Node.js实战项目原型

  • 使用express-generator脚手架,创建工程项目
    npm install express-generator -g
    express --view=ejs project   // -e 指的是使用 EJS模板引擎
    cd project
    npm install
    npm start   // nodemon ./bin/www
    
  • 使用mongoose驱动,连接数据库 ```js var mongoose = require(‘mongoose’)

mongoose.connect(‘mongodb://localhost/test’, { useNewUrlParser: true, useUnifiedTopology: true })

var db = mongoose.connection db.on(‘error’, function(err) { console.log(‘数据库连接成败’) }) db.once(‘open’, function() { console.log(‘数据库连接成功’) })


* 使用mongoose创建Model模型,用于对集合进行增删改查
```js
var mongoose = require('mongoose')

var userSchema = mongoose.Schema({
  name: String,
  age: Number
})

var userModel = mongoose.model('users', userSchema)

module.exports = userModel

使用 express-generator 搭建工程项目

1、express 简介

  • express官网
  • express的优势:让Web开发更简洁,这也是框架存在的意义。
  • 学习 express-generator 这个脚手架工具
  • express安装:npm install express -S

2、使用express-generator初始化项目

npm install express-generator -g
express --view=ejs project   // -e 指的是使用 EJS模板引擎
cd project
npm install
npm start   // nodemon ./bin/www

3、详解express-generator脚手架搭建的项目架构

(1)package.json文件详解
	body-parser 用于解析HTTP请求体中的body数据
	cookie-parser 用于解析cookie会话数据
	ejs 页面模块
	morgan 是一个日志工具
	serve-favicon 用于设置网站的favicon

(2) /bin/www 入口文件详解
	端口号设置
	服务启动时的事件监听onError / onListen

(3)app.js文件详解
	中间件:next()表示交给下一个中间件处理
	路由配置:res.render('index', {})渲染index模板引擎
	模块引擎的配置
	express.static(),设置静态资源目录
	bodyParser中间件,用于处理form表单默认的 application/x-www-form-urlencoded 数据编码
	cookieParser中间件,用于解析cookie数据
	404中间件,渲染error模板引擎

(4)路由详解
	不能两次调用 res.send()
	向前端发送文件
		res.sendFile(__dirname+'form.html')
	如何定义一个路由?
		router.get('/xxx',function(req,res,next){})
		router.post('/xxx',function(req,res,next){})
		同时支持GET/POST:
		router.all('/xxx', function(req,res,next){})
		路由可以使用正则表达式
		router.get('/ab*cd', fn)

(5)ejs模板引擎详解
	结合ejs官网,介绍什么是ejs?
	常用标签介绍(在代码中演示一下这些标签的用法):
		<% %>  流程控制标签,用于包裹js代码
		<%= %> 直接输出值,不对“值”执行解析
		<%- %> 输出值,会对“值”中的HTML进行解析
		<%# %> 注释标签
		% 对特殊符号进行转义,如 %%
		<%- include('head', data) %>
	express渲染ejs的语法:
		res.render('ejs', data, options)

RSETful规范及相关概念

1、RESTful规范

2、接口测试

curl -h
curl --help

// GET请求
curl https://cnodejs.org/api/v1/topics

// POST请求:使用-d 参数传递数据
curl -d'login=emma&password=123'-X POST https://google.com/login

3、BSR客户端渲染

前端利用ajax等数据交互手段获取服务端提供的数据之后,渲染到HTML页面. 方法:(ajax/jsonp/fetch) -> 获取数据 -> 文档碎片插入,拼接字符串,模板引擎,客户端运行了页面之后才进行.

  • 优点:灵活,真正的前后端分离,方便于前后台各自更新维护。
  • 缺点:对SEO不友好,增加了http请求次数,减缓了页面加载速度。

4、SSR服务端渲染

在后端看来,页面文件其实就是一个“字符串”,所以服务端完全可以在获取到HTML文件的内容之后经过一些处理再返回给客户端,也就说,服务端可以将数据插入到HTML字符串中之后再返回给客户端。

  • 优点: 对SEO友好,减少了http请求次数,加速了页面初次渲染速度。
  • 缺点:不灵活,前后端耦合度太高。

综合项目实战

1、实现注册功能

<div class="form-box">
    <form action="/users/regist" method="post">
      <input type="text" name="username" value="" placeholder="请输入用户名">
      <input type="password" name="password" value="" placeholder="请输入密码">
      <input type="password" name="password2" value="" placeholder="请确认密码">
      <input type="submit" name="" value="注册">
    </form>
    <div>已有账号,<a href="/login">立即登录</a>!</div>
</div>
router.post('/users/regist', (req, res, next)=>{})

2、实现登录功能

<div class="form-box">
    <form action="/users/login" method="post">
      <input type="text" name="username" value="" placeholder="请输入用户名">
      <input type="password" name="password" value="" placeholder="请输入密码">
      <input type="submit" name="" value="登录">
    </form>
    <div>已有账号,<a href="/regist">立即注册</a>!</div>
</div>
router.post('/users/login', (req, res, next)=>{})

3、ES6解构赋值

var obj = { a:1, b:2, c:3 }
let { a, b, c } = obj

4、实现文章发布功能

<div class="article">
	<form action="/articles/write" method="post">
		<input type="text" name="title" placeholder="请输入文章标题" value="">
		<textarea name="content" class="xheditor"></textarea>
		<input type="submit" value="发布">
	</form>
</div>

<script type="text/javascript" src="/xheditor/jquery/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="/xheditor/xheditor-1.2.2.min.js"></script>
<script type="text/javascript" src="/xheditor/xheditor_lang/zh-cn.js"></script>
<script>
    $('.xheditor').xheditor({
      tools: 'full',
      skin: 'default',
      upImgUrl: '/upload/img',  // 指定文件上传的接口
      html5Upload: false,
      upMultiple: 1    // 每次允许一张图片上传
    });
</script>
  • 标签的 enctype 属性
    • application/x-www-form-urlencoded 对所有数据进行默认编码,用于普通表单数据提交。
    • multipart/form-data 不对数据进行编码,在使用包含文件上传控件的表单时必须使用该值,用于文件上传。
  • 图片文件上传过程
    • 前端,富文本编程器指定<form enctype="multipart/form-data"></form>
    • Node.js端使用multiparty解析文件数据
    • Node.js端使用fs管道流,把文件数据写入到 /public 静态资源服务器中
    • 文件写入成功后,把文件的访问路径返回给前端,插入到的 src中。

5、首页文章列表与分页查询

<div class="list">
    <div class="row">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span>
            <a href="">编辑</a>
            <a href="">删除</a>
        </span>
    </div>

    <div class="pages">
        <a href=""></a>
    </div>
</div>

6、文章详情页

<div class="detail">
    <div class="title"><%=item.title%></div>
    <div class='desc'>
      <span>作者:<%=item.username%></span>
      <span>发布时间:<%=item.time%></span>
    </div>
    <div class="content"><%-item.content%></div>
</div>

7、阶段性Bug修复

  • 修复图片上传Bug,POST /articles/upload
    // 把 form 对象的创建,放到 /upload 接口内部去
    var form = new multiparty.Form()
    
  • 连接mongodb时,如果控制台提示 URL 解析有问题,在db/connect.js中为 connect方法添加两个配置项。 ```js // 引入模块 var mongoose = require(‘mongoose’) // 连接数据库 mongoose.connect(‘mongodb://localhost/express-project’, { useNewUrlParser: true, useUnifiedTopology: true })

var db = mongoose.connection db.on(‘error’, (err)=>{ console.log(‘数据库连接错误’) }) db.once(‘open’, ()=>{ console.log(‘数据库连接成功’) })



#### 8、文章编辑功能

* 复用`/write`页面,复用`POST /articles/write`接口。
* 使用`doc._id`来判断是文章新增,还是文章编辑。
* 当文章编辑时,`/articles/write`不能使query字符串传参,我们使用input隐藏域来传递参数。
```html
<%# POST请求不能使用 query字符串的方式传值 %>
<%# 我们使用 input 隐藏域传值 %>
<input type="hidden" name="username" value="<%= doc.username %>">
<input type="hidden" name="id" value="<%= doc._id %>">

9、Session与登录拦截

  • Cookie 与 Session 入门学习
  • 安装:npm install express-session -S
  • express-session参考手册 ```js // session配置 app.use(session({ secret: ‘qf project’, // 任意字符串 resave: false, saveUninitialized: true, cookie: { maxAge: 1000 * 60 * 5 } // 指定登录会话的有效时长 }))

// 登录拦截 app.get(‘*’, function(req, res, next) { var username = req.session.username var path = req.path console.log(‘session’, username) if (path != ‘/login’ && path != ‘/regist’) { if (!username) { res.redirect(‘/login’) } } next() })

* 在登录`POST /users/login`成功时,设置Session
```js
req.session.username = username
req.session.isLogin = true
  • 在网站内部页面顶部显示用户名、Logo、退出按钮等 ```html
<%= username %> 写文章 退出 首页

在其它页面中:<%- include(‘bar’, {title: ‘’}) %>

* 在退出登录`GET /users/logout`时,重置或销毁Session

req.session.username = ‘’ req.session.isLogin = false 或者: res.session.distroy() // 跳转至登录页 res.redirect(‘/login’)


#### 10、给网站加favicon图标

* [制作favicon图标](https://tool.lu/favicon/)
* 把生成的favicon图标,放到`/public`目录中去
* 安装:`npm install serve-favicon -S`

var favicon = require(‘serve-favicon’) var app = express(); // favicon配置 app.use(favicon(path.join(__dirname, ‘public’, ‘favicon.ico’)))



## WebSocket

#### 1、WebSocket是什么?

> 它用于建立客户端到服务端的双向交互通信,打开一条会话通道,让双方可以进行实时通信,这个连接的每一端就被称为是Socket。避免了使用传统的轮询方式来实现通信。

* 它的主要功能有二:
	* 一是向对方发送消息
	* 二是基于事件驱动的方式来接受对方发来的消息

#### 2、WebSocket 和 socket.io 之间的区别是什么

* socket.io是一个开源库,它对WebSocket进行分装。
	* 增强了浏览器的兼容性
	* 使用起来更加方便、功能也更加强大

#### 3、任务明确

* socket服务端开发
	* 接收客户端发来的消息
	* 然后分发给聊天室里所有其它的用户
* socket客户端开发
	* 把消息发送至服务端
	* 接收服务端的消息,并在客户端进行展示

#### 4、学习资源

* [HTML5 WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
* [socket.io 官网](https://socket.io/)
* [socket.io 仓库](https://github.com/socketio/socket.io/blob/HEAD/docs/README.md)
* [BootCDN 静态资源](https://www.bootcdn.cn/)



## 跨域问题

#### 1、什么跨域?

> 跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。

* 同源策略:是指协议、域名、端口都要相同,只要三者有一个不同就会产生跨域。
* 例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名有不同,所进行的访问行为都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:**跨域限制访问,事实上是浏览器的限制策略,理解这一点很重要**。


#### 2、三种方式解决跨域问题

* JSONP(需前后端配合来完成)
	* 前端使用script标签来调用接口。
	* 后端需要配合使用res.jsonp()返回数据。
	* JSONP只适用于GET请求,POST请求用不了。
```html
// 第一种写法
<script>
	function handleData(res) {console.log('res', res)}
</script>
<script src="http://localhost:8001/user/list?callback=handleData"></script>
// 第二种写法
<script>
	function handleData(res) {console.log('res', res)}

	var oScript = document.createElement('script')
	oScript.src = "http://localhost:8001/user/list?callback=handleData"
	oScript.type = "text/javascript"
	document.body.appendChild(oScript)
</script>
  • 代理:使用http-proxy-middleware中间件来实现代理
    • 在Node.js中使用http-proxy-middleware来进行接口代理
    • 后端还可以使用Nginx进行接口代理
    • 前端可以使用Webpack进行接口代理(Vue阶段会讲) ```js const app = require(‘express’)() const {createProxyMiddleware} = require(‘http-proxy-middleware’)

app.use(‘/api’, createProxyMiddleware({ target: ‘http://10.36.136.170:9999’, changeOrigin: true }))


* CORS
	* 只需要后端工程师来处理
```js
const app = require('express')()
//设置跨域访问(前端需要清除缓存后,才能再测试跨域)
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By",' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

Token 规范

  • JSON Web Token 入门教程
  • JSON Web Tokens
  • 接口文档 参考
  • Token长什么样?
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    
  • Token从哪里来? Token在服务端生成,当用户登录时,或者调用指定接口时,会返回Token给客户端用户,客户端用户收到Token后,保存在前端(比如保存在localStorage中),之后再请求其它有访问权限的后端接口时,需要把Token携带上传递给后端进行验证。
  • 如何把Token传递至后端呢?这根据后端的要求,通常会把Token放在 headers 中进行传递。用 jQuery.ajax() 示例如下:
    $.ajax({
      type: "GET"
      url: "http://192.168.0.204:8000/api/getOrderList",
      data: {},
      headers: {
          token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
      },
      success: function(data){
          console.log(data)
      }
    })
    

Git基础操作

  • 如何解决Git Bash 面板中的中文乱码问题?右键->options->text->把编码格式改为UTF-8。

1、提交

git add .
git commit -m "修复了登录的Bug"   // -m 很重要
git push

2、分支创建

git branch xia
git checkout xia

3、分支合并

  • 把xia合并至master
    git checkout master
    git merge xia
    git push
    
  • 把master合并至xia
    git checkout xia
    git merge master
    git push
    

4、版本回退,回退到指定的版本

git log
git reset --hard dd769f83ef186c7f026ec28c1d0c32cc10f7db60   // commit_id

5、Git连接远程仓库

git init
git remote add origin https://github.com/geekxia/node-express-mongoose.git
git pull origin master --allow-unrelated-histories
git add --all
git commit -m '提交'
git push origin master

PM2

  • 安装:npm install pm2 -g

1、pm2 管理单个node项目

pm2 start app.js
pm2 list
pm2 stop id
pm2 delete id

2、pm2 管理多个node项目

  • 初始化配置文件 ecosystem.config.js pm2 ecosystem
  • 配置 ecosystem.config.js 文件
    • name 指定项目名称
    • script 指定node项目的入口启动文件
  • 启动、重启、停止、删除多个node服务
    pm2 [start|restart|stop|delete] ecosystem.config.js
    pm2 list
    

Mocha测试

mocha介绍:最流行的JS测试框架之一,在浏览器和Node环境下都可以使用。

什么是测试框架? 就是运行测试的工具,使用测试框架可以为JS应用添加测试,从而保证代码质量。

mocha安装与环境搭建: npm install mocha -D 在项目根目录创建 test 目录 在npm scripts中配置运行命令: “test”: “mocha” 运行测试: (1)在命令行中直接运行: ./node_modules/mocha/bin/mocha (2)使用npm scripts命令:npm test test目录下测试文件,会按排列顺序逐个运行,从上到下 test是mocha默认的支持目录 (3)当测试目录不是默认目录test时: “test”: “mocha costom_test_dir” (4)当测试目录有嵌套时,如果一次性执行所有测试文件? “test”: “mocha costom_test_dir –recursive” // 表示“递归”

编写一个测试文件:
	四个钩子函数
	it() 用例:一个it()就是一个测试

使用断言assert:
	断言库chai:should风格断言、expect风格断言
	安装:`npm install chai -D`
	使用chai.assert
	使用chai.should()
	使用chai.expect

测试项目实战:
	测试同步方法
	测试异步方法 setTimeout()
		it('', function(done) {
			// 断言代码
			done() // 让异步完成后再断言测试
		})
		测试运行:
			"test": "mocha -t 6000"  // mocha默认是2s
	测试HTTP接口:
		示例(在浏览器地址中演示数据抓取):https://cnodejs.org/api/v1/topics
		使用https模块封装接口方法
		同上,使用`done()` 和 `mocha -t 6000`

	测试函数异常抛出:
		expect(fn).to.throw()
		当目标函数抛出异常时,测试通过;反之不通过。

END