前端之路(node.js篇)

node.js 是什么?

node.js,也叫作node,或者nodejs,指的都是一个平台。

  1. node.js官方网站

  2. node.js中文网

  3. node.js 中文社区

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可以干什么?

  1. 开发服务端程序

  2. 开发命令行工具(CLI),比如npm,webpack,gulp,less,sass等 vue-cli

  3. 开发桌面应用程序(借助 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介绍

  1. REPL 全称: Read-Eval-Print-Loop(交互式解释器)

    • R 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。

    • E 执行 - 执行输入的数据结构

    • P 打印 - 输出结果

    • L 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。

  2. 在REPL中编写程序 (类似于浏览器开发人员工具中的控制台功能)

    • 直接在控制台输入 node 命令进入 REPL 环境

  3. 按两次 Control + C 退出REPL界面 或者 输入 .exit 退出 REPL 界面

    • 按住 control 键不要放开, 然后按两下 c 键

环境变量

当要求系统运行一个程序而没有告诉它程序所在的完整路径时,

  1. 首先在当前目录中查找和该字符串匹配的可执行文件

  2. 进入用户 path 环境变量查找

  3. 进入系统 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;

箭头函数的注意点

  1. 箭头函数内部没有this,因此箭头函数内部的this指向了外部的this(很爽)

  2. 箭头函数不能作为构造函数,因为箭头函数没有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模块的方法非常多,用到了哪个查哪个即可。

文档地址:http://nodejs.cn/api/fs.html

在nodejs中,提供了fs模块,这是node的核心模块

注意:

  1. 除了global模块中的内容可以直接使用,其他模块都是需要加载的。

  2. 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("写入文件成功");
});

注意:

  1. 写文件的时候,会把原来的内容给覆盖掉

追加文件

语法: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");
});

详细说明

  1. 给服务器注册request事件,只要服务器接收到了客户端的请求,就会触发request事件

  2. request事件有两个参数,request表示请求对象,可以获取所有与请求相关的信息,response是响应对象,可以获取所有与响应相关的信息。

  3. 服务器监听的端口范围为: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); 设置响应头,同时可以设置状态码和状态信息。

注意:必须先设置响应头,才能设置响应主体。

 

根据不同请求输出不同响应数据

  • request.url

  • 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模块

  • 作用:获取文件的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' }

服务端重定向

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为例

  1. 如果加载的模块是一个路径,表示加载的自定义模块,根据路径查找对应的js文件

  2. 如果加载的模块是一个名字,不是一个路径,说明加载的是核心模块或者是第三方模块

  3. 判断是否是核心模块,如果不是核心模块

  4. 会在当前目录下查找是否有node_modules目录, 如果有

  5. 在node_modules目录下查找mime这个文件夹

  6. 找到mime文件夹下的package.json文件,找到main属性,模块的入口文件

  7. 如果没有main,默认查找当前目录下的index.js文件

  8. 如果没有找到对应的模块,回去上一层目录,继续查找, 一直找到根目录 C: || D: || E:

  9. 报错: can not find module xxx

CommonJS 规范参考文档

模块化改造hackerNews

Express

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

    • 注意:只能使用一次

  • reqres:与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 官网

  • mongodb 中文

  • 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

  1. 双击安装包一直点击next,中间点击custom选项更改储存位置,注意路径不要有空格 最好不要改

  2. 注意取消勾选 MongoDB compass,不然安装过程会非常久

  3. 配置环境变量,

通过命令: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)
})

猜你喜欢

转载自blog.csdn.net/weixin_44137393/article/details/87929516
今日推荐