node.js

node.js阶段总结


什么是前端和后端

后端的主要工作

  1. 后端为前端程序员暴露API接口;
  2. 后端也要操作数据库;
  3. 优化后端业务的性能;

    前端的主要工作

  4. 绘制网页的页面(HTML)
  5. 写CSS样式美化页面、写JS做网页交互(更多的是网页的特效)
  6. 借助于 XHR($.ajax $.get $.post)请求后端的接口;实现前后端分离开发
  7. 使用前端的(框架)去完成界面的开发
  8. 总结:前端的主要工作:用户能看到的东西,基本上都是前端做出来;

    前后端协作流程

  • 将来进入工作,大家会接触到【前后端分离开发】;
  • 协作开发的流程:后端为我们暴露数据接口,前端单纯的调用后端接口;
  • 在当前Node阶段中,我们学习的是 后端开发;
  • 在Node阶段,我们主要教大家如何写后端的接口;

环境安装

LTS 和 Current 版本区别

  1. LTS 是长期稳定版的意思(这个安装包用起来比较稳定)【推荐在企业中使用】
  2. Current 是最新特征版,这个安装包中有最新的Node特性,但是,可能有一些潜藏的Bug未解决;【推荐学习或尝鲜去使用】

    下载安装

    查看 Node 版本号

    打开终端,在命令行输入命令node -v即可
    如何进入终端呢?
  3. 使用快捷键windows徽标 + R打开运行面板,输入 cmd 后直接回车;
  4. 在任意目录的空白位置,先按住shift键不松开,然后,在空白位置,鼠标右键单击,会出来一个右键菜单,选择在此处打开 powershell/cmd 窗口【将来第二种方式用的会多一些】

    环境变量

    什么是环境变量

    Path环境变量的作用:能够让我们通过命令行的形式,快速启动一些应用程序;

    系统环境变量和用户环境变量的区别

  5. 用户环境变量,是每个用户私有的,用户之间不会共享;
  6. 全局环境变量,是共享的,只要你能登录这台计算机,就能访问到全局的环境变量;【今后在配置环境变量的时候,推荐直接配置到系统环境变量】

    通过命令行快速启动应用程序时,路径的查找规则

  7. 先在当前 cmd 终端的目录中查找,如果有则直接运行;
  8. 如果当前目录中没有,则去全局的path环境变量中查找;

浏览器中的 JavaScript

Javascript的诞生

  1. JS 诞生 和 网景公司 有关;form
  2. JS 诞生的需求,就是为了做客户端表单验证的;
  3. JS作者刚把JS创建出来之后,它叫 LiveScript -> Javascript

    浏览器一战

    主角是 IE 和 网景浏览器;
  4. 一战的果实:ECMAScript 规范的确立!
  5. 短暂的和平期:在和平期阶段,JS都在做什么事情呢(进行表单的验证、做简单的网页动效、狗皮膏药),一战之后,JS能力有限,当时被称作是“脚本语言”

    浏览器二战

  • 在 2008 年,二战开始了;
  • 谷歌chrome、IE、火狐(浴火重生)
  • 在 2008 年,谷歌 利用 XHR 这个对象,实现了网页的局部刷新;
  • 2010 - 2013年左右,公司中,不管是Java,还是.NET, 还是 PHP(会不会Ajax)
  1. 二战的果实:XHR 对象的诞生; chrome 浏览器的 JS V8 解析引擎;

    注意

  • 在 一战 和 二战期间,JS 只能运行在 浏览器中;
    浏览器中的JS组成部分:ECMAScript核心 + DOM + BOM
    浏览器属于前端环境,所以,之前的JS只运行在前端浏览器中;也就是,无法使用Javascript实现后端编程;

Node中的Javascript

  • Node.js 的诞生,解放了Javascript,从此之后,Javascript 就可以在 服务器端运行了;

    ECMAScript

  • Node中的Javascript也有一个ECMAScript核心

    没有 BOM 和 DOM

  • Node中并没有浏览器的概念,所以,BOM和 DOM ,Node中不需要,因此,就把它们给剔除了;

    全局成员(网页和Node中都可以用)

  1. console
  2. setInterval
  3. setTimeout
  4. …其它全局成员

    模块系统

  5. Node中自己扩展出来的一套API规范

    Node中的JS组成部分

  • ECMAScript核心 + 全局成员 + 模块系统成员(这是Node平台所独有的)
  1. 全局成员(console.log, setTimeout setInterval)
  2. 模块系统成员(就是Node中的一些核心模块,提供了一些后端编程的能力)

ECMAScript 规范 和 浏览器中的JS 以及 Node 中的 JS 之间的关系

  1. ECMAScript 规范(标准):就是一本书,这本书中记录了基本的语法定义;
  2. 浏览器中的 JS:浏览器中的JS是一门具体的编程语言,实现了 ECMAScript 规范;
    • 浏览器中的JS组成部分: ECMAScript 核心 + DOM + BOM
  3. Node中的JS:也是一门具体的编程语言,也实现了 ECMAScript 规范;
    • Node中的JS组成部分: ECMAScript 核心 + 全局成员 + 模块系统成员

总结-什么是 Node.js

  • 基于 Chrome 的V8 JS 解析引擎之上,解放了Javascript的编程能力,为 Javascript 提供了 后端编程的能力;
  • 所以说,Node.js 是 一个后端编程的平台,用到的语言是Javascript;

Node.js 环境中执行JS代码的两种方式

REPL 环境

  1. 如何进入 REPL 环境: 打开任意终端,直接输入 node 并回车,就会进入到 REPL 环境中;
  2. 如何离开 REPL 环境:按两次ctrl + c 就能退出 REPL 环境;
  3. REPL中,每个字母代表什么意思呢:
    • R: Read 的意思,每当我们输入完毕代码之后,只要敲击回车,Node环境就会读取用户输入的代码
    • E:Evaluate 的意思,表示把 Read 进来的用户代码,调用 类似于 Eval 的函数,去解析执行
    • P:Print 输出的意思;把第二步中解析执行的结果,输出给用户;
    • L:Loop 循环的意思,表示当输出完毕之后,进入下一次的 REP循环

      node 命令【推荐形式】

  • (Node为JS提供了一个脱离浏览器平台也能被解析的执行环境)
  • 直接使用node 要执行的js文件的路径 来执行指定的JS文件
  1. 使用 ↑ 快速定位到上一次执行的命令
  2. 使用 tab 键能够快速补全路径
  3. 使用 cls 可以清屏

ECMAScript 6常用语法

let 与 const

  • 之前定义变量,用 var 关键字,用var有没有缺点:
  1. 变量提升问题
  2. 没有块级作用域
  • let特性:
    1. 使用let关键字定义变量,不存在变量提升的问题
    2. let定义的变量受到{ } 作用域的影响(有{}的作用域)
    3. let有块级作用域
  • const特性:
    1. 没有变量提升的问题
    2. const 定义的量叫常量,只要被定义了,无法被重新赋值
    3. 当定义常量的时候,必须定义且初始化,否则报语法错误

      变量的解构赋值

  1. 所谓的解构赋值,就是把 某个对象中的属性,当作变量,给解放出来,这样,今后就能够当作变量直接使用了
  2. 可以使用 :为解构出来的变量重命名
    1
    2
    3
    // 变量的解构赋值
    const { name : name123, age, gender } = person
    console.log(name123)

字符串扩展

  1. 模板字符串
  • 模板字符串的特点: 支持换行,可以嵌入变量的值
    1
    2
    3
    4
    5
    str +=`<tr>
    <td>${arr[i].id}</td>
    <td>${arr[i].name}</td>
    <td>${arr[i].age}</td>
    </tr>`
  1. startsWith() 和 endsWith()
    • startsWith() 用来判断字符串,是否以指定的字符开头,如果是,返回值是 true,否则返回 false
    • endsWith() 用来判断字符串,是否以指定的字符结尾;如果是,返回值是 true,否则返回 false
  2. padStart() 和 padEnd()
  • padStart()第一个参数是填充完毕之后总长度是多少;第二个参数是填充的内容

    函数扩展

  1. 形参默认值

    1
    2
    3
    function add(x, y = 0) {
    return x + y;
    }
  2. 解构赋值和形参默认值结合使用

  3. rest参数(定义函数的时候使用…来定义的参数叫做rest参数,定义一个接受所有参数的数组)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // ------------------rest参数-------------------
    function add(...args) {
    console.log(args instanceof Array)

    let total = 0
    args.forEach(item => {
    total += item
    })
    console.log(total)
    }

    add(1, 2, 3, 4)
  4. 扩展运算符(在调用函数的时候,使用的…的运算符来扩展数组,…运算符称为扩展运算符)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // ----------------------扩展运算符--------------
    function add(...values) {
    let total = 0
    values.forEach(item => {
    total += item
    })

    console.log(total)
    }

    const arr = [1, 2, 3]
    add(...arr)

箭头函数(是ES6中新增的函数形式)【今后我们会每天写箭头函数的】

  1. 如何把 function 改成 箭头函数呢: 先把 function 删掉,然后,在 () 和 { } 之间,添加一个 => 就好了
  2. 箭头函数的特性: 箭头函数内部的 this, 永远和 箭头函数外部的 this 保持一致;
  3. 箭头函数,本质上就是一个匿名函数 (不能直接调用)
  4. 最标准的箭头函数格式是 ( 参数列表 ) => { 函数体 }
  5. 变体1: 如果 箭头函数左侧的 形参列表中,只有一个 形参,那么,( ) 可以省略 ( x ) => { console.log(x) } 可以改造成 x => { console.log(x) }
  6. 变体2:如果 箭头函数右侧的 函数体中,只有一行代码,那么, { } 可以省略 (x, y) => {console.log(x + y)} 可以改造成 (x, y) => console.log(x + y)
  7. 变体3:如果箭头函数 左侧 只有一个形参,右侧只有一行代码,那么, 左侧的 () 和 右侧的 {} 都可以省略 ( x ) => { console.log(x) } 可以改造成 x => console.log(x)
  8. 注意: 如果我们省略了 右侧的 { }, 那么,默认就会把 右侧函数体中的代码执行结果,返回出去 (x, y) => { return x + y } 可以简写成 (x, y) => x + y

    对象中定义方法和属性的便捷方式

在Node中使用fs模块进行文件操作

文件读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  // 1. 使用require 来导入需要的模块
const fs = require('fs')
// 参数一: 要读取文件的路径
// 参数二: 可选参数,表示字符编码, 默认为空null
// 参数三: callback回调函数
// fs.readFile('./files/1.txt', function (err,buf){
// console.log(err)//null
// console.log(buf.toString())//Buffer是一种类型,表示二进制的意思
// 如果读取文件没有出错,则err默认为null,buf是读取到的二进制
// 可以调用buffer.toString转换为正常的字符串

// Buffer 实例和字符串拼接,得到的结果,是一个字符串
// console.log(buf + '')
// })

// fs.readFile('./files/1.txt',(err,buf) => {
// Error 实例对象中,有个message属性,表示错误信息
// if(err)return console.log(err.message)
// console.log(buf.toString())
// })
// 第二个参数可以用'utf-8'的编码格式,这样buf就不用转换为字符串
fs.readFile('./files/1.txt','utf-8',(err,dataStr) => {
// Error 实例对象中,有个message属性,表示错误信息
if(err)return console.log(err.message)
console.log(dataStr)
})

文件写入

  • 参数一: 文件路径
  • 参数二: 要写入的数据
  • 参数三: 可选的编码格式, 默认utf-8
  • 参数四: 文件写入后的回调函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
      const fs = require('fs')
    // 写入文件到指定文件中去(会覆盖原来的内容)
    fs.write('文件路径','内容',(err) => {
    // 内容的改变会覆盖上一次的上传
    if(err) return console.log('写入失败了:' + err.message)
    console.log('写入成功了!')
    })
    // fs.copyFile 也可以,但它只支持8.5.0以上的版本
    fs.copyFile('./files/1.txt''./files/1-copy.txt'(err) => {
    if(err) return console.log('写入文件失败:' + err.message)
    console.log('恭喜,写入成功')
    })

文件追加

  • fs.appendFile(file,data[,option],callback)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 1. 导入 fs 文件系统模块
    const fs = require('fs')
    // 参数一: 要追加的文件的路径
    // 参数二: 要追加的数据内容
    // 第三个参数:追加时候的编码格式 默认为utf-8
    // 参数三: 追加完毕之后的回调函数
    fs.appendFile('./files/2.txt','\n降龙十八掌',(err) => {
    if(err) return console.log('写入文件失败:' + err.message)
    console.log('恭喜,写入成功')
    })

fs模块中路径操作问题【难点】

  • 在Node中,使用fs模块来操作文件的时候,如果我们给定的路径是一个以’./‘开头的相对路径,找文件指的是Node命令所在的目录来查找 ,所以最好使用绝对路径(拼接成绝对路径 __dirname + ‘文件名’)
  1. __dirname 表示当前文件所在的目录
  2. __filename 表示当前文件的完整路径
  • const path = require(‘path’) const abspath = path.join(__dirname,’文件名’)来解决/的问题
  • 注意: 今后在使用fs模块中任意方法时只要涉及路径的操作都是用_dirname做拼接

读取文件信息 fs.stat

  • fs.stat(path,callback)

    读取指定目录中所有文件的名称 fs.readdir

  • fs.readir(path[,option],callback)

在ES6中用path来进行路径操作

  1. path.join([…paths])
  • path.join 来解决路径分隔符的问题
  • 今后只要涉及到路径的拼接,推荐大家使用 path.join 方法
  1. path.sep
  2. path.basename(path[, ext])
  3. path.dirname(path)
  4. path.extname(path)

    Javascript 是单线程的一门语言

  5. 什么是单线程:用户无法主动开启子线程,对于JS的运行来说,永远是主线程在执行关键代码;
  6. 什么是多线程:用户可以主动开启子线程; Thread td = new Thread()
  7. 在Node中,操作文件和网络都是比较耗时的操作;

Node中为什么大量使用异步方法

  1. 为什么要使用 异步方法呢: 因为 异步方法,不会阻塞CPU去执行其它任务;
  2. 为什么在Node中不推荐使用同步呢: 因为 同步,需要一个一个执行,耗时的操作会阻碍CPU执行后续任务,因此,效率慢;

CommonJS 模块规范和模块的使用

Node.js 实现了 CommonJS 模块化规范;

  1. 什么是 CommonJS 规范?
    • CommonJS 是为了实现 Javascript 的模块化,而制定的一套规范;
  2. 为什么 Javascript 需要模块化?(require,exports,module)
    • 所谓的模块化,就是在一个js文件中,能够引入其他的js文件
    • 浏览器中的Javascript有没有实现模块化?(在一个JS文件中,能不能引用另外JS文件中的方法)
    • 因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
  3. 如何实现 Javascript 的模块化?
    • 为了统一大家编写模块时候的规则,方便各模块之间的依赖和调用,于是 CommonJS 规范就应运而生了。
  4. 那么,CommonJS 模块化规范,到底是个什么东西??
    • 定义了什么是模块
    • 在NodeJs中,可以认为一个js文件就是一个模块,一个模块可以是一个js文件,也可以是由多个js文件组成的
    • 一个JS模块中,如何引入其它的JS模块
    • 用require引入: require(‘路径’)
    • 一个JS模块中,如何向外暴露一些成员,供其它模块调用;
    • exports.xxx = xxxx
    • 使用module对象去访问当前模块, module.exports.xxx=xxx
    • 如果没有类似于 CommonJS 的规范,行不行?
    • 只有大家遵守同样的规范,才能够协作开发,方便别人,同时也方便自己;

全局作用域和模块作用域

  1. 每个JS文件,就是一个独立的模块,在这个JS文件中,所定义的任何方法、属性、变量、常量、对象,默认都属于模块作用域,并不会属于 全局作用域;
  2. 如果在某个模块内部,想为 全局的 global 作用域挂载一些属性,需要显示的调用global.***来挂载;

    global(全局作用域)

  • 使用global这个全局对象,会造成全局变量的污染(推荐使用CommonJS规范中定义模块化机制来向外暴露成员)

    require(模块引用)

    每一个实现了 CommonJS 规范的模块,必须定义一个 require() 函数,使用这个 require 函数,就能够 很方便的导入其它 模块中的成员,供自己使用;

    exports(模块定义)

    每一个模块中,如果想要把自己的一些私有成员,暴露给别人使用,那么,必须实现一个 exports 对象,这个对象,将来,如果你想把自己的成员,暴露给别人使用,只需要把自己的成员,挂载到 exports 上就行了(exports默认是空对象)

    module(模块标识)

    这个 module 也是Common JS 规定的,它表示一个具体的模块,也是一个对象;

module.exports 和 exports 的关系

  • 在一个模块中,默认情况下module.exports和exports是指向同一个空对象的
  • 在一个模块中,在向外暴露成员的时候,永远以module.exports指向的对象为准

Node 中的 Javascript 由几部分组成

  1. ECMAScript 核心
  2. 全局成员
  3. 模块系统成员
    • 模块系统成员,根据一些区别,又可以分为三大类: 核心模块、第三方模块、用户自定义模块

模块分类

核心模块

  1. 什么是核心模块:由Node官方提供的好用的模块,叫做核心模块;只要大家在计算机中,安装了Node这个应用程序,那么,我们的计算机中就已经安装了所有的 核心模块;
  2. 如何使用核心模块:require('核心模块标识符')

    第三方模块

  3. 什么是第三方模块:一些非官方提供的模块,叫做第三方模块;注意,第三方模块,并不在我们的计算机上,如果大家需要使用某些第三方模块,必须去一个叫做 NPM 的网站上搜索并下载才能使用;
  4. 如何使用第三方模块:
    • 先从 npm 官网上下载指定的第三方模块
    • 使用 require('第三方模块的名称标识符')来导入这个模块
    • 根据 第三方模块的 官方文档,尝试使用

      用户自定义模块

  5. 什么是用户模块:程序员自己写的JS文件,就叫做 用户自定义模块;
  6. 如何使用用户模块:require('路径标识符')

什么是包

  1. 英文名叫做 Packages,包是在模块基础上更深一步的抽象,目的是:方便分发推广基于 CommonJS 规范实现的 应用程序 或 类库;
  2. 包可以看作是 模块、代码 和 其它资源 组合起来形成的 独立作用域;

定义一个包

  1. 首先包应该是一个独立的文件夹(名字应该是英文)
  2. 包的文件夹里面应该有一个package.json文件
  3. package.json是一个json文件,在文件里编写的代码必须符合json规范(编写一个对象,对象有三个属性: name,version, main)
  4. 自定义的包目录(图片)

规范的包结构

  1. 包都要以一个单独的目录而存在
  2. package.json 必须在包的顶层目录下
  3. package.json 文件必须符合 JSON 格式,并且必须包含如下三个属性:name, version, main
    • name: 包的名字(名字不能为中文)
    • version: 包的版本号
    • main: 表示包的入口文件
  4. 二进制文件应该在bin目录下;
  5. javaScript代码应该在lib目录下;
  6. 文档应该在doc目录下;
  7. 单元测试应该在test目录下;
  8. Node.js对包要求并没有那么严格,只要顶层目录下有package.json,并符合基本规范即可;

    包描述文件 package.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    name:包的名称,必须是唯一
    description:包的简要说明
    version:符合语义化版本识别规范的版本字符串
    keywords:关键字数据,通常用于搜索
    maintainers:维护者数组,每个元素要包含name、email、web可选字段
    contributors:贡献者数组,格式与maintainers相同。包的坐着应该是贡献者数据的第一个元素
    bugs:提交bug的地址,可以是网址或者电子邮件地址
    licenses:许可证数组,每个元素要包含type和url字段
    repositories:仓库托管地址数组,每个元素要包含type、url和path字段
    dependencies:包的依赖,一个关联数组,由包名称和版本号组成。
    devDependencies:开发依赖项,表示一个包在开发期间用到的依赖项

npm

npm 的两层含义

  1. NPM 是一个 第三方模块的托管网站,指的就是https://www.npmjs.com/
  2. NPM 是Node的包管理工具(全名叫做 Node package manager),在我们安装Node时候,就已经顺便也安装了 NPM 这个管理工具;

    安装和卸载全局包

  3. 什么是全局的包:通过 npm install 包名 -g 方式安装的包,都安装到了全局;一般全局的安装目录是C:\Users\自己的用户文件夹\AppData\Roaming\npm
  4. 带大家演示如何安装一个全局的包:npm install i5ting_toc -g, 注意:这里的-g表示全局安装包的意思;
  5. 注意:一般,只有一些工具,才有全局安装的必要性;
  6. 如果要全局卸载某个包,比如要卸载 i5ting_toc了,直接运行npm uninstall i5ting_toc -g就可以全局卸载包了!

    安装和卸载本地包

  7. 什么是本地的包:跟着项目安装的包,叫做本地包;
  8. 如果拿到一个空项目,必须先初始化一个package.json的配置文件,npm init或者npm init -y(方便些)
  9. 运行npm i 包名@版本号 --save去安装指定的包,本地安装的包,都安装到了node_modules的目录下
  10. 如果大家用的是npm 5.x的版本,可以不指定--save命令,如果用的是 npm 3.x 的版本,则需要手动指定 --save, 同时,--save有缩写形式,是:-S
  11. package-lock.json文件中记录了曾经装过的包的下载地址,方便下次直接下载包;
  12. 卸载本地包: npm uninstall 包名 --save

    其它常用命令

  13. --save-dev它的缩写是-D
  14. 注意:dependencies节点,表示项目上线部署时候需要的依赖项;devDependencies节点,表示项目在开发阶段需要的依赖项,但是当项目要部署上线了,devDependencies节点中的包,就不再需要了!
  15. 注意:当使用npm i快速装包的时候,npm会检查package.json文件中,所有的依赖项,然后都为我们安装到项目中
  16. --production 表示只安装 dependencies 节点下,记录的包,不安装devDependencies节点下的包;当项目要上线了,才会使用--production命令

    解决 npm 下载慢问题

  17. 默认,NPM在下载包的时候,连接的是国外的服务器,所以,有时候如果网速不是特别好,可能下载不下来包;此时,大家可以安装一个工具,叫做nrm,里面记录了好多下载NPM包的服务器地址,可以让我们方便的切换下载包时候请求的服务器;
  18. 运行npm i nrm -g(注意:只要是工具,一般都是全局 -g 安装)
  19. 当装完 nrm 之后,可以运行nrm ls 查看所有可用的服务器列表
  20. 可使用nrm use 服务器名称来切换下载包时候的服务器地址

构建web应用

Apache 是一个PHP的服务器,当我们把做好的网站,丢到 WWW 目录下,就能够使用 IP地址 + 端口号 访问我们的网站了;

  • IP地址的作用,是为了表示当前网络中这台计算机唯一身份的;
  • 端口号:端口号和应用程序有关联,每一个应用程序,只能独占一个端口号,不能说多个应用程序公用一个端口;
    疑问:Node中,有没有类似于 Apache 这样的服务器软件,来提供对应的网站服务呢?注意:Node中,并没有现成的 类似于 Apache 的服务器软件,如果我们想通过Node,来对外托管一个网站的话,需要自己手写一个 类似于 Apache 的服务器;

    BS 交互模型

  1. HTTP 协议是基于 请求 - 处理 - 响应 通信模型的

    实现静态资源服务器

  2. 后端路由的本质
  • 重点理解URL(统一资源定位符)的本质

    在 Node 中使用模板引擎

    使用 http 核心模块 - 构建自己的 web server(web服务器)

    理解 BS 交互模型

    B/S:表示 Browser / Server C/S Client / Server
  1. 什么是服务器:在网络节点中,专门对外提供资源服务的一台电脑;
  2. 什么是客户端:在网络节点中,专门用来消耗或呈现服务器中返回的数据的电脑;
  3. 什么是静态资源:像 .js , .css, .jpg, .html ;所谓的静态资源,就是无需数据交互,服务器直接把资源读取,并响应给客户端就完事儿;
  4. 什么是动态资源:当一些资源,服务器上并没有现成的,需要现在服务器端,做一层处理,最后把处理的结果返回给客户端,这样的资源,叫做动态资源;
  5. HTTP 协议的通信模型:请求 - 处理 - 响应的过程;

    创建基本http服务器

  • 1.先导入Node中提供分核心模块 http : const http = require(‘http’)
    1. 创建服务器 : const server = http.createServer()
    1. 为这个server服务器,通过on方法,绑定一个事件: server.on(‘request’,function(){
      console.log(‘ok)
      })
    1. 启动服务器: server.listen(3000,function(){
      console.log(‘通知大家,服务器已经启动’)
      })

      解决返回内容的编码格式

  • 200 是状态码
    1
    2
    3
    res.writeHeader(200,{
    "Content-Type":'text/html;chartset=utf-8'
    })

实现静态资源服务器

  1. 后端路由的本质
  • 重点理解URL(统一资源定位符)的本质
  • 注意: res.end()方法只接受字符串或Buffer二进制,在网络传输中内容都是以二进制进行传输的

    结合模板引擎实现动态资源服务器

静态资源请求路径问题

  1. 在浏览器的眼中,只要是地址栏中的地址,浏览器永远把 最后一个 / 后面的符号,认为是文件名
  2. 对于服务器来说,客户端请求的 URL 资源路径只是一个 标识符 而已,URL 不一定非要对应实际的物理磁盘路径!
  3. 一定要区分html中书写的路径标识符 ../ .// 在浏览器中代表含义!
  • / 表示,直接从端口号后面,开始资源请求
  • ./ 表示,在发起资源请求之前,浏览器需要先 把 URL 地址 ,和 资源路径做一层拼接
  • ../ 表示,在发起资源请求之前,浏览器需要先 把 URL 地址 ,和 资源路径做一层拼接

    使用 nodemon 工具来自动重启web服务器

  • 这个工具的作用:能够实时监听 web 服务器中,代码的改变,只要代码被修改并保存了,则 nodemon 工具,会自动重新启动 web 服务器;
  • 运行 npm i nodemon -g 就能够在全局环境中,安装这个工具了
  • 当安装完毕 nodemon 之后,就可以 使用 nodemon 要执行的js文件路径 来运行JS文件了
  • 今后在开发Web项目的时候,推荐使用 nodemon 来执行 web 服务器

Node 中的 Web 快速开发框架 - Express

定义什么是Express:

  1. 基于 Node.js 后端Javascript平台之上,开发出来的一套Web开发框架;
  2. Express中,基于 原生Node的特性,做了进一步的封装,提供了一些更加好用的方法,来提高Web开发的体验;
  3. Express中,并没有覆盖或者删除原生的http模块方法;

    express 框架的安装和基本使用

  4. 直接运行 npm install express --save 就可以安装Express框架了

    使用 express 快速托管静态资源

  5. 如果我们网站中,有很多静态资源需要被外界访问,此时,使用 res.sendFile 就有点力不从心了,这时候,express 框架,为我们提供了一个 内置的(中间件) express.static('静态资源目录') , 来快速托管指定目录下的所有静态资源文件;
  6. 用法: app.use(express.static('public'));
  • 其中, express.static 是一个express的内置中间件;
  • app.use()方法,是专门用来注册 中间件;
  1. 当使用 第二步中的方法,把指定目录托管为静态资源目录之后,那么,这一层被托管的目录,不应该出现在 资源访问的 URL地址中;

  2. 在一个Web项目中,我们可以多次调用app.use(express.static())

  3. 在多次调用 express.static 的时候,如果文件名称有重复的,则以先注册的中间件为主!

  4. 如果项目要部署了,推荐大家配置一个叫做compression的中间件,它能够开启服务器的GZip压缩功能;

  5. 1
    var compression = require('compression') app.use(compression())

为 express 框架配置模板引擎渲染动态页面

  1. 安装 ejs 模板引擎npm i ejs -S
  2. 使用 app.set() 配置默认的模板引擎 app.set('view engine', 'ejs')
  3. 使用 app.set() 配置默认模板页面的存放路径 app.set('views', './views')
  4. 使用 res.render() 来渲染模板页面res.render('index.ejs', { 要渲染的数据对象 }),注意,模板页面的 后缀名,可以省略不写!
  5. 使用res.send()来解决中文乱码问题

    使用 express 框架中提供的路由来分发请求

  6. 什么叫做路由:前端请求的URL地址,都要对应一个后端的处理函数,那么 这种URL地址到 处理函数之间的对应关系,就叫做后端路由;
  7. 在Express中,路由主要负责 分发请求处理的;
  8. 在Express中,如何 定义并使用路由呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 1. 封装单独的 router.js 路由模块文件
    const express = require('express')
    // 创建路由对象
    const router = express.Router()

    router.get('/', (req, res)=>{})
    router.get('/movie', (req, res)=>{})
    router.get('/about', (req, res)=>{})

    // 导出路由对象
    module.exports = router
  9. express创建的 app 服务器,如何使用 路由模块呢?

    1
    2
    3
    4
    // 导入自己的路由模块
    const router = require('./router.js')
    // 使用 app.use() 来注册路由
    app.use(router)

Express 框架里 中间件的概念

  1. 什么是中间件
  • 中间件是一个函数
  • 中间价这个函数是一个路由的处理函数
  • 中间件这个函数,不但是一个路由处理函数,而且在参数列表中,还有一个很重要的形参,叫做next
  • 这个next是一个函数, 因此,我们在中间件中,调用这个next()函数
  • 中间件的表现形式: 只要这个函数的形参列表中,有一个next函数,它就是中间件
  1. 中间件的作用是
  • 中间件表示每一个处理环节,这些处理环节,只负责单独的处理,每当上一个中间件处理完毕后,必须把处理的原材料交给下一个中间件来继续处理
  • 中间件之间,共享的是 req和res这两个对象
  1. 如何注册中间件
  • app.use(中间件的函数)
  1. 中间件里next函数的作用
  • 从上一个中间件处理函数中,进入下一个中间件处理函数

    自己模拟一个解析表单数据的中间件

  1. 自己模拟中间件的时候,需要使用 req.on('data', (chunk)=>{})req.on('end', ()=>{}) 来获取表单,并使用 querystring模块的parse方法来解析成对象;
  2. 我们在开发中,推荐大家直接使用第三方的body-parser中间件来解析表单;

    Express 框架中对中间件的5种分类

  3. 应用级别中间件:挂载到 app 对象身上的 中间件(函数 )
  4. 路由级中间件: 挂载到 router 对象上的中间件
  5. 错误处理中间件:参数列表中要有四个形参,从前到后分别是 err, req, res, next
  6. 内置中间件: Express 中唯一的内置中间件 express.static(root, [options])
  7. 第三方中间件: 通过 npm安装的中间件,叫做 第三方中间件!

Express 中进行数据库操作

  1. 用的数据库是MySql
  2. 大家是直接安装的MySql
  3. 安装了一个叫做 Navicat 的软件,来方便我们操作数据库

    配置 MySql 数据库环境

    mysql 第三方模块的介绍和基本配置

    使用 mysql 第三方模块实现 CRUD

  • C: Create
  • R: Read
  • U: Update
  • D: Delete

模块加载机制

  1. 只要使用 require 来加载指定的模块了,那么,必然会执行被加载模块中的代码!

    优先从缓存中加载

  2. Node中,默认会把曾经加载过的模块,缓存到内存中,这样,当下次再使用相同的模块,就直接从缓存中加载就行了,能够提高模块的运行效率;

    核心模块的加载机制

  3. 先从缓存中查找,如果有,则直接使用;
  4. 如果缓存中没有,则加载本地的核心模块并缓存起来,供下次使用;

    用户模块的加载机制

  5. 先从缓存中查找,如果有,则直接使用;
  6. 如果缓存中没有,则根据路径标识符,加载本地的用户模块并缓存起来,供下次使用;
  7. 用户模块的查找规则:index -> index.js -> index.json -> index.node

    第三方模块的加载机制

  8. 先从缓存中查找,如果有,则直接使用;
  9. 如果缓存中没有,则根据第三方模块的标识符,加载第三方模块并缓存起来,供下次使用;

    通过学习moment这个第三方模块来了解第三方模块的加载规则

  10. 会根据包的名称,直接在当前项目的根目录中,去查找一个叫做 node_modules 的文件夹;
  11. 如果有,则在 node_modules 中,继续查找,一个叫做 模块引用名称的文件夹;
  12. 如果有,则 在 模块对应的文件夹中,查找一个叫做 package.json 的文件;
  13. 如果有 package.json, 则查找 其中的 main 属性,并尝试加载 main 指定的文件作为入口;
  14. 如果能正常加载 main 属性中指定的文件,则模块/包加载成功!
  15. 如果 在 package.json 文件中,没有 main 属性,则会依次尝试加载 包根目录中的 index.js, index.json, index.node
  16. 如果在 包的根目录中,根本没有 package.json 文件,或者 在 node_modules 目录中没有 index 相关的文件,或者,根本没有对应的包文件夹,或者在项目根目录中根本没有node_modules, 则会向上层目录中,去查找node_modules, 查找规则同上;
  17. 如果在上一层目录中还是没有找到对应的模块,则继续向上翻,直到翻到项目所在的磁盘根目录位置;
  18. 如果翻到了磁盘的根目录中,还没有找到,此时,会报错!cannot find module ***

    express中获取参数的几种形式

    从URL地址中获取查询参数

    通过 URL 地址栏中,? 形式传递的参数,可以直接通过 req.query 来获取;

    从URL地址中获取路径参数

    直接通过路径标识符来传递参数,/userinfo/10/zs, 可以通过 req.params来获取参数

    从post表单中获取提交的数据

  • 借助于body-parser来解析表单数据
  • npm i body-parser -S
  • const bodyParser = require('body-parser')
  • app.use(bodyParser.urlencoded({ extended: false }))

Web 开发模式

混合模式(传统开发模式)

  • 以后端程序员为主,基本上不需要前端程序员,或者,前端程序员只负责画页面、美化样式、写JS特效,前端程序员不需要进行数据的交互;
  • 这种开发模式,在前几年比较常见;
  • 他们用的最多的是 Jquery + 模板引擎
  • 后端页面 .php .jsp .aspx .cshtml

    前后端分离(趋势)

  • 后端负责操作数据库、给前端暴露接口
  • 前端负责调用接口,渲染页面、前端就可以使用一些流行的前端框架 Vue, React, Angular

需求分析

  1. 后端项目运行地址:http://127.0.0.1:5000
  2. 前端项目运行地址:http://127.0.0.1:4000
  3. 前后端分离开发模式的注意点:
    • 跨域问题
    • 如果不考虑 表单的 Post 提交,则 可以使用 JSONP的形式来请求接口
    • 但是,我们的项目中,涉及到了 英雄表单的 提交,表单提交一般都是Post
    • 经过分析,由于JSONP,不支持Post,所以,我们的后端接口,无法设计成JSONP的接口;

JSONP 和 CORS 的区别

  1. JSONP的原理:动态创建script标签;
    • JSONP发送的不是Ajax请求
    • 不支持 Post 请求;
  2. CORS中文意思是跨域资源共享 , 本质,就是使用 XHR 对象,发送Ajax请求,来进行跨域的资源共享;
    • CORS 发送的是真正的Ajax请求
    • CORS 支持Ajax的跨域
    • 如果要启用 CORS 跨域资源共享,关键在于 服务器端,只要 服务器支持CORS跨域资源共享,则 浏览器肯定能够正常访问 这种 CORS 接口;而且,客户端在 发送 Ajax的时候,就像发送普通AJax一样,没有任何代码上的变化;
  3. 对于Node来说,如果想要开启 CORS 跨域通信,只需要安装cors的模块即可;

    HTTP协议的无状态性

  4. HTTP协议的通信模型:基于请求 - 处理 - 响应的!
  5. 由于这个通信协议的关系,导致了HTTP每个请求之间都是没有关联的,每当一个请求完成之后,服务器就忘记之前谁曾经请求过!
  6. 如果纯粹基于HTTP通信模型,是无法完成登录状态保持的!每次请求服务器,服务器都会把这个请求当作新请求来处理!
  7. 我们可以通过 cookie 技术,实现状态保持,但是由于cookie是存储在客户端的一门技术,所以安全性几乎没有,因此不要使用cookie存储敏感的数据!

cookie介绍

什么是cookie,作用是什么

  • 由于Http协议是无状态的,且传统服务器只能被动的响应请求,所以,当服务器获取到请求的时候,并不知道当前请求属于哪个客户端!
  • 服务器为了能够明确区分每个客户端,需要使用一些小技术,来根据不同的请求区分不同的客户端;
  • 只要有请求发生,那么必然对应一个客户端,那么,我们可以在每次客户端发起请求的时候,向服务器自动发送一个标识符,告诉服务器当前是哪个客户端正在请求服务器的数据;
  • 如何提供这个标识符呢?我们可以在请求头(Request Headers)中添加一个标签,叫做cookie,这样,每次发送请求,都会把这个cookie随同其他报文一起发送给服务器,服务器可以根据报文中的cookie,区分不同的客户端浏览器。
  • 如何在客户端请求头中添加标识符?
    • 在Node中可以在writeHeader的时候,通过Set-Cookie来将cookie标识通过响应报文发送给客户端!
    • 客户端也可以通过一些方式来操作自己的cookie,比如通过jquery.cookie这个插件!

cookie的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var http = require('http');

var server = http.createServer();

server.on('request', function (req, res) {
// 解析cookie
var cookies = {};
var cookieStr = req.headers.cookie; // 从请求的headers中获取cookie信息
cookieStr && cookieStr.split(';').forEach(function (item) {
var parts = item.split('=');
cookies[parts[0].trim()] = parts[1].trim(); // 将cookie解析出来,保存到对象中
});

res.writeHeader(200, {
'Content-Type': 'text/plain; charset=utf-8',
"Set-Cookie": ['issend=ok', 'age=20']
});

if(cookies.issend ==='ok'){
res.end('不要太贪心哦!');
}else{
res.end('呐,赏你一朵小红花~~');
}
});

server.listen(4000, function () {
console.log('服务器已启动!');
});

通过expires设置Cookie的过期时间

1
2
3
4
5
6
7
8
// 设置 过期时间 为60秒之后
// 注意:在设置过期时间的时候,需要将时间转换为 UTC 格式
var expiresTime = new Date(Date.now() + 1000 * 60).toUTCString();
res.writeHeader(200, {
'Content-Type': 'text/html; charset=utf-8',
'Set-Cookie': ['isvisit=true;expires=' + expiresTime, 'test=OK']
});
res.end('<h3>你好,欢迎光临,送给你一个苹果!</h3>');

GMT和UTC有什么区别?格林尼治标准时(GMT)与世界时(UTC)是怎么回事

cookie可以被伪造,不安全

使用谷歌插件edit this cookie,就能伪造cookie数据!所以不要使用cookie存储敏感的数据!比如登录状态和登录信息;
一些敏感的数据,应该存储都服务器端!

什么是Cookie的应用场景

  1. 对安全性要求不高
  2. 不需要存储大量的数据
  3. 主要应用场景,是用来做 客户端 与 服务器之间的 状态保持技术;

使用express-session来保存登录状态

什么是session

由于HTTP是无状态的,所以服务器在每次连接中持续保存客户端的私有数据,此时需要结合cookie技术,通过session会话机制,在服务器端保存每个HTTP请求的私有数据;

session原理

在服务器内存中开辟一块地址空间,专门存放每个客户端私有的数据,每个客户端根据cookie中保存的私有sessionId,可以获取到独属于自己的session数据。

在express中使用session

  1. 安装session模块

    1
    npm install express-session -S
  2. 导入session模块

    1
    var session = require('express-session')
  3. 在express中使用session中间件:

    1
    2
    3
    4
    5
    6
    // 启用 session 中间件
    app.use(session({
    secret: 'keyboard cat', // 相当于是一个加密密钥,值可以是任意字符串
    resave: false, // 强制session保存到session store中
    saveUninitialized: false // 强制没有“初始化”的session保存到storage中
    }))
  4. 将私有数据保存到当前请求的session会话中:

    1
    2
    3
    4
    // 将登录的用户保存到session中
    req.session.user = result.dataValues;
    // 设置是否登录为true
    req.session.islogin = true;
  5. 通过destroy()方法清空session数据:

    1
    2
    3
    4
    5
    6
    req.session.destroy(function(err){
    if(err) throw err;
    console.log('用户退出成功!');
    // 实现服务器端的跳转,这个对比于 客户端跳转
    res.redirect('/');
    });

相关文章

  1. node.js中express-session配置项详解
  2. MD5在线生成器1
  3. MD5在线生成器2
  4. JavaScript-MD5