node.js 是什么?
node.js,也叫作node,或者nodejs,指的都是一个平台。
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,nodejs允许javascript代码运行在服务端
1. nodejs不是一门新的编程语言,nodejs是在服务端运行javascript的运行环境
2. 运行环境:写得程序想要运行必须要有对应的运行环境
php代码必须要有apache服务器
在web端,浏览器就是javascript的运行环境
在node端,nodejs就是javascript的运行环境
2. javascript并不只是能运行在浏览器端,浏览器端能够运行js是因为浏览器有js解析器,因此只需要有js解析器,任何软件都可以运行js。
3. nodejs可以在服务端运行js,因为nodejs是基于chrome v8的js引擎。
nodejs的本质:不是一门新的编程语言,nodejs是javascript运行在服务端的运行环境,编程语言还是javascript
nodejs与浏览器的区别
相同点:nodejs与浏览器都是js的运行环境,都能够解析js程序。对于ECMAScript语法来说,在nodejs和浏览器中都能运行。
不同点:nodejs无法使用DOM和BOM的操作,浏览器无法执行nodejs中的文件操作等功能
nodejs可以干什么?
-
开发服务端程序
扫描二维码关注公众号,回复: 5343731 查看本文章 -
开发命令行工具(CLI),比如npm,webpack,gulp,less,sass等 vue-cli
-
开发桌面应用程序(借助 node-webkit、electron 等框架实现)
运行nodejs程序
方式一:使用node执行js文件
-
创建js文件
helloworld.js
-
写nodejs的内容:
console.log('hello nodejs')
-
打开命令窗口
cmd
-
shift加右键打开命令窗口,执行
node 文件名.js
即可
-
-
执行命令:
node helloworld.js
注意:在nodejs中是无法使用DOM和BOM的内容的,因此document, window
等内容是无法使用的。
方式二:REPL介绍
-
REPL 全称: Read-Eval-Print-Loop(交互式解释器)
-
R 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
-
E 执行 - 执行输入的数据结构
-
P 打印 - 输出结果
-
L 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
-
-
在REPL中编写程序 (类似于浏览器开发人员工具中的控制台功能)
-
直接在控制台输入
node
命令进入 REPL 环境
-
-
按两次 Control + C 退出REPL界面 或者 输入
.exit
退出 REPL 界面-
按住 control 键不要放开, 然后按两下 c 键
-
环境变量
当要求系统运行一个程序而没有告诉它程序所在的完整路径时,
-
首先在当前目录中查找和该字符串匹配的可执行文件
-
进入用户 path 环境变量查找
-
进入系统 path 环境变量查找
配置环境变量:
找到环境变量:计算机 --右键--> 属性 --> 高级系统设置 --> 高级 --> 环境变量
ES5-数组的新方法
forEach
forEach()
方法对数组的每个元素执行一次提供的函数。专门用来遍历数组的方法. 不关心返回值
应用场景:为一些相同的元素,绑定事件!
map
map()
方法返回一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
filter
filter
用于过滤掉“不合格”的元素 返回一个新数组,如果在回调函数中返回true,那么就留下来,如果返回false,就扔掉
some
some
用于遍历数组,如果有至少一个满足条件,就返回true,否则返回false。
every
every
用于遍历数组,只有当所有的元素返回true,才返回true,否则返回false。
find
find()
方法返回数组中满足条件的第一个元素的值。否则返回 undefined
// 获取第一个大于10的数
var array1 = [5, 12, 8, 130, 44];
var found = array1.find(function(element) {
return element > 10;
});
console.log(found); //12
findIndex
findIndex()
方法返回数组中满足条件的第一个元素的索引。否则返回-1。
// 获取第一个大于10的下标
var array1 = [5, 12, 8, 130, 44];
var index = array1.findIndex(function (element) {
return element > 13;
});
console.log(index); //3
ES6(ECMAScript)
ECMAScript 6.0(以下简称 ES6)是在 2015 年 6 月正式发布的标准。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
let与const
ES6中提供了两个声明变量的关键字:const和let
let的使用
ES6 新增了let
命令,用来声明变量。它的用法类似于var
。
-
let声明的变量只有在当前作用域(块作用域)有效
if(true){
var a = 1;
let b = 2;
}
console.log(a); //1
console.log(b); // b is not defined
-
不存在变量提升
console.log(b); // b is not defined
let b = 2;
-
不允许重复声明
let a = 10;
let a = 1;//报错 Identifier 'a' has already been declared
const的使用
const
声明一个常量。常量:代码执行的过程中,不可以修改常量里面的值
-
const声明的量不可以改变
const PI = 3.1415;
PI = 3; //报错
-
const声明的变量必须赋值
const num;
-
如果const声明了一个对象,仅仅保证地址不变
const obj = {name:'zs'};
obj.age = 18;//正确
obj = {};//报错
-
其他用法和let一样
1. 只能在当前代码块中使用
2. 不会提升
3. 不能重复
let与const的使用场景
1. 如果声明的变量不需要改变,那么使用const
2. 如果声明的变量需要改变,那么用let
3. 学了const和let之后,尽量别用var
模板字符串(模板字面量)
模板字面量 是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。
// 1. 通过``可以定义一个字符串
let str = `hello world`
// 2. 模板字符串内部允许换行
let str = `
hello
world
`
// 3. 模板字符串内部可以使用表达式
let str = `
你好,我是${name}
`
箭头函数
ES6标准新增了一种新的函数:Arrow Function(箭头函数)。
为什么叫Arrow Function?因为它的定义用的就是一个箭头:
基本使用
var fn = function(x, y) {
console.log(x + y);
}
相当于
//语法: (参数列表) => {函数体}
var fn = (x, y) => {
console.log(x + y);
}
参数详解
-
如果没有参数列表,使用()表示参数列表
var sum = function() {
console.log('哈哈')
};
// 等同于:
var sum = () => {
console.log('哈哈')
};
-
如果只有一个参数,可以省略()
// 等同于:
var sum = function(n1) {
console.log('哈哈')
};
var sum = n1 => {
console.log('哈哈')
};
-
如果有多个参数,需要使用()把参数列表括起来
var sum = function(n1, n2) {
console.log('哈哈')
};
var sum = (n1, n2) => {
console.log('哈哈')
};
返回值详解
-
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来
var sum = function(n1) {
console.log('哈哈');
return n1;
};
var sum = n1 => {
console.log('哈哈');
return n1;
};
-
如果函数体只有一行一句,并且需要返回这个值,那么可以省略{}和return
var fn = function(n1, n2) {
return n1 + n2;
}
var fn = (n1, n2) => n1 + n2;
箭头函数的注意点
-
箭头函数内部没有this,因此箭头函数内部的this指向了外部的this(很爽)
-
箭头函数不能作为构造函数,因为箭头函数没有this
【定义一个对象,定时器打招呼】
let obj = {
name: 'zs',
sayHi: function() {
setInterval(() => {
console.log('大家好,我是' + this.name)
}, 1000)
}
}
obj.sayHi()
global模块-全局变量
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。
在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global
,所有全局变量(除了 global 本身以外)都是 global 对象的属性。
在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。
常用的global属性
console: 用于打印日志
setTimeout/clearTimeout: 设置清除延时器
setInterval/clearInterval: 设置清除定时器
__dirname: 当前文件的路径,不包括文件名
__filename: 获取当前文件的路径,包括文件名
//与模块化相关的,模块化的时候会用到
require
exports
module
fs模块
fs模块是nodejs中最常用的一个模块,因此掌握fs模块非常的有必要,fs模块的方法非常多,用到了哪个查哪个即可。
在nodejs中,提供了fs模块,这是node的核心模块
注意:
-
除了global模块中的内容可以直接使用,其他模块都是需要加载的。
-
fs模块不是全局的,不能直接使用。因此需要导入才能使用。
var fs = require("fs");
读取文件
语法:fs.readFile(path[, options], callback)
方式一:不传编码参数
//参数1: 文件的名字
//参数2: 读取文件的回调函数
//参数1:错误对象,如果读取失败,err会包含错误信息,如果读取成功,err是null
//参数2:读取成功后的数据(是一个Buffer对象)
fs.readFile("data.txt", function(err, data){
console.log(err);
console.log(data);
});
方式二:传编码参数
//参数1: 文件的路径
//参数2: 编码,如果设置了,返回一个字符串,如果没有设置,会返回一个buffer对象
//参数3: 回调函数
fs.readFile("data.txt", "utf8",function(err, data){
console.log(err);
console.log(data);
});
关于Buffer对象
1. Buffer对象是Nodejs用于处理二进制数据的。
2. 其实任意的数据在计算机底层都是二进制数据,因为计算机只认识二进制。
3. 所以读取任意的文件,返回的结果都是二进制数据,即Buffer对象
4. Buffer对象可以调用toString()方法转换成字符串。
写文件
语法:fs.writeFile(file, data[, options], callback)
//参数1:写入的文件名(如果文件不存在,会自动创建)
//参数2:写入的文件内容(注意:写入的内容会覆盖以前的内容)
//参数3:写文件后的回调函数
fs.writeFile("2.txt", "hello world, 我是一个中国人", function(err){
if(err) {
return console.log("写入文件失败", err);
}
console.log("写入文件成功");
});
注意:
-
写文件的时候,会把原来的内容给覆盖掉
追加文件
语法:fs.appendFile(path, data[, options], callback)
//参数1:追加的文件名(如果文件不存在,会自动创建)
//参数2:追加的文件内容
//参数3:追加文件后的回调函数
fs.appendFile("2.txt", "我是追加的内容", function(err){
if(err) {
return console.log("追加文件内容失败");
}
console.log("追加文件内容成功");
})
思考:如果没有appendFile,通过readFile与writeFile应该怎么实现?
文件同步与异步的说明
fs中所有的文件操作,都提供了异步和同步两种方式
异步方式:不会阻塞代码的执行
//异步方式
const fs = require("fs");
console.log(111);
fs.readFile("2.txt", "utf8", function(err, data){
if(err) {
return console.log("读取文件失败", err);
}
console.log(data);
});
console.log("222");
同步方式:会阻塞代码的执行
//同步方式
console.log(111);
const result = fs.readFileSync("2.txt", "utf-8");
console.log(result);
console.log(222);
总结:同步操作使用虽然简单,但是会影响性能,因此尽量使用异步方法,尤其是在工作过程中。
其他api(了解)
方法有很多,但是用起来都非常的简单,学会查文档
文档:http://nodejs.cn/api/fs.html
方法名 | 描述 |
---|---|
fs.readFile(path, callback) |
读取文件内容(异步) |
fs.readFileSync(path) |
读取文件内容(同步) |
fs.writeFile(path, data, callback) |
写入文件内容(异步) |
fs.writeFileSync(path, data) |
写入文件内容(同步) |
fs.appendFile(path, data, callback) |
追加文件内容(异步) |
fs.appendFileSync(path, data) |
追加文件内容(同步) |
fs.rename(oldPath, newPath, callback) |
重命名文件(异步) |
fs.renameSync(oldPath, newPath) |
重命名文件(同步) |
fs.unlink(path, callback) |
删除文件(异步) |
fs.unlinkSync(path) |
删除文件(同步) |
fs.mkdir(path, mode, callback) |
创建文件夹(异步) |
fs.mkdirSync(path, mode) |
创建文件夹(同步) |
fs.rmdir(path, callback) |
删除文件夹(异步) |
fs.rmdirSync(path) |
删除文件夹(同步) |
fs.readdir(path, option, callback) |
读取文件夹内容(异步) |
fs.readdirSync(path, option) |
读取文件夹内容(同步) |
fs.stat(path, callback) |
查看文件状态(异步) |
fs.statSync(path) |
查看文件状态(同步) |
path模块
路径操作的问题
在读写文件的时候,文件路径可以写相对路径或者绝对路径
//data.txt是相对路径,读取当前目录下的data.txt, 相对路径相对的是指向node命令的路径
//如果node命令不是在当前目录下执行就会报错, 在当前执行node命令的目录下查找data.txt,找不到 fs.readFile("data.txt", "utf8", function(err, data) { if(err) { console.log("读取文件失败", err); } console.log(data); });
相对路径:相对于执行node命令的路径
推荐使用绝对路径:__dirname
: 当前文件的目录,__filename
: 当前文件的目录,包含文件名
path模块的常用方法
关于路径,在linux系统中,路径分隔符使用的是
/
,但是在windows系统中,路径使用的\
在我们拼写路径的时候会带来很多的麻烦,经常会出现windows下写的代码,在linux操作系统下执行不了,path模块就是为了解决这个问题而存在的。
常用方法:
path.join();//拼接路径
//windows系统下
> path.join("abc","def","gg", "index.html")
"abc\def\gg\a.html"
//linux系统下
> path.join("abc","def","gg", "index.html")
'abc/def/gg/index.html'
【优化读写文件的代码】
其他方法:
path.basename(path[, ext]) 返回文件的最后一部分
path.dirname(path) 返回路径的目录名
path.extname(path) 获取路径的扩展名(后缀名)
var path = require("path");
var temp = "abc\\def\\gg\\a.html";
console.log(path.basename(temp));//a.html
console.log(path.dirname(temp));//abc\def\gg
console.log(path.extname(temp));//.html
path模块其他api(了解)
方法名 | 描述 |
---|---|
path.basename(path[, ext]) |
返回文件的最后一部分 |
path.dirname(path) |
返回路径的目录名 |
path.extname(path) |
获取路径的扩展名 |
path.isAbsolute(path) |
判断目录是否是绝对路径 |
path.join([...paths]) |
将所有的path片段拼接成一个规范的路径 |
path.normalize(path) |
规范化路径 |
path.parse(path) |
将一个路径解析成一个path对象 |
path.format(pathObj) |
讲一个path对象解析成一个规范的路径 |
http模块
创建服务器基本步骤
//1. 导入http模块,http模块是node的核心模块,作用是用来创建http服务器的。
const http = require("http");
//2. 创建服务器
let server = http.createServer();
//3. 服务器处理请求
server.on("request", function(req, res) {
console.log("我接收到请求了");
});
//4. 启动服务器,监听某个端口
server.listen(9999, function(){
console.log("服务器启动成功了, 请访问: http://localhost:9999");
});
详细说明
-
给服务器注册request事件,只要服务器接收到了客户端的请求,就会触发request事件
-
request事件有两个参数,request表示请求对象,可以获取所有与请求相关的信息,response是响应对象,可以获取所有与响应相关的信息。
-
服务器监听的端口范围为:1-65535之间,推荐使用3000以上的端口,因为3000以下的端口一般留给系统使用 6666-6669被浏览器禁用了,不要使用
request对象详解
文档地址:http://nodejs.cn/api/http.html#http_message_headers
常见属性:
headers: 所有的请求头信息
method: 请求的方式
rawHeaders: 所有的请求头信息(数组的方式)
url: 请求的地址
注意:在发送请求的时候,可能会出现两次请求的情况,这是因为谷歌浏览器会自动增加一个favicon.ico
的请求。
小结:request对象中,常用的就是method和url两个参数
response对象详解
文档地址:http://nodejs.cn/api/http.html#http_class_http_serverresponse
常见的属性和方法:
res.write(data): 给浏览器发送响应体,可以调用多次,从而提供连续的响应体
res.end(); 通知服务器,所有响应头和响应主体都已被发送,即服务器将其视为已完成。
res.end(data); 结束请求,并且响应一段内容,相当于res.write(data) + res.end()
res.statusCode: 响应的的状态码 200 404 500
res.statusMessage: 响应的状态信息, OK Not Found ,会根据statusCode自动设置。
res.setHeader(name, value); 设置响应头信息, 比如content-type charset=utf-8
res.writeHead(statusCode, statusMessage, options); 设置响应头,同时可以设置状态码和状态信息。
注意:必须先设置响应头,才能设置响应主体。
根据不同请求输出不同响应数据
-
req.url
:获取请求路径-
例如:请求
http://127.0.0.1:3000/index
获取到的是:/index
-
例如:请求
http://127.0.0.1:3000/
获取到的是:/
-
例如:请求
http://127.0.0.1:3000
获取到的是:/
-
服务器响应文件
-
注意:浏览器中输入的URL地址,仅仅是一个标识,不与服务器中的目录一致。也就是说:返回什么内容是由服务端的逻辑决定
server.on('request', function(req, res) {
let url = req.url
if(url === '/') {
fs.readFile('./index.html', function(err, data) {
if(err) {
return res.end('您访问的资源不存在~')
}
res.end(data)
})
}
})
http模块
练习-模拟Apache服务器
apache的功能
在浏览器中访问某个文件,apache就能在根目录找到对应的页面返回。
localhost:9999/index.html 希望访问index.html文件
localhost:9999/list.html 希望访问list.html文件
......
如果发现没有这个文件, 返回404即可。
MIME类型
-
MIME(Multipurpose Internet Mail Extensions)多用途Internet邮件扩展类型 是一种表示文档性质和格式的标准化方式
-
浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理文档;因此服务器将正确的MIME类型附加到响应对象的头部是非常重要的
mime模块
-
作用:获取文件的MIME类型
-
安装:
npm i mime
var mime = require('mime')
// 获取路径对应的MIME类型
mime.getType('txt') // ⇨ 'text/plain'
// 根据MIME获取到文件后缀名
mime.getExtension('text/plain') // ⇨ 'txt'
npm - Node包管理工具
npm的基本概念
1. npm 是node的包管理工具,
2. 它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package) (即,代码模块)。
3. 来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您能够轻松跟踪依赖项和版本。
npm 由三个独立的部分组成:、
网站
注册表(registry)
命令行工具 (CLI)
-
作用:通过
npm
来快速安装开发中使用的包 -
npm不需要安装,只要安装了node,就自带了
npm
npm基本使用
初始化包
npm init; //这个命令用于初始化一个包,创建一个package.json文件,我们的项目都应该先执行npm init
npm init -y; //快速的初始化一个包, 不能是一个中文名
安装包
npm install 包名; //安装指定的包名的最新版本到项目中
npm install 包名@版本号; //安装指定包的指定版本
npm install 包名 包名 .. //一次
npm i 包名; //简写
卸载包
npm uninstall 包名; //卸载已经安装的包
package.json文件
package.json文件,包(项目)描述文件,用来管理组织一个包(项目),它是一个纯JSON格式的。
-
作用:描述当前项目(包)的信息,描述当前包(项目)的依赖项
-
如何生成:
npm init
或者npm init -y
-
作用
-
作为一个标准的包,必须要有
package.json
文件进行描述 -
一个项目的node_modules目录通常都会很大,不用拷贝node_modules目录,可以通过package.json文件配合
npm install
直接安装项目所有的依赖项
-
-
描述内容
{
"name": "03-npm", //描述了包的名字,不能有中文
"version": "1.0.0", //描述了包的的版本信息, x.y.z 如果只是修复bug,需要更新Z位。如果是新增了功能,但是向下兼容,需要更新Y位。如果有大变动,向下不兼容,需要更新X位。
"description": "", //包的描述信息
"main": "index.js", //入口文件(模块化)
"scripts": { //配置一些脚本,在vue的时候会用到,现在体会不到
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [], //关键字(方便搜索)
"author": "", //作者的信息
"license": "ISC", //许可证,开源协议
"dependencies": { //重要,项目的依赖, 方便代码的共享 通过 npm install可以直接安装所有的依赖项
"bootstrap": "^3.3.7",
"jquery": "^3.3.1"
}
}
注意:一个合法的package.json,必须要有name和version两个属性
本地安装和全局安装
有两种方式用来安装 npm 包:本地安装和全局安装。选用哪种方式来安装,取决于你如何使用这个包。
-
全局安装:如果你想将其作为一个命令行工具,那么你应该将其安装到全局。这种安装方式后可以让你在任何目录下使用这个命令。比如nrm, nodemon等命令 。
-
本地安装:如果你自己的模块依赖于某个包,并通过 Node.js 的
require
加载,那么你应该选择本地安装,这种方式也是npm install
命令的默认行为。
// 全局安装,会把npm包安装到C:\Users\自己的用户名\AppData\Roaming\npm目录下,作为命令行工具使用
npm install 包名 -g ;
//本地安装,会把npm包安装到当前项目的node_modules文件中,作为项目的依赖
npm install 包名;
注意: 如果网速慢的话,会导致下载失败。 再重新下载之前,建议使用以下命令,清除刚才下载的缓存.否则有可能一直无法下载成功
清除npm缓存:npm cache clean -f
npm下载加速
nrm
-
nrm:npm registry manager(npm仓库地址管理工具)
-
安装:
npm i -g nrm
# 带*表示当前正在使用的地址
# 查看仓库地址列表
nrm ls
# 切换仓库地址
nrm use taobao
nodemon 自动重启
-
作用:监视到js文件修改后,自动重启node程序
-
安装:
npm i -g nodemon
-
使用:
nodemon app.js
运行node程序
package.json 和package-lock.json的区别:
node5.0版本之后的node.js,在安装项目的时候,会产生package-lock.json文件
package.json文件记录你项目中所需要的所有模块。当你执行npm install的时候,node会先从package.json文件中读取所有dependencies信息,然后根据dependencies中的信息与node_modules中的模块进行对比,没有的直接下载,已有的检查更新(最新版本的nodejs不会更新,因为有package-lock.json文件)。另外,package.json文件只记录你通过npm install方式安装的模块信息,而这些模块所依赖的其他子模块的信息不会记录。
package-lock.json文件锁定所有模块的版本号,包括主模块和所有依赖子模块。当你执行npm install的时候,node从package.json文件读取模块名称,从package-lock.json文件中获取版本号,然后进行下载或者更新。因此,正因为有了package-lock.json文件锁定版本号,所以当你执行npm install的时候,node不会自动更新package.json文件中的模块,必须用npm install packagename(自动更新小版本号)或者npm install [email protected](指定版本号)来进行安装才会更新,package-lock.json文件中的版本号也会随着更新。
当package.json与package-lock.json都不存在,执行"npm install"时,node会重新生成package-lock.json文件,然后把node_modules中的模块信息全部记入package-lock.json文件,但不会生成package.json文件,此时,你可以通过"npm init --yes"来初始化生成package.json文件。
总结:
以前只有package.json的时候,执行npm install没有的依赖会下载,有的依赖会检查更新到最新版本
现在有了package-lock.json, 执行npm install, 没有的依赖会下载,有的依赖不会自动更新到最新版本了
NodeJS
hackerNews介绍
art-template 模板引擎
-
安装
npm install art-template
-
核心方法
// 基于模板路径渲染模板
//参数1:文件的路径
//参数2:数据
//返回值:返回渲染后的内容
// template(filename, data)
let html = template(path.join(__dirname, "pages", "index.html"), {name:"大吉大利,今晚吃鸡"});
注意点:文件的路径必须是绝对路径
url模块
-
说明:用于 URL 处理与解析
-
注意:通过url拿到的查询参数都是字符串格式
// 导入url模块
var url = require('url')
// 解析 URL 字符串并返回一个 URL 对象
// 第一个参数:表示要解析的URL字符串
// 第二个参数:是否将query属性(查询参数)解析为一个对象,如果为:true,则query是一个对象
var ret = url.parse('http://localhost:3000/details?id=1&name=jack', true)
console.log(ret.query) // { id: '1', name: 'jack' }
querystring模块
-
用于解析与格式化 URL 查询字符串
-
注意:只在专门处理查询字符串时使用
// foo=bar&abc=xyz&abc=123
var querystring = require('querystring')
// 将查询参数转化为对象
// 第一个参数: 要解析的 URL 查询字符串
querystring.parse('foo=bar&abc=xyz') // { foo: 'bar', abc: 'xyz' }
服务端重定向
-
说明:服务端可以通过HTTP状态码让浏览器中的页面重定向
res.writeHead(302, {
'Location': '/'
})
res.end()
POST请求参数的处理
-
说明:POST请求可以发送大量数据,没有大小限制
-
var result = ""; // data事件:用来接受客户端发送过来的POST请求数据 req.on('data', function (chunk) { // chunk 默认是一个二进制数据,和 result 拼接会自动 toString result += chunk; }) // end事件:当POST数据接收完毕时,触发 req.on('end', function () { cosnole.log(result); //result 最终拼接成一个键值对的字符串 })
模块化
前端模块化规范:
-
AMD: require.js
-
CMD: sea.js
-
commonJS: node.js
-
ES6标准模块化规范
基本概念
Node 应用由模块组成,采用 CommonJS 模块规范。
每个文件就是一个模块,有自己的作用域
node中模块分类
-
1 核心模块
-
由 node 本身提供,不需要单独安装(npm),可直接引入使用
-
-
2 第三方模块
-
由社区或个人提供,需要通过npm安装后使用
-
-
3 自定义模块
-
由我们自己创建,比如:tool.js 、 user.js
-
核心模块
-
fs:文件操作模块
-
http:网络操作模块
-
path:路径操作模块
-
url: 解析地址的模块
-
querystring: 解析参数字符串的模块
-
基本使用:1 先引入 2 再使用
// 引入模块
var fs = require('fs');
第三方模块
-
第三方模块是由 社区或个人 提供的
-
比如:mime模块/art-template/jquery...
-
基本使用:1 先通过npm下载 2 再引入 3 最后使用
用户自定义模块
-
由开发人员创建的模块(JS文件)
-
基本使用:1 创建模块 2 引入模块
-
注意:自定义模块的路径必须以
./
获取../
开头
// 加载模块
require('./a') // 推荐使用,省略.js后缀!
require('./a.js')
模块的导入与导出
模块导入
-
通过
require("fs")
来加载模块 -
如果是第三方模块,需要先使用npm进行下载
-
如果是自定义模块,需要加上相对路径
./
或者../
,可以省略.js
后缀,如果文件名是index.js
那么index.js也可以省略。 -
模块可以被多次加载,但是只会在第一次加载
模块导出
-
在模块的内部,
module
变量代表的就是当前模块,它的exports
属性就是对外的接口,加载某个模块,加载的就是module.exports
属性,这个属性指向一个空的对象。
//module.exports指向的是一个对象,我们给对象增加属性即可。
//module.exports.num = 123;
//module.exports.age = 18;
//通过module.exports也可以导出一个值,但是多次导出会覆盖
module.exports = '123';
module.exports = "abc";
module.exports与exports
-
exports 不是 module.exports 的缩写, exports是单独存在的
-
exports 和 module.exports 默认指向同一个对象
-
模块最终导出的一定是module.exports中的 数据
-
结论:
-
1 直接添加属性两者皆可
-
2 赋值对象时,只能使用
module.exports
-
console.log( module.exports === exports ) // ==> true
// 等价操作
module.exports.num = 123
exports.num = 123
// 赋值操作:不要使用 exports = {}
module.exports = {}
nodejs中require加载模块的规则:
require('mime') 以mime为例
-
如果加载的模块是一个路径,表示加载的自定义模块,根据路径查找对应的js文件
-
如果加载的模块是一个名字,不是一个路径,说明加载的是核心模块或者是第三方模块
-
判断是否是核心模块,如果不是核心模块
-
会在当前目录下查找是否有node_modules目录, 如果有
-
在node_modules目录下查找mime这个文件夹
-
找到mime文件夹下的package.json文件,找到main属性,模块的入口文件
-
如果没有main,默认查找当前目录下的index.js文件
-
如果没有找到对应的模块,回去上一层目录,继续查找, 一直找到根目录 C: || D: || E:
-
报错: can not find module xxx
CommonJS 规范参考文档
模块化改造hackerNews
Express
Express 框架
-
基于 Node.js 平台,快速、开放、极简的 web 开发框架
-
express 中文网 目前只有部分内容翻译成中文
起步
-
安装:
npm i express
// 导入 express
var express = require('express')
// 创建 express实例,也就是创建 express服务器
var app = express()
// 路由
app.get('/', function (req, res) {
res.send('Hello World!')
})
// 启动服务器
app.listen(3000, function () {
console.log('服务器已启动')
})
API说明
-
express()
:创建一个Express应用,并返回,即:app -
app.get()
:注册一个GET类型的路由-
注意:只要注册了路由,所有的请求都会被处理(未配置的请求路径,响应404)
-
-
res.send()
:发送数据给客户端,并自动设置Content-Type-
参数可以是:字符串、数组、对象、Buffer
-
注意:只能使用一次
-
-
req
和res
:与http模块中的作用相同,是扩展后的请求和响应对象
注册路由的三种方式
-
1 app.method app.get/app.post/app.put/app.delete
-
2
app.all(path, callback)
-
注意:path 与 请求地址必须完全相同 代码:/a/b/c --> path:/a/b/c
-
注意:可以处理任意的请求类型
-
-
3
app.use(path, callback)
-
注意:只要是以path开头的请求地址,都可以被use处理 代码:/a -->path: /a/b/c
-
注意:可以处理任意的请求类型
-
注意:path参数可省略,默认值为:
/
-
模拟Apache服务器
-
req.path
:请求路径-
示例:URL为'example.com/users?sort=desc',path表示:
/users
-
-
res.sendFile(文件路径)
传入文件的绝对路径
处理静态资源
作用: 指定某个文件夹为静态资源目录,将来资源访问的时候,就会直接到这个文件夹下访问
-
静态资源:图片、CSS、JavaScript 文件 等
-
注意:
express.static()
可以调用多次
// 托管web目录下的静态资源
app.use(express.static('web')) // 相当于:app.use('/', express.static('web'))
浏览器访问: http://localhost:3000/img/anoceanofsky.jpg
当请求达到服务器后,express就会到web目录下去查找img目录,然后查找anoceanofsky.jpg
并读取该文件,返回给浏览器
注意: 工作中,常常会配置一个虚拟路径,
代码:app.use('/aaa', express.static('pages')) /aaa就是虚拟路径
而项目中,其实并不需要有aaa这个文件夹, 真正的文件其实是放在pages这个文件夹下的
那么为什么要配置虚拟路径呢?
仅仅是为了区分要访问静态资源还是要访问其他数据
127.0.0.1:8888/aaa/静态资源的文件名 //获取静态资源的接口
127.0.0.1:8888/api/其他标识符 //获取指定数据的接口
request常用属性和方法
// 获取请求路径中的参数,是一个对象 ---> Get请求参数
req.query
// 获取POST请求参数,需要配置`body-parser`模块, POST请求参数
req.body
-
获取
POST
请求参数
// 导入body-parser模块
var bodyParser = require('body-parser');
// 将POST请求参数转化为对象,存储到req.body中
//extended:false表示使用querystring模块来解析字符串
//如果extended:true,用的是一个人自己封装的ps模块
app.use(bodyParser.urlencoded({extended:false}));
// 此时,就可以获取到POST请求参数了
app.post((req, res)=>{
console.log(req.body)
})
response常用属性和方法
// send() 发送数据给客户端,并自动设置Content-Type
//- 参数可以是:字符串、数组、对象、Buffer
//- 注意:只能使用一次
res.send()
// 发送文件给浏览器,并根据文件后缀名自动设置Content-Type
// 注意:文件路径必须是绝对路径
res.sendFile(path.join(__dirname, 'index.html'))
// 设置HTTP响应码
res.status(200) //相当于 res.statusCode
res.sendStatus(200) // 相当于 res.status(200)+ res.send('OK')
// 设置响应头
res.set('Content-Type', 'text/plain')
res.set({
'Content-Type': 'text/plain',
'cute': 'fangfang'
})
// 重定向
res.redirect('/index')
express
express中使用模版引擎
安装
npm install express //安装express
npm install art-template //安装art-template
npm install express-art-template //把express 和 art-template关联起来的包
给express绑定一个模版引擎
//给express设置模版引擎
//参数1: 模版引擎的后缀名, 以后的模版文件都应该是 html结尾
//参数2: 使用什么模版引擎
app.engine("html", require('express-art-template'))
通过res.render()
渲染模版引擎
//参数1; 模版文件的路径
//相对路径,默认会去当前路径下的views目录下查找对应的index.html.
//也可以传入一个绝对路径
//参数2: 数据
res.render('index.html', {name:"zs"})
关于模版引擎的配置
设置模板的默认路径
//第一个参数: 'views'
//第二个参数: 新的路径, 可以是相对路径, 也可以写绝对路径
//将默认路径从viwes修改为pages
app.set("views", "pages");
设置模板引擎的默认后缀
//第一个参数: 'view engine'
//第二个参数: 文件后缀名
//默认后缀是html. 那么 res.render('index', {name:"zs"}) 可以忽略index后面的.html
app.set("view engine", "html");
express中间件
Express里有个中间件(middleware)的概念。所谓中间件,就是在收到请求后和发送响应之前这个阶段执行的一些函数。
中间件函数是可以访问请求对象(req), 响应对象(res)以及应用程序请求 - 响应周期中的下一个中间件函数的函数.
下一个中间件函数通常由名为next的变量表示
app.use([path],function(request,response,next){ 代码 });
app.use('/',function(req,res, next) {
console.log(1);
//next表示下一个中间键函数
next()
});
app.get('/list', function(req, res, next) {
console.log(2)
res.send('呵呵')
})
app.get('/index', function(req, res, next) {
console.log(3)
res.send('哈哈')
})
请求:127.0.0.1:8888/index
执行的结果:
1
3
响应:哈哈
中间件的分类
-
应用程序级的中间键
-
使用app.use()和app.METHOD()函数
-
-
内置中间件
-
express.static 就是一个内置的中间件
app.use('/', express.static('路径'))
-
-
第三方中间件
-
body-parser 就是一个第三方中间件
在express中通过req.body可以获取到请求主体中的数据
不能直接获取到,需要使用body-parser,对数据进行解析.才能获取到数据
-
body-parser中间件的使用
-
获取get请求的参数:
req.query
-
获取post请求的参数
req.body
,但是需要借助body-parser
中间件
安装:
npm install body-parser
使用
//1. 导入body-parser中间件
var bodyParser = require('body-parser');
//使用body-parser中间件
//extended:true 表示使用qs模块来解析查询字符串 extended:false表示使用querystring模块来解析字符串
//官方不推荐使用extended:true
app.use('/',bodyParser.urlencoded({extended:false}));
//3. 通过req.body获取参数
app.post("/", function(req, res) {
console.log(req.body);
res.send("哈哈");
});
实现自己的body-parser中间件
在浏览器中请求的地址: 127.0.0.1:8888/list 发送一个post请求
function bodyParser(req, res, next){
//给req增加一个body属性
var result = "";
req.on('data', function(chunk){
result += chunk;
});
req.on("end", function(){
req.body = querystring.parse(result);
next();
});
}
app.use(bodyParser);
app.post('/list', function(req, res) {
console.log(req.body) //可以获取到post请求上传的参数
res.send('列表')
})
外置路由的使用
目的:将路由封装到一个独立的路由模块中,有利于代码的封装和模块化
/*
router.js 文件代码如下:
*/
// 1 加载 express 模块
var express = require('express')
// 2 调用 express.Router() 方法,得到一个路由容器实例
var router = express.Router()
// 3 为 router 添加不同的路由
router.use((req, res, next)=>{
console.log('1');
next();
})
router.get('/index', (req, res)=>{
res.send('首页')
})
router.all('/list', (req, res)=>{
res.send('列表')
})
// 4. 将 router 路由容器导出
module.exports = router
/*
在 app.js 文件中:
*/
var express = require('express')
// 1 加载上面自定义的路由模块
var router = require('./router')
var app = express()
// 2. 将自定义路由模块 router 通过 app.use() 方法挂载到 app 实例上
// 这样 app 实例程序就拥有了 router 的路由
app.use( router )
app.listen(8888, function () {
console.log('running...')
})
使用express重构HackerNews案例
mongodb
数据库的分类
关系型数据库;mysql oracle sql server db2
非关系数据库: mongodb redis memcache
MongoDB简介
-
MongoDB 是一个非关系型数据库 介于关系型和非关系型的数据库
-
NoSQL(=Not Only SQL),意即“不仅仅是SQL”,泛指非关系型的数据库
-
为什么在node中学习mongodb数据库
因为对 mongodb 对JavaScript 兼容较好,和 Node.js 结合最好
mongodb数据库中的结构
数据库: 一个项目对应一个数据库 一个mongodb中可以建立多个数据库。
集合(collection): 类似于表 user teacher
文档(document): 类似于表中的一条记录 一个学生的信息 一条文档 {name:"zs", age: 18}
字段: name , age
mongodb安装
下载地址:
https://www.mongodb.com/download-center/community
-
双击安装包一直点击next,中间点击custom选项更改储存位置,注意路径不要有空格 最好不要改
-
注意取消勾选 MongoDB compass,不然安装过程会非常久
-
配置环境变量,
通过命令:mongod --version
看是否安装成功
-
注意:MongoDB最新版的安装包已经不再支持32位的windows操作系统了
解决mongodb安装时出错 “mongodb 无法启动此程序,因为计算机中丢失 api-ms-win-crt-runtime-l1-1-0.dll”,安装 vc_redist.x64.exe
https://helpx.adobe.com/tw/creative-cloud/kb/error_on_launch.html
mongodb启动与连接
-
1 通过命令:
mongod
启动 mongodb数据库服务(不要关闭)-
注意, 需要配置mongodb存储数据的路径,否则无法启动成功
-
-
2 重新开启一个终端,输入命令:
mongo
就可以连接到mongod服务了
1 在 C中创建 data文件夹, 在data文件夹中创建 db文件夹 db文件夹就是mongodb存储数据的位置
2 在终端中输入命令: mongod ,就可以启动mongodb数据库服务了
3. 创建 c:\data\db 的目的: 告诉 mongodb 数据存储到这个文件夹中, 但是, 只能在C盘中启动 mongod
4. 如果需要在 D盘 启动, 需要在 D中也创建 data/db 目录
# 终端1 -- 启动mongodb数据库服务
mongod
# 终端2 -- 连接到服务
# 此时,就可以在 终端 对数据库进行操作了
mongo
数据库存储路径的说明
-
注意:mongod 会在执行命令的磁盘根目录中查找
data/db
目录作为数据库文件存储路径 -
可以通过命令:
mongod --dbpath 路径
修改默认配置
# 64位:
mongod --dbpath C:\data\db 开启服务并且修改默认路径, 服务关闭之后失效
# 32位:
mongod --journal --storageEngine=mmapv1
mongod --dbpath c:\data\db --journal --storageEngine=mmapv1
MongoDB终端操作
数据库操作
以下的命令都是在mongo终端下完成
-
查看数据库
# 注意:自己创建的数据库,如果数据为空,不会显示
show dbs
-
切换(创建)数据库
# 如果数据库存在,切换到该数据库, 如果数据库不存在,创建新的数据库
# 如果数据库里面没有数据的话,数据库不显示
use 数据库名
use test
use users
-
查看当前使用的数据库
# 查看当前正在使用的数据库
db
-
删除当前数据库
db.dropDatabase()
//注意: 删除成功之后, 输入db,查看当前数据库,还会显示刚才删除的数据库,使用use 切换一下就可以了
插入数据(文档)
-
说明: 在 mongodb 中不需要提前创建集合(类似表), 直接通过 db.集合名称.inseret() 就可以往集合中添加数据了
// 插入一条
db.集合名称.insertOne({name: 'zs', age:18 })
// 插入多条, 传入一个数组
db.集合名称.insertMany([{name: 'tom', age: 19}, {name: 'jerry', age: 20}])
查询数据
-
语法:
db.集合名称.find()
// 查询所有数据
db.集合名称.find()
// 美化输出格式:
db.集合名称.find().pretty()
// 指定条件查询:
db.集合名称.find({name: 'jack'})
修改数据
-
语法:
db.集合名称.updateOne(条件, 更新后的数据)
// 修改name属性为jack的数据,将age改为20
// 第一个参数: 表示要修改哪个数据, 会根据指定的name属性, 去查找值为jack的数据
// 第二个参数: 表示修改修改后的数据, 会修改 age 属性的值为 20
db.集合名称.updateOne({name: 'jack'}, {$set: {age: 20}})
// 修改age大于18岁的数据,将name设置为 成年人
db.集合名称.updateMany({age: {$gt: 18}},{$set: {name: '成年人'}})
删除数据
-
语法:
db.集合名称.deleteOne(条件)
// 删除 age 为18的数据:
// 参数: 删除条件
db.集合名称.deleteOne({age: 18})
// 删除所有name为jack的文档
db.集合名称.deleteMany({ name: 'jack' })
MondoDB 查询语句
操作 | 格式 | 示例 | SQL语句 |
---|---|---|---|
等于 | {} | db.col.find({ name :'jack'}) | where name = 'jack' |
小于 | $lt | db.col.find({ age: {$lt:18}}) | where age < 18 | |
小于或等于 | $lte | db.col.find({ age: {$lte:18}}) | where age <= 18 | |
大于 | $gt | db.col.find({ age: {$gt:18}}) | where age > 18 | |
大于或等于 | $gte | db.col.find({ age: {$gte:18}}) | where age >= 18 | |
不等于 | $ne | db.col.find({ age: {$ne:18}}) | where age != 18 |
nodejs如何操作mongodb
nodejs通过一个模块,用于连接mongodb
mongodb这是mongodb官方提供的一个模块
安装
npm i mongodb
连接mongodb
注意: 保证mongodb的服务器是启动的,才能连接上
// 导入
const mongodb = require('mongodb')
// 专门用于连接mongodb服务器的客户端
const MongoClient = mongodb.MongoClient
// 连接mongodb服务器
// 参数1:数据库的地址
// 参数2:连接的回调函数
// err: 连接失败了 返回一个失败的对象
// client: 连接成功了 client对象,用于操作mongodb, 不成功是一个null
MongoClient.connect(
'mongodb://localhost:27017',
(err, client) => {
if (err) {
return console.log('连接数据库失败了', err)
}
// 通过client来操作数据库即可
// 增删改查
console.log('进行增删改查操作')
// client一定需要关闭连接
client.close()
}
)
插入数据
插入单条数据
//注意: 如果没有对应的数据库和文档,自动创建
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
//insertOne() 插入一条新的数据
//第一个参数: 一条数据
// 第二参数: 操作完成之后的回调函数
teacher.insertOne(
{ name: 'ww', age: 18, desc: '小' },
(err, result) => {
if (err) {
return console.log('插入数据失败了', err)
}
//result的值: { result: { n: 1, ok: 1 },}
//ok为1表示成功
//n表示操作的数据的个数
if (result.result.ok === 1) {
console.log('插入数据成功了')
}
}
)
插入多条数据
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
//注意: 第一个参数是一个数组
teacher.insertMany(
[{ name: 'zs', age: 20, desc: '老' },
{ name: 'ls', age: 20, desc: '帅' }],
(err, result) => {
if (err) {
return console.log('插入数据失败了', err)
}
console.log(result.result) //{ ok: 1, n: 2 }
}
)
查询数据
查询多条
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
// 查询某个数据库中的teacher集合
// db.teacher.find()
// 把最终的数据转换成一个数组
teacher.find().toArray((err, res) => {
if (err) {
return console.log('查询数据库失败了')
}
console.log(res)
})
查询单条
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
teacher.findOne({ name: 'test' }, (err, result) => {
if (err) {
return console.log('查询数据库失败', err)
}
//{ _id: 5c2e1458cf6fade4c2da5ee0, name: 'test',age: 20, desc: '都成年了' }
console.log(result)
})
修改操作
修改一条数据
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
//第一个参数: 条件
//第二个参数: 修改后的数据
teacher.updateOne(
{ age: 18 },
{ $set: { desc: '成年了' } },
(err, result) => {
if (err) {
return console.log('修改失败', err)
}
//{ n: 1, nModified: 1, ok: 1 }
console.log(result.result)
}
)
修改多条数据
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
//第一个参数: 条件
//第二个参数: 修改后的数据
teacher.updateMany(
{ age: { $gt: 18 } },
{ $set: { desc: '都成年了' } },
(err, result) => {
if (err) {
return console.log('修改失败', err)
}
//{ n: 操作的数据的个数, nModified: 已经修改的个数, ok: 1 }
console.log(result.result)
}
)
删除数据
删除一条数据
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
//注意: 第一个参数是条件
teacher.deleteOne({ name: 'zs' }, (err, result) => {
if (err) {
return console.log('删除数据失败', err)
}
// { n: 1, ok: 1 }
console.log(result.result)
})
删除多条数据
//1. 获取当前使用的数据库
let db = client.db('letao')
//获取对应这个数据库中的集合
let teacher = db.collection('teacher')
teacher.deleteMany({ age: 20 }, (err, result) => {
if (err) {
return console.log('删除数据失败', err)
}
// { n: 8, ok: 1 }
console.log(result.result)
})