浏览器运行环境内置V8引擎(JS运行平台) - 把编写的JS代码来解析和执行
Java是个语言, JVM虚拟机, 是运行它的环境
Python是个语言, 也需要安装Python运行环境
PHP也是个语言, 需要安装PHP运行环境
Node.js 的官网地址: https://nodejs.org/zh-cn/
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境
其实Node本身是个软件环境, 但是因为它的语言和JavaScript近乎一样, 所以也叫它Node.js
Node脱离浏览器独立执行JS代码的软件环境
前端
运行环境。后端
运行环境。必须在计算机上安装 Node.js 环境才行
安装包进入官网(中文),可以看到如下两个版本:
LTS 为长期稳定版,推荐安装 LTS 版本。建议
Current 为新特性尝鲜版,可能存在隐藏的 Bug 或安全性漏洞,不推荐企业开发使用
人机交互的一个窗口和工具, 只有程序员才会用到终端(也叫命令行工具)
之前我们学习过git 实际上那就是一个终端, 敲敲命令, 就能让电脑/软件帮助我们做一些事情
windows
打开终端,在终端输入命令 node –v
后,按下回车键,即可查看已安装的 Node.js 的版本号
如果你能够看到版本号,说明你已经安装成功了。
使用node命令执行JS文件
// 编写和执行js文件步骤
// 1. 新建js文件
// 2. 编写一些js代码(注意, 没有DOM和BOM)
// 3. 打开终端, 确保路径, 在此文件, 所在文件夹
// 4. 执行命令 node 文件名.js 回车 - node开始逐行执行js文件里代码, 输出结果到控制台
// node是独立(脱离浏览器)的一个环境, 使用node环境+命令来执行js文件
console.log("我是nodejs哦, 欢迎来学习我");
注意终端的路径,注意在此路径中,是否能找到你的js文件 / tab键可以补全剩下的路径文件名
常见问题
终端快捷键
模块 = js文件, 里面是封装的基础 JS 代码
核心模块: node 系统自带的内置的 JS 文件
模块 = 以前学的插件
处理路径的模块
path
模块。path
是 Node 本身提供的 API,专门用来处理路径。path
仅仅用来处理路径的字符串,不一定存在对应的物理文件。使用方法
加载模块
// 使用核心模块之前,首先加载核心模块
let path = require('path');
// 相当于
// let path = path内部暴露出的一个全局对象
// 然后就可以使用path对象里的一些功能方法, 然后拿到返回值咯(如果有返回值)
调用path模块中的方法,来处理相应的问题,下面列举path模块中的几个方法
方法 | 作用 |
---|---|
path.basename(path) | 返回 path 的最后一部分(文件名) |
path.dirname(path) | 返回目录名 |
path.extname(path) | 返回路径中文件的扩展名(包含.) |
path.join([…paths]) | 拼接路径 |
path.resolve([…paths]) | 基于当前工作目录拼接路径(绝对地址) |
const path = require('path');
// basename - 获取路径最后一部分(一般用于文件名获取)
console.log(path.basename("http://134.23.43.55/a/b/c/d.html")); // d.html
// dirname - 提取到最后一个文件夹的全部路径
console.log(path.dirname("http://134.23.43.55/a/b/c/d.html")); // http://134.23.43.55/a/b/c
console.log(path.dirname("../a/b/c/d.html")); // ../a/b/c
// extname -- 获取文件后缀
console.log(path.extname('index.html')); // .html
console.log(path.extname('index.coffee.md')); // .md
console.log(path.extname("http://5.4.3.2/a/b/c/d.png")); // .png
// join -- 智能拼接路径 (相对地址)
console.log(path.join('./html', 'index.html')); // html\index.html
console.log(path.join(__dirname, 'html', 'index.html'));
// __dirname 获取的是当前文件所在文件夹的绝对地址
// resolve -- 基于当前路径, 拼接路径(绝对地址)
console.log(path.resolve('./html', 'index.html'));\html\index.html
console.log(path.resolve(__dirname, 'html', 'index.html'));
文件操作模块
const fs = require("fs"); // 引入内置的fs模块
console.log("文件开始");
// 异步读取文件
// 参数1: 文件路径
// 参数2: 读取所用编码(不设置读出来的是Buffer字节流, 文件本质上还是0和1的组成)
// 参数3: 回调函数 - 读取后无论成功与否都触发
fs.readFile("./html/index.html", "utf-8", (err, data) => {
console.log(err);
console.log("----");
console.log(data);
})
console.log("nodejs执行完毕"); // 测试是否上面代码是异步
// 异步读取文件
// 参数1: 文件路径
// 参数2: 读取所用编码
// 参数3: 回调函数 - 读取后无论成功与否都触发
fs.readFile("./html/index.html", "utf-8", (err, data) => {
if (err) { // 如果err有值, 证明报错
console.log(err.message); // 打印错误信息(message属性固定名字)
} else {
console.log(data);
}
})
文件无需提前创建
const fs = require("fs"); // 引入内置的fs模块
// 异步写入
// 参数1: 文件路径
// 参数2: 写入内容
// 参数3: 回调函数
fs.writeFile("./txt/1.txt", "我是插入的内容", (err) => {
if (err) {
console.log(err.message);
} else {
console.log("写入成功");
}
})
// 判断文件存在不
// 参数1: 文件路径
// 参数2: 回调函数
fs.access("./txt/1.txt", function(err){
if (err) {
console.log(err.message);
} else {
// 同步写入(带Sync就是同步方法, 无回调函数)
// 参数1: 文件路径
// 参数2: 写入内容
fs.writeFileSync("./txt/1.txt", "同步写入的内容");
}
})
总结: 所有方法如果带Sync结尾都是同步方法, 主线程在原地等待结果
成绩.txt
小红=99 小白=100 小黄=70 小黑=66 小绿=88
利用fs模块和js代码, 把成绩.txt里的数据转换成如下格式
const fs = require("fs");
fs.access("./txt/成绩.txt", function(err){
if (!err) { // 如果无错误, 再读取这个文件
fs.readFile("./txt/成绩.txt", "utf-8", function(err, data){
// 把=换成: , 把空格换成\n
let str = data.replace(/\s/g, "\n").replace(/=/g, ": ");
// 写入到新的文件中
fs.writeFile("./txt/成绩ok.txt", str, err => {
if (err) console.log(err.message);
else console.log("修改成功");
})
})
}
})
const fs = require("fs");
const path = require("path");
// 1. 知道什么是./ 什么是../
// . 当前文件所在文件夹 /代表开启文件夹访问下面的东西
// .. 代表返回上一级文件夹 /代表开启文件夹访问下面的东西
// 2. 为什么要使用绝对路径呢?
// 例如: 想给txt/1.txt写入内容 - 用相对路径
// fs.writeFile("./txt/1.txt", "写入内容", err => {
// if (err) console.log(err.message);
// else console.log("写入成功");
// })
// 实际运行, 以cmd中, node命令所在路径拼接这个相对路径
// 3. 解决方案
// nodejs有个内置的变量 __dirname 返回当前文件的文件夹的绝对路径
console.log(__dirname);
fs.writeFile(path.join(__dirname, "./txt/1.txt"), "写入内容", err => {
if (err) console.log(err.message);
else console.log("写入成功");
})
查询字符串(id=1&name=zs&age=20)处理模块
const querystring = require("querystring"); // 引用内置核心模块 querystring
// 1. 模拟前台url形参过来的字符
let urlStr = "id=5&page=10&bookname=西游记";
// 2. 调用parse方法格式化成对象
let queryObj = querystring.parse(urlStr);
console.log(queryObj); // { id: '5', page: '10', bookname: '西游记' }
console.log(queryObj.id);
// 总结: 可以用于解析key=value&key=value格式的字符串
上网, 客户端, 服务器
上网: 访问服务器, 获取和消费资源
客户端: 可以浏览和使用资源的终端设备, 例如电脑, 手机等
服务器: 用于提供资源的24开机, 性能和带宽要求很高的计算机 (无显示器)
服务器磁盘上的文件, 本身不能被直接访问, 需要通过web服务才可以把磁盘上的文件返回给客户端查看
HTTP模块
http模块 是 node 核心自带的, 用于搭建web服务功能 (服务就是一堆代码实现功能)
URL地址
url地址: 协议://域名或IP:端口号/文件夹路径/文件?参数
总结: 127.0.0.1(本机) -> 需要加上端口号(访问哪个功能) -> 路径和资源目标
终端里: ping 域名, 可以得到对应的地址
使用http模块搭建Web服务器
创建 Web 服务器的步骤
// 1. 引入 - 核心http模块 (核心内置 - node自带的)
const http = require('http');
// 2. 创建服务对象
const server = http.createServer(); // create创建、server服务器
// 3. 给server对象注册请求(request)事件,监听浏览器的请求。只要有浏览器的请求,就会触发该事件处理函数
server.on('request', () => {
console.log('我发现你的请求了,但是不想搭理你, 不响应返回任何结果');
});
// 4. 设置端口,开启服务, 成功触发一次函数体执行
server.listen(3000, () => {
console.log('服务器启动了');
});
// cmd终端 - 执行这个文件 (注意路径)
// 现象: 发现光标一直在闪烁, 代表代码没执行完(如果有路径>就代表上一个命令执行完了, 等待你下一个命令输入)
// 原因: 这是因为终端里正在有一个进程启动着呢(这是因为http会启动一个进程来监测别人的请求 - 相当于开机了一个服务器)
// 注意: 如果终端关闭就相当于web服务关闭了
// 用浏览器/能发起请求的工具
// 前台发起请求 -> 后台控制台打印但是并未作出响应, 所以前台转圈 等待后台的响应返回
// 服务器的最终目的是要根据请求做出响应
// ctrl+c 停止当前的进程
当收到浏览器的请求后,会触发request事件的处理函数( request 和 response参数对象)
// 代码片段
server.on('request', function (req, res) {
// 遇到前端发来的请求动作, 这个函数执行
})
形参res
响应代码
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => { // req(请求对象), res(响应对象)
// 请求req: 前端发送过来的一切东西
// 响应res: 服务器给浏览器返回的响应内容,可以通过该对象设置
res.setHeader("Content-Type", "text/html; charset=utf-8;"); // 告诉前端, 我给你返回的内容是html文本, 编码格式是UTF-8请你这样去解析我给你返回的内容
res.statusCode = 200; // 设置本次响应的状态码 (200 ok)
res.end('响应的内容'); // 返回给前台响应内容, 并结束本次响应(一定要放在最下面)
});
server.listen(3000, () => {
console.log('服务器启动了');
});
// 注意如果你当前3000的端口被使用, 再开启一个js作为服务器就会报错, 3000端口已经被占用
注意
server.on('request', function (req, res) {
// 形参req 是 请求request的意思,所有和请求相关的信息,都在req对象中
})
形参req
代码实例
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
console.log(req.method); // 获取前端使用的请求方式 GET
console.log(req.url); // 获取前端使用的请求地址(从域名和端口往后的部分)
console.log(req.headers); // 获取前端使用的请求头
res.end();
});
server.listen(3000, () => {
console.log('服务器启动了');
});
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
console.log(req.method); // POST
console.log(req.url);
console.log(req.headers);
// 接受参数
let str = ''; // 定义一个用于保存数据的空字符串
req.on('data', (chunk) => { // 给req注册data事件,只要有数据提交过来,就会触发;用于接收提交过来的数据(数据过大, 会多次触发, 接收字节)
str += chunk; // 拼接到变量上
});
req.on('end', () => { // 给req注册end事件,当完全接收了提交过来的数据,就会触发
console.log(str);
});
res.end(str);
});
// 前端请求插件: 发送POST请求和参数
server.listen(3000, () => {
console.log('服务器启动了');
});
需求: 后端监测前端发送的请求方式, 以及请求的url, 以及请求的参数
功能: GET /api/list 后台把数据返回给前端
功能: POST /api/add 前端把参数key=value&key=value字符串发到后台, 保存到数组里
const http = require('http');
const querystring = require("querystring");
const server = http.createServer();
let arr = []; // 不要写在下面的函数里, 不然每次请求都会初始化, 但是这个js重启代码会重新执行清空数组
server.on('request', (req, res) => {
let { method, url } = req; // 提取请求方式, 和请求地址
res.setHeader("Content-Type", "text/html; charset=utf-8;"); // 设置响应头, 返回的是中文
// 1. GET方式 - 查询所有数据
if (method == "GET" && url == "/api/list") {
res.end(JSON.stringify({ // 把数据响应回给前端
status: 200,
msg: "获取成功",
data: arr
}));
} else if (method == "POST" && url == "/api/add") { // 接收前端发来的数据, 组织格式保存到数组里
// POST方式 - 添加数据到arr数组里 (要求前台传递一个JSON字符串数据)
let str = '';
req.on('data', (chunk) => {
str += chunk;
});
req.on('end', () => {
// 直接用queryString可以把key=value&key=value字符串转成对象格式
let obj = querystring.parse(str.replace("?", ""));
arr.push(obj); // 把发来的数据对象保存到全局 数组里
res.end(JSON.stringify({
status: 201,
msg: "添加成功"
}));
});
} else {
res.end("请确认接口地址和对应的请求方式是否正确");
}
});
server.listen(3000, () => {
console.log('服务器启动了, http://127.0.0.1:3000');
});
网址 -> 服务器响应 -> 返回HTML等资源内容 (这也是上网的过程)
网页访问过程:
注意: 浏览器上什么都没有, 都是通过网址来访问, 网址对应的服务器会返回内容给浏览器渲染展示
浏览器请求html / css文件 / js文件 / 图片文件 - 都是要从服务器上请求的
// 本文件讲解, 浏览器请求url后, 服务器是如何把网页和静态资源返回给浏览器显示的
// 除了json字符串, html/css/js/图片都是资源, 也是由服务器返回给浏览器的 - 注意后端要设置响应头告诉浏览器返回数据的类型, 浏览器好解析
const fs = require('fs');
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
// 1. req.method - 获取前端使用的请求方式
// 2. req.url - 获取前端使用的请求地址(从域名和端口往后的部分)
// 3. req.headers - 获取前端使用的请求头
// url随便写(根据前端地址栏决定), 但是磁盘地址必须是相对/绝对路径
if (req.url == "/clock/clock.html") {
fs.readFile(__dirname + "/静态资源clock/clock.html", (err, data) => {
res.setHeader("Content-Type", "text/html; charset=utf-8;");
res.end(data);
})
} else if (req.url == "/clock/clock.css") {
fs.readFile(__dirname + "/静态资源clock/clock.css", (err, data) => {
res.setHeader("Content-Type", "text/css"); // 返回的是css代码
res.end(data);
})
} else if (req.url == "/clock/clock.js") {
fs.readFile(__dirname + "/静态资源clock/clock.js", (err, data) => {
res.setHeader("Content-Type", "text/javascript"); // 返回的是js代码
res.end(data);
})
} else if (req.url == "/clock/a.jpg") {
fs.readFile(__dirname + "/静态资源clock/a.jpg", (err, data) => {
res.setHeader("Content-Type", "image/jpeg"); // 返回的是图片数据
res.end(data);
})
} else { // 如果上面都未匹配, 则这里执行
res.statusCode = 404;
res.statusMessage = "Not Found"
res.end();
}
});
server.listen(3000, () => {
console.log('服务器启动了');
});
// 总结:
// MIME类型, 就是后台返回内容, 设置的内容类型的固定字段
// 更多的MIME类型: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
一台服务器, 既能返回JSON字符串数据, 也能返回静态资源(html/css/js/图片) 给前端使用
const fs = require('fs');
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
if (req.url == "/index.html") { // 此接口返回的是网页
fs.readFile(__dirname + "/带Ajax请求的网页/index.html", (err, data) => {
res.setHeader("Content-Type", "text/html; charset=utf-8;");
res.end(data);
})
} else if (req.url == "/api/list") { // 此接口返回数据
res.end(JSON.stringify({
status: 200,
msg: "获取成功",
data: [{
name: "小明",
age: 39,
sex: "男"
}, {
name: "小爱",
age: 20,
sex: "女"
}]
}));
} else {
res.statusCode = 404;
res.statusMessage = "Not Found"
res.end();
}
});
server.listen(3000, () => {
console.log('服务器启动了');
});
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!