【Node.js】初探node
一、Node基本概念
01. 基本概念
- 略…
02. 基本组成
Node.js
是由ECMAScript
及Node
环境提供的一些附加API
组成的,包括文件、网络、路径等一些更加强大的APl
03. 安装与使用
-
安装
-
使用:命令行运行
js
文件node index.js
二、模块化开发
01. JS文件的不足之处
-
文件依赖
-
命名冲突
02. 开发规范
-
Node.js
规定:- 一个
JavaScrip
文件就是一个模块 - 模块内部定义的变量和函数默认情况下在外部无法得到
- 一个
-
在模块内部:
- 使用
exports.属性名
对象进行成员导岀 - 使用
module.exports.属性名
导出成员 - 使用
require
方法导入其他模块require
方法返回模块导出的exports
对象
- 使用
03. 模块化
-
一个功能就是一个模块,多个模块可以组宬完整应用,抽离一个模块不会影响其他功能的运行
-
语法示例:
// first.js let number = 10; let say = arg => console.log(arg); // 导出成员,使用 exports exports.number = number; exports.say = say; // 这里的exports.number是exports的一个属性名,可以设置其他值,然后把number值赋值给了exports的这个属性
// second.js // 导入模块first.js,使用 require const first = require("./first.js"); // 注意模块路径 // 可以调用模块first.js中的属性、方法 first.number; // 结果:10 first.say("Hello"); // 结果:Hello
-
exports
和module.exports
的区别exports
是module.exports
的别名(地址引用关系)- 当重新赋值后,如果
exports
对象和moudle.exports
对象指向的不是同一个对象时,以module.exports
为准
三、系统模块
Node
运行环境提供的API
为系统模块
01. 文件模块fs
file system:文件操作系统
- 引入模块:
const fs = require("fs");
【特别注意】
- 文件模块fs的所有方法都是异步的
- 可以加上
Sync
转为同步
(1)读取文件内容
-
语法结构
fs.readFile("要读取的文件", ["文件编码"], callback);
callback
:获取文件读取结果,错误优先回调函数
-
语法示例
fs.readFile("./index.js","utf8",(error,doc) =>{ // 如果文件读取发生错误,error 的值为一个对象,内部包含错误信息 if(error){ res.end("文件读取失败"); throw error; // 在此处抛出错误 } // 文件读取成功,则 error 的值为 null // doc 参数为文件内容 res.end("文件读取成功!!"); });
(2)写入文件内容
-
语法结构
fs.writeFile("要写入的文件", "要写入的数据", callback);
- 写入文件:有则写入,没有则创建并写入
- 【注意】:第二个参数是写入的内容
-
语法示例
fs.writeFile("./demo.txt", "即将写入的内容", (error) => { if(error){ res.end("文件写入失败"); throw error; } res.end("文件写入成功!!"); });
02. 路径模块
-
引入模块
const path = require("path");
-
作用:进行路径拼接
const myPath = path.join("路径1", "路径2", ...);
-
绝对路径:使用
__dirname
获取当前文件所在的绝对路径
03. HTTP模块
-
引入模块:
const http = require("http");
-
使用模块:
http.createServer( (req,res) => { // req,res的处理 });
-
req
:获取客户端传递的信息-
req.url
:获取url
-
req.method
:获取客户端提交信息的方式“GET”、“POST”【注意】是大写的
-
-
res
:给浏览器的响应信息-
res.writeHead(状态码,{...})
:设置响应头res.write(400,{ "content-type": "text/html;charset=utf8" }); // 200 是状态码 // "content-type"是文件编码类型
-
res.write()
:在页面输出内容 -
res.end()
:结束响应必须的,只能有一个
因为结束响应后,便不再响应消息了
res.end("响应结束"); // 可在里面添加字符串 // 等价于 res.write("响应结束"); res.end();
-
-
04. URL模块
-
引入模块
const url = require("url");
-
作用:解析客户端提交的URL信息
url.parse(myUrl);
- 使用
url.parse()
,可以解析URL信息为对象
url.parse(myUrl,true).query;
- 添加第二个参数
true
,可以将URL对象里的query
属性中的数据从【字符串】转换为【对象】
url.parse(req.url).pathname;
- 请求路径,不包含参数,端口号后面的路径
- 使用
四、第三方模块
01. 获取方法
(1)基本概念
- 具有特定玏能的、能直接使用的模块即第三方模块,又称为包
- 两种形式
js
文件形式,提供实现项目具体功能的API接口- 命令行工具形式,辅助项目开发
(2)下载安装
-
npm
(node package manager):node
的第三方模块管理工具 -
下载
npm install 模块名称
默认下载到当前工作目录
-
删除
npm uninstall 模块名称
02. 关于nrm
-
作用:用于切换
npm
下载地址 -
使用:
npm install nrm -g
- 下载并安装,
-g
是全局安装
nrm ls
- 查询可用下载地址列表
nem use 查询到的下载地址
- 切换要下载的地址
- 下载并安装,
03. 关于nodemon
-
作用:可以在文件修改后,自动执行文件
-
使用:
- 用
nodemon
命令替代node
- 使用
Ctrl+c
终止操作
nodemon app.js
- 用
04. 关于gulp
(详情看后文~~~)
五、package.json
01. node.js
产生的问题
- 使用node,会产生许多文件夹,过于零碎
- 模块之间的依赖关系
02. 关于package.json
文件
-
项目描述文件,记录了当前项目信息
- 例如项目名称、版本、当前项目依赖了哪些第三方模块等
-
使用
npm init
命令生成 -
语法结构:
{ "name": "test-project", "version": "1.0.0", "description": "A Vue.js project", "main": "src/main.js", "private": true, "scripts": { "test": "npm run unit" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "vue": "^2.5.2" }, "devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.22.1", } }
-
语法释义:
name
设置项目的名称version
表明了项目的版本description
是项目的简短描述main
设置了项目的主入口点private
如果设置为true
,则可以防止应用程序/软件包被意外地发布到npm
scripts
定义了一组可以运行的 node 脚本命令别名keywords
:关键字,用关键字描述项目author
:项目作者license
:项目协议dependencies
设置项目所依赖的第三方模块devDependencies
设置了作为开发依赖安装的npm
软件包的列表
03. 项目依赖与开发依赖
-
项目依赖:
dependencies
其他人使用时,输入
npm install --production
,会自动到项目根目录下找package.json
,在文件中找到dependencies
,然后安装下面对应的第三方模块 -
开发依赖:
devDependencies
在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖
使用
npm install
,--save-dev
命令,将包添加到package.json
文件的devDependencies
字段中
04. 关于package-lock.json
文件
-
作用:
- 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题
- 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可
六、模块加载机制
01. 在node.js
中是如何查找(加载)模块的?
(1)当模块【拥有路径】但【没有后缀】时
require
方法根据模块路径査找模块,如果是完整路径,直接引入模块- 如果模块后缀省略,先找同名
JS
文件,没有再找同名JS
文件夹- 如果找到了同名文件夹,找文件夹中的
index.js
- 如果文件夹中没有
index.js
就会去当前文件夹中的**package.json
文件中查找main
选项中的入口文件**- 如果找指定的入口文件不存在,或者没有指定入口文件就会报错,模块没有被找到
(2)当模块【没有路径】且【没有后缀】时
Node.js
会假设它是系统模块Node.js
会去node modules
文件夹中- 首先看是否有该名字的
JS
文件- 没有,则再看是否有该名字的文件夹
- 如果是文件夹,看里面是否有
index.js
- 如果没有
index.js
,查看该文件夹中的package.json中
的main
选项,确定模块入口文件- 否则找不到报错
七、第三方模块Gulp
01. gulp
的基本概念
- 基于node平台开发的前端构建工具
- 将机械化操作编写成任务
- 想要执行机械化操作时执行命令行任务就能自动执行了
- 作用:
- HTML+CSS+JS文件压缩合并
- 语法转换
- 公共文件抽离
- 修改文件后,浏览器自动刷新
02. gulp
的使用方法
(1)下载
npm install gulp
(2)创建
- 在项目根目录下建立
gulpfile.js
文件 - 重构项目的文件夹结构
src
目录:源代码文件dist
目录:构建后文件
(3)方法
- 在
gulpfile.js
文件中编写任务 - 在命令行工具中执行
gulp
任务
(4)使用
-
基本方法
gulp.task()
:建立gulp
任务gulp.src()
:获取任务要处理的文件gulp.dest()
:输出文件gulp.watch()
:监控文件的变化
-
语法示例
// 引入 gulp模块 const gulp = require("gulp"); // 建立任务,两个参数:任务名+回调函数 // gulp.task("任务名",callback) gulp.task("first", () => { //获取处理文件 gulp.src(".src/css/style.css"); //将处理的文件输出,必须把要处理的代码写在.pipe里 .pipe(gulp.dest("./dist/css")); })
- 执行任务
gulp first
03. gulp
的插件
-
常用插件
-
gulp-htmlmin
:html
文件压缩 -
gulp-csso
:压缩CSS
-
gulp-babel
:JavaScript
语法转化 -
gulp-less
:less
语法转化 -
gulp-uglify
:压缩混淆JavaScript
-
gup-file-include
:公共文件包含-
将公共部分提取出来单独作为一个
html
文件 -
在要使用这个公共部分的
html
文件里引回来// @@include("文件"); @@include("./common/header.html");
-
-
browsersync
:浏览器实时同步
-
-
使用方法
// npm install ~ // 压缩html文件 const htmlmin = require("gulp-htmlmin"); // 创建任务 gulp.task("gulp_html",function(){ // 获取处理文件 gulp.src("./index.html"); // 压缩文件 .pipe(htmlmin({collpaseWhitespace: true})); //这一句是压缩空格、换行 // 输出文件 .pipe(gulp.dest("./dist")); });
// 压缩CSS、less const csso = require("gulp.csso"); const less = require("gulp.less"); //创建任务 gulp.task("gulp_css",() =>{ // 获取处理文件 gulp.src(["./src/css/*.less","./src/css/*.css"]); // 数组形式,获取多个,*.css选择所有CSS文件 // 转换less语法 .pipe(less()); // 压缩CSS文件 .pipe(css0()); // 输出文件 .pipe(gulp.dest("./dist/css")); });
// js任务 const uglify = require("gulp.uglify"); // 压缩 const babel = require("gulp.babel"); //转换ES6 // 创建任务 gulp.task("gulp_js",() =>{ // 获取 gulp.src("./src/js/*.js"); // 转换语法 .pipe(babel({ // 判断当前代码运行环境,将代码转换为当前运行环境所支持的代码 presets: ["@babel/env"] })) // 压缩JS文件 .pipe(uglify()); // 输出 .pipe(gulp.dest("./dist/js")); });
// 构建任务,一次执行多个任务 // 创建任务:default gulp.task("default",["gulp_html", "gulp_css", "gulp_js"]); // 使用:gulp default // 或者 gulp (会直接执行任务名为default的任务)
八、搭建HTTP服务器
01. 服务器基本概念
02. 如何创建服务器
-
语法示例
// 引用http模块 const http = require("http"); // 创建web服务器 const app = http.createServer(); // 当客户端发送请求的时候 // 用 on 监听请求事件 request app.on("request", (req, res) => { res.end("<h1>服务器创建成功!</h1>"); }); // 监听3000端口 app.listen(3000); console.log("服务器已启动,监听3000端口,请访问--localhost:3000");
九、HTTP请求与响应处理
01. 关于HTTP协议
- 超文本传输协议(英文:
Hyper Text Transfer Protocol
,缩写:HTTP)- 规定了如何从网站服务器传输超文本到本地浏览器
- 它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和响应的标准
02. 关于报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式
(1)请求报文
-
请求方式:GET、POST
req.method
:获取客户端发送请求的方式req.headers
:获取请求报文req.url
:获取请求地址
(2)响应报文
-
语法结构
res.writeHead(http状态码,{ "content-type": "内容类型" });
-
HTTP状态码:
200:请求成功
404:请求的资源没有被找到
500:服务器端错误
400:客户端请求有语法错误
-
内容类型
text/html
text/css
application/javascript
image/jpeg
application/json
-
04. 关于请求参数
客户端向服务器发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端
(1)GET请求参数
-
参数被放置在浏览器地址栏中的
?
后面,每个参数一&
符号分隔- 以键值对的形式存在
-
使用
url
系统模块方法获得:const url = require("url"); // 要解析的url地址:req.url // 将查询参数解析成对象形式 url.parse(req.url,true).query;
(2)POST请求参数
-
参数被放置在请求体中进行传输
-
获取
POST
参数需要使用data
事件和end
事件req.on()
监听事件是异步 -
使用
querystring
系统模块将参数转换为对象格式// 导入系统模块querystring,用于将HTTP参数转换为对象格式 const querystring = require("querystring"); app.on("request",(req,res)=>{ // post参数是通过事件的方式接受的 // data 当请求参数传递的时候 // end 当参数传递完成的时候 let postParams = ""; // 监听参数开始传输事件:data req.on("data",params => { postParams += params; }); // 监听参数传输完毕事件:end req.on("end",() =>{ console.log(querystring.parse(postParams)); }); res.end("post请求完成"); })
05. 路由的概念
-
路由是指客户端请求地址与服务器端程序代码的对应关系
简单的说,就是请求什么就响应什么
-
使用方法
-
引入系统模块
http
-
创建网站服务器
-
为网站服务器对象添加请求事件
-
实现路由功能
-
获取客户端请求方式
-
获取客户端请求地址
-
const http = require("http"); const url = require("url"); const app = http.createServer(); app.on("request",(req.res)=>{ // 获取请求方式 const method = req.method; // 获取请求地址 const pathname = url.parse(req.url).pathname; if(method == "get"){ if(pathname == "/" || pathname =="/index"){ res.end(); }else if(pathname == "/list"){ res.end(); }else{ res.end(); } }else if(method == "post"){ // ... } }); app.listen(3000);
-
06. 静态资源的访问
-
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源
例如CSS、JavaScript. image,html文件
动态资源:相同的请求地址,不同的传递参数,会有不同的响应资源,这种资源就是动态资源
-
访问方法
用当前文件的绝对路径+文件夹的路径+请求资源的url
目的是:让用户不需要输入文件夹路径,直接输入html页面名称即可访问到
-
语法示例
// 首先获取当前页面url let pathname = url.parse(req.url).pathname; // 如果页面为 "/",则让它默认显示为 "index.html"页面 pathname = pathname == "/" ? "/index.html" : pathname; // 再用当前文件的绝对路径+文件夹的路径+请求资源的url路径,即为要访问的页面的绝对路径 let staticPath = path.join(__dirname,"public" + pathname); res.write("当前页面地址是:" + staticPath); //在页面写入静态资源的地址
十、Nodejs异步编程
01. 同步API和异步API
-
同步API:只有当前API执行完成后,才能继续执行下一个API
- 从返回值中拿到执行的结果
-
异步API:当前API的执行不会阻塞后续代码的执行
-
从回调函数中拿到执行结果
-
容易出现回调地狱
-
02. 第一种方法——Promise
-
目的:解决
Node.js
异步编程中回调地狱的问题 -
语法结构:
实际上是一个构造函数,所以需要
new
把异步函数写在Promise内部的回调函数中
let promise = new Promise((resolve,reject)=>{ if(true){ // 传出成功结果 resolve(result); }else{ // 传出失败信息 reject(error); } });
-
两个参数
resolve
:一个函数,当异步API有执行结果时,传出这个结果reject
:一个函数,当异步API执行失败时,传出失败信息
-
结果处理(Promise提供链式编程)
- 结果是包含在
Promise
对象里面的,通过then()
和catch()
获取对象里面的内容
promise.then( (data) => { console.log(data); }).catch( (error) => { console.log(error); });
- 结果是包含在
-
-
让异步函数顺序执行的方法
首先,将各个异步函数分别放入
Promise
的回调函数中然后,把
Promise
实例放入函数中,作为返回值返回最后,使用
Promise
的结果处理方式依次处理function p1(){ // 返回Promise实例 return new Promise(...); } function p2(){ return new Promise(...); } // 处理结果 p1().then((result) => { console.log(result); // 这里为了满足链式编程的需求,需要返回下一个执行的函数 return p2(); }).then((result) => { console.log(result); })
03. 第二种方法——async
-
使用
async
,使同步函数变为异步函数-
语法结构:
- 在普通函数定义的前面加上
async
关键字
const fun = async() =>{ // 函数体 }; // 或者 async function fun(){ // 函数体 }
-
默认返回值就是
Promise
对象(省去了Promise
对象的创建)-
如果手动替换了
return
返回值,返回值也会被包含在Promise
对象中 -
如果发生错误,则需要
throw
来抛出异常throw
后面的内容不会执行了async function fun(){ throw "发生错误了"; }
-
- 在普通函数定义的前面加上
-
-
使用
await
使异步函数变成同步函数-
只能出现在异步函数中
-
后面只能写
Promise
对象 -
作用:暂停异步函数向下执行,直到
Promise
返回结果await promise; // 等价于 promise.then();
-
-
语法示例:
// promisify 可以改造现有的异步API,让其返回值是 Promise对象,从而支持异步语法 const promisify = require("util").pomisify; // 用 promisify 处理[异步函数],得到[新的异步函数],这个新的异步函数的返回值就是Promise对象 const readFile = promisify(fs.readFile); // 统一执行异步函数 async function run(){ // await 写在异步函数内,后面跟Promise对象,返回Promise对象内部的信息 let p1 = await readFile(); let p2 = await readFile(); } run();