# work19node **Repository Path**: SunChenW/work19node ## Basic Information - **Project Name**: work19node - **Description**: 19级node课程代码 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-01 - **Last Updated**: 2021-04-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 第一章:Node简介和安装-4 ### 课程目标 1. 了解JavaScript与ECMAscript与node.js 2. 了解Node的简介、特色、适用场景、版本更新 3. 完成Node的下载安装与测试 4. 实战:在Node中运行电脑磁盘中的JavaScript文件 ### 课前练习 我们学过的JavaScript知识点有哪些?; - 注释、 变量 、数据类型(数字、字符串、boolean、null 、undefined)、流程控制(if、for、do+while、while)、函数、对象、数组 - DOM :获取标签+操作标签(内容+属性+css)+事件 - BOM:history、location、窗口操作、屏幕的信息 ### 课程内容 > 1、了解JavaScript与ECMAscript与node.js 1. JavaScript的组成 ECMAScript - JavaScript的核心 ```html 定义了javascript的语法规范 JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关 ``` BOM - 浏览器对象模型 ```html 一套操作浏览器功能的API 通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等 ``` DOM - 文档对象模型 ```html 一套操作页面元素的API DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作 ``` 2. API的概念 API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。 - 任何开发语言都有自己的API - API的特征输入和输出(I/O) - API的使用方法(console.log()) Web API的概念 浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM) 此处的Web API特指浏览器提供的API(一组方法),Web API在后面的课程中有其它含义 3. 整体对比 js(ecma+(web api)dom+bom) 在浏览器中运行 实现页面操作的功能 js(ecma+node提供的api) 运行在node中 实现服务器端的功能 > 2、了解Node的简介、特色、适用场景、版本更新 1. node简介 - Node.js(以下简称Node),是一个可以让JavaScript运行在服务器端的平台(环境、软件)。 - Node可以轻松的进行服务端应用程序的开发,其他后台语言能做的Node几乎都能做到,甚至可以做得更好。 - Node是一个划时代的技术,也正是因为Node的出现,让JavaScript成为当今最为流行的开发语言之一。 2. node的特点 - Node使用的JavaScript引擎是来自Google Chorme的V8,V8号称是目前世界上最快的JavaScript引擎。 - Node采用了`单线程`、`异步I/O`、`事件驱动式的程序设计模式`。这些特性不仅带来了巨大的性能提升,还减少了多线程程序设计的复杂性,提高了开发效率。 - 单线程:保证的占用内存小 - 异步+事件驱动:又可以同时处理大量请求 3. 版本发展 - 2009年3月Ryan Dahl在其博客上宣布准备基于V8创建一个轻量级的Web服务器并提供了一套库 - 2009年5月Ryan Dahl在GitHub上发布了最初的Node版本,Node的开发思想获得了广大开发者的关注和推崇。 - 2011年7月Node在微软的支持下发布了Windows版本,Node开始走上巅峰,成为GitHub上关注度最高的项目。 4.适合的项目 目前已经有很多著名程序使用了Node进行开发,相对于其他的后台语言,Node在处理高并发上有先天的优势,所以如果需要处理大量的并发I/O,且在向客户端发出响应之前无需进行很复杂的的数据处理,就应该优先考虑使用Node,Node在以下项目中的表现更为出色。 - 聊天室服务端:在一个具有很高人气的聊天应用程序中,同一时刻通常存在大量用户与服务器之间的并发连接,而服务器本身不需要针对这些请求做出太复杂的处理。斗鱼直播平台就使用了Node。 - 电子商务网站服务端:在某些特定的时间(例如”双11”),电子商务网站服务端可能会在短时间内收到多达数千条甚至上万条的订单请求,Node的防阻塞机制是解决这类问题的有效方式。淘宝就使用了Node。 > 3、完成node的下载及安装 - 下载:访问Node官网中https://nodejs.org/en/download/。 或者访问node中文网 http://nodejs.cn/。 `百度搜索node` ![](lessions\01-node安装\截图\搜索node.png) `点击下载` ------ ![](lessions\01-node安装\截图\下载.png) `选择合适的版本 32位 64位都可以` ------ ![](lessions\01-node安装\截图\版本选择.png) - 安装:双击安装包——选择运行——一直选择下一步...下一步———选择finish完成安装 - 测试安装结果:按住`window+r`,在打开的系统搜索框中输出`CMD`,按下回车键开启系统的`命令行窗口`,在命令行窗口中输入`node –v`并按下回车键,如果看到node的版本好就代表安装成功。 > 4、实战:在Node中运行电脑磁盘中的JavaScript文件 - 任务目标: - 编写一段简单的JavaScript代码,并且以JavaScript文件形式存在电脑磁盘中 - 在Node中运行这个JavaScript文件 - 需求说明 - 编写一个输出”Hello!Node!”的JavaScript文件,并在Node中运行,得出结果 - 实现思路: - 在电脑中安装Node。 - 在系统桌面新建项目,项目名为hello。 - 在hello项目中新建名为hello.js的JavaScript文件。 - 在hello.js中编写输出”Hello!Node!”的JavaScript代码。 - 开启命令行工具,通过DOS窗口命令移动到hello项目中,使用Node命令运行hello.js文件。 ### 本节作业: 1、完成node的安装及测试 2、创建一个js文件、编写代码实现计算1~100所有数的和、使用node运行js文件 # 第一章:Node REPL模式-2 ### 课程目标: 1. 进入REPL并了解REPL的作用。 2. 了解REPL的简单使用 3. 了解REPL中的常用命令 4. 实战:使用Node REPL实现输出100以内与7相关的数字 ### 课前练习: 1. node的主要特点是什么? ### 课程内容: > 1、进入REPL并了解REPL的作用。 node提供了一个类似浏览器控制台的功能,我们可以使用它进行代码的编写和运行。一般临时的代码调试来使用。 在`系统命令行窗口`中输入`node`然后回车,`系统命令行窗口`就进入了`Node的REPL`模式。 ![](lessions\02-node_REPL模式\截图\进入REPL.png) > 2、了解REPL的简单使用 1. REPL作为输入、运行js的环境,与直接代码编辑还是有些不同的。 - REPL 中只能单行输入代码,enter(换行)为执行。 - 单行语句,enter(换行)之后直接执行,并且打印语句返回值。无返回值侧打印undefined - 多行语句,会自动识别,并在完成以后自动识别结束,并打印多行语句的结果。 for 、if、while - REPL 中 下划线 `_`,具有作为特殊变量存在,代表上一个代码的返回值。 ![](lessions\02-node_REPL模式\截图\REPL简单使用.png) > 3、了解REPL 模式常用的命令 ![](lessions\02-node_REPL模式\截图\命令.png) > 4、实战:使用Node REPL实现输出100以内与7相关的数字 - 任务目标 - 在Node中打开REPL编译环境 - 在REPL中使用变量和运算 - 在REPL中使用循环、判断和输出 - 在REPL中使用多行命令输入 - 需求说明 - 输出100以内与7有关的数字,包括含7(例如7,17,78等) 或者7的倍数(例如14,28等) - 思路说明 - 进入REPL,定义一个空数组,用来装与7相关的数。 - 通过for循环语句,为数组填数字。 - 7的倍数可以通过对7求余等于0来判断. a%7 ==0 - 含有7的数字分两种情况, - 个位数为7可以通过对10求余等于7来判断, a%10 ==7 - 十位数为7可以通过除以10取整等于7来判断。Math.floor(a/10) parseInt(a/10) - 使用push()方法将满足条件的数字添加到数组中。 - 通过console.log()方法输出第二个数组。 ### 本节作业: 1、编写代码实现输出1-100所有数的和,用node运行。 # 第一章:Node模块与包01-2 ### 本节目标: 1. 了解node中模块module的概念 2. 了解node中包package的概念 3. 掌握node中模块与包的使用 ### 课前练习: 1. ### 本节内容: > 1、了解Node中模块的概念及使用 在实际开发中,我们会根据功能需求将js代码拆分为多个js文件,这种开发模式称为`模块化开发`。 在Node中,每一个js文件称为一个模块, 我们要学习的是在一个模块中加载另外一个模块,并且可以获取另一个模块中的数据。 Node.js 提供了require和 exports 对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。 **第一步:同目录下分别创建hello.js和index.js。** ![](lessions\03-node模块与包\截图\a1.png) **第二部:在系统命令行工具中用node运行index.js** ![](lessions\03-node模块与包\截图\a2.png) `我们用node执行了index.js,可以看到hello.js的输出,证明require可以引入并执行其他模块。` **第三步:接着补充index.js的代码并使用node 运行index.js** ![](lessions\03-node模块与包\截图\a3.png) ```shell 总结:require的两个作用 - 引入另一个模块,并执行。 - 接受引入模块提供的数据。 但是一个空对象满足不了我的需求,我想要的更多~~例如,我想把hello.js的变量b直接拿过来用用。 ``` **第四步:修改和hello.js,用来讲解exports的作用** ![](lessions\03-node模块与包\截图\a4.png) > 2、了解node中包的概念及使用 `node中包就是一个文件夹,但绝不会这么简单。` 将多个模块放在一个文件夹中,以整体的方式向外部提供一个完整的功能。这个文件夹就是包。 包是模块基础上的更进一步抽象,它将独立的功能封装起来,用于发布、更新、依赖管理和版本控制。 `以下是对于一个包的要求:` ![](lessions\03-node模块与包\截图\b1.png) ![](lessions\03-node模块与包\截图\b2.png) `不改变以上代码,将hello.js变成包` **第一步:创建phello文件件,在phello文件家中创建package.json文件,复制hello.js文件。phello文件夹同目录下复制index.js文件** ![](lessions\03-node模块与包\截图\b3.png) **第二部:稍微修改index.js代码,并是使用node运行index.js** ![](lessions\03-node模块与包\截图\b4.png) ### 本节作业: 1.自主练习。 # 第一章:Node模块与包02-2 ### 本节目标: 1. 了解NPM程序的作用 2. 掌握NPM程序的常用功能 3. 实战演练——使用Node开发数组去除重复元素的包 ### 课前练习: 1. node中的模块是什么? 2. 如何实现模块间的引用? 3. node中的包是什么? ### 本节内容: > 1、了解NPM程序的作用 node本身提供的功能很少,我们以后用node开发各种功能,都需要依赖于node的内置包,或者是下载第三方包。 其实要实现简洁高效的开发,我们需要使用大量的第三方包。 Node官方提供了一个包的管理工具——NPM(Node Package Manager),用于包的下载、更新、删除、发布、传播、依赖控制等。 `与检测node一样,我们在命令行工具中输入npm -v,即可检测npm是否安装成功。` ![](lessions\03-node模块与包\截图\c1.png) `npm的常用命令及功能` | **命令** | **说明** | **示例** | | --------- | ------------------------ | ------------------- | | help | 可查看某条命令的详细帮助 | $npm help 指令名 | | update | 更新对应包至最新版本 | $npm update 包名 | | search | 搜索包 | $ npm search 包名 | | publish | 发布包 | $npm publish 包名 | | uninstall | 卸载包 | $npm uninstall 包名 | | list | 许可证数组 | $npm uninstall 包名 | | install | 下载包 | $npm install 包名 | `演示并讲解使用npm下载任意一个第三方包` - 内置包与下载的第三方包在引用时,都不需要写路径,写名称即可。 - 自定义包引用时要写路径。 | 示例 | 作用 | | --------------------- | ------------------------ | | `npm i express` | 当前目录安装 | | `npm i express -g` | 全局安装 | | npm update expess | 更新当前目录的包 | | npm uninstall express | 卸载(删除)当前目录的包 | | npm list -g | 查看全局安装包的目录 | | npm list grunt | 查看grunt的版本号 | | npm ls | 查看安装包的目录 | | npm serch express | 本地搜索包 | | `npm init` | 初始化包(创建自定义包) | | npm publish | 发布包 | > 2、设置NPM从淘宝镜像下载包 因为官方的NPM服务器放在境外,受网络影响大,使用时速度非常慢,甚至可能出现异常,为此淘宝团队在中国境内设置了一台服务器并且部署了NPM的镜像服务,取名为CNPM。CNPM每十分钟同步一次官方NPM的数据,尽量保证与NPM数据一致,在命令行窗口中输入以下指令即可安装CNPM。 `npm install cnpm -g --registry=https://registry.npm.taobao.org` - 这个配置只需要做一次,以后再下载包时,都是从国内的淘宝服务器下载。 - 配置以后可以只用命令进行查看配置结果,或者是取消淘宝镜像 ```powershell 使用阿里定制的cnpm命令行工具代替默认的npm,输入以下代码 npm install -g cnpm --registry=https://registry.npm.taobao.org 或 npm config set registry https://registry.npm.taobao.org/ 检测是否安装成功 cnpm -v // 配置后可通过下面方式来验证是否成功 npm config get registry --得到结果:https://registry.npm.taobao.org/ 代表已经设置成功 删除淘宝镜像 npm config delete registry npm config delete disturl ``` > 2-2使用nrm设置淘宝镜像 - 使用npm全局安装nrm `npm i nrm -g` - 特别注意(2021年3月4日):nrm源码有问题。需要将`C:\Users\Administrator\AppData\Roaming\npm\node_modules\nrm\cli.js`的第17行替换为`const NRMRC = path.join('HOME', '.nrmrc');` - nrm 常用指令 - nrm -V 查看nrm的版本 - nrm ls 查看所有可用镜像 - nrm test 检测所有镜像服务器的下载速度 - nrm use taobao 切换使用的镜像 - 切换到淘宝镜像之后,依旧正常使用npm指令:`npm i koa`仿佛一起都没有变化,但是速度已经嗖嗖的~~~~~ > 3、实战演练——使用Node开发数组去除重复元素的包 - 任务目标 - 在Node中创建一个包 - 包的功能是去除数组中重复元素 - 需求说明 - 在 Node 中创建一个包,包的内容是去除数组中重复元素,并且返回去除重复元素后的结果 - 实现思路 - 创建名为array的项目,在项目中创建名为arraypack的文件夹,作为包的文件夹。 - 在arraypack文件夹中创建package.json文件和arraypack.js文件,并在arraypack.js中编写数组去重的方法。 - 数组去重的逻辑是:创建一个新的数组,遍历旧的数组,使用indexOf()方法判断每个元素是否 - 在新数组中存在,如果不存在就添加到新数组中,最后得到的新数组就是去重后的结果。 - 在array文件夹中创建index.js文件,在index.js文件中加载包,并运行得到结果。 ### 本节作业: 1. 一定!一定!一定!给NPM配置淘宝镜像,我们以后会经常下载包,速度太慢你会崩溃!!! 2. 完成实战演练。 # 第二章:Node全局对象及函数-2 ### 本节目标: 1. 了解Node全局对象 变量----存着数据----值、函数、组数、对象---作用是什么 2. 掌握Node全局变量和全局函数的使用 ### 课前练习: 1. 课后作业讲解 2. npm补充---`npm init` 1. 创建包时可以先创建文件夹-----然后打开命令行工具----输入 `npm init`----以此输入包的名称、描述.....(都不需要改,直接一路回车enter就可以)-------回得到一个package.json 3. npm补充--- `-g` 原来:exports = {} 4. exports.a = 123; //在接口中添加数据 5. modules.exports = 123;//把这借口数据直接换掉,必须写成这样 ### 本节内容: > 1、了解Node全局对象 **Node.js 全局对象** ------ JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。 在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。 **全局对象与全局变量** ------ global 最根本的作用是作为全局变量的宿主。按照 ECMAScript 的定义,满足以下条 件的变量是全局变量: - 在最外层定义的变量; - 全局对象的属性; - 隐式定义的变量(未定义直接赋值的变量)。 当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。需要注 意的是,在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。 > 2、掌握Node全局变量 1. **__filename** filename 表示当前正在执行的脚本(js文件)的文件名。它将输出文件所在位置的绝对路径。 以下示例使用filename输出了脚本文件所在位置的绝对路径。 2. **__dirname** dirname 表示当前正在执行的脚本所在目录,通常会输出脚本文件的父级文件夹路径。 以下示例使用dirname 输出了文件父级文件夹的绝对路径。 3. **process** process是用于描述当前Node进程状态的全局对象,Node通过他提供了一个操作系统的简单接口,process提供了很多有用的属性和方法,便于更好的控制系统的交互。常用process属性及描述如表所示,常用process属性及描述如下表所示。 | 常用process属性 | **描述** | 常用process属性 | **描述** | | --------------- | ------------------------ | --------------- | ---------------------- | | stdout | 标准输出流 | version | Node 的版本 | | stderr | 标准错误流 | platform | 运行程序所在的平台系统 | | stdin | 标准输入流 | execPath | 当前脚本的运行路径 | | stdout | 标准输出流 | version | Node 的版本 | | uptime() | 返回 Node 已经运行的秒数 | cwd() | 返回当前进程的工作目录 | > 3、掌握Node全局函数 Node中提供了四个和在浏览器中写法与作用一样的定时器函数。 - setTimeout()用来在指定的毫秒数后执行指定函数 - clearTimeout()用来停止之前通过setTimeout()创建的定时器 - setInterval()用来在指定的间隔毫秒数后执行指定函数 - clearInterval()函数用来停止使用setInterval()函数定义的定时器。 > 4、实战演练:实战演练——使用 Node 全局函数制作时钟 - 任务目标 - 编写实时输出本地时间的包 - 在项目中加载这个包,运行程序在Node中实时输出时间 - 需求说明 - 编写实时输出本地时间的包, 在项目中加载这个包,运行程序在Node中实时输出时间 - 实现思路 - 创建项目命名为clock。 - 在clock项目中创建输出时间的包。 - 通过Node全局函数每隔1秒输出一次时间。 - 在clock项目中创建index.js文件,在index.js中加载创建的包。 - 开启命令行工具,并运行index.js。 ### 本节作业: 1. 完成实战演练 # 第二章:Node文件系统fs模块-4 ### 本节目标: 1. 掌握fs模块文件读取功能 2. 了解fs模块异步操作与同步操作的就别 3. 掌握fs模块文件打开功能 4. 掌握fs模块文件写入功能 5. 掌握fs模块获取文件信息功能 6. 掌握fs模块文件文件(夹)创建功能 7. 掌握fs模块文件删除功能 ### 课前练习: 1. node中的全局对象是谁? 2. 我们学习了哪三个全局属性? 3. 我们学习了哪四个全局函数? ### 本节内容: > 1、掌握fs模块文件读取功能 #### readfile node.js的功能绝大部分都是由包(模块)提供的,从今天开始我们来学习node内置模块(包)。这些模块是直接内置在node的安装程序员中,不需要下载。 fs模块的提供文件操作相关的功能。 ```powershell fs.readFile()函数用来通过异步方式读取文件,它可以接收三个参数,分别是文件的路径、文件的编码格式和回调函数,标准语法如下所示。 fs.readFile(filename,encoding,function(err,data){}) 其中filename是必选参数,encoding是可选参数,如果不选将会以二进制的模式读取文件内容,回调函数中的err参数表示是否有错误,data参数表示文档的内容。\ ``` > 2、了解fs模块异步操作与同步操作的就别 fs模块提供了两套功能一样的操作方式,一套为异步操作,另一套为同步方式。 我们主要学习的是异步方式,大家对比下异步代码与同步代码的差别。 ```javascript var fs = require("fs"); // 异步方式实现文件读取 fs.readFile("./test.txt","utf8",function(err,data){}) // 同步方式实现文件读取 var data = fs.readFileSync("./test.txt","utf8"); ``` 这里注意: - 异步操作与同步操作的差别在哪?———— node的主要特点之一,异步I/O - 代码写法的差别,所有的代码都是这么写的。 > 2、掌握fs模块文件打开功能 #### open fs.open()函数用来打开文件,如果设置特定的参数,也可以用来创建文件。 fs.open()函数可以接收四个参数,分别是文件的路径、文件打开行为、设置文件模式及回调函数,其中通过设置特定的文件打开行为参数,可以设置文件打开的模式。文件打开行为参数及描述如表所示。 **语法** 以下为在异步模式下打开文件的语法格式: ``` fs.open(path, flags[, mode], callback) ``` **参数** 参数使用说明如下: - **path** - 文件的路径。 - **flags** - 文件打开的行为。具体值详见下文。 - **mode** - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。 - **callback** - 回调函数,带有两个参数如:callback(err, fd)。 flags 参数可以是以下值: | Flag | 描述 | | ---- | -------------------------------------------------------- | | r | 以读取模式打开文件。如果文件不存在抛出异常。 | | r+ | 以读写模式打开文件。如果文件不存在抛出异常。 | | rs | 以同步的方式读取文件。 | | rs+ | 以同步的方式读取和写入文件。 | | w | 以写入模式打开文件,如果文件不存在则创建。 | | wx | 类似 'w',但是如果文件路径不存在,则文件写入失败。 | | w+ | 以读写模式打开文件,如果文件不存在则创建。 | | wx+ | 类似 'w+', 但是如果文件路径不存在,则文件读写失败。 | | a | 以追加模式打开文件,如果文件不存在则创建。 | | ax | 类似 'a', 但是如果文件路径不存在,则文件追加失败。 | | a+ | 以读取追加模式打开文件,如果文件不存在则创建。 | | ax+ | 类似 'a+', 但是如果文件路径不存在,则文件读取追加失败。 | **实例** 接下来我们创建 file.js 文件,并打开 input.txt 文件进行读写,代码如下所示: ```JavaScript var fs = require("fs"); // 异步打开文件console.log("准备打开文件!"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { console.error(err); } else{ console.log("文件打开成功!"); } }); ``` > 3、掌握fs模块文件写入功能 #### writeFile fs.writeFile()函数用来通过异步的方式在文件中写入内容。fs.writeFile()函数可以接收四个参数,分别是文件路径、要写入的内容、写入方式和回调函数。 如果文件存在,fs.writeFile()函数写入的内容会覆盖旧的文件内容。 以下示例通过fs.writeFile()函数写入了文件内容,并且在回调函数中读取了文件的内容。 **语法** 以下为异步模式下写入文件的语法格式: ``` fs.writeFile(filename, data[, options], callback) ``` 如果文件存在,该方法写入的内容会覆盖旧的文件内容。 **参数** 参数使用说明如下: - **path** - 文件路径。 - **data** - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象。 - **options** - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w' - **callback** - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。 **实例** 接下来我们创建 file.js 文件,代码如下所示: ```JavaScript var fs = require("fs"); fs.writeFile('input.txt', '我是通过写入的文件内容!', function(err) { if (err) { console.error("写入失败"); }else{ console.log("写入成功") } }); ``` > 4、掌握fs模块获取文件信息功能 #### stat fs.stat()函数用来通过以异步方式获取文件的信息。 fs.stat()函数可以接收两个参数,分别是文件的路径和回调函数,回调函数也带有两个参数,分别是err和 stats,其中stats 是 fs.Stats 对象,可以通过stats类中的提供方法判断文件的相关属性。例如判断是否为文件或者文件夹等。fs.Stats 对象中的方法及描述如表下所示。 **语法** 以下为通过异步模式获取文件信息的语法格式: ``` fs.stat(path, callback) ``` **参数** 参数使用说明如下: - **path** - 文件路径。 - **callback** - 回调函数,带有两个参数如:(err, stats), **stats** 是 fs.Stats 对象。 fs.stat(path)执行后,会将stats类的实例返回给其回调函数。可以通过stats类中的提供方法判断文件的相关属性。例如判断是否为文件: ``` var fs = require('fs'); fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) { console.log(stats.isFile()); //true}) ``` stats类中的方法有: | 方法 | 描述 | | ------------------------- | ------------------------------------------------------------ | | stats.isFile() | 如果是文件返回 true,否则返回 false。 | | stats.isDirectory() | 如果是目录返回 true,否则返回 false。 | | stats.isBlockDevice() | 如果是块设备返回 true,否则返回 false。 | | stats.isCharacterDevice() | 如果是字符设备返回 true,否则返回 false。 | | stats.isSymbolicLink() | 如果是软链接返回 true,否则返回 false。 | | stats.isFIFO() | 如果是FIFO,返回true,否则返回 false。FIFO是UNIX中的一种特殊类型的命令管道。 | | stats.isSocket() | 如果是 Socket 返回 true,否则返回 false。 | **实例** 接下来我们创建 file.js 文件,代码如下所示: ```JavaScript var fs = require("fs"); console.log("准备打开文件!"); fs.stat('input.txt', function(err, stats) { if (err) { console.error("失败"); }else{ console.log(stats); console.log("读取文件信息成功!"); // 检测文件类型 console.log("是否为文件(isFile) ? " + stats.isFile()); console.log("是否为目录(isDirectory) ? " + stats.isDirectory()); } }); ``` > 5、掌握fs模块文件删除功能 #### unlink fs .unlink()函数用来通过异步的方式删除文件,该函数可以接收两个参数,分别是文件路径和回调函数。 **语法** 以下为删除文件的语法格式: ``` fs.unlink(path, callback) ``` **参数** 参数使用说明如下: - **path** - 文件路径。 - **callback** - 回调函数,没有参数。 **实例** input.txt 文件内容为: ``` site:www.shouce.ren ``` 接下来我们创建 file.js 文件,代码如下所示: ```JavaScript var fs = require("fs"); console.log("准备删除文件!"); fs.unlink('input.txt', function(err) { if (err) { console.error("错误"); }else{ console.log("文件删除成功!"); } }); ``` 再去查看 input.txt 文件,发现已经不存在了。 > 6、掌握fs模块文件文件(夹)创建功能 #### mkdir fs.mkdir()函数用来通过异步的方式创建目录(文件夹),fs.mkdir()函数可以接收三个参数,分别是目录路径、设置目录权限、回调函数。 **语法** 以下为创建目录的语法格式: ``` fs.mkdir(path[, mode], callback) ``` **参数** 参数使用说明如下: - **path** - 文件路径。 - **mode** - 设置目录权限,默认为 0777。 - **callback** - 回调函数,没有参数。 **实例** 接下来我们创建 file.js 文件,代码如下所示: ```JavaScript var fs = require("fs"); console.log("创建目录 /tmp/test"); fs.mkdir('/tmp/test', function(err) { if (err) { console.error("错误"); }else{ console.log("目录创建成功。"); } }); ``` > 7、实战:完成文件的复制功能。 - 使用fs模块完成文件的复制功能。 ### 本节作业: 1. 自学fs的目录删除函数、读取目录列表函数。完成小案例,截图发群里 # 第二章:events、buffer、stream... # 第三章:node服务器 ## http登录注册 > 线上临时测试地址:47.105.187.88:8080 ## 技术准备 ### http模块的使用 ```javascript // 引入http模块(包) var http = require("http"); // 创建服务 var server = http.createServer(function(req,res){ // req ===用户信息,请求信息 // res ===响应信息 // console.log(req); console.log(req.method); //用户的请求方式 get post console.log(req.url); //用户访问的地址 路径 + 数据 console.log(req.headers); //请求头:本次请求的其他说明 console.log(req.httpVersion); //遵循的协议版本 // 发送响应应头 : 文件类型、重定向地址、下载文件名称、数据长度、cookie、编码方式、缓存 // 用来发送单个响应头,需要在writeHead之前使用 res.setHeader("content-lenght",3) // 获取发送的某个头的信息 res.getHeader("content-type") // 用来删除单个响应头,也要在writeHead 之前使用 res.removeHeader("content-lenght") // 可以用来发送过个响应头 res.writeHead(200,{"content-type":"text/plain"}) // 数据发送,要在响应头之后 res.write("123") //服务器发送响应的数据 这个数据只能是 字符串 或者是 二进制数据 // write() 可以重复使用 // res.write("456") res.end(); //本次访问结束,也能发数据。只能在数据响应的最后一句使用一次 }); // 服务绑定都电脑端的端口 server.listen(8080,"127.0.0.1",function(){ console.log("server is running"); }); ``` ### fs模块的使用 ```javascript // 内置包随node程序安装,引用的时候直接写名字 var fs = require("fs"); // console.log(fs); // 文件读入函数 地址、读取编码方式、回调函数 // fs.readFile(path, options, callback); // option :默认是二进制 可以不写 所有的文本文件utf8 、 非文本 (图片。视频,音频) // callback:文件读取完成以后,自动运行,并接受读取到的结果 console.log(1); fs.readFile("./test.txt","utf8", function(err,data){ // 运行时,有数据就没err。。。。。 // 有err就没数据,所以我们通过判断err是否存在,来之道是否读取成功 if(err){ console.log("读取失败"); }else{ console.log(data); } }); ``` ```javascript var fs = require("fs"); // 写入的文件路径、写入的数据、参数{}、回调函数 // 写入的文件路径 :文件不存在,会自动创建 // 参数:可以不写 {encoding:"utf8",mode:"0666",flag:"w"} encoding, mode, flag // fs.writeFile(path, data, options, callback); // 默认的写入函数,写入之前会删除原有的数据,然后再写 fs.writeFile("./test.txt", "woshi sunchen,hahah",{flag:"a"}, function(err){ if(err){ console.log("写入失败"); }else{ console.log("写成成功"); } }); ``` ### url模块的使用 url模块转换url,我们这里用来将拆分url字符串。 这是一个完整的url示例: `https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash ` ![](lessions\04-node_http\截图\url.png) 我们在node服务中获取到的是完整url的一部分`/p/a/t/h?query=string#hash`,暂时的话我们只需要能够将路径pathname和查询字符串query拆分出来即可。 ```javascript //浏览器使用 http://127.0.0.1:8080/login?username=孙晨&password=123456 var server = http.createServer(function(req,res){ // 获取用户的请求地址和查询数据 var userUrl = req.url; //获取用户请求的url==="/login?username=孙晨&password=123456" userUrl = url.parse(userUrl, true); //将获取的url进行转换对象 var pathname = userUrl.pathname; //从url对象中获取 纯路径部分 pathname===“/login” //从url对象中获取 纯查询数据部分 query==={username:"孙晨",password:"123456"} var query = userUrl.query; //{} var username = query.username; //获取用户名==="孙晨" var password = query.password; //获取密码===="123456" }); ``` ### JSON数据处理 对象数据不能直接用来发送和保存,我们要先把对象转换为字符串才能进行存储。 ```javascript // 这是对象 var obj1 = { username:"孙晨", password:"123456" } // 这也是对象,但是注意写法与上不同,所以它也叫JSON格式的对象 var obj2 = { "username":"孙晨", "password":"123456" } // JSON 的作用就是将对象转换为字符串或相反 // 使用JSON转换对象为JSON字符串 var str1 = JSON.stringify(obj1); // "{"username":"孙晨","password":"123456"}" var str2 = JSON.stringify(obj2); // "{"username":"孙晨","password":"123456"}" // 使用JSON转换JSON字符串为对象 var obj3 = JSON.parse(str1) // /*{ "username":"孙晨", "password":"123456" }*/ ``` ### 字符串及数组操作 - 字符串的aplit() ```javascript //字符串的split()方法,用来将字符串,拆分为数组 // 声明字符串 var str = "我.是.一个.大好人"; // 从 . 位置分割,获取数组 var arr = str.split(".") //arr = ["我","是","一个","大好人"] ``` - for循环操作数组 ## 功能分析 综合考虑前后台功能,这个是项目经理的活。 | 描述 | 路径 | 作用 | | ------------------------ | -------------- | ------------ | | 通过域名或者ip访问服务器 | / | 提供登录页面 | | 打开登录页面 | /login.html | 提供登录页面 | | 打开注册页面 | /register.html | 提供注册页面 | | 登录功能 | /login | 处理注册逻辑 | | 注册功能 | /register | 处理注册逻辑 | | | | | | | | | ## 数据库设计 - 数据库:user - 集合:users - 数据结构:{username:"String",password:"String"} ## 路由功能 ```javascript // 根据请求地址不同写服务器逻辑 if(pathname=="/"){ // 如果路径是 / 就读取login.html 并发送给客户端 }else if(pathname=="/login.html"){ // 如果路径是 /login.html 就读取login.html 并发送给客户端 }else if(pathname=="/register.html"){ // 如果路径是 /register.html 就读取register.html 并发送给客户端 }else if(pathname=="/login"){ // 如果路径是 /login 就处理登录逻辑 }else if(pathname=="/register"){ // 如果路径是 /register 就处理注册逻辑 }else{ // 其他路径的请求不处理 res.end(); } ``` ## 登录功能 简单描述: - 获取本次请求发送的数据——用户名和密码 - 读取存储的用户信息 - 对比是否存在 - 存在则响应-------登录成功 - 不存在则响应-----登录失败 ```javascript fs.readFile("./data.txt", "utf8", function(err, data) { // data.json 为空---直接登录失败 if (data == "") { res.writeHead(200, { "content-type": "text/plain;charset=utf8" }); //设置响应头 防止中文乱码 res.end("登陆失败!用户信息不存在,请注册~~~") } else { // 将字符串从 + 处分割后,组成数组 dataArr = data.split("+"); for (var i = 1; i < dataArr.length; i++) { var dataObj = JSON.parse(dataArr[i]); if (dataObj.username == query.username && dataObj.password == query.password) { res.writeHead(200, { "content-type": "text/plain;charset=utf8" }); //设置响应头 防止中文乱码 res.end("登录成功"); return; } } // for 循环 ,对比用户名及密码是否存在,不存在代码才会走到这里,存在的代码在for循环中就会自动终止。 // 写入成功---响应客户端 res.writeHead(200, { "content-type": "text/plain;charset=utf8" }); //设置响应头 防止中文乱码 res.end("登录失败!用户名或密码错误~~") } }) ``` ## 注册功能 简单描述: - 获取本次请求发送的数据——用户名和密码 - 读取存储的用户信息 - 对比是否存在 - 存在则响应——注册失败,用户名已存在 - 不存在则响应——注册成功~~~ ```javascript var msg = "+" + JSON.stringify(query); //将用户的注册信息转换为字符串 // 如果路径是 /register 就处理注册逻辑 // 读取data.json中的数据 fs.readFile("./data.txt", "utf8", function(err, data) { // data.json 为空---直接存储用户信息---注册成功 if (data == "") { // 将用户信息字符串追加写入到data.json中 fs.writeFile("./data.txt", msg, { flag: "a" }, function(err) { // 写入成功---响应客户端 res.writeHead(200, { "content-type": "text/plain;charset=utf8" }); //设置响应头 防止中文乱码 res.end("注册成功~~~") }); } else { // 将字符串从 + 处分割后,组成数组 dataArr = data.split("+"); for (var i = 1; i < dataArr.length; i++) { var dataObj = JSON.parse(dataArr[i]); if (dataObj.username == query.username) { res.writeHead(200, { "content-type": "text/plain;charset=utf8" }); //设置响应头 防止中文乱码 res.end("注册失败,用户名已存在"); return; } } // for 循环 ,对比用户名是否存在,不存在代码才会走到这里,存在的代码在for循环中就会自动终止。 fs.writeFile("./data.txt", msg, { flag: "a" }, function(err) { // 写入成功---响应客户端 res.writeHead(200, { "content-type": "text/plain;charset=utf8" }); //设置响应头 防止中文乱码 res.end("注册成功~~~") }); } }) ``` # 第四章:MongoDB 了解MongoDB基础概念 完成MongonDB的安装 配置及简单使用mongoDB的shell工具管理数据库 掌握mongoDB的shell进行数据库的增删改查 了解使用第三方软件submon软件进行MongoDB数据库管理--图形化软件 使用node操作MongoDB----mongoose ## 01-了解MongoDB的作用 MongoDB 是一个基于`分布式文件存储`的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 MongoDB是一个`面向文档`的`非关系型`数据库,数据在数据库中存储格式与要在服务端与客户端间处理的格式非常相近,这是MongoDB流行起来的原因之一。 MongoDB数据库在JavaScript脚本环境中支持BSON 对象的存取,因此对于数据存取的效率非常高。在MongoDB数据库中,将每一条等待插人的数据记录存储在内存中,因此,该数据库是一种非阻塞型数据库,在需要记载大量日志数据、实时测量数据或实时统计数据时,该数据库可以达到令人满意的效果。由于MongoDB数据库支持在查询语句中使用JavaScript函数,也大大加强他读取数据的能力。 另外,MongoDB数据库是一个面向文档的数据库,它允许用户在父记录中存储子记录。例如,可以将一篇博客文章及它的所有评论存储在一条记录中,从数据库中可以很方便的在抽取这篇文章的同时,也抽取文章的所有评论。 > mongodb数据存储形式 ![](lessions\05-mongdb安装\截图\数据结构分析.png) ## 01-完成MongonDB的安装 > - 下载地址:https://www.mongodb.com/download-center/community > - server选项 > - 选择版本version:建议选择最老的版本 > - 选择系统OS:我们大部分都是 window x64 > - 选择文件类型:默认为.msi,建议选择.msi > ![](lessions\05-mongdb安装\截图\download.png) > - 下载地址:http://dl.mongodb.org/dl/win32/x86_64 > - 选择后缀为.msi的文件 > - 选择发布时间较早的老版本,2018/2017年的都可以。 > - 安装:默认安装,不要更改配置 > - 安装完成:在安装目录可以找到mongoDB安装文件代表安装成功。 > ![](lessions\05-mongdb安装\截图\安装目录.PNG) ## 01-配置及简单使用mongoDB的shell工具管理数据库 - > 找到mongoDB的安装目录,我们使用mongoDB时,要先启动mongod.exe,然后再使用mongo.exe进行数据库的管理。 ![](C:\Users\Administrator\Desktop\github\work18node\lessions\05-mongdb安装\截图\bin.png) - > 第一次启动mongod.exe,可能会出现闪退的情况,这事因为mongoDB的默认存储目录需要手动创建。我们手动创建db文件夹c:\data\db,然后再启动mongod.exe ![](lessions\05-mongdb安装\截图\db.png) > - 使用mongo.exe,简单感受数据库的管理 > - db:查看当前正在使用的数据库 > - db.sc.insert():在当前数据库的sc集合中插入数据,集合不存在会自动创建。 > - db.sc.find():查看当前数据库sc集合中的数据。 > - 使用mongo.exe,完成实战演练,将班级学生成绩输入到数据库中。 | **姓名****name** | **学号****id** | **语文成绩****chinese** | **数学成绩****math** | **英语成绩****english** | **总分****total** | **排名****list** | | ---------------- | -------------- | ----------------------- | -------------------- | ----------------------- | ----------------- | ---------------- | | 陈一 | 1 | 96 | 94 | 94 | 284 | 3 | | 刘二 | 2 | 77 | 88 | 78 | 243 | 8 | | 张三 | 3 | 98 | 99 | 94 | 291 | 1 | | 李四 | 4 | 66 | 77 | 69 | 212 | 10 | | 王五 | 5 | 96 | 95 | 91 | 282 | 4 | | 赵六 | 6 | 88 | 81 | 89 | 258 | 6 | | 钱七 | 7 | 91 | 84 | 92 | 267 | 5 | | 孙八 | 8 | 79 | 78 | 77 | 234 | 9 | | 冯九 | 9 | 94 | 93 | 99 | 286 | 2 | | 夏十 | 10 | 81 | 86 | 88 | 255 | 7 | ## 02-掌握mongoDB的shell进行数据库的增删改查 - 了解基本概念:database、collection、document - 数据库操作: | 操作方式 | 作用 | | ------------------ | ------------------------------------------------------------ | | db | 查看正在使用数据库 | | user "名称" | 切换正在使用的数据库不存在则创建,空的数据库不显示 | | show dbs | 查看所有的数据库列表 | | db. dropDatabase() | 删除正在使用的数据库中,一定注意,删除以后要切换其他数据库再操作 | - 集合操作: | 操作方式 | 作用 | | ----------------------- | ---------------------------- | | db.sc.insert() | 插入数据时,集合不存在则创建 | | db.createCollection() | 创建集合 | | db.drop() | 删除集合 | | show collections | 查看当前数据库的集合列表 | - 文档操作:我们学习的关键就是文档的操作,基本包括`增删改查`四大部分 > 增加数据 db.sc.insert({}) > 查询数据 db.sc.find() db.sc.findOne() 查询时,可以传入相应的查询条件。 | **操作** | **格式** | **示例** | | ------------ | ---------------------- | ------------------------------ | | 等于 | {:} | db.col.find({"name":"xiaoge"}) | | 小于 | {:{$lt:}} | db.col.find({"age":{$lt:30}}) | | 小于或等于 | {:{$lte:}} | db.col.find({"age":{$lte:50}}) | | 大于 | {:{$gt:}} | db.col.find({"age":{$gt:50}}) | | 大于或者等于 | {:{$gte:}} | db.col.find({"age":{$gte:50}}) | | 不等于 | {:{$ne:}} | db.col.find({"age ":{$ne:50}}) | ```javascript db.sc.find({“name”:”xiaoge”}) ``` > 删除数据 db.sc.deleteMany() db.sc.deleteOne() - 删除必须添加删除条件(查找条件) - 全部删除使用deleteMany()---传入空对象 db.sc.deletaMany({}) 删除时,也可以传入与查询一样的条件。 | **操作** | **格式** | **示例** | | ------------ | ---------------------- | ------------------------------------ | | 等于 | {:} | db.col.deleteMany({"name":"xiaoge"}) | | 小于 | {:{$lt:}} | db.col.deleteMany({"age":{$lt:30}}) | | 小于或等于 | {:{$lte:}} | db.col.deleteMany({"age":{$lte:50}}) | | 大于 | {:{$gt:}} | db.col.deleteMany({"age":{$gt:50}}) | | 大于或者等于 | {:{$gte:}} | db.col.deleteMany({"age":{$gte:50}}) | | 不等于 | {:{$ne:}} | db.col.deleteMany({"age ":{$ne:50}}) | ```javascript db.sc.deleteMany({“name”:”xiaolai”}) ``` > 更改数据--更新数据 db.sc.updateMany() db.sc.updateOne() 更新时,要传入两个参数,第一个数查询条件,第二个是更新数据。 ```javascript db.sc.updateMany({“name”:”xiaolai”},{$set:{“score”:99}}) ``` > 实战演练: 某班有8名学生,在某次考试中的成绩如表所示,在MongoDB中创建数据库和集合,录入所有的成绩文档,在查询文档时发现有一名学生的成绩重复了一次,以及一名学生的成绩需要修改,删除MongoDB中重复的文档并且修改学生“方芳”的成绩为99。 | **学生姓名****name** | **考试分数****score** | | -------------------- | --------------------- | | 王小双 | 97 | | 张媛媛 | 95 | | 何勤勤 | 89 | | 袁莉莉 | 94 | | 张松 | 87 | | 李清清 | 79 | | 钟鹏 | 83 | | 张媛媛 | 95 | | 方芳 | 91 | # 第五章:KOA框架 ## koa错误处理 如果代码运行过程中发生错误,我们需要把错误信息返回给用户。HTTP 协定约定这时要返回500状态码。Koa 提供了ctx.throw()方法,用来抛出错误,ctx.throw(500)就是抛出500错误。 > 案例一:使用throw抛出500错误 ```javascript // 抛出错误---与真实的错误效果一样 server.use((ctx)=>{ ctx.throw(500); }) ``` 404错误可以通过设置Context 对象的response.status属性值为404来实现,也可以使用ctx.throw(404)来实现。 > 案例二:演示抛出其他状态码,客户端收到的结果。 ```javascript // 总结:throw() // 向客户端发送对应的状态吗及结果 // 在服务器抛出一个错误 server.use((ctx)=>{ ctx.throw(200); }) ``` > 案例三:服务监听error事件,对错误进行处理 ```javascript //监听服务器报错----所有错误 server.on("error",(err)=>{ console.log("捕捉到了服务器错误") }) ``` ## cookies读写 cookie称为缓存数据,会自动随同源的请求发送的服务器端。 虽然前后端都可以操作cookie,但是一般cookie是由服务端进行操作,用来进行简单数据存储或者是身份验证。 > 案例演示:服务器发送及读取cookie ```javascript app.use(route.get("/",(ctx)=>{ if(ctx.cookies.get("num")==undefined){ var num = 1; ctx.cookies.set("num",num) //cookies 不能设置数字0 }else{ var num = ctx.cookies.get("num"); ctx.cookies.set("num",++num) } ctx.body = "欢迎第"+num+"访问" })) ``` ## koa-body模块 回顾性知识点: ```reStructuredText 1、页面中发送的请求可以有四种方式 get post ...... 2、大部分请求的默认方式都是get 3、post方式只能通过表单 或者ajax 发送 4、get方式:发送的数据会直接拼接的url上 不安全、数据有限制(只能发送几Kb,原因是浏览器的上url长度有限制 5、post方式:发送的数据在请求体里(一定不在url上) 更安全、数据量理论上无限制 6、get适合请求数据 post适合发送数据 ``` ### 获取post提交的数据 当客户端使用post方式向服务器提交数据时,koa框架无法直接获取post提交的数据,需要使用koa-body包才能正常获取数据。 > 案例:使用表单发送post请求 + 服务端获取post请求的数据 ```javascript const Koa = require("koa"); const static = require("koa-static") const route = require("koa-route"); const koaBody = require("koa-body"); const server = new Koa(); server.use(static("./public")); // 接受所有的请求,如果是post会将数据进行处理,get什么都不做。 这个自动调用了next server.use(koaBody()); server.use(route.post("/login",(ctx)=>{ console.log(ctx.request.body) ctx.body = "你使用post发了数据,我应该是可以拿到的!" })) server.listen(8080,()=>{ console.log("server is running"); }) ``` ### 实现文件上传功能 我们常见的文件上传功能,就是使用表单来完成。 `文件上传问什么一定是post方式?` **页面注意事项:** 1、form表达的请求方式为post 2、form表单要设置enctype(数据编码类型) ```html
``` 3、使用`multiple="multiple"`设置是否可以多文件上传 ```html
文件选择:
``` **服务器注意事项:** 1、使用koa-body包 时,要添加必要配置 ```javascript server.use(koaBody({ multipart: true })); ``` 2、使用fs模块进行文件的读写(复制文件) - 使用同步方式进行文件操作 - 使用async + await 配合fs.promised模块进行文件操作 ```javascript const Koa = require("koa"); const static = require("koa-static"); const route = require("koa-route"); const koaBody = require("koa-body"); const fs = require("fs") const server = new Koa(); server.use(static("./public")); // 接受所有的请求,如果是post会将数据进行处理,get什么都不做。 这个自动调用了next server.use(koaBody({ multipart: true })); server.use(route.post("/upload",(ctx)=>{ // console.log(ctx.request.files.file) let files= ctx.request.files.file || {}; if(files instanceof Array){ // 多文件上传 for(var i=0;i{ console.log("server is running"); }) ```