node基础

Node.js - day1

1. 整个node.js课程学习目标

  1. 【基础概念】
    • 什么是node.js以及node.js的特点
    • node.js适合做什么
    • 什么是模块化、什么是Common.js模块化规范
    • 模块、包、npm、包加载机制
    • etc…
  2. 【基本能力】
    • 掌握node.js中基本API的使用
    • 能够使用npm管理包
    • 能够使用ES6新语法
    • 能够使用node.js创建基本的web服务器
  3. 【高阶内容】
    • 能够使用Express框架、结合mysql数据库实现后端网站

注意:整个node.js学习阶段,概念类知识点较多,需要理解透彻!

2. 了解基本概念

2.1 语言 和 环境(平台) 之间的关系

  1. 语言,是编写代码的语法规范;程序员遵循特定的语法规范,编写出来的代码,只是单纯的文本字符串而已,并不具备可执行的特点
  2. 环境(平台),提供了执行代码的能力,如果程序员编写的代码想要成功执行,必须要依赖于特定的执行环境;
    • 例如:Javascript代码可以被浏览器中的JS解析引擎执行;
    • 所以,浏览器,就是一个 Javascript 的执行环境;因为 Javascript 代码可以在浏览器中被执行;

2.2 前端 和 后端

  1. 前端主要工作
    1. 页面结构
    2. 美化页面样式
    3. 书写页面的业务逻辑
    4. 使用Ajax调用后台接口
  2. 后端主要工作
    1. 操作数据库
    2. 对外暴露操作数据库的API接口
  3. 前后端协作开发

2.3 浏览器环境 中的 Javascript

  1. 诞生:上世纪 90年代左右,网景 liveScript、表单验证
  2. 一战:
    • 主角:微软和网景
    • 结果:微软IE胜出
    • 胜利的果实:ECMAScript 语言标准的诞生
  3. 二战
    • 主角:谷歌、火狐、IE
    • 结果:谷歌胜出
    • 胜利的果实:谷歌的V8引擎是最快的JS解析引擎
  4. 回顾:浏览器中 Javascript 的组成部分
    • ECMAScript核心 + DOM + BOM

2.4 Node环境 中的 Javascript

  1. 什么是 Node.js:就是 Javascript 的服务器端运行环境,可以让程序员使用 Javascript 来实现服务器端的编程;

    是基于chorm的v8引擎的javascript运行环境,事件驱动,非阻塞I/O

    node是后台语言,用javascript实现

  2. Node.js 中 Javascript 的组成部分

    • ECMAScript 核心 + 全局成员 + 核心 API 模块
    • 全局成员:console、setInterval、setTimeout。。。
    • 核心 API 模块:就是 Node 平台 单独提供的一些API,这些API是Node平台所独有的;
  3. 注意:Node.js 中 没有 BOM 和 DOM

    1. 由于 Node 服务器端运行环境中,没有浏览器 和 HTML 的概念,所以,Node中的javascript 提出了 DOM 和 BOM 这两个对象模型,取而代之的,是 全局成员 和 核心 API 模块;

2.5 ECMAScript 规范,浏览器中的js,Node中的js之间的关系

  1. ECMAScript 规范(标准):就是一本书,这本书中规定了语言的特性;
  2. 浏览器中的js:
    • 浏览器中的 js 组成部分:ECMAScript 核心 + DOM + BOM
  3. Node中的js:
    • Node中的 js 组成部分: ECMAScript 核心 + 全局成员 + 核心API成员

2.6 画图说明 浏览器中的js 与 Node中 js 之间的区别

2.7 总结

  1. 什么是 Node.js(我们自己给它下个定义)

    就是一个服务器端的Javascript运行环境,可以让程序员,通过 Javascript 做 后台服务器编程开发

  2. 学习 Node.js 可以做什么

    • 像 PHP 一样,使用 Javascript 编写符合规范的后端 API 接口网站
    • 使用 Node.js 开发一些实用的工具
    • 基于 Socket 技术,开发类似于聊天室之类的即时通讯项目
    • 基于 Electron 环境,开发桌面软件
    • ect…

3. 环境安装

3.1 LTS 和 Current 版本区别

  1. LTS:【推荐在企业中使用】,是长期稳定版的安装包,运行稳定、安全;
  2. Current:【推荐学习或尝鲜去使用】,是最新特征版,这个安装包中有最新的Node特性;

3.2 下载安装

安装的 node中包含了npm

3.3 查看 Node 版本号

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

3.4 path 环境变量

  1. 什么是path环境变量
    • Path环境变量的作用:能够让我们在终端中执行相关的命令,从而快速启动应用程序;
    • 输入命令时,先在当前目录下查找此程序,如果没有找到,就去系统的path变量里找
  2. 系统环境变量和用户环境变量的区别
    • 用户环境变量:电脑上可以设置不同的用户,各个之间的用户变量是私有的
    • 系统环境变量:是公共的,不同用户都可以使用
  3. 通过终端输入命令的形式,快速启动应用程序时,路径的查找规则
    • 先在当前运行 终端命令的 目录中查找,如果有则直接运行;
    • 如果当前运行 终端命令的 目录中没有,则去全局的path环境变量中查找;

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

4.1 node 命令【推荐】

  1. 直接使用 node 要执行的js文件的路径 来执行指定的JS文件

小技巧:

  1. 在终端中,使用键盘的↑, 可以快速定位到上一次执行的命令
  2. 在终端中,使用键盘的 tab 键能够快速补全路径
  3. windows 系统上,在终端中输入 cls 可以清屏

4.2 REPL 环境 - 了解

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

5. ECMAScript 6常用语法

5.1 let(变量) 与 const(常量)

vscode打开集成的终端 ctrl+反引号(tab上方的),用tab可以补全路径

  1. 之前定义变量,用 var 关键字;有如下主要缺点:
    • 存在变量提升问题,降低 js 代码的可阅读性
    • 没有块级作用域,容易造成变量污染
  2. let 主要特性:用于定义变量
    • 不存在变量提升问题,只有定义之后才能使用此变量
    • { } 作用域
  3. const 主要特性:
    • 不存在变量提升问题,只有定义之后才能使用此变量
    • const 定义的常量无法被重新赋值
    • 当定义常量的时候,必须定义且初始化,否则报语法错误
    • const 定义的常量,也有 块级作用域

5.2 变量的解构赋值(常用)

定义:所谓的解构赋值,就是把 某个对象中的属性,当作变量,给解放出来,这样,今后就能够当作变量直接使用了;

  • 可以使用 :为解构出来的变量重命名
    js
    // 变量的解构赋值
    let user={
    name:'zs',
    age:18,
    gender:'女'
    }
    // 结构赋值的标准写法
    //把user中的变量释放出来
    let {name,age:userage,gender}=user;
    console.log(name);
    //起了别名之后不能再访问age,只能方位userage

5.3 箭头函数(常用)

  1. 语法:

    • 定义普通的 function 函数

      function show() {
               
       console.log('这是普通 function 定义的 show 方法')
      }
      
    • 把方法定义为箭头函数(最完整的写法)

      (形参列表) => { 函数体代码 }
      
      () => {
       console.log('这是普通 function 定义的 show 方法')
      }
  2. 箭头函数,本质上就是一个匿名函数

  3. 箭头函数的特性: 箭头函数内部的 this, 永远和 箭头函数外部的 this 保持一致;

    document.getElementById('btn').onclick = function () {
                  setTimeout(function() {
                       this.style.backgroundColor = 'red';
                   }, 1000);
                }//因为This的指向问题,此代码会报错
  4. 变体

    • 变体1:如果箭头函数,左侧的形参列表中,只有一个参数,则,左侧小括号可以省略;
    • 变体2:如果右侧函数体中,只有一行代码,则,右侧的 { } 可以省略,同时rreturn也可以省略;
    • 变体3:如果箭头函数左侧 只有一个 形参,而且右侧只有一行代码,则 两边的 (){} 都可以省略
  5. 注意: 如果我们省略了 右侧函数体的 { },那么,默认会把 右侧函数体中代码的执行结果,当作箭头函数的调用结果 return 出去;

    var add = x => x+x

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

let name='zs';
let age = 13;
let show =() => {
    console.log('show');
}

let person = {
    name,
    age,
    show,
    // see : function () {
     
    //     console.log('see');
    // }
     see() {
         console.log('see');
     }
}
console.log(person)

6. 文件操作

6.1 文件读取

  • fs.readFile
const fs = require('fs');
fs.readFile__dirname+'/files/2.txt','utf-8',(err,data)=>{
    if(err) return console.log('文件读取失败'+err.message);
    console.log('读取成功,文件的内容是'+data);
})

6.2 文件写入

  • fs.writeFile
const fs = require('fs');
fs.writeFile(__dirname+'/files/2.txt','222你好',(err)=>{
    if(err) return console.log('文件写入失败'+err.message);
    console.log('文件写入成功');
})

6.3 文件追加

  • fs.appendFile
const fs = require('fs');
fs.appendFile(__dirname+'/files/2.txt','\n444',(err)=>{
    if(err) return console.log('追加失败'+err.message);
    console.log('追加成功');
})

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

  • 使用 fs 模块操作文件的时候,如果提供的操作路径是 相对路径, 则会根据当前执行node命令时的磁盘目录,去拼接提供的文件的相对路径,从而容易出现问题;例如:

    const fs = require('fs')
    
    // 调用 fs.readFile 方法时,提供的第一个参数是相对路径,容易出现问题
    fs.readFile('./files/1.txt', 'utf-8', (err, data) => {
      if(err) return console.log(err.message)
      console.log(data)
    })
  • 推荐使用 node 中提供的 __dirname 来解决 fs 模块操作文件时候的路径问题

6.5 读取文件信息 fs.stat

fs.stat(__dirname+'/1.txt',(err,stats)=>{
    if(err) return console.log('获取文件信息失败'+err.message);
    console.log(stats.birthtime);
    console.log(stats.size);
})

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

6.7 复制文件 fs.copyFile

const fs = require('fs');
fs.copyFile('./2.txt',__dirname+'/files/copy.txt',(err)=>{
    if(err) return console.log('拷贝失败'+err.message);
    console.log('拷贝成功')
})

7. 练习

  1. 整理成绩.txt文件中的数据到成绩 - ok.txt文件中,整理好的文件中,格式类似于:
小红:99
小白:100
小黄:70
小黑:66
小绿:88
原来的格式:小红=99    小白=100    小黄=70    小黑=66    小绿=88
const fs = require('fs')
const path = require('path')

fs.readFile(path.join(__dirname,'/成绩.txt'),'utf-8',(err,data)=>{
    if(err) return console.log('读取文件失败'+err.message)
    // console.log(data)
    let newStr = data.split(' ')
    // console.log(newStr)
    let newData = []
    newStr.forEach(item=>{
        if(item.length>0){
            // console.log(item)
            let newScore = item.replace('=',':')
            //console.log(newScore)
            newData.push(newScore)

        }   
    })
    //  console.log(newData)
    fs.writeFile(path.join(__dirname,'/成绩-ok.txt'),newData.join('\n'),err=>{
        if(err) return console.log('写入失败')
        console.log('写入成功')
    })
})

8. 路径操作

  1. path.join([…paths])
  2. path.sep 输出当前系统的路径分隔符(windows为,Linux为/)
  3. path.basename(path[, ext]) 获取文件名,最后一个\之后的
  4. path.dirname(path) 获取路径名,最后一个\之前的
  5. path.extname(path) 获取文件的扩展名

Node.js - day2

0. 学习目标

  1. 了解同步和异步的概念
  2. 认识模块化 和 了解 Node.js 中CommonJS 模块化规范
  3. 了解npm包的规范
  4. 掌握npm的基本使用
  5. Node.js中http模块的基本使用

1. Javascript 的单线程和异步

Javascript 的解析和执行一直是单线程的,但是宿主环境(浏览器或node)是多线程的;

异步任务(ajax,定时器等耗时的任务)是由宿主环境开启子线程完成,并通过事件驱动、回调函数、队列,把完成的任务, 交给主线程执行;

所有的一部任务都是由浏览器执行的

Javascript解析引擎,一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行

事件队列中任务执行的条件:1)主线程已经空闲

                  2)任务满足触发条件:定时函数(延时时间已经达到),事件函数(事件被触发),ajax回调函数(服务器端数据被响应)

2. Node中的API为什么几乎都是异步操作

  1. 什么样的操作需要使用异步处理:要把 耗时的操作,放到异步中由浏览器去执行,浏览器在执行过程中碰到回调函数时,会把它放到事件队列中,待到js引擎空闲时,就会去事件队列中查找他们并执行。;
  2. 异步执行任务的好处:能够提高 耗时的任务它的执行效率,提高 JS 解析引擎的工作效率;

3. 认识模块化

模块化就是一种约定,一定规范;

场景模拟:小强,小黄,小刚 共同基于 Node.js 开发项目!

  1. 为什么要有模块化:为了解决文件之间的依赖关系
  2. 注意:模块化是一种开发思想;具体开发中需要定制符合实际需求的模块化规范
  3. 大家可以把模块化规范,认为是一种明文的约定,大家都按照相同的约定写代码,减少了不必要的沟通成本,极大方便了各个模块之间的调用,方便别人,同时也方便自己;

4. 了解 CommonJS 规范

  1. 作用:是一套 Javascript 的模块化规范,规定了 模块的特性各模块之间如何相互依赖
  2. 用途:Node.js 中使用了 CommonJS 规范;
  3. 特点:同步加载模块;不适合在浏览器端使用,在浏览器端适合使用cmd.amd;
  4. CommonJS规范都定义了哪些内容:require,export,moudelwiki 对于 Modules 的描述

5. 模块作用域 和 全局作用域

在Node.js中有两个作用域,分别是 全局作用域 和 模块作用域;

  1. 全局作用域使用 global 来访问,类似于浏览器中的window
  2. 每个 Javascript 文件,都是一个单独模块,每个模块都有自己独立的作用域,因此:使用require时模块中的成员,默认无法被其它模块访问。

5.1 使用 global 全局作用域在模块之间共享成员

  1. 如果在某个模块内部,想为 全局的 global 作用域挂载一些属性,需要显示的调用global.***来挂载;
  2. 注意:在开发中,一般情况下,不推荐使用global全局作用域来共享成员,会存在全局变量污染问题;

5.2 模块作用域

  1. module(模块标识)

    module 属性是 Common JS 规范中定义的,它是一个对象,表示当前这个具体的 js 模块;

  2. require(引用模块)

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

  3. exports(暴露模块成员)

    每一个模块中,如果想要把自己的一些私有成员,暴露给别人使用,那么,必须实现一个 exports 对象,通过exports对象,可以方便的把模块内私有的成员,暴露给外界使用;

5.3 module.exports 和 exports 的关系

  1. module.exportsexports 默认引用了同一个空对象,指向地址相同;
  2. module.exportsexports 作用一致,都可以向外暴露成员;
  3. 一个模块作用域中,向外暴露私有成员时,永远以 module.exports 为准,开发中推荐使用,画内存图理解;

6. 了解 - 浏览器端的 AMD 和 CMD 模块化规范

注意:浏览器端不能使用 CommonJS规范;因为 CommonJS 下,模块是同步加载的;

AMD/CMD可以理解为是commonjs在浏览器端的解决方案,AMD/CMD下,模块都是异步加载的;

  1. AMD模块化规范代表:RequireJS
    • 主要特性1:对于依赖的模块,AMD 是提前执行;
    • 主要特性2:推崇依赖前置;
  2. CMD模块化规范代表:SeaJS
    • 主要特性1:对于依赖的模块,CMD 是延迟执行;CMD 推崇 as lazy as possible.
    • 主要特性2:推崇依赖就近;
  3. ES6的模块化(大趋势):es6是在语言标准层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案

7. Node.js 中 模块 的概念

回顾:Node.js 由三部分组成:ECMAScript 核心 + 全局成员 + 模块成员

7.1 模块成员的分类

模块成员,根据一些区别,又可以分为三大类: 核心模块、第三方模块、用户自定义模块

7.1.1 核心模块

  1. 什么是核心模块:
    • 随着Node.js的安装包,一同安装到本地的模块,叫做核心模块;
    • 例如:fspath等模块,都是由Node.js官方提供的核心模块;
    • 只要大家在计算机中,安装了Node这个应用程序,那么,我们的计算机中就已经安装了所有的 核心模块;
  2. 如何使用核心模块require('核心模块标识符')

7.2.2 第三方模块

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

7.2.3 用户自定义模块

  1. 什么是用户模块:
    • 程序员在自己项目中写的 Javascript 文件,就叫做 用户自定义模块;
  2. 如何使用用户模块:require('路径标识符')

7.2 包的定义和使用

7.2.1 什么是包

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

7.2.2 规范的包结构

  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,并符合基本规范即可;

7.2.3 包描述文件 package.json【了解】

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

7.2.4 自己尝试定义一个包含 加减乘除的 calc 计算器包

8. npm

8.1 npm 的两层含义

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

8.2 安装和卸载全局包(i5ting_toc)

  1. 什么是全局的包:安装到计算机全局环境中的包,叫做全局包;安装的全局包可以在当前电脑的任何目录下,直接通过命令行来访问;
  2. 如何安装全局包:运行 npm install 包名 -g 即可;其中 -g 参数,表示 把包安装到全局目录中的意思;
  3. 全局包的安装目录:C:\Users\用户目录\AppData\Roaming\npm
  4. 什么样的包适合安装到全局:工具性质的包,适合安装到全局;
  5. 如何卸载全局包:要卸载某个全局的包,直接运行npm uninstall 包名 -g即可;其中 uninstall 表示卸载的意思;

8.3 安装和卸载本地包

  1. 什么是本地的包:跟着项目安装的包,叫做本地包;本地包都会被安装到 node_modules 目录下;
  2. 注意:如果拿到一个空项目,必须在当前项目根目录中,先运行 npm init或者npm init -y 命令,初始化一个package.json的配置文件,否则包无法安装到本地项目中;
  3. 如何安装本地包:运行npm i 包名 --save 即可安装本地包;都安装到了当前项目的 node_modules目录下;
    • 如果大家用的是npm 5.x的版本,可以不指定--save命令,如果用的是 npm 3.x 的版本,则需要手动指定 --save
  4. package-lock.json文件中记录了曾经装过的包的下载地址,方便下次直接下载包,能够加快装包的速度,提升装包的体验;
  5. 如何卸载本地包:使用 npm uninstall/remove 包名 -S/-D即可卸载指定的本地包;

8.4 其它常用命令

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

8.5 解决 npm 下载慢问题

  1. 默认,NPM在下载包的时候,连接的是国外的服务器,所以,有时候如果网速不是特别好,可能下载不下来包;此时,大家可以全局安装一个的工具,叫做 cnpm
  2. 如何安装cnpm:运行 npm i cnpm -g 即可;
  3. 如何使用cnpm:在装包的时候,只需要把 npm 替换成 cnpm 即可,例如:
    • 使用 npm 安装 jquery:运行 npm i jquery -S
    • 使用 cnpm 安装 jquery: 运行 cnpm i jquery -S

9. 使用 Node 构建 web 应用

  1. PHP是后端的网站开发语言,PHP 开发出来的网站,可以通过 Apache 服务器托管运行起来;
  2. 疑问:Node中,可以使用 Javascript 编写后端网站,那么,有没有类似于 Apache 这样的服务器软件,来提供对应的网站服务呢?必须自己手写一个静态资源服务器

9.1 B/S 交互模型

什么是B/S:特指基于 浏览器(Browser) 和 服务器(Server) 这种交互形式;

  1. 什么是服务器:在网络节点中,专门对外提供资源服务的一台电脑;
  2. 什么是客户端:在网络节点中,专门用来消费服务的一台电脑;
  3. HTTP 协议的通信模型:请求 - 处理 - 响应的过程;
    • 请求:由客户端发起请求;
    • 处理:由服务器端处理请求;
    • 响应:服务器端把处理的结果,通过网络发送给客户端;
  4. 什么是静态资源:服务器端只需要读取并直接发送给客户端、不需要进一步处理的资源,叫做静态资源;
  5. 什么是动态资源:服务器端没有现成的资源,需要服务器端动态生成的资源,叫做动态资源;

9.2 实现一个类似于Apache的 静态资源服务器

使用http核心模块,创建最基本的web服务器

  1. 创建最基本的web服务器

    • 创建服务器:使用 const server = http.createServer() 创建服务器;
    • 绑定监听事件:通过 server.on('request', function(req, res) { 请求的处理函数 }) 绑定事件 并 指定 处理函数;
    • 启动服务器:通过 server.listen(端口, IP地址, 启动成功的回调函数) 来启动服务器;
    //引入http模块
    const http = require('http');
    //创建服务器
    const server = http.createServer();
    //绑定监听事件
    server.on('request',function (req,res) {
           
       res.writeHeader(200,{
           'Content-Type':'text/plain;charset=utf-8'
       })
       res.end('你好');
    })
    //启动服务器
    server.listen(3000,'127.0.0.1',function () {
           
       console.log('server running at http://127.0.0.1:3000');
    })
  2. 防止响应内容中文乱码问题

    • 通过 设置响应报文头的 Content-Type,来指定响应内容的编码类型,从而防止乱码:

      res.writeHeader(200, { 
      'Content-Type': 'text/plain; charset=utf-8'
       //浏览器默认编码为gbk,此处设置text/plain表示普通文本,charset=utf-8表示以utf-8的形式进行编码
      })
      需要在res.end()之前来设置
  3. 根据不同的URL返回不同的文本内容

    • 使用 req.url 获取客户端请求的URL地址
  4. 根据不同的URL返回不同的HTML页面

    • 主要思路:使用 fs 模块 读取URL对应的HTML页面内容,并使用 res.end() 响应给客户端即可;
    text/plain 表示普通文本字符串
    text/html 表示以html标签的形式解析文本
  5. 处理并返回css样式表

  6. 处理并返回Javascript文件

  7. 优化

const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer();
server.on('request',(req,res)=>{
    // res.writeHeader(200,{
     
    //     'Content-Type':'text/html;charset=utf-8'
    // })在html页面中已经设置了编码格式
    let url = req.url;
    if(url==='/') url = '/view/index.html';
    fs.readFile(path.join(__dirname,url),(err,buf)=>{
        if(err) return res.end('404,not found');
        res.end(buf);
        //buf表示二进制,能用二进制就不用string,因为在传递的数据时始终以二进制形式传递,用buf少了一层转化
    })
})

server.listen(3000,'127.0.0.1',function () {
     
    console.log('server running at http://127.0.0.1:3000');
})

9.3 在 Node 中 结合模板引擎 实现动态资源服务器

<h2>这是一个动态渲染的页面 h2>
    <p>name:{
    {name}}p>
    <p>gender:{
    {gender}}p>  
    <p>
    {
    {each hobby}}
        {
    {$value}}
    {
    {/each}}
    p> 
const http = require('http');
const template = require('art-template');
const path = require('path');

const server = http.createServer();

server.on('request',(req,res)=>{
    let url = req.url;
    if(url==='/'){
        const htmlStr = template(path.join(__dirname,'/view/1.html'),{name:'zs',gender:'女',hobby:['唱歌','跳舞']});
        res.end(htmlStr);
    }

})

server.listen(3000,'127.0.0.1',()=>{
    console.log('server running at http://127.0.0.1:3000');
})

10. 作业

  1. 练习自定义 calc 计算器包,并尝试向 calc 包中,添加 mod 求余数的方法
  2. 自己使用Node写一个类似于Apache的静态资源服务器

相关文章

  • js模块化编程之彻底弄懂CommonJS和AMD/CMD!
  • 把模块化彻底搞个明白(ES6模块/CommonJS/AMD/CMD)
  • wiki 对于 Modules的描述
  • wiki 对于 Packages的描述
  • nodejs模块与包
  • [js的单线程和异步](http://www.cnblogs.com

Node.js - day3

0. 今天主要学习目标

  1. 掌握nodemon工具的使用
  2. 掌握express框架的基本用法
  3. 了解express框架提供的中间件的概念
  4. 能够连接并操作数据库
  5. 了解模块加载机制

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

  • nodemon的作用:能够实时监听当前项目中,文件的变化;只要监听到了文件的变化,则 nodemon 工具,会自动重新启动 web 服务器,从而使最新的代码生效;免去了程序员手动重启服务器的困扰;
  • 如何安装:运行 npm i nodemon -g 全局安装即可;
  • 如何使用:
    • 之前使用 node 要执行的文件路径 来运行 Node.js 代码;
    • 现在使用 nodemon 要执行的文件路径 来运行 Node.js 代码;
  • 注意:今后在开发Web项目的时候,推荐使用 nodemon 来启动 web 服务器

2. Node 中开发web项目的框架 - express

定义(什么是Express):一个快速的网站开发框架,封装了原生的http模块,用起来更方便;API更人性化

2.1 express 框架的特点

  1. 基于Node.js平台之上,进一步封装了 http 模块,从而提供了更好用,更友好的 API
  2. 使用Express创建网站,比使用原生的http模块更加方便;
  3. Express 并没有覆盖 原生 http 模块中的方法,而是基于 原生方法之上,做了更友好的封装,让用户体验更好

2.2 express 框架的安装和基本使用

  1. 安装:运行 npm i express -S 即可安装
  2. 创建基本的 express 服务器:
    • 导入 express 第三方模块;
    • 创建服务器的实例:调用 const app = express() 方法;
    • 通过 app.get()app.post() 方法,来监听客户端的 getpost 请求,具体语法:
      • 监听 GET 请求:app.get('请求地址', (req, res) => { 处理函数 })
      • 监听 POST 请求: app.post('请求地址', (req, res) => { 处理函数 })
    • 启动 express 服务器:通过 app.listen(端口, IP地址, 启动成功后的回调函数) 启动服务器;
//导入express模块
const express = require('express');
//创建服务器
const app = express();
//监听路径
app.get('/',(req,res)=>{
    res.send('ok你好吗');
})
//开启服务器
app.listen(3000,'127.0.0.1',()=>{
    console.log('server running at http://127.0.0.1:3000');
})
//说明:express主要封装了http模块,使得创建服务器更加便捷

2.3 express 中的快捷方法

  1. res.send()
    1. 支持 发送 字符串 Content-Type: text/html;
    2. 支持 发送 对象 或 数组 Content-Type: application/json
    3. 支持 发送 Buffer 此时会当作文件下载;
  2. res.sendFile()
    • 用法1:res.sendFile(path.join(__dirname, './view/index.html'))
    • 用法2:res.sendFile('./view/movie.html', { root: __dirname })
    • 注意:res.sendFile() 可以向浏览器发送 静态页面;

2.4 使用 express.static() 快速托管静态资源

如果我们网站中,有很多静态资源需要被外界访问,此时,使用 res.sendFile 就有点力不从心了;

这时候,express 框架,为我们提供了 express.static('静态资源目录')

来快速托管指定目录下的所有静态资源文件;

  1. 语法1: app.use(express.static('public'));

    • app.use()方法,是专门用来注册 中间件;
    • express.static 是express的内置中间件;
    const express = require('express');
    const app = express();
    //指定目录下的所有文件都可以直接被浏览器访问
    app.use(express.static('./view'));
    app.listen(3000,'127.0.0.1',()=>{
       console.log('http://127.0.0.1:3000')
    })
  2. 语法2:app.use('/虚拟目录', express.static('public'))

2.5 为 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', { 要渲染的数据对象 }),注意,模板页面的 后缀名,可以省略不写!

注:用ejs渲染的页面后缀名只能叫.ejs

const express = require('express')
const app = express()
//1.使用app.set()配置默认的模板引擎
app.set('view engine','ejs')
//2.配置模板页面的存放路径
app.set('views','./ejs-page')
app.get('/',(req,res)=>{
    //3.使用render函数渲染页面
    res.render('index.ejs',{name:'zs',gender:'女',hobby:['唱歌,跳舞,打游戏']})
})
app.listen(3000,'127.0.0.1',()=>{
    console.log('http://127.0.0.1:3000')
})
<body>
    <h1>这是用ejs渲染的页面h1>
    <p><%= name%>p>
    <p><%= gender%>p>
    <%hobby.forEach(item=>{%>
        <%= item%>
    <%})%>
body>

2.6 在 express 中配置 art-template

  1. 安装 两个包 cnpm i art-template express-art-template -S
  2. 自定义一个模板引擎 app.engine('自定义模板引擎的名称', 渲染函数)

注意:此处模板引擎的名字一定是要渲染页面 的后缀名

  1. 将自定义的模板引擎,配置为 express 的默认模板引擎 app.set('view engine', '具体模板引擎的名称')
  2. 配置 模板页面得存放路径 app.set('views', '路径')
<body>
  <h1>这是使用 art-template 渲染的模板页面h1>

  <p>姓名:{
    {name}}p>
  <p>年龄:{
    {age}}p>
  {
    {each hobby}}
  <p>{
    {$index}} -- {
    {$value}}p>
  {
    {/each}}
body>
const express = require('express')
const app = express()

// 1. 使用 app.engine() 方法自定义模板引擎
app.engine('html', require('express-art-template'))
// 2. 使用 app.set('view engine', '指定模板引擎名称') 来配置项目中用到的模板引擎
app.set('view engine', 'html')
// 3. 配置模板页面的存放路径
app.set('views', './art_page')

app.get('/', (req, res) => {
  res.render('index.html', { name: 'zs', age: 22, hobby: ['玩游戏', '唱歌'] })
})

app.listen(3000, () => {
  console.log('server running at http://127.0.0.1:3000')
})

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

  1. 什么是路由:路由就是对应关系;
  2. 什么叫做后端路由:前端请求的URL地址,都要对应一个后端的处理函数,那么 这种URL地址到 处理函数之间的对应关系,就叫做后端路由;
  3. 在Express中,路由的主要职责 就是 把请求分发到对应的处理函数中
const express = require('express')

const app = express()
//引入路由模块
const router = require('./router.js')
//使用app.use()注册路由
app.use(router)

app.listen(3000,'127.0.0.1',()=>{
    console.log('http://127.0.0.1:3000')
})
//这里是路由模块
const express = require('express')
//通过express.Router()得到一个路由对象
const router = express.Router();
//将路由规则挂载到router对象上
router.get('/',(req,res)=>{
    res.sendFile('./view/index.html',{root:__dirname})
})
router.get('/about',(req,res)=>{
    res.sendFile('./view/about.html',{root:__dirname})
})
//将路由对象暴露出去供外界使用
module.exports = router
  1. 在Express中,如何 定义并使用路由呢?
  // 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
  1. express 创建的 app 服务器,如何使用 路由模块呢?
  // 导入自己的路由模块
  const router = require('./router.js')
  // 使用 app.use() 来注册路由
  app.use(router)

4. Express 框架里 中间件的概念

4.1 什么是中间件

在应用程序开发中每一个处理环节都是一个中间件,中间件之间要共享数据,中间件之间要有先后的调用顺序,数据如果想要从上一个中间件流转到下一个中间件,必须调用相关的方法才可以

定义:中间件就是一个处理函数;只不过这个函数比较特殊,包含了三个参数,分别是 reqresnext

中间件中间共享的是req和res,调用下一个中间件时用的是next()

注意:中间件方法中的三个参数:

  • req:请求对象;
  • res:响应对象;
  • next:next()可以被调用,表示调用下一个中间件方法;

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

  1. 应用级别的中间件: 挂载到 app 上的中间件 app.get('URL地址', (req, res, next)=> {});绑定到app对象上的
  2. 路由级别的中间件: 挂载到 router 对象上的中间件 router.get('url地址', (req, res, next)=>{})绑定到router上的
  3. 错误级别的中间件: 回调函数中,有四个参数 app.use((err, req, res, next)=>{})
  4. 唯一内置的中间件: express.static()
  5. 第三方中间件: 非express框架提供的,需要程序员手动安装才能使用的中间件;body-parser 解析post 表单数据

中间件的概念,了解即可,因为实际开发中,我们都直接使用第三方现成的中间件;

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

// 导入 express 模块
const express = require('express')
const querystring = require('querystring')
// 创建 express 的服务器实例
const app = express()

// 定义 应用级别的中间件
app.use((req, res, next) => {
  let dataStr = ''
  // 只要客户端向服务器提交了表单,都会触发 req 的 data 事件
  // 在 data 事件中,可以获取到客户端每次提交过来的,不完整的数据,每次发过来的数据都是chunk
  req.on('data', chunk => {
    dataStr += chunk
  })

  // 只要 req 触发了 end 事件,就表示表单数据,提交完毕了,dataStr 中存储的数据,就是完整的表单数据
  req.on('end', () => {
    console.log(dataStr)
    const obj = querystring.parse(dataStr)
    // querystring.parse可以把按照&,=分割的字符串解析成对象
    //想要把 username=ls&password=123 字符串,解析为 { username: 'ls', password: 123 }
    console.log(obj)
    req.body = obj//把从客户端解析的数据挂载到req上,req,res在各个中间件之间共享
    // 进入下一个中间件的处理环节;
    // 注意:在中间件中,最后,一定要合理的调用一下 next() 方法,否则,服务器 无法结束这次响应!
    next()
  })
})

// 这是应用级别的中间件
app.get('/', (req, res) => {
  res.sendFile('./11.index.html', { root: __dirname })
})

app.post('/postdata', (req, res) => {
  console.log(req.body)
  // 需求:如何从客户端提交的表单中,获取到 客户端提交过来的数据呢?
  res.send(req.body)
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(3001, function() {
     
  console.log('Express server running at http://127.0.0.1:3001')
})

<body>
  <h3>演示中间件 表单 Post 提交数据h3>

  <form action="/postdata" method="POST">
    <p>用户名:
      <input type="text" name="username">
    p>
    <p>密码:
      <input type="password" name="password">
    p>
    <input type="submit" value="提交">
  form>
body>

5. Express 中进行数据库操作

配置 MySql 数据库环境

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

使用 mysql 第三方模块实现 CRUD

const express = require('express')
const app = express()
const mysql = require('mysql')

var con = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '',
    database: '0907'
});
con.connect();

app.get('/',(req,res)=>{
    //--------------新增------------
    const data = {username:'修改前',password:'18814072922'}
    //此数据库插入数据的语法仅在node中适用,其他语言中语法不同
    //?表示要传的参数,如果有两个参数,传递时用数组
    con.query('insert into user set ?',data,(err,result)=>{
        if(err) return console.log('数据插入失败'+err.message)
        console.log('数据插入成功')
    })

    //--------------修改------------
    const newData = {username:'修改后',password:'18834072922'}
    con.query('update user set ? where username=?',[newData,'修改前'],(err,result)=>{
        if(err) return console.log('修改数据失败'+err.message)
        console.log('数据修改成功')
    })

    //--------------查询-------------
    con.query('SELECT * from user', function (error, result) {
     
        if(error) return console.log('获取数据失败'+error.message)
          res.send(result)
    });

    //--------------删除-------------
    con.query('delete from user where username=?','修改后',(err,result)=>{
        if(err) return console.log('删除失败'+err.message)
        console.log('数据删除成功')
    })

})
app.listen(3000,'127.0.0.1',()=>{
    console.log('http://127.0.0.1:3000')
})

6. 模块加载机制

优先从缓存中加载

  • 当一个模块初次被 require 的时候,会执行模块中的代码,当第二次加载相同模块的时候,会优先从缓存中查找,看有没有这样的一个模块!
  • 好处:提高模块的加载速度;不需要每次都重新执行并加载模块!

核心模块的加载机制

  • 先查找缓存;如果缓存中没有,再去加载核心模块;

用户模块的加载机制

  • 先查找缓存;

  • 如果缓存中没有则尝试加载用户模块;

  • 如果在加载用户模块时候省略了后缀名,则:

    首先,严格按照指定的名称去查找
    其次,尝试加载后缀名是 .js 的文件
    如果没有.js的文件,则尝试加载 .json 结尾的文件
    如果没有 .json 的文件,则尝试加载 .node 结尾的文件
    查找规则:index  ->  index.js   ->   index.json   ->  index.node

第三方模块的加载机制【了解】

  1. 先在项目根目录中查找node_modules文件夹
  2. node_modules文件夹下,查找模块相关的文件夹
  3. 在对应的文件夹下,查找package.json的文件
  4. 查找package.json文件中的main属性(指定了模块的入口文件)
  5. 如果找到了main属性,同时,main属性指定的文件路径存在,那么尝试加载指定的文件模块
  6. 加入没有main属性,或者main属性对应的文件不存在,或者没有package.json,那么会依次尝试加载index.jsindex.json,index.node;
  7. 如果没有index相关的文件,或者没有指定模块对应文件夹,或者,当前项目根目录中没有node_modules文件夹,则向上一层目录中查找node_modules,查找规则同上!
  8. 最后,如果在项目所在磁盘的盘符根目录中,还找不到对应模块,则报错:cannot find module ***

7. 作业

  1. 自己使用 express.static 把 今天 资料文件夹下,vue-cms 项目,托管成静态资源服务器;
  2. 自己在 express 中,配置和使用 ejs/art-template 来渲染一个动态页面;
  3. 创建三个静态页面 index.htmlmovie.htmlabout.html;分别使用三种方式,显示这三个静态页面;
    • 直接 把 路由 挂载到 app 上;
    • 把 路由 抽离为 单独的路由模块,然后把路由,挂载到 router 上;
    • router.js 模块中,再抽离出 一个 handler.js 模块;

相关文章

art-template 官方文档

Node.js - day4

今天主要内容

  1. 获取参数的几种形式
  2. 了解web开发模式
  3. 设计数据库和后台接口
  4. 实现前端功能

1. express中获取参数的几种形式

  1. 获取 http://127.0.0.1:3001/user?id=10&name=zs 中的查询参数:
    • 直接使用 req.query 获取参数即可;
    • 注意:URL 地址栏中通过 查询字符串 传递的参数,express 框架会直接解析,大家只需要使用 req.query 直接获取 URL 中 查询字符串的参数;
  2. 从URL地址中获取路径参数:
    • 假设后台的路由是 app.get('/user/:id/:name', (req, res) => {})(此处冒号表示参数项)
    • 假设客户端浏览器请求的URL地址为:http://127.0.0.1:3001/user/10/zs
    • 直接使用 req.params 可以获取URL地址中传递过来的参数;
  3. 从post表单中获取提交的数据:
    • 借助于body-parser来解析表单数据
    • 安装:npm i body-parser -S
    • 导入:const bodyParser = require('body-parser')
    • 注册中间件:app.use(bodyParser.urlencoded({ extended: false }))
    • 使用解析的数据: req.body 来访问解析出来的数据

2. Web 开发模式

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

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

2.2 前后端分离(趋势)

  • 后端负责操作数据库、给前端暴露接口
  • 前后端分离的好处:保证了各个岗位的职责单一;
  • 前端负责调用接口,渲染页面、前端就可以使用一些流行的前端框架 Vue, React, Angular

3. 需求分析

  1. 后端项目运行地址:http://127.0.0.1:5000
  2. 前端项目运行地址:http://127.0.0.1:3000
  3. 前后端分离开发模式的注意点:

    • 跨域问题
    • 解决跨域问题,一般使用jsonp,但是jsonp只支持get请求,不支持post(表单提交常用)请求
    • 如果不考虑 表单的 Post 提交,则 可以使用 JSONP 的形式来请求接口
    • 但是,我们的项目中,涉及到了 英雄表单的 提交,表单提交一般都是Post
    • 经过分析,由于JSONP,不支持Post,所以,我们的后端接口,无法设计成JSONP的接口;
    • 前端项目 Jquery + 模板引擎 + Semantic UI

4. JSONP 和 CORS 的区别

  1. JSONP的原理:动态创建script标签;

    • JSONP发送的不是Ajax请求
    • 不支持 Post 请求;
    • jsonp的原理是插入script标签,它只支持get请求
  2. CORS中文意思是跨域资源共享 ,需要服务器端进行 CORS 配置;

    • CORS 发送的是真正的Ajax请求
    • CORS 支持Ajax的跨域
    • 如果要启用 CORS 跨域资源共享,关键在于 服务器端,只要 服务器支持CORS跨域资源共享,则 浏览器肯定能够正常访问 这种 CORS 接口;而且,客户端在 发送 Ajax的时候,就像发送普通AJax一样,没有任何代码上的变化;
  3. 对于Node来说,如果想要开启 CORS 跨域通信,只需要安装cors的模块即可;

5. 数据库设计 - heros

字段名 字段类型 字段描述
id int 主键Id(自增)
name varchar 英雄名称
gender varchar 性别
ctime varchar 创建时间
isdel tinyint(布尔值) 是否被删除 0 表示未删除;1 表示已经被删除

在 mysql 中的 tinyint 等同于 bool 值

6. 后台接口设计

获取所有英雄列表

  • 请求类型:GET
  • 请求地址:http://127.0.0.1:5001/getallhero
  • 请求的参数:无

插入新的英雄数据

  • 请求类型:POST
  • 请求地址:http://127.0.0.1:5001/addhero
  • 请求参数:{ name, gender }

根据Id获取英雄信息

请求类型:GET

请求地址:http://127.0.0.1:5001/gethero/:id

请求参数:通过 URL 地址,把要查询的英雄Id,携带过去

根据Id更新英雄数据

请求类型: POST

请求地址:http://127.0.0.1:5001/updatehero/:id

请求参数:{ name, gender }

根据Id软删除英雄数据

请求类型:GET

请求地址:http://127.0.0.1:5001/deletehero/:id

请求参数:通过 URL 地址栏传参,把 要删除的英雄Id提交给服务器

7. 后台接口项目实现

8. 前端功能实现

相关文章

  • 跨域资源共享 CORS 详解
  • cors模块解决Node跨域资源共享问题

Node.js(黑马博客案例)

今天主要内容

  1. 博客项目开发
  2. 能够看懂和绘制流程图
  3. Cookie 和 Session 知识点的复习和使用
  4. 登录、注销功能

1. 分析

  1. 昨天的英雄案例使用{前后端分离}开发的(先定义接口文档,根据接口文档编写后端API, 前端写页面同时根据文档调用接口)
  2. 今天的黑马博客,使用{传统的开发方式},我们切换到后端开发人员的身份,一边定义接口,一边写页面调用接口;

2. 搭建博客案例基本结构并渲染首页

主要目标1:在渲染首页的时候,把相关的配置搭建起来(模板引擎、静态文件托管)

3. 用户注册模块的实现

  • 要求大家,能够根据自己对于业务逻辑的了解,能够画出业务流程图
  • 圆角矩形或圆圈: 表示开始/结束
  • 矩形: 表示一个处理过程
  • 菱形: 表示判断
  • 箭头:表示流转关系

4. 用户登录模块的实现

5. HTTP协议的无状态性

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

6. cookie介绍

6.1 什么是cookie,作用是什么

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

  • 在Node中可以在writeHeader的时候,通过Set-Cookie来将cookie标识通过响应报文发送给客户端!

  • 客户端也可以通过一些方式来操作自己的cookie,比如通过jquery.cookie这个插件!

6.2 cookie的基本使用

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('服务器已启动!');
});

6.3 通过expires设置Cookie的过期时间

// 设置 过期时间 为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('

你好,欢迎光临,送给你一个苹果!

'
);

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

6.4 cookie可以被伪造,不安全

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

6.5 什么是Cookie的应用场景

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

7. 登录退出及状态保存

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

7.1.1 什么是session

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

7.1.2 session原理

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

7.1.3 在express中使用session

使用流程:先在app.js安装注册express-session,然后在服务器端能访问到req对象的对应的函数时为session挂载属性(user,islogin),然后在contoll中的index.js渲染首页的时候把挂载的属性传递过来,同时在index.ejs中用ejs模板语法控制按钮组的现实

  1. 安装session模块
npm install express-session -S
  1. 导入session模块
var session = require('express-session')
  1. 在express中使用session中间件:
// 启用 session 中间件,只要注册了中间件。之后凡是能访问到req,必然就能访问到req,session
app.use(session({
  secret: 'keyboard cat', // 相当于是一个加密密钥,值可以是任意字符串
  resave: false, // 强制session保存到session store中
  saveUninitialized: false // 强制没有“初始化”的session保存到storage中
}))
  1. 将私有数据保存到当前请求的session会话中:
// 将登录的用户保存到session中
req.session.user = result.dataValues;
// 设置是否登录为true
req.session.islogin = true;
  1. 通过destroy()方法清空session数据:
req.session.destroy(function(err){
     
  if(err) throw err;
  console.log('用户退出成功!');
  // 实现服务器端的跳转,这个对比于 客户端跳转
  res.redirect('/');
});

8. 注销功能的实现

req.session.destroy(()=>{
            //使用redirect方法让客户端重新访问首页
            res.redirect('/')
        })

作业:

  1. 自己晚自习尝试着 把 用户注册 和 用户登录 的流程图画出来
  2. 大家自己能够搭建一个 类似于黑马博客的Web服务器,然后,把 express-session 尝试着配置好使用一下;

相关文章

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

Node.js - day6(黑马博客案例)

MD5 的特性

  1. MD5 是一种加密算法,在调用这个算法的时候,提供一个密码的明文, 调用的结果,得到一个 32 位长度的密文;
  2. MD5 算法的特性:相同的字符串,如果多次调用 md5 算法,得到的结果,完全一样;
  3. MD5 算法,无法被逆向解密
  4. 但是,基于 md5 算法的第二个特性,我们可以进行碰撞暴力破解;(MD5 存在被暴力破解的安全性问题)
  5. 为了解决 简单的明文密码,被 md5 加密后,通过 暴力破解的安全性问题, 然后就出现了加盐的MD5加密;
  6. 目前,md5的暴力破解,又升级了,升级到了 彩虹表
  7. 由于彩虹表出现,我们推荐大家,在存储网站密码的时候,使用 bcrypt 加密算法,得到加密之后的密文进行存储;

bcrypt 加密算法

  1. 在调用加密算法的时候,需要手动提供一个 幂次;
  2. 调用加密算法,得到的加密结果格式:$版本号$循环的幂次$22位的随机盐 31位的密文
    • 加密的随机盐加密的幂次,和加密算法的版本号已经被存储到了真正的密文中;

项目中使用 bcrypt 的步骤

  1. 运行 npm i node-pre-gyp -g

  2. 在项目根目录中,打开终端,运行 cnpm install bcrypt -S

  3. 导入 bcrypt

    // 导入加密的模块
    const bcrypt = require('bcrypt')
  4. 定义幂次:

    // 定义一个 幂次
    const saltRounds = 10    // 2^10
  5. 调用 bcrypt.hash() 加密:

    // 加密的方法
    bcrypt.hash('需要加密的密码', saltRounds, (err, pwdCryped) => {
      console.log(pwdCryped)
       要在此方法中写数据库的操作,否则不起作用
    })
  6. 调用bcrypt.compare()对比密码是否正确:

    // 对比 密码的方法
    bcrypt.compare('123', '$2b$10$i1ufUKnC9fXTsF9oqqvLMeDnpNfYIvhyqKRG03adiebNFPkjW3HPW', function(err, res) {
           
     console.log(err)
     // 内部对比的过程:
     // 1. 先获取 输入的明文
     // 2. 获取输入的密文
     // 2.1 从密文中,解析出来  bcrypt 算法的 版本号
     // 2.2 从密文中,解析出来 幂次
     // 2.3 从密文中,解析出来前 22 位 这个随机盐
     // 3. compare 方法内部,调用 类似于 hash 方法 把 明文,幂次,随机盐 都传递进去     最终得到正向加密后的密文
     // 4. 根据最新得到的密文,和 compare 提供的密文进行对比,如果相等,则 返回 true ,否则返回 false;
    })

使用模板引擎处理公共部分

在PHP中,抽取公共的区域,直接使用PHP语法就行;
但是,在Express的框架中,并没有抽取页面公共部分的语法,需要模板引擎提供这样的语法;

添加文章并跳转到文章详情

  1. 发表文章之前,需要使用 第三方的插件,叫做 markdown + editor => mditor
  2. 注意:mditor这个第三方模块,提供了两个功能:

    • 功能1: 可以当作一个纯粹的MarkDown编辑器插件,在前端页面中使用;
    • 功能2: 在Node端,我们可以require('mditor'),使用这个模块,提供的方法,把markdown文本,解析转换为HTML内容;

设计文章表的字段

文章字段设计

完成文章编辑功能

首页文章列表渲染

把markdown文本转化成html

1.安装marked

2.导入

3。调用marked marked(要转化的文本)

使用Sql语句进行联表查询

首页文章列表分页功能的实现

相关文章

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

你可能感兴趣的:(IT技术,node)