Node.js
Node.js奠定了现在前端的基础 2009
Node.js概述
JavaScript语音在浏览器中需要解析运行(浏览器内核进行解析) 浏览器的内核主要包括两部分:js引擎(解析器)和渲染引擎
- js引擎负责解析并运行JavaScript代码
 - 渲染引擎负责把HTML和CSS渲染成页面效果
 浏览器除了提供js引擎之外,还提供了什么?从而方便写代码?WebAPI(DOM和BOM)
- 为什么JavaScript可以运行在浏览器中?
- Chrome —- V8
 - Firefox —- OdinMonkey
 - Safri —- JSCore
 - IE —- Chakra
 - etc……
 
 

总结:对于前端来说,最基础的编程语言层面需要学习:javascript语法基础;WebAPI javascript运行在浏览器的内核中js引擎(解析器) 如果没有浏览器,只有js引擎,那么js是否可以运行呢?可以
- Node.js是什么?
 

- Node.js是什么?
- Node.js是一个开发平台,在这个平台上可以进行软件开发,并且开发出功能更加强大软件(网站),并且在这个平台上进行开发很方便,因为Node.js提供了丰富的API和优秀的运行性能。
 - 在Node.js平台中开发语言使用的依然是JavaScript
 - Node.js平台:js解析器;内置的API
 - 浏览器:js解析器;内置的API(WebAPI、内置对象Array、Date…)
 
 - Node.js可以做什么?
- 服务端Web开发,就是做网站(操作数据库)
- java、python、PHP、ruby、go……node.js
 
 - 服务端后台接口API项目
 - 桌面程序(基于Electron)
- Typora
 - VSCode
 - Postman
 
 - 命令行工具
 - App(React Native)(IOS-swift/Android-java)
- uniapp(vue)
 - react native
 
 - ……
 - javascript可能会一统江湖
 
 - 服务端Web开发,就是做网站(操作数据库)
 
Node.js开发环境配置
关于命令行用法补充
命令行:操作计算机的一种方式;另一种方式是图形界面
- 打开命令行三种方式
- 文件夹空白处,按住shift,右键 —> 在此处打开xxx窗口
 - windows + R,在出现的运行窗口中,输入 cmd,回车
 - 鼠标放到文件路径中,输入cmd,然后回车
 
 - 切换盘符命令: 盘符 + :
 
d:
- cd切换目录命令:cd 路径名称
 
cd .. 切换到上一层路径cd ./01-outline 切换到当前路径下目录中cd D:\myajax\day01 切换到指定路径中
- 查看当前目录下的文件 dir
 - 清屏命令:cls
 - 创建文件夹 mkdir 文件夹名称
 - ……
 
总结:命令行是一种操作计算机的方式 cd dir cls mkdir d:
安装配置Node.js环境
- 从官网下载安装包 node-v12.18.1-x64.msi
 - 双击一直下一步完成安装
 - 如何证明安装成功了?
 
在命令行模式下输入 node -v 然后回车,看到版本号,证明安装成功

Node.js初体验
- Node.js中代码执行方式
 
- 命令行窗口执行
 - 退出命令行环境的方式
- 两次 ctrl + c
 - 输入 .exit
 
 

- 代码写到文件中运行
 
- 必须在js文件所在的目录中运行node命令
 

安装可能的问题
- Vscode内置命令行运行出现如下问题
 

解决方式:按照如下方式进行配置,然后重启vscode即可

- 安装Node环境后,出现如下问题
 

需要配置环境变量:在【此电脑】右键->属性->高级系统设置

- 点击环境变量-》打开的窗口中,双击Path
 

- 在打开的窗口中,新建,然后把node.js的安装路径粘贴进去即可 C:\Program Files\nodejs
 
Node.js核心模块介绍
核心模块:主要提供如下的一些功能 核心模块提供了开箱即用的丰富的API,方便开发项目时进行调用。
- 文件操作(文件的读写操作)
 - 网络通信(处理客户端请求并返回结果)
 - 进程管理
 - ……
 

总结:Node.js中提供的API(核心模块),这些模块的主要职责是提供服务 我们侧重点是接口服务(如何从后端的角度向前端提供一个接口服务?):处理网络通信;文件读写
fs模块
文件系统(File System):操作系统(windows/mac/linux)的主要职能是管理硬件和软件 磁盘(硬盘),硬盘中的数据需要管理,由操作系统的文件系统进行管理 从编程的角度,也可以通过API进行文件操作(读文件;写文件)
- fs FileSystem 文件系统模块,用于操作文件
- 读取文件内容 fs.readFile
 - 写入文件内容 fs.writeFile
 
 
/*文件系统操作-读取文件1、引入文件模块2、使用模块的API操作文件*/var fs = require('fs')/*参数一:表示读取文件的路径参数二:读取文件的编码参数三:读取文件成功的回调函数*/fs.readFile('./data.txt', 'utf-8', function (err, data) {// 参数err表示读取成功或者失败(如果err===null表示读取成功,否则err表示错误信息)// 参数data表示读取到的文件内容// 如果err是true,表示读取失败,此时一般要终止后续代码的运行if (err) returnconsole.log(err)console.log(data)})
/*写文件操作fs.writeFile(file, data[, options], callback)*/const fs = require('fs')/*参数一表示写入文件的路径参数二表示写入的文件内容参数三表示写入内容的编码参数四写入成功的回调函数*/fs.writeFile('./data.json', 'hello world', 'utf8', function (err) {if (err) {// 写入文件失败console.log(err)return}console.log('写入文件成功')})
- 读写文件的方法一般都是成对出现
- fs.readFile
 - fs.readFileSync
 - fs.writeFile
 - fs.writeFileSync
 
 
/*同步的文件读写方法同步的方式代码简单,但是性能不如异步的方式好*/const fs = require('fs')// 采用同步的方式读取文件// let ret = fs.readFileSync('./abc.txt', 'utf8')// console.log(ret)// 采用同步的方式写文件fs.writeFileSync('./abc.txt', 'coniqiwa', 'utf8')console.log(ret)
总结:
- 文件读写操作都支持同步和异步方式
 - 同步的方式性能比异步的方式要差
 
成绩处理案例
- 需求:将横线之上的数据转换为横线之下的数据
- 读取文件中原始数据,加工后写入到另一个文件中
 - \r\n 表示回车换行
 - str.replace(‘=’, ‘:’) 把等号换成冒号
 
 
小红=99 小白=100 小黄=70 小黑=66 小绿=88------------------------------------------------小红:99小白:100小黄:70小黑:66小绿:88
- 功能实现
 
// 1. 导入 fs 文件系统模块const fs = require('fs')// 2. 使用 fs.readFile() 读取 成绩.txt 文件fs.readFile('./成绩.txt', 'utf8', (err, dataStr) => {// 3. 判断文件是否读取成功if (err) {return console.log('读取文件失败!' + err.)}// 3.1 读取成功// 4. 处理成绩// 4.1 把字符串按照空格进行 split 分割const arr = dataStr.split(' ')// 4.2 循环数组,把每一项中的 = 替换成 :const newArr = []// 4.3 对原数组中的每一项进行字符串的替换操作arr.forEach(function (item) {const ite = item.replace('=', ':')// 将处理完毕的这一项,添加到新数组newArr.push(ite)})// 4.4 将新数组中的每一项,进行拼接,形成新的字符串const newStr = newArr.join('\r\n')// 5. 调用 fs.writeFile() 写入文件fs.writeFile('./成绩-ok.txt', newStr, function (err){if (err) {return console.log('文件写入失败!' + err.message)}console.log('文件写入成功!')})})
- 方法二:
 
const fs = require('fs')fs.readFile('./info.txt', 'utf8', function (err, score) {if (err) return// 把所有的等号替换为:let ret = score.replace(/=/g, ':')// 把上次的结果再次替换:把空格替换为回车换行ret = ret.replace(/[\s]/g, '\r\n')fs.writeFile('./result.txt', ret, 'utf-8', function (err) {if (err) returnconsole.log('写入成功')})})
- 同步方式读写文件
 
// 采用同步的方式实现let data = fs.readFileSync('./scores.txt', 'utf8')let str = data.replace(/=/g, ':')str = str.replace(/\s/g, '\r\n')fs.writeFileSync('./scores2.txt', str, 'utf8')
关于文件的路径问题
const fs = require('fs')// let ret = fs.readFileSync('./abc.txt', 'utf8')// ./表示命令行当前所处的路径// 我们希望文件的路径相对js文件本身来找比较方便// __dirname表示当前执行的js文件所处的路径console.log(__dirname) // D:\current\day14\02-code// 右斜杠需要做转义处理let ret = fs.readFileSync(__dirname + '\\abc.txt', 'utf8')console.log(ret)
总结:
- ./表示命令行当前所处的路径
 - __dirname表示当前执行的js文件所处的路径
 
path模块
path模块的主要职能:操作路径
- path.sep用法
 
const path = require('path')const fs = require('fs')// let data = fs.readFileSync(__dirname + '\\scores.txt', 'utf8')// 如果是windows那么path.sep的值是右斜杠,如果是Linux系统,那么path.sep是左斜杠let data = fs.readFileSync(__dirname + path.sep + 'scores.txt', 'utf8')console.log(data)
总结:path.sep的作用:自动检测平台环境,根据环境自动选择斜杠类型 windows \ linux/mac/unix /
- 拼接路径的基本用法
 
/*核心模块:path 路径操作1、导入核心模块2、使用模块相关API处理路径// path.join方法的作用:把参数中所有的路径拼接到一块并且规范化路径path.join([...paths]), 参数表示Rest参数*/const path = require('path')// D:\current\day14\02-code\data.json// let p = path.join(__dirname, './data.json')// D:\current\day14\02-code\abc\hello\data.json// let p = path.join(__dirname, 'abc', 'hello', './data.json')// D:\current\day14\02-code\hello\data.jsonlet p = path.join(__dirname, 'abc', '../hello', './data.json')console.log(p)
总结: path.join方法的作用:拼接路径并且对路径进行规范化处理
- 路径相关方法和属性(了解)
 
// path.parse 方法的作用:提取路径的相关信息// windows平台的路径格式// let str = 'D:\\current\\day14\\02-code\\abc\\hello\\data.json'// linux平台路径格式let str = '/home/user/dir/file.txt'let obj = path.parse(str)console.log(obj)/*{root: 'D:\\',dir: 'D:\\current\\day14\\02-code\\abc\\hello',base: 'data.json',ext: '.json',name: 'data'}*/// 获取路径中的文件名称let name = path.basename(str, '.js')console.log(name)
时钟案例
- 将html文件中的js、css代码拆解到对应得文件中,然后以链接的方式导入到HTML文件中
- 涉及文件的读写操作
 - 如何找到要拆分的文本内容?正则匹配
 
 
/*时钟案例*/// 1、读取html文件的内容const path = require('path')const fs = require('fs')// oldHtml 路径let oldHtml = path.join(__dirname, './clock.html')// newHtml 路径let newHtml = path.join(__dirname, './index.html')// js 文件路径let jsPath = path.join(__dirname, 'js', 'clock.js')// css 文件路径let cssPath = path.join(__dirname, 'css', 'clock.css')let htmlFile = fs.readFileSync(oldHtml, 'utf8')// 2、分析网页内容,拆分js和css代码// 基于正则表达式处理字符串笔记容易// 注意:小括号只是提取分组的一个作用// 匹配 <style></style>const regStyle = /<style>([\s\S]*)<\/style>/// 匹配 <script></script>const regScript = /<script>([\s\S]*)<\/script>/let cssRet = regStyle.exec(htmlFile)// 要被替换的代码let cssTagCode = cssRet[0]// 要写入文件的代码let cssCode = cssRet[1]let jsRet = regScript.exec(htmlFile)// 要被替换的代码let jsTagCode = jsRet[0]// 要写入文件的代码let jsCode = jsRet[1]// 3、把拆分出的代码分别写入对应文件并且更新链接// 3-1、写入css代码fs.writeFileSync(cssPath, cssCode, 'utf8')// 3-2、写入js代码fs.writeFileSync(jsPath, jsCode, 'utf8')// 3-3、替换html文件中的链接htmlFile = htmlFile.replace(cssTagCode, '<link rel="stylesheet" href="./css/clock.css" />')htmlFile = htmlFile.replace(jsTagCode, '<script src="./js/clock.js"></script>')// 3-4、把替换好链接的HTML页面写入文件fs.writeFileSync(newHtml, htmlFile, 'utf8')
总结:
- 文件读写
 - 正则用法
 
总结
- js运行在哪里?浏览器的内核中的js引擎中
 - Node.js提供了js引擎,所以js可以运行在Node.js环境中
 - 浏览器提供了:js引擎 + WebAPI
 - Node.js提供了:js引擎 + 内置API(核心模块)
 - Node.js是一个平台,可以做很多事情
 - 熟悉命令行基本操作
 - 安装Node.js的开发环境
 - Node.js代码运行方式:直接命令行写代码(了解);运行js文件
 - Node.js中的内置API和浏览器中的WebAPI做的事情不一样
 - Node.js的内置模块主要用于提供服务(给前端提供服务)
 - 关注点:提供接口服务(处理网络通信;文件读写操作)
 - 文件读写相关方法
- fs.readFile
 - fs.readFileSync
 - fs.writeFile
 - fs.writeFileSync
 
 - 理解文件读写的路径问题(斜杠和拼接问题)
 - 为了更加规范的拼接路径,node提供专门的模块path
 - 重点掌握path.join方法的用法以及__dirname的作用
 - 成绩处理案例
 - 时钟案例
 
服务器介绍
服务器主要职能:提供服务(网页服务、视频服务、音频服务、邮箱服务。。。)数据接口服务
大量的数据资源需要存储在服务器中,客户端要想看到,需要先请求对应资源加载到客户端才能呈现出来
- 客户端请求时,服务器的这些资源是如何返回给客户端的?(文件读取;文件写入-上传文件)
 - 服务器如何处理客户端请求?需要协议,对于这些协议,Node.js实现了相关的API,从而处理请求
 
总结:服务器是提供服务的,我们现在关注点是:网站服务。客户端请求服务器,服务器返回资源给浏览器。我们要从服务器提供服务的角度写代码,用于向客户端提供接口数据。
http模块
- 服务器提供Web服务基本流程
 
// 提供接口服务// 1、导入核心模块const http = require('http')// 2、基于http模块提供的方法实现接口服务// 2-1、创建一个服务器实例对象const server = http.createServer()// 2-2、监听端口// 3000表示端口:端口用于区分电脑中的那个网络应用程序server.listen(3000, function () {// 监听成功后,该回调函数会执行console.log('running...')})// 3、监听客户端发送的请求动作// 如果客户向3000端口发送了请求,那么如下的事件request就会触发// 那么对应的回调函数就会执行server.on('request', function (req, res) {// req表示请求对象,用于处理请求相关信息// res表示响应对象,用于处理响应相关信息// 响应客户端请求的结果res.end('hello world')})
总结:基于核心模块http实现基本的接口服务
- 关于请求对象的两个属性的作用
- req.url表示请求地址中的路径(端口之后的部分)
 - req.method表示客户端的请求方式(GET/POST/PUT/DELETE)
 - 后端可以根据这两个属性的组合分支判断实现多个接口
 
 - 接口开发流程演示
 
/*基于http模块实现一个接口服务*/var http = require('http')var path = require('path')var fs = require('fs')// 创建一个服务器实例对象var server = http.createServer()// 监听一个端口: 0 - 65535server.listen(3000, function () {console.log('running...')})// 接收客户端的请求(当客户端有请求时,就会触发request事件)server.on('request', function (req, res) {// req表示请求对象// req.url表示请求路径// console.log(req.url)// req.method表示请求方式// console.log(req.method)// res表示响应对象// 该回调函数用于处理请求并相应合适的内容// res.end('hello nihao')if (req.url === '/getbooks' && req.method === 'GET') {// 读取文件fs.readFile(path.join(__dirname, './books-data.txt'), 'utf8', function (err, data) {if (err) {// 如果读取失败,就返回错误提示res.end('server error')}// 返回读取的数据给前端res.end(data)})} else if (req.url === '/data' && req.method === 'GET') {res.end('test data')} else {res.end('404')}})
总结:接口服务主要有两个核心流程:处理网络通信;文件读写操作
- 设置响应数据的格式和编码
 
// 设置响应头:告诉浏览器服务器返回的数据格式和编码是什么情况?res.setHeader('content-type', 'application/json; charset=utf8')
图书接口案例
项目基本结构
/*图书管理后端接口*/var path = require('path')var fs = require('fs')var http = require('http')// 创建服务器实例对象var server = http.createServer()// 监听端口server.listen(3000, function () {console.log('running...')})// 监听客户端请求事件server.on('request', function (req, res) {if (req.url === '/books' && req.method === 'GET') {// 查询图书列表res.end('list books')} else if (req.url === '/delbook' && req.method === 'GET') {res.end('delete book')} else if (req.url === '/addbook' && req.method === 'POST') {res.end('add book')} else {res.end('404')}})
获取图书
- 通过读取文件的方式获取图书数据
 
// 监听客户端请求事件server.on('request', function (req, res) {if (req.url === '/books' && req.method === 'GET') {// 查询图书const filePath = path.join(__dirname, './books-data.txt')fs.readFile(filePath, 'utf-8', function (err, data) {if (err) {// 读取失败res.end('server error')return}// 正常返回数据res.end(data)})} else if (req.url === '/delbook' && req.method === 'GET') {res.end('delete book')} else if (req.url === '/addbook' && req.method === 'POST') {res.end('add book')} else {res.end('404')}})
删除图书
- 删除图书的流程
- 读取原始文件
 - 删除数据(处理)
 - 写会处理后结果到文件
 
 
// 监听客户端请求事件server.on('request', function (req, res) {console.log(req.url)if (req.url === '/books' && req.method === 'GET') {// 查询图书列表// 从文件读取所有的图书信息var list = fs.readFileSync(path.join(__dirname, './books.json'), 'utf8')res.end(list)} else if (req.url.indexOf('/delbook') !== -1 && req.method === 'GET') {// 删除图书:// 如果是删除请求地址,那么req.url=/delbook?id=123// 获取要删除的图书的idlet id = nullif (req.url.indexOf('id=') !== -1) {// 获取等号字符之后一个字符的索引位置const index = req.url.lastIndexOf('=') + 1// 截取索引之后的字符串(包括索引)id = req.url.substring(index)}if (id === null) {// 前端没有传递id值res.end('请传递图书id')return}// 删除图书:// 1、读取所有的文件数据const filePath = path.join(__dirname, './books-data.txt')const books = fs.readFileSync(filePath, 'utf8')// 2、把读取到的数据删除一条(根据id删除)// 把字符串books转换为对象let booksArr = JSON.parse(books)booksArr = booksArr.filter(function (item) {console.log(item.id !== parseInt(id))return item.id !== parseInt(id)})// 3、把剩余数据写回文件中fs.writeFileSync(filePath, JSON.stringify(booksArr), 'utf8')// 4、返回状态信息res.end(JSON.stringify({status: 0,message: '删除图书成功'}))} else if (req.url === '/addbook' && req.method === 'POST') {res.end('add book')} else {res.end('404')}})
总结:监听前端的请求,操作文件、响应状态
- 获取请求参数
 - 删除的流程:读文件;操作数组;写文件
 

