0 预备知识
https://gitee.com/lurongtao/f...
1.命令行窗口(小黑屏)、CMD/dos窗口、终端、shell
- 开始菜单 --> 运行 --> CMD --> 回车
- 所有程序 附件 命令提示符
- shift + 右键
- 地址栏 cmd
2.常用的指令:
- dir 列出当前目录下的所有文件
- cd 目录名 进入到指定的目录
- md 目录名 创建一个文件夹
- rd 目录名 删除一个文件夹
- . 表示当前目录d
.. 表示上一级目录
3.环境变量
日常生活中,都是使用图形化界面,直接双击exe打开文件。最早的系统叫做dos系统,都是命令行窗口。图形化操作最终都会转换为指令。默认显示当前位置。然后可以使用常见指
- 环境变量(windows系统中变量) 在任何地方都可使用
组成 变量名:变量值
分类:用户变量:只对当前用户起作用 系统变量:整个系统
当我们在命令行窗口打开一个文件【直接文件名】,或调用一个程序时,系统会首先在当前目录下寻找文件程序,如果找到了则直接打开如果没有找到则会依次到环境变量path的路径中寻找,直到找到为止如果没找到则报错【类似于作用域链】所以我们可以将一些经常需要访问的程序和文件的路径添加到path中。这样我们就可以在任意位置来访问这些文件和程序了
I/O (Input/Output)
- I/O操作指的是对磁盘的读写操作
Node
- Node是对ES标准一个实现,Node也是一个JS引擎
- 通过Node可以使js代码在服务器端执行
- Node仅仅对ES标准进行了实现,所以在Node中不包含DOM 和 BOM
- Node中可以使用所有的内建对象
String Number Boolean Math Date RegExp Function Object Array
而BOM和DOM都不能使用
但是可以使用 console 也可以使用定时器(setTimeout() setInterval())
- Node可以在后台来编写服务器
Node编写服务器都是单线程的服务器
- 进程:负责为程序的运行提供必备的环境 我们的代码都是存在进程里的
- 进程就是一个一个的工作计划(工厂中的车间)任务管理器,很多个进程,代表服务被挂起,但是并不是执行
- 线程
- 线程是计算机最小的运算单位,线程负责保存在进程中程序(工厂中的工人)
线程是干活的
- 传统的服务器都是多线程的,js是单线程的,执行js与渲染是两个线程。
- 每进来一个请求,就创建一个线程去处理请求
- Node的服务器单线程的
- Node处理请求时是单线程,但是在后台拥有一个I/O线程池
node.js
- node是一款对ES标准实现的JS引擎
- 通过node可以使js在服务器中运行
- node就是一款使用js编写的web服务器
- node底层是使用c++的编写的
- node的中js引擎使用的chrome的v8引擎
- node的特点:
1.非阻塞、异步的I/O
2.事件和回调函数
3.单线程(主线程单线程,后台I/O线程池)
4.跨平台
- 模块化
- ES5中没有原生支持模块化,我们只能通过script标签引入js文件来实现模块化
- 在node中为了对模块管理,引入了CommonJS规范
- 模块的引用
- 使用 require()函数来引入一个模块
- 例子:
var 变量 = require("模块的标识");
- 模块的定义
- 在node中一个js文件就是一个模块
- 默认情况下在js文件中编写的内容,都是运行在一个独立的函数中,
外部的模块无法访问
- 导出变量和函数
- 使用 exports
- 例子:
exports.属性 = 属性值;
exports.方法 = 函数;
- 使用module.exports
- 例子:
module.exports.属性 = 属性值;
module.exports.方法 = 函数;
module.exports = {};
- 模块的标识
- 模块的标识就是模块的名字或路径
我们node通过模块的标识来寻找模块的
对于核心模块(npm中下载的模块),直接使用模块的名字对其进行引入
var fs = require("fs");
var express = require("express");
对于自定义的文件模块,需要通过文件的路径来对模块进行引入
路径可以是绝对路径,如果是相对路径必须以./或 ../开头
var router = require("./router");
- npm的命令
- npm -v 查看npm的版本
- npm version 查看所有模块的版本
- npm search 包名 搜索包
- npm install / i 包名 安装包
- npm remove / r 包名 删除包
- npm install 包名 --save 安装包并添加到依赖中 *****
- npm install 下载当前项目所依赖的包
- npm install 包名 -g 全局安装包(全局安装的包一般都是一些工具)
node
- 包(package)
- 将多个模块组合为一个完整的功能,就是一个包
- 包结构
bin
- 二进制的可执行文件,一般都是一些工具包中才有
lib
- js文件
doc
- 文档
test
- 测试代码
package.json
- 包的描述文件
- package.json
- 它是一个json格式的文件,在它里面保存了包各种相关的信息
name 包名
version 版本
dependencies 依赖
main 包的主要的文件
bin 可执行文件
- npm(Node Package Manager node的包管理器)
- 通过npm可以对node中的包进行上传、下载、搜索等操作
- npm会在安装完node以后,自动安装
- npm的常用指令
npm -v 查看npm的版本
npm version 查看所有模块的版本
npm init 初始化项目(创建package.json)
npm i/install 包名 安装指定的包
npm i/install 包名 --save 安装指定的包并添加依赖
npm i/install 包名 -g 全局安装(一般都是一些工具)
npm i/install 安装当前项目所依赖的包
npm s/search 包名 搜索包
npm r/remove 包名 删除一个包
文件系统(File System)
- Buffer(缓冲区)
- Buffer和数组的结构的非常类似,Buffer是用来存储二进制数据的
- Buffer的方法
- Buffer.from(字符串)
- 将一个字符串中内容保存到一个buffer中
- buf.toString()
- 将buffer转换为一个字符串
- Buffer.alloc(size)
- 创建一个指定大小的buffer对象
- Buffer.allocUnsafe(size)
- 创建一个指定大小的buffer对象,可以包含敏感数据
- fs模块
- 在Node通过fs模块来对系统中的文件进行操作,fs模块是node中已经继承好了,不需要在使用npm下载,直接引入即可
- 引入fs
var fs = require("fs");
- fs模块中的大部分操作都提供了两种方法,同步方法和异步方法
同步方法带sync
异步方法没有sync,都需要回调函数
- 写入文件
1.同步写入
2.异步写入
3.简单写入
4.流式写入
- 读取文件
1.同步读取
2.异步读取
3.简单读取
4.流式读取
- 方法
- 打开文件
fs.open(path, flags[, mode], callback)
fs.openSync(path, flags[, mode])
- 读写文件
fs.write(fd, string[, position[, encoding]], callback)
fs.writeSync(fd, string[, position[, encoding]])
fs.read(fd, buffer, offset, length, position, callback)
fs.readSync(fd, buffer, offset, length, position)
- 关闭文件
fs.close(fd,callback)
fs.closeSync(fd);
- 简单文件读取和写入
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
fs.readFile(path[, options], callback)
fs.readFileSync(path[, options])
- 流式文件读取和写入
- 流式读取和写入适用于一些比较大的文件
fs.createWriteStream(path[, options])
fs.createReadStream(path[, options])
Node介绍 base LPZ
链接 https://www.yuque.com/lipengz...
链接2 https://lurongtao.gitee.io/fe...
- Node.js是JavaScript 运行时
- 通俗易懂的讲,Node.js是JavaScript的运行平台
- Node.js既不是语言,也不是框架,它是一个平台
浏览器中的JavaScript
EcmaScript
- 基本语法
- if
- var
- function
- Object
- Array
- Bom
- Dom
Node.js中的JavaScript
- 没有Bom,Dom
EcmaScript 基本的 JavaScript 语言部分
- 变量
- 方法
- 数据类型
- 内置对象
- Array
- Object
- Date
- Math
在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
- JavaScript 长久以来一直被限制在浏览器的沙箱中运行, 它的能力取决于浏览器中间层提供的支持多少。 Node 将高性能的 V8 带到了服务器端,使 JavaScript 也可以开发出实时高性能的服务器。 在 Node 中,不再与 CSS 样式表,DOM 树打交道, 可以随意的访问本地文件,搭建 WebSocket 服务器,连接数据库等系统级底层操作。 Node 不处理 UI,只关心数据,无论是本地数据还是网络数据。 前后端编程统一,大大降低了前后端编程切换的代码。对于前端工程师而言,自己熟悉的 JavaScript 如今竟然可以在另一个地方大放异彩, 不谈其他原因,仅仅因为好奇,也值得去关注和探究它。
Node 运行机制
Node能做什么
- web服务器后台
命令行工具
- npm(node)
- git(c语言)
- hexo(node)
- ...
对于前端工程师来讲,接触最多的是它的命令行工具
- 自己写的很少,主要是用别人第三方的
- webpack
- gulp
- npm
相关链接
● Node.js 官方文档
https://nodejs.org/en/docs/
● Node.js 中文文档(非官方)
http://nodejs.cn/
● 深入浅出 Node.js
https://read.douban.com/ebook...
● Node.js 权威指南
https://book.douban.com/subje...
● Node.js 实战
https://book.douban.com/subje...
● Node.js 实战
https://book.douban.com/subje...
● Node.js 实战(第 2 季)
https://book.douban.com/subje...
● Node.js 中文社区
http://cnodejs.org/
● Node.js 包教不包会
https://github.com/alsotang/n...
● EcmaScript 6 入门
http://es6.ruanyifeng.com/
● 七天学会 NodeJS
https://github.com/nqdeng/7-d...
代码风格规范:
- Airbnb JavaScript Style
起步
安装Node环境
- 查看Node环境的版本号
- 下载:https://nodejs.org/en/
安装:
- 傻瓜式安装,一路
next
- 安装过再次安装会升级
- 傻瓜式安装,一路
确认Node环境是否安装成功
- 查看node的版本号:
node --version
- 或者
node -v
- 查看node的版本号:
- 配置环境变量
解析执行JavaScript
- 创建编写JavaScript脚本文件
- 打开终端,定位脚本文件的所属目录
- 输入
node 文件名
执行对应的文件
注意:文件名不要用node.js
来命名,也就是说除了node
这个名字随便起,最好不要使用中文。
文件的读写
http
服务器:
// 1.加载http核心模块
var http = require('http');
// 2.使用http.createServer()创建一个web服务器
var server = http.createServer();
// 3.服务器要做的事儿
// 提供服务:对数据服务
// 发请求
// 接收请求
// 处理请求
// 反馈(发送响应)
// 当客户端请求过来,就会自动触发服务器的request请求事件,然后执行第二个参数:回调处理函数
server.on('request',function(res, req){
console.log('收到客户的请求了')
console.log('请求我的客户端的地址是:', req.socket.remoteAddress, req.socket.remotePort)
})
// 4.绑定端口号,启动服务
server.listen(3000,function(){
console.log('runing...')
})
Node.js 中的 JavaScript
ECMAScript
和浏览器一样,不过在Node中没有Bom和Dom
全局成员
https://nodejs.org/dist/lates...
Node中的模块系统
核心模块
核心模块的本质也是文件。核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了,你可以在github 的lib 找源码,已经编译在node.exe中了
核心模块是由 Node 提供的一个个的具名的模块,它们都有自己特殊的名称标识,例如
- fs 文件操作模块
- http 网络服务构建模块
- os 操作系统信息模块
- path 路径处理模块
- 。。。。
* 所有核心模块在使用的时候都必须手动的先使用 `require` 方法来加载,然后才可以使用,例如:
- `var fs = require('fs')`
http
创建编写服务器的
端口号 ip地址和端口号.png
- ip 地址定位计算机
- 端口号定位具体的应用程序 所有需要联网通信的应用程序都会占用一个端口号
Content-Type
- 在服务端默认发送的数据,其实是 utf8 编码的内容,但是浏览器不知道你是 utf8 编码的内容。浏览器在不知道服务器响应内容的编码的情况下会按照当前操作系统的默认编码去解析。中文操作系统默认是 gbk。 解决方法就是正确的告诉浏览器我给你发送的内容是什么编码的,请用此编码解析 。 在 http 协议中,Content-Type 就是用来告知对方我给你发送的数据内容是什么类型
- 服务器最好把每次响应的数据是什么内容类型都告诉客户端,而且要正确的告诉
- 不同的资源对应的 Content-Type 是不一样,具体参照:http://tool.oschina.net/commons
- 对于文本类型的数据,最好都加上编码,目的是为了防止中文解析乱码问题
- 在node眼里 返回的都是字符串如果你发送的是 html 格式的字符串,则也要告诉浏览器我给你发送是 text/html 格式的内容。让浏览器以标签形式解析默认浏览器是会解析html的,但是其中的中文还是会乱码,使用content解决。
- 图片不需要指定编码 一般只为字符数据才指定编码
通过网络发送文件
- 发送的并不是文件,本质上来讲发送是文件的内容
- 当浏览器收到服务器响应内容之后,就会根据你的 Content-Type 进行对应的解析处理
状态码问题
重定向
301
302
url
解析请求路径中的查询字符串 url.parse(path)
path
./ 当前目录,不可省略
../ 上一级目录,不可省略
/xxx / 在这里表示的是当前文件模块所属磁盘根路径
d:/a/foo.js 几乎不用
参考文档:https://nodejs.org/docs/lates...
- path.basename:获取路径的文件名,默认包含扩展名
- path.dirname:获取路径中的目录部分
- path.extname:获取一个路径中的扩展名部分
path.parse:把路径转换为对象
- root:根路径
- dir:目录
- base:包含后缀名的文件名
- ext:后缀名
- name:不包含后缀名的文件名
- path.join:拼接路径 很棒 会智能去除多余的斜杠
- path.isAbsolute:判断一个路径是否为绝对路径[![image-20200315150610001]()]
fs
浏览器中的 JavaScript 是没有文件操作的能力.但是 Node 中的 JavaScript 具有文件操作的能力
fs.readFile
第一个参数就是要读取的文件路径
第二个参数是一个回调函数
读取成功 data :数据 error :null【false】
读取失败 data:undefined error: 错误对象 【true】
文件中存储的其实都是二进制数据 0 1 输出data看到的不是 0 和 1 呢?原因是二进制转为 16 进制.但是无论是二进制01还是16进制,人类都不认识
fs.writeFile
第一个参数:文件路径
第二个参数:文件内容
第三个参数:回调函数
Node中的其它成员(__dirname,__filename)
在每个模块中,除了require
,exports
等模块相关的API之外,还有两个特殊的成员:
__dirname
,是一个成员,可以用来动态获取当前文件模块所属目录的绝对路径__filename
,可以用来动态获取当前文件的绝对路径(包含文件名)__dirname
和filename
是不受执行node命令所属路径影响的[![image-20200315151551873]()](https://github.com/smallC-L-Y...学习笔记.md)
在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。
所以为了解决这个问题,只需要把相对路径变为绝对路径(绝对路径不受任何影响)就可以了。 但是你不能使用自己计算机的绝对路径,别人的计算机没有的。
就可以使用__dirname
或者__filename
来帮助我们解决这个问题
在拼接路径的过程中,为了避免手动拼接带来的一些低级错误(因为操作系统的不同 路径的分隔符方向不用),推荐使用path.join()
来辅助拼接
// aa.js
var fs = require('fs');
var path = require('path');
// console.log(__dirname + 'a.txt');
// fs.readFile('./a.txt') 这里的./a.txt 不是相对于当前文件路径
// ./a.txt 相对于执行 node 命令所处的终端路径 这不是错误,Node 就是这样设计的. 就是说,文件操作路径中,相对路径设计的就是相对于执行 node 命令所处的路径 所以cd .. 在上一层 node 目录/aa.js 会报错 可以在控制要看需错误信息 你会发现路径有问题
// path.join方法会将文件操作中的相对路径都统一的转为动态的绝对路径
fs.readFile(path.join(__dirname + '/a.txt'),'utf8',function(err,data){
if(err){
throw err
}
console.log(data);
});
注意:
模块中的路径标识和文件操作中的相对路径标识不一致
模块中的路径标识就是相对于当前文件模块,不受node命令所处路径影响
第三方模块
第三方模块 必须通过npm来下载才可以使用
Node 中使用 art-template 模板引擎
art-template 不仅可以在浏览器使用,也可以在 node 中使用.
npm install art-template
该命令在哪执行就会把包下载到哪里。默认会下载到 node_modules 目录中. node_modules 不要改,也不支持改.模板引擎最早就是诞生于服务器领域,后来才发展到了前端。
web : template('script 标签 id', {对象}) 浏览器中需要引用 lib/template-web.js 文件
node: template.render('模板字符串'替换对象)
默认读取到的 data 是二进制数据而模板引擎的 render 方法需要接收的是字符串所以我们文件读取后的 data 二进制数据转为 字符串 才可以给模板引擎使用。
模板引擎不关心你的字符串内容,只关心自己能认识的模板标记语法,例如 {{}}
自定义模块
- 自己创建的文件
CommonJS模块规范
- 什么是模块化
- 文件作用域(模块是独立的,在不同的文件使用必须要重新引用)【在node中没有全局作用域,它是文件模块作用域】
通信规则
- 加载require
- 导出exports
在Node中的JavaScript还有一个重要的概念,模块系统。
- 在 Node 中没有全局作用域的概念
- 在 Node 中,只能通过 require 方法来加载执行JavaScript 脚本文件
require 加载只能是执行其中的代码,具体可不可以访问需要看模块是否暴露。文件与文件之间由于是模块作用域,所以不会有污染的问题
- 模块完全是封闭的,外部无法访问内部,内部也无法访问外部
- 模块作用域固然带来了一些好处,可以加载执行多个文件,可以完全避免变量命名冲突污染的问题。但是某些情况下,模块与模块是需要进行通信的,在每个模块中,都提供了一个对象:
exports
,该对象默认是一个空对象,你要做的就是把需要被外部访问使用的成员手动的挂载到exports
接口对象中, 然后谁来require
这个模块,谁就可以得到模块内部的exports
接口对象
### tips
使用exports暴露,才可以在rquire时使用。只能得到我想要给你的成员,而且解决变量命名冲突的问题、
exports 是一个对象,require得是这个exports(实际是module.exports)对象
我们可以通过多次为这个对象添加成员实现对外导出多个内部成员
exports.add = add
exports.str = 'hello'
require默认得到的是对象 使用对象中的成员必须 . 点儿某个成员来访问
你可以认为在每个模块的最后 return 了这个 exports
现在我有一个需求。有时候,对于一个模块,我仅仅就是希望导出一个方法就可以了我希望加载得到直接就是一个:方法 字符串 数字 数组
单独暴露的需求:而非 exports = xxx 挂载的方式
这时候就 exports = fun1; 这样会发现得到的是一个空对象
正确使用方式 module.exports = fun1; 你会发现引入的就是个函数
这种暴露方式存在覆盖的情况
### 加载require
语法:
var 自定义变量名 = require('模块')
作用:require 是一个方法它的作用就是用来加载模块的。有时候,我们加载文件模块的目的不是为了简简单单的执行里面的代码,更重要是为了使用里面的某个成员。
- 执行被加载模块中的代码
- 得到被加载模块中的
exports
导出接口对象
### 导出exports
- Node中是模块作用域,默认文件中所有的成员只在当前模块有效
在每个文件模块中都提供了一个对象exports, 默认是一个空对象
你要做的就是把所有需要被外部访问的成员挂载到这个 exports 对象中
对于希望可以被其他模块访问到的成员,我们需要把这些公开的成员都挂载到
exports
接口对象中就可以了导出多个成员(必须在对象中):
exports.a = 123; exports.b = function(){ console.log('bbb') }; exports.c = { foo:"bar" }; exports.d = 'hello';
导出单个成员(拿到的就是函数,字符串)/多个成员:
module.exports = 'hello';
module.exports = { foo = 'hello', add:function(){ return x+y; } };
以下情况会覆盖:
module.exports = 'hello'; //后者会覆盖前者 module.exports = function add(x,y) { return x+y; }
也可以通过以下方法来导出多个成员:
模块原理
在 Node 中,每个模块内部都有一个自己的 module 对象
该 module 对象中,有一个成员叫:exports 也是一个对象
var module = {
exports: {
foo: 'bar',
add: function
}
}
也就是说如果你需要对外导出成员,只需要把导出的成员挂载到 module.exports 中。我们发现,每次导出接口成员的时候都通过 module.exports.xxx = xxx 的方式很麻烦,点儿的太多了所以,Node 为了简化你的操作,专门提供了一个变量:exports 等于 module.exports 也就是说在模块中还有这么一句代码 var exports = module.exports 最后 return 的是 module.exports 不是 exports
当一个模块需要导出单个成员的时候 直接给 exports 赋值是不管用的。谁来require 我,谁就得到 module.exports 给 exports 赋值会断开和 module.exports 之间的引用同理,给 module.exports 重新赋值也会断开 exports !== module.exports但是可以重新建立两者的引用关系 exports = module.exports
前面再牛逼,在这里都全部推翻了,重新赋值 最终得到的是 Function
module.exports = function () {
console.log('hello')
}
exports和module.exports
的一个引用:
console.log(exports === module.exports); //true
exports.foo = 'bar';
//等价于
module.exports.foo = 'bar';
当给exports重新赋值后,exports!= module.exports.
最终return的是module.exports,无论exports中的成员是什么都没用。
真正去使用的时候:
导出单个成员:exports.xxx = xxx;
导出多个成员:module.exports 或者 modeule.exports = {};
总结
// 引用服务
var http = require('http');
var fs = require('fs');
// 引用模板
var template = require('art-template');
// 创建服务
var server = http.createServer();
// 公共路径
var wwwDir = 'D:/app/www';
server.on('request', function (req, res) {
var url = req.url;
// 读取文件
fs.readFile('./template-apche.html', function (err, data) {
if (err) {
return res.end('404 Not Found');
}
fs.readdir(wwwDir, function (err, files) {
if (err) {
return res.end('Can not find www Dir.')
}
// 使用模板引擎解析替换data中的模板字符串
// 去xmpTempleteList.html中编写模板语法
var htmlStr = template.render(data.toString(), {
title: 'D:/app/www/ 的索引',
files:files
});
// 发送响应数据
res.end(htmlStr);
})
})
});
server.listen(3000, function () {
console.log('running....');
})
1.jQuery中的each 和 原生JavaScript方法forEach的区别:
提供源头:
原生js是es5提供的(不兼容IE8),
jQuery的each是jQuery第三方库提供的(如果要使用需要用2以下的版本也就是1.版本),它的each方法主要用来遍历jQuery实例对象(伪数组),同时也可以做低版本forEach的替代品,jQuery的实例对象不能使用forEach方法,如果想要使用必须转为数组([].slice.call(jQuery实例对象))才能使用
2.模块中导出多个成员和导出单个成员
3.301和302的区别:
301永久重定向,浏览器会记住
302临时重定向
4.exports和module.exports的区别:
每个模块中都有一个module对象
module对象中有一个exports对象
我们可以把需要导出的成员都挂载到module.exports接口对象中
也就是`module.exports.xxx = xxx`的方式
但是每次写太多了就很麻烦,所以Node为了简化代码,就在每一个模块中都提供了一个成员叫`exports`
`exports === module.exports`结果为true,所以完全可以`exports.xxx = xxx`
当一个模块需要导出单个成员的时候必须使用`module.exports = xxx`的方式,=,使用`exports = xxx`不管用,因为每个模块最终return的是module.exports,而exports只是module.exports的一个引用,所以`exports`即使重新赋值,也不会影响`module.exports`。
有一种赋值方式比较特殊:`exports = module.exports`这个用来新建立引用关系的。
require的加载规则
核心模块
- 模块名
第三方模块
- 模块名
用户自己写的
- 路径
require的加载规则:
优先从缓存加载
什么意思呢?
必须有个文件a, 文件b中引用了它, 但是文件c引用了b.
这时执行文件c后,必然会执行文件b,因为文件b引用了a
所以也会执行a . 但是当你执行文件b你会发现文件a不会执行。
但是文件暴露的东西 文件b还是可以接收到的。这样做的目的是为了避免重复加载,提高模块加载效率
判断模块标识符
- 核心模块
- 自己写的模块(路径形式的模块)
第三方模块(node_modules)
- 第三方模块的标识就是第三方模块的名称(不可能有第三方模块和核心模块的名字一致)
npm
- 开发人员可以把写好的框架库发布到npm上
- 使用者通过npm命令来下载
引用第三方包会去哪里找的问题: 比如express
var 名称 = require('npm install【下载包】 的包名')
- node_modules/express/package.json require的是main属性指向的入库文件
- 如果package.json或者main不成立,则默认回去查找当前目录index.js作为require.也就是说index.js是一个备选项,如果main没有指定,则加载index.js文件
- 如果以上条件都不满足,则继续进入上一级目录中的node_modules按照上面的规则依次查找,一个项目只有一个node_modules,放在项目根目录中,子目录可以直接调用根目录的文件.直到当前文件所属此盘根目录都找不到最后报错 can not find module xxx
// 如果条件都不满足则会进入上一级目录进行查找
// 注意:
var template = require('art-template');
模块标识符中的/
和文件操作路径中的/
文件操作路径:
// 咱们所使用的所有文件操作的API都是异步的
// 就像ajax请求一样
// 读取文件
// 文件操作中 ./ 相当于当前模块所处磁盘根目录
// ./index.txt 相对于当前目录
// /index.txt 相对于当前目录
// /index.txt 绝对路径,当前文件模块所处根目录
// d:express/index.txt 绝对路径
fs.readFile('./index.txt',function(err,data){
if(err){
return console.log('读取失败');
}
console.log(data.toString());
})
模块操作路径:
// 在模块加载中,相对路径中的./不能省略
// 这里省略了.也是磁盘根目录
require('./index')('hello')
服务端渲染以及客户端渲染
todo png
- 简单来说,在服务端使用模板引擎就是服务端渲染,只需要请求一次。服务端会把html文件渲染好。所以速度更快.
客户端渲染
浏览器请求,在服务端存放的一个html 文件。包含ajax以及模板渲染语法,浏览器请求后,得到服务器响应的字符串了。解析文件后。发现script 以及ajax等 再次向服务器请求,得到服务器的响应数据后,最终使用模板引擎来替换显示在界面..... 问题是页面响应了,但是数据量大的话,可能会出现白屏...
script link img iframe audio vedio 等具有外链的资源标签 src link的href属性时浏览器会再次请求 a链接的href 属性不会立即请求是需要点击的
辨别渲染方式
打开网页源代码,可以看到的就是服务端渲染的,客户端后续添加上的是没法看见的.... 还有一个特征,会发生刷新 点击下一步后你会发现刷新这种的就是服务端渲染。前端一般是异步无刷新,只是局部更新
- 这里为什么需要两种 客户ajax异步渲染不利于seo 不易于搜素引擎爬虫到。
服务端渲染
- 说白了就是在服务端使用模板引擎
- 模板引擎最早诞生于服务端,后来才发展到了前端
服务端渲染和客户端渲染的区别
- 客户端渲染不利于 SEO 搜索引擎优化
- 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
- 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的
- 而是两者结合来做的
- 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化
- 而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染
npm
- node package manage(node包管理器)
- 通过npm命令安装jQuery包(npm install --save jquery),在安装时加上--save会主动生成说明书文件信息(将安装文件的信息添加到package.json里面)
npm网站
npmjs.com 网站 是用来搜索npm包的
npm命令行工具
www.cnblogs.com/PeunZhang/p/5553574.html
npm是一个命令行工具,只要安装了node就已经安装了npm。
npm也有版本概念,可以通过npm --version
来查看npm的版本
升级npm(自己升级自己):
npm install --global npm
常用命令
npm init(生成package.json说明书文件)
- npm init -y(可以跳过向导,快速生成)
npm install
- 一次性把dependencies选项中的依赖项全部安装
- 简写(npm i)
- 该命令在哪执行 就会在当前目录生成一个node_modules文件夹 并将文件放进去
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被墙问题
npm存储包文件的服务器在国外,有时候会被墙,速度很慢,所以需要解决这个问题。
https://developer.aliyun.com/...淘宝的开发团队把npm在国内做了一个镜像(也就是一个备份)。
安装淘宝的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;
只要经过上面的配置命令,则以后所有的npm install
都会通过淘宝的服务器来下载
package.json
每一个项目都要有一个package.json
文件(包描述文件,就像产品的说明书一样)
这个文件可以通过npm init
自动初始化出来
D:\code\node中的模块系统>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (node中的模块系统)
Sorry, name can only contain URL-friendly characters.
package name: (node中的模块系统) cls
version: (1.0.0)
description: 这是一个测试项目
entry point: (main.js)
test command:
git repository:
keywords:
author: xiaochen
license: (ISC)
About to write to D:\code\node中的模块系统\package.json:
{
"name": "cls",
"version": "1.0.0",
"description": "这是一个测试项目",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "xiaochen",
"license": "ISC"
}
Is this OK? (yes) yes
对于目前来讲,最有用的是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
这个文件
- npm5以后的版本安装都不要加
--save
参数,它会自动保存依赖信息 - 当你安装包的时候,会自动创建或者更新
package-lock.json
文件 package-lock.json
这个文件会包含
node_modules
中所有包的信息(版本,下载地址。。。)
- 这样的话重新
npm install
的时候速度就可以提升
- 这样的话重新
从文件来看,有一个
lock
称之为锁
- 这个
lock
使用来锁版本的 - 如果项目依赖了
1.1.1
版本 - 如果你重新install其实会下载最细版本,而不是
1.1.1
package-lock.json
的另外一个作用就是锁定版本号,防止自动升级
Express(快速的)
作者:Tj
原生的http在某些方面表现不足以应对我们的开发需求,所以就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码高度统一。
在node中有很多web开发框架。主要学习express
- 这个
http://expressjs.com/
,其中主要封装的是http。// 1 安装 // 2 引包 var express = require('express'); // 3 创建服务器应用程序 也就是原来的http.createServer(); // 相当于以前的创建server var app = express(); // 公开指定目录 // 只要通过这样做了,就可以通过/public/xx的方式来访问public目录中的所有资源 // 在Express中开放资源就是一个API的事 app.use('/public/',express.static('/public/')); //模板引擎在Express中开放模板也是一个API的事 // 当服务器收到请求方式为get 且地址为 / 的时候,执行回调处理函数 // 再也不需要你在自己判断url app.get('/',function(req,res){ // 不推荐使用以前的res.write () res.end() // 不用担心你的content type 中文 end 会有乱码 send就不会 res.send('hello express'); }) // 相当于server.listen app.listen(3000,function(){ console.log('app is runing at port 3000'); })
学习Express
起步
安装:[![image-20200310123723079]()](https://github.com/smallC-L-Y...学习笔记.md)
cnpm install express
hello world:[![image-20200310124850557]()](https://github.com/smallC-L-Y...学习笔记.md)
// 引入express
var express = require('express');
// 1. 创建app
var app = express();
// 2.
app.get('/',function(req,res){
// 1
// res.write('Hello');
// res.write('World');
// res.end()
// 1还可以写成2
// res.end('hello world');
// 3是express中的写法 不用判断响应头 而且express 内置了错误处理 请求不存在的url 状态码是404 且内容为 cant ....
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');})
Express静态服务API 开放静态资源
// app.use不仅仅是用来处理静态资源的,还可以做很多工作(body-parser的配置)app.use(express.static('public'));app.use(express.static('files'));// 公开public 目录app.use('/stataic',express.static('public'));// 引入expressvar express = require('express');// 创建appvar app = express();// 开放静态资源// 1.当以/public/开头的时候,去./public/目录中找对应资源// 访问:http://127.0.0.1:3000/public/login.htmlapp.use('/public/',express.static('./public/')); // 2.当省略第一个参数的时候,可以通过省略/public的方式来访问// 访问:http://127.0.0.1:3000/login.html// app.use(express.static('./public/')); // 3.访问:http://127.0.0.1:3000/a/login.html// a相当于public的别名// app.use('/a/',express.static('./public/')); // app.get('/',function(req,res){ res.end('hello world');});app.listen(3000,function(){ console.log('express app is runing...');});
在Express中配置使用art-templete
模板引擎
- art-template官方文档
在node中,有很多第三方模板引擎都可以使用,不是只有
art-template
- 还有ejs,jade(pug),handlebars,nunjucks
安装:
npm install --save art-templatenpm install --save express-art-template//两个一起安装npm i --save art-template express-art-template
配置:
app.engine('html', require('express-art-template'));
使用:
app.get('/',function(req,res){ // express默认会去views目录找index.html res.render('index.html',{ title:'hello world' });})
如果希望修改默认的views
视图渲染存储目录,可以:
// 第一个参数views千万不要写错app.set('views',目录路径);
在Express中获取表单请求数据
获取get请求数据:
Express内置了一个api,可以直接通过req.query
来获取数据
// 通过requery方法获取用户输入的数据// req.query只能拿到get请求的数据 var comment = req.query;
获取post请求数据:
在Express中没有内置获取表单post请求体的api,这里我们需要使用一个第三方包body-parser
来获取数据。
安装:
npm install --save body-parser;
配置:
// 配置解析表单 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))
})
在Express中配置使用express-session
插件操作
参考文档:https://github.com/expressjs/...
安装:
npm install express-session
配置:
//该插件会为req请求对象添加一个成员:req.session默认是一个对象//这是最简单的配置方式//Session是基于Cookie实现的app.use(session({ //配置加密字符串,他会在原有的基础上和字符串拼接起来去加密 //目的是为了增加安全性,防止客户端恶意伪造 secret: 'keyboard cat', resave: false, saveUninitialized: true,//无论是否适用Session,都默认直接分配一把钥匙 cookie: { secure: true }}))
使用:
// 读//添加Session数据//session就是一个对象req.session.foo = 'bar';//写//获取session数据req.session.foo//删req.session.foo = null;delete req.session.foo
提示:
默认Session数据时内存储数据,服务器一旦重启,真正的生产环境会把Session进行持久化存储。
利用Express实现ADUS项目
模块化思想
模块如何划分:
- 模块职责要单一
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');
// Express专门提供了一种更好的方式
// 专门用来提供路由的
var express = require('express');
// 1 创建一个路由容器
var router = express.Router();
// 2 把路由都挂载到路由容器中
router.get('/students', function(req, res) {
// res.send('hello world');
// readFile的第二个参数是可选的,传入utf8就是告诉他把读取到的文件直接按照utf8编码,直接转成我们认识的字符
// 除了这样来转换,也可以通过data.toString()来转换
fs.readFile('./db.json', 'utf8', function(err, data) {
if (err) {
return res.status(500).send('Server error.')
}
// 读取到的文件数据是string类型的数据
// console.log(data);
// 从文件中读取到的数据一定是字符串,所以一定要手动转换成对象
var students = JSON.parse(data).students;
res.render('index.html', {
// 读取文件数据
students:students
})
})
});
router.get('/students/new',function(req,res){
res.render('new.html')
});
router.get('/students/edit',function(req,res){
});
router.post('/students/edit',function(req,res){
});
router.get('/students/delete',function(req,res){
});
// 3 把router导出
module.exports = router;
app.js:
var router = require('./router');
// router(app);
// 把路由容器挂载到app服务中
// 挂载路由
app.use(router);
设计操作数据的API文件模块
es6中的find和findIndex:
find接受一个方法作为参数,方法内部返回一个条件
find会便利所有的元素,执行你给定的带有条件返回值的函数
符合该条件的元素会作为find方法的返回值
如果遍历结束还没有符合该条件的元素,则返回undefined[![image-20200313103810731]()](https://github.com/smallC-L-Y...学习笔记.md)
/**
* student.js
* 数据操作文件模块
* 职责:操作文件中的数据,只处理数据,不关心业务
*/
var fs = require('fs');
/**
* 获取所有学生列表
* return []
*/
exports.find = function(){
}
/**
* 获取添加保存学生
*/
exports.save = function(){
}
/**
* 更新学生
*/
exports.update = function(){
}
/**
* 删除学生
*/
exports.delete = function(){
}
步骤
- 处理模板
- 配置静态开放资源
- 配置模板引擎
- 简单的路由,/studens渲染静态页出来
- 路由设计
- 提取路由模块
- 由于接下来的一系列业务操作都需要处理文件数据,所以我们需要封装Student.js'
先写好student.js文件结构
- 查询所有学生列别哦的API
- findById
- save
- updateById
- deleteById
实现具体功能
- 通过路由收到请求
接受请求中的参数(get,post)
- req.query
- req.body
- 调用数据操作API处理数据
- 根据操作结果给客户端发送请求
业务功能顺序
- 列表
- 添加
- 编辑
- 删除
子模板和模板的继承(模板引擎高级语法)【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 }}
最终的显示效果:
[![image-20200316134759517]()](https://github.com/smallC-L-Y...学习笔记.md)
MongoDB
关系型和非关系型数据库
关系型数据库(表就是关系,或者说表与表之间存在关系)。
- 所有的关系型数据库都需要通过
sql
语言来操作 - 所有的关系型数据库在操作之前都需要设计表结构
而且数据表还支持约束
- 唯一的
- 主键
- 默认值
- 非空
非关系型数据库
- 非关系型数据库非常的灵活
- 有的关系型数据库就是key-value对儿
但MongDB是长得最像关系型数据库的非关系型数据库
- 数据库 -》 数据库
- 数据表 -》 集合(数组)
- 表记录 -》文档对象
一个数据库中可以有多个数据库,一个数据库中可以有多个集合(就是表,数组),一个集合中可以有多个文档(表记录)
{
qq:{
user:[
{},{},{}...
]
}
}
- 也就是说你可以任意的往里面存数据,没有结构性这么一说
安装
下载
安装
npm i mongoose
- 配置环境变量
TODO 为什么配置环境变量
- 最后输入
mongod --version
测试是否安装成功
启动和关闭数据库
启动:
# mongodb 默认使用执行mongod 命令所处盼复根目录下的/data/db作为自己的数据存储目录
# 所以在第一次执行该命令之前先自己手动新建一个 /data/db
mongod
method 2:
power shell net start mongd
如果想要修改默认的数据存储目录,可以:
mongod --dbpath = 数据存储目录路径
停止:
在开启服务的控制台,直接Ctrl+C;
或者直接关闭开启服务的控制台。
[![image-20200314101047100]()](https://github.com/smallC-L-Y...学习笔记.md)
连接数据库
连接:
# 该命令默认连接本机的 MongoDB 服务
mongo
退出:
# 在连接状态输入 exit 退出连接
exit
[![image-20200314100821112]()](https://github.com/smallC-L-Y...学习笔记.md)
基本命令
show dbs
- 查看数据库列表(数据库中的所有数据库)
db
- 查看当前连接的数据库
use 数据库名称
- 切换到指定的数据库,(如果没有会新建)
show collections
- 查看当前目录下的所有数据表
db.表名.find()
- 查看表中的详细信息
在Node中如何操作MongoDB数据库
Navicat premium
使用官方的
MongoDB
包来操作http://mongodb.github.io/node...
使用第三方包
mongoose
来操作MongoDB数据库第三方包:
mongoose
基于MongoDB官方的mongodb
包再一次做了封装,名字叫mongoose
,是WordPress项目团队开发的。https://mongoosejs.com/
[![image-20200314105632745]()](https://github.com/smallC-L-Y...学习笔记.md)
[![image-20200314105717993]()](https://github.com/smallC-L-Y...学习笔记.md)
学习指南(步骤)
官方学习文档:https://mongoosejs.com/docs/i...
设计Scheme 发布Model (创建表)
// 1.引包// 注意:按照后才能require使用var mongoose = require('mongoose');// 拿到schema图表var Schema = mongoose.Schema;// 2.连接数据库// 指定连接数据库后不需要存在,当你插入第一条数据库后会自动创建数据库mongoose.connect('mongodb://localhost/test');// 3.设计集合结构(表结构)// 用户表var userSchema = new Schema({ username: { //姓名 type: String, require: true //添加约束,保证数据的完整性,让数据按规矩统一 }, password: { type: String, require: true }, email: { type: String }});// 4.将文档结构发布为模型// mongoose.model方法就是用来将一个架构发布为 model// 第一个参数:传入一个大写名词单数字符串用来表示你的数据库的名称// mongoose 会自动将大写名词的字符串生成 小写复数 的集合名称// 例如 这里会变成users集合名称// 第二个参数:架构// 返回值:模型构造函数var User = mongoose.model('User', userSchema);
添加数据(增)
// 5.通过模型构造函数对User中的数据进行操作var user = new User({ username: 'admin', password: '123456', email: 'xiaochen@qq.com'});user.save(function(err, ret) { if (err) { console.log('保存失败'); } else { console.log('保存成功'); console.log(ret); }});
删除(删)
根据条件删除所有:
User.remove({ username: 'xiaoxiao' }, function(err, ret) { if (err) { console.log('删除失败'); } else { console.log('删除成功'); console.log(ret); } });
根据条件删除一个:
Model.findOneAndRemove(conditions,[options],[callback]);
根据id删除一个:
User.findByIdAndRemove(id,[options],[callback]);
更新(改)
更新所有:
User.remove(conditions,doc,[options],[callback]);
根据指定条件更新一个:
User.FindOneAndUpdate([conditions],[update],[options],[callback]);
根据id更新一个:
// 更新 根据id来修改表数据User.findByIdAndUpdate('5e6c5264fada77438c45dfcd', { username: 'junjun'}, function(err, ret) { if (err) { console.log('更新失败'); } else { console.log('更新成功'); }});
查询(查)
查询所有:
// 查询所有 User.find(function(err,ret){ if(err){ console.log('查询失败'); }else{ console.log(ret); } });
条件查询所有:
// 根据条件查询 User.find({ username:'xiaoxiao' },function(err,ret){ if(err){ console.log('查询失败'); }else{ console.log(ret); } });
条件查询单个:
// 按照条件查询单个,查询出来的数据是一个对象({}) // 没有条件查询使用findOne方法,查询的是表中的第一条数据 User.findOne({ username: 'xiaoxiao' }, function(err, ret) { if (err) { console.log('查询失败'); } else { console.log(ret); } });
使用Node操作MySQL数据库
文档:https://www.npmjs.com/package...
安装:
npm install --save mysql // 引入mysql包 var mysql = require('mysql');
// 创建连接
var connection = mysql.createConnection({
host : 'localhost', //本机
user : 'me', //账号root
password : 'secret', //密码12345
database : 'my_db' //数据库名
});
// 连接数据库 (打开冰箱门)
connection.connect();
//执行数据操作 (把大象放到冰箱)
connection.query('SELECT * FROM users
', function (error, results, fields) {
if (error) throw error;//抛出异常阻止代码往下执行
// 没有异常打印输出结果
console.log('The solution is: ',results);
});
//关闭连接 (关闭冰箱门)
connection.end();
# 异步编程
首先你要明确一个问题,为什么需要一个回调函数。一般我们我们是通过一个
**回调函数**获取异步操作的结果。
## 回调函数
想得到异步操作的结果 比如setTimeout的结果:
function add(x,y){
console.log(1);
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 4
ok 这里你可以使用全部变量来解决,既然1s后有结果,那么我等他执行后再操作
let ret;
function add(x,y){
console.log(1);
setTimeout(function(){
console.log(2);
var ret = x + y;
},1000);
}
add(2,2);
setTimeout(function (
console.log(ret)
))
使用回调函数解决:回调函数:通过一个函数,获取函数内部的操作。(根据输入得到输出结果)
var ret;
function add(x,y,callback){
// callback就是回调函数
// var x = 10;
// var y = 20;
// var callback = function(ret){console.log(ret);}
console.log(1);
setTimeout(function(){
var ret = x + y;
callback(ret);
},1000);
console.log(3);
}
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(){
//console.log(oReq.responseText);
callback(oReq.responseText);
}
oReq.open("GET", url,true);
oReq.send();
}
get('data.json',function(data){
console.log(data);
});
## Promise
callback hell(回调地狱):
[![image-20200314143410972]()](https://github.com/smallC-L-Y/Demo/blob/notes/nodeJS学习笔记.md)
因为文件的读取是无法判断执行顺序(文件的执行顺序是依据文件的大小来决定的)(异步api无法保证文件的执行顺序)
var fs = require('fs');
fs.readFile('./data/a.text','utf8',function(err,data){
if(err){
// 1 读取失败直接打印输出读取失败
return console.log('读取失败');
// 2 抛出异常
// 作用1 阻止程序的执行
// 作用2 把错误信息打印到控制台
throw err;
}
console.log(data);
});
fs.readFile('./data/b.text','utf8',function(err,data){
if(err){
return console.log('读取失败');
throw err;
}
console.log(data);
});
// a b 文件的读取顺序不一定的
通过回调嵌套的方式来保证顺序:
var fs = require('fs');
fs.readFile('./data/a.text','utf8',function(err,data){
if(err){
return console.log('读取失败');
throw err;
}
console.log(data);
// 保证先读取a 然后读取b
fs.readFile('./data/b.text','utf8',function(err,data){
if(err){
return console.log('读取失败');
throw err;
}
console.log(data);
fs.readFile('./data/a.text',function(err,data){
if(err){
return console.log('读取失败');
throw err;
}
console.log(data);
});
});
});
为了解决以上编码方式带来的问题(回调地狱嵌套),所以在EcmaScript6新增了一个API:`Promise`。
- Promise:承诺,保证
- Promise本身不是异步的,但往往都是内部封装一个异步任务
基本语法:
// 在EcmaScript 6中新增了一个API Promise
// Promise 是一个构造函数
var fs = require('fs');
// 1 创建Promise容器 resolve:解决 reject:失败
var p1 = new Promise(function(resolve, reject) {
fs.readFile('./a.text', 'utf8', function(err, data) {
if (err) {
// 把容器的Pending状态变为rejected
reject(err);
} else {
// 把容器的Pending状态变为resolve
resolve(1234);
}
});
});
// 当p1成功了,然后就(then)做指定的操作
// then方法接收的function就是容器中的resolve函数
p1
.then(function(data) {
console.log(data);
}, function(err) {
console.log('读取文件失败了', err);
});
[![image-20200315100611620]()](https://github.com/smallC-L-Y/Demo/blob/notes/nodeJS学习笔记.md)
链式循环:[![image-20200315125559136]()](https://github.com/smallC-L-Y/Demo/blob/notes/nodeJS学习笔记.md)
对于普通值,第一个then函数中return的结果就可以在后面的then中function接收到比如 return 123 后面就接收到 123 return 'hello' 后面就接收到 'hello' 没有 return 后面收到的就是 undefined
然而我们一般并不是这样使用,真正有用的是:return 一个 Promise 对象
后续的 then 中的 方法的第一个参数会作为 p2 的 resolve。也就是p2.then
封装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('./a.txt')
.then(function(data) {
console.log(data);
return pReadFile('./b.txt');
})
.then(function(data) {
console.log(data);
return pReadFile('./a.txt');
})
.then(function(data) {
console.log(data);
})
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('注册成功');
})
## Generator
async函数
# 其他
## 修改完代码自动重启
我们在这里可以使用一个第三方命名行工具:`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);
})
## 数组的遍历方法,都是对函数作为一种参数
[![image-20200314094620191]()](https://github.com/smallC-L-Y/Demo/blob/notes/nodeJS学习笔记.md)
## EcmaScript 6
> 参考文档:https://es6.ruanyifeng.com/
# 项目案例
## 目录结构
.
app.js 项目的入口文件
controllers
models 存储使用mongoose设计的数据模型
node_modules 第三方包
package.json 包描述文件
package-lock.json 第三方包版本锁定文件(npm5之后才有)
public 公共静态资源
routes
views 存储视图目录
## 模板页
- 子模板
- 模板继承
## 路由设计
| 路由 | 方法 | get参数 | post参数 | 是否需要登录 | 备注 |
| --------------- | ---- | ------- | ----------------------- | ------------ | ------------ |
| / | get | | | | 渲染首页 |
| /register(登录) | get | | | | 渲染注册页面 |
| /register | post | | 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
[![image-20200316202757617]()](https://github.com/smallC-L-Y/Demo/blob/notes/nodeJS学习笔记.md)
中间件:把很复杂的事情分割成单个,然后依次有条理的执行。就是一个中间处理环节,有输入,有输出。
说的通俗易懂点儿,中间件就是一个(从请求到响应调用的方法)**方法**。该方法接收三个参数 Request 请求对象 Response 响应对象 next 下一个中间件
把数据从请求到响应分步骤来处理,每一个步骤都是一个中间处理环节。
var http = require('http')
var url = require('url')
var cookie = require('./middlewares/cookie')
var postBody = require('./middlewares/post-body')
var query = require('./middlewares/query')
var session = require('./middlewares/session')
var server = http.createServer(function (req, res) {
// 我需要自这一些操作
/* 解析表单 get 请求体 解析表单 post 请求体 解析 Cookie
处理 Session 使用模板引擎
*/
// 解析请求地址中的 get 参数
// var urlObj = url.parse(req.url, true)
// req 中没有query的 那我就给他添加 这既是中间件函数做的事情
// req.query = urlObj.query
query(req, res)
// 解析请求地址中的 post 参数
// req 中没有body的
// req.body = {
// foo: 'bar'
// }
postBody(req, res)
// 解析 Cookie
// req.cookies = {
// isLogin: true
// }
cookie(req, res)
// 配置 Session
// req.session = {}
session(req, res)
// 配置模板引擎
// req 中没有render的
res.render = function () {
}
if (req.url === 'xxx') {
// 处理
// query、body、cookies、session、render API 成员
} else if (url === 'xx') {
// 处理
}
//把每个过程制作为中间件 require进来
// console.log(req.query)
// console.log(req.body)
// console.log(req.cookies)
// console.log(req.session)
// 上面的过程都是了为了在后面做具体业务操作处理的时候更方便
})
server.listen(3000, function () {
console.log('3000. running...')
})
同一个请求对象所经过的中间件都是同一个请求对象和响应对象。
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.');
});
[![image-20200317110520098]()](https://github.com/smallC-L-Y/Demo/blob/notes/nodeJS学习笔记.md)
## 中间件的分类:
### 应用程序级别的中间件
万能匹配(不关心任何请求路径和请求方法的中间件):
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('get');
});
post:
app.post('/a',function(req,res){
res.send('post');
});
put:
app.put('/user',function(req,res){
res.send('put');
});
delete:
app.delete('/delete',function(req,res){
res.send('delete');
});
### 总
var express = require('express');
var app = express();
// 在express中,对中间件有几种分类
// 1 不关心任何请求路径和请求方法的中间件
// 也就是说任何请求都会进入这个中间件
// // 全局匹配中间件
// app.use(function(req, res, next) {
// console.log('1');
// // 当一个请求进入中间件后
// // 如果需要请求另外一个方法则需要使用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(){
console.log('/');
});
app.post('/a',function(){
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');
});
配置全局错误处理中间件:
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。