Node是一个基于Chrome V8引擎的Javascript代码运行环境。
什么是V8引擎?
V8引擎是一款专门解释和执行JS代码的虚拟机, 任何程序只要集成了V8引擎都可以执行JS代码
例如:
1、V8引擎嵌入到浏览器中,那么我们写的JavaScript代码就会被浏览器所执行;
2、将V8引擎嵌入到NodeJS中,那么我们写的JavaScript代码就会被NodeJS所执行。
什么是运行环境?
运行环境 就是 生存环境
地球是人类的生存环境
浏览器是网页的生存环境
windows是.exe应用程序的生存环境
Android是.apk应用程序的生存环境
也就是说运行环境就是特定事物的生存环境
NodeJS也是一个生存的环境, 由于NodeJS中集成了V8引擎
所以NodeJS是JavaScript应用程序的一个生存环境
总结
NodeJS不是一门编程语言, NodeJS是一个运行环境,由于这个运行环境集成了V8引擎, 所以在这个运行环境下可以运行我们编写的JS代码, 这个运行环境最大的特点就是提供了操作"操作系统底层的API",通过这些底层API我们可以编写出网页中无法实现的功能(诸如: 打包工具, 网站服务器等)
失败原因:系统账户权限不足。
解决方法:
1、以管理员身份运行powershell命令工具。
2、输入运行安装包命令 msiexec / package node 安装包位置。
失败原因:Node安装目录写入环境变量失败。
解决方法:将Node安装目录添加到环境变量中。
存储系统中的目录,在命令行中执行命令的时候系统会自动去这些目录中查找命令的位置。
Node程序执行方式:
NodeJS环境和浏览器环境一样都是一个JS的运行环境, 都可以执行JS代码.
但是由于宿主不同所以特点也有所不同
所有ECMAScript语法在Node环境中都可以使用。
在Node环境下执行代码,使用Node命令执行后缀为.js的文件即可。如:node test.js
在浏览器中全局对象是window,在Node中全局对象是global。
和浏览器一样Node环境中的全局对象也提供了很多方法属性供我们使用。Node中全局对象下有以下方法,可以在任何地方使用,global可以省略。
中文文档地址: http://nodejs.cn/api/
什么是模块?
// a.js
// 在模块内部定义变量
let version = 1.0;
// 在模块内部定义方法
const sayHi = name => `${name}`;
// 向模块外部导出数据
exports.version = version;
exports.sayHi = sayHi;
在NodeJS中想要导出模块中的变量函数有三种方式:
注意点:
无论通过哪种方式导出, 使用时都需要先导入(require)才能使用
通过global.xxx方式导出不符合CommonJS规范, 不推荐使用。
// b.js
// 在b.js模块中导入模块a
let a = require('./b.js');
// 输出b模块中的version变量
console.log(a.version);
// 调用b模块中的sayHi方法,并输出其返回值
console.log(a.sayHi('lgg'));
导入模块时后缀可以省略
require注意点:
module.exports.version = version;
module.exports.sayHi = sayHi;
注意点:
在企业开发中无论哪种方式都不要直接赋值, 这个问题只会在面试中出现。
Node运行环境提供的API,因为这些API都是以模块化的方式进行开发的,所以我们又称Node运行环境提供的API为系统模块。
f: file文件,s: system系统,文件操作系统。封装了各种文件相关的操作。
注意:后面带Sync是同步方法,不带Sync是异步方法
let fs = require("fs");
let path = require("path");
/* // 1.拼接读取的路径
let str = path.join(__dirname, "lnj.txt");
// 2.创建一个读取流
let readStream = fs.createReadStream(str, {encoding : "utf8", highWaterMark : 1});
// 3.添加事件监听
readStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
readStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
readStream.on("data", function (data) {
console.log("表示通过读取流从文件中读取到了数据", data);
});
readStream.on("close", function () {
console.log("表示数据流断开了和文件的关系, 并且数据已经读取完毕了");
}); */
/*
// 1.拼接写入的路径
let str = path.join(__dirname, "it666.txt");
// 2.创建一个写入流
let writeStream = fs.createWriteStream(str, {encoding : "utf8"});
// 3.监听写入流的事件
writeStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
writeStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
writeStream.on("close", function () {
console.log("表示数据流断开了和文件的关系");
});
let data = "abcdefghijk";
let index = 0;
let timerId = setInterval(function () {
let ch = data[index];
index++;
writeStream.write(ch);
console.log("本次写入了", ch);
if(index === data.length){
clearInterval(timerId);
writeStream.end();
}
}, 1000); */
/* // 1.生成读取和写入的路径
let readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4");
// 2.创建一个读取流
let readStream = fs.createReadStream(readPath);
// 3.创建一个写入流
let writeStream = fs.createWriteStream(writePath);
// 4.监听读取流事件
readStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
readStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
readStream.on("data", function (data) {
// console.log("表示通过读取流从文件中读取到了数据", data);
writeStream.write(data);
});
readStream.on("close", function () {
console.log("表示数据流断开了和文件的关系, 并且数据已经读取完毕了");
writeStream.end();
});
// 5.监听写入流的事件
writeStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
writeStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
writeStream.on("close", function () {
console.log("表示数据流断开了和文件的关系");
}); */
// 1.生成读取和写入的路径
let readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4");
// 2.创建一个读取流
let readStream = fs.createReadStream(readPath);
// 3.创建一个写入流
let writeStream = fs.createWriteStream(writePath);
// 利用读取流的管道方法来快速的实现文件拷贝
readStream.pipe(writeStream);
封装了各种路径相关的操作。和Buffer一样,NodeJS中的路径也是一个特殊的模块。不同的是Buffer模块已经添加到Global上了, 所以不需要手动导入。而Path模块没有添加到Global上, 所以使用时需要手动导入。
let path = require("path");
// basename用于获取路径的最后一个组成部分
// let res = path.basename('/a/b/c/d/index.html');
// let res = path.basename('/a/b/c/d');
// let res = path.basename('/a/b/c/d/index.html', ".html");
// console.log(res);
// dirname用于获取路径中的目录, 也就是除了最后一个部分以外的内容
// let res = path.dirname('/a/b/c/d/index.html');
// let res = path.dirname('/a/b/c/d');
// console.log(res);
// extname用于获取路径中最后一个组成部分的扩展名
// let res = path.extname('/a/b/c/d/index.html');
// let res = path.extname('/a/b/c/d');
// console.log(res);
/*
isAbsolute用于判断路径是否是一个绝对路径
注意点:
区分操作系统
在Linux操作系统中/开头就是绝对路径
在Windows操作系统中盘符开头就是绝对路径
在Linux操作系统中路径的分隔符是左斜杠 /
在Windows操作系统中路径的分隔符是右斜杠 \
* */
// let res = path.isAbsolute('/a/b/c/d/index.html'); // true
// let res = path.isAbsolute('./a/b/c/d/index.html'); // false
// let res = path.isAbsolute('c:\\a\\b\\c\\d\\index.html'); // true
// let res = path.isAbsolute('a\\b\\c\\d\\index.html'); // false
// console.log(res);
// path.sep用于获取当前操作系统中路径的分隔符的
// 如果是在Linux操作系统中运行那么获取到的是 左斜杠 /
// 如果是在Windows操作系统中运行那么获取到的是 右斜杠 \
console.log(11, path.sep); // 11 \
// path.delimiter用于获取当前操作系统环境变量的分隔符的
// 如果是在Linux操作系统中运行那么获取到的是 :
// 如果是在Windows操作系统中运行那么获取到的是 ;
console.log(path.delimiter); // ;
/*
path.parse(path): 用于将路径转换成对象
{
root: '/',
dir: '/a/b/c/d',
base: 'index.html',
ext: '.html',
name: 'index'
}
path.format(pathObject): 用于将对象转换成路径
* */
let obj = path.parse("/a/b/c/d/index.html");
console.log(obj);
/* {
root: '/',
dir: '/a/b/c/d',
base: 'index.html',
ext: '.html',
name: 'index'
} */
let obj11 = {
root: '/',
dir: '/a/b/c/d',
base: 'index.html',
ext: '.html',
name: 'index'
};
let str = path.format(obj11);
console.log(str); // /a/b/c/d\index.html
/*
path.join([...paths]): 用于拼接路径
注意点:
如果参数中没有添加/, 那么该方法会自动添加
如果参数中有.., 那么会自动根据前面的参数生成的路径, 去到上一级路径
* */
/*
// let str = path.join("/a/b", "c"); // /a/b/c
// let str = path.join("/a/b", "/c"); // /a/b/c
// let str = path.join("/a/b", "/c", "../"); // /a/b/c --> /a/b
// let str = path.join("/a/b", "/c", "../../"); // /a/b/c --> /a
// console.log(str);
*/
/*
path.normalize(path): 用于规范化路径
* */
let res11 = path.normalize("/a//b///cd/index.html");
console.log(22, res11); // \a\b\c\d\index.html
/*
path.relative(from, to): 用于计算相对路径
第一个参数: /data/orandea/test/aaa
第二个参数: /data/orandea/impl/bbb
/data/orandea/test/aaa --> ../ --> /data/orandea/test
/data/orandea/test --> ../ --> /data/orandea
..\..\impl\bbb
* */
let res111 = path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
console.log(111, res111); // ..\..\impl\bbb
/*
path.resolve([...paths]): 用于解析路径
注意点: 如果后面的参数是绝对路径, 那么前面的参数就会被忽略
* */
let res33 = path.resolve('/foo/bar', './baz');
let res44 = path.resolve('/foo/bar', '../baz');
let res55 = path.resolve('/foo/bar', '/baz');
console.log(res33); // D:\foo\bar\baz
console.log(res44); // D:\foo\baz
console.log(res55); // D:\baz
相对路径VS绝对路径
准备知识
1、计算机只能识别0和1(因为计算机只认识通电和断电两种状态),
2、所有存储在计算机上的数据都是0和1组成的(数据越大0和1就越多)
3、计算机中的度量单位
1 B(Byte字节) = 8 bit(位)
// 00000000 就是一个字节
// 111111111 也是一个字节
// 10101010 也是一个字节
// 任意8个 0或1的组合都是一个字节
1 KB = 1024 B
1 MB = 1024KB
1 GB = 1024MB
什么是Buffer?
Buffer是NodeJS全局对象上的一个类, 是一个专门用于存储字节数据的类。
NodeJS提供了操作计算机底层API, 而计算机底层只能识别0和1,所以就提供了一个专门用于存储字节数据的类。
如何创建一个Buffer对象
1、创建一个指定大小的Buffer
Buffer.alloc(size[, fill[, encoding]])
2、根据数组/字符串创建一个Buffer对象
Buffer.from(string[, encoding])
Buffer对象本质
本质就是一个数组。
注意点: 通过console.log();输出Buffer. 会自动将存储的内容转换成16进制再输出。
// let buf = Buffer.alloc(5);
// console.log(buf); //
// 注意点: 通过console.log();输出Buffer. 会自动将存储的内容转换成16进制再输出
let buf1 = Buffer.alloc(5, 7);
console.log(buf1);
// let buf = Buffer.from("abc");
// console.log(buf); //
let buf = Buffer.from([1, 3, 5]);
console.log(buf); //
console.dir(buf); // Buffer(3) [Uint8Array] [ 1, 3, 5 ]
buf[0] = 6;
console.log(buf); //
let http = require("http");
// 1.创建一个服务器实例对象
let server = http.createServer();
// 2.注册请求监听
server.on("request", function (req, res) {
// end方法的作用: 结束本次请求并且返回数据
// res.end("www.it666.com");
// writeHead方法的作用: 告诉浏览器返回的数据是什么类型的, 返回的数据需要用什么字符集来解析
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end("在在在");
});
// 3.指定监听的端口
server.listen(3000);
http.createServer(function (req, res) {
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end("在在在666");
}).listen(3000);
let http = require("http");
// 1.创建一个服务器实例对象
let server = http.createServer();
// 2.注册请求监听
/*
req对象其实是http.IncomingMessage 类的实例
res对象其实是http.ServerResponse 类的实例
* */
server.on("request", function (req, res) {
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8"
});
// console.log(req.url);
if(req.url.startsWith("/index")){
// 注意点: 如果通过end方法来返回数据, 那么只会返回一次
// res.end("首页1");
// res.end("首页2");
// 注意点: 如果通过write方法来返回数据, 那么可以返回多次
// write方法不具备结束本次请求的功能, 所以还需要手动的调用end方法来结束本次请求
res.write("首页1");
res.write("首页2");
res.end();
}else if(req.url.startsWith("/login")){
res.end("登录");
}else{
res.end("没有数据");
}
});
// 3.指定监听的端口
server.listen(3000);
/*
startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的,根据判断结果返回 true 或 false。
str.startsWith(searchString [, position]);
1、searchString:要搜索的子字符串。
2、position:在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。
var str = "To be, or not to be, that is the question.";
alert(str.startsWith("To be")); // true
alert(str.startsWith("not to be")); // false
alert(str.startsWith("not to be", 10)); // true
*/
let http = require("http");
let path = require("path");
let fs = require("fs");
// 1.创建一个服务器实例对象
let server = http.createServer();
// 2.注册请求监听
server.on("request", function (req, res) {
if(req.url.startsWith("/index")){
// let filePath = path.join(__dirname, "www", "index.html");
// let filePath = path.join(__dirname, "www", req.url);
// fs.readFile(filePath, "utf8", function (err, content) {
// if(err){
// res.end("Server Error");
// }
// res.end(content);
// });
readFile(req, res);
}else if(req.url.startsWith("/login")){
// let filePath = path.join(__dirname, "www", req.url);
// fs.readFile(filePath, "utf8", function (err, content) {
// if(err){
// res.end("Server Error");
// }
// res.end(content);
// });
readFile(req, res);
}else{
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end("没有数据");
}
readFile(req, res);
});
// 3.指定监听的端口
server.listen(3000);
function readFile(req, res) {
let filePath = path.join(__dirname, "www", req.url);
fs.readFile(filePath, "utf8", function (err, content) {
if(err){
res.end("Server Error");
}
res.end(content);
});
}
// let path = require("path");
let fs = require("fs");
let mime = require("./mime.json");
function readFile(req, res, rootPath) {
let filePath = path.join(rootPath, req.url);
// console.log(filePath);
/*
注意点:
1.加载其它的资源不能写utf8
2.如果服务器在响应数据的时候没有指定响应头, 那么在有的浏览器上, 响应的数据有可能无法显示
* */
let extName = path.extname(filePath);
let type = mime[extName];
if(type.startsWith("text")){
type += "; charset=utf-8;";
}
res.writeHead(200, {
"Content-Type": type
});
fs.readFile(filePath, function (err, content) {
if(err){
res.end("Server Error");
}
res.end(content);
});
}
exports.StaticServer = readFile;
let http = require("http");
let path = require("path");
// let fs = require("fs");
// let mime = require("./mime.json");
let ss = require("./15-StaticServer.js");
// 1.创建一个服务器实例对象
let server = http.createServer();
// 2.注册请求监听
server.on("request", function (req, res) {
// readFile(req, res);
// let rootPath = path.join(__dirname, "www");
let rootPath = "C:\\Users\\Desktop\\abc";
ss.StaticServer(req, res, rootPath);
});
// 3.指定监听的端口
server.listen(3000);
/*
function readFile(req, res) {
let filePath = path.join(__dirname, "www", req.url);
// console.log(filePath);
// 注意点:
// 1.加载其它的资源不能写utf8
// 2.如果服务器在响应数据的时候没有指定响应头, 那么在有的浏览器上, 响应的数据有可能无法显示
let extName = path.extname(filePath);
let type = mime[extName];
if(type.startsWith("text")){
type += "; charset=utf-8;";
}
res.writeHead(200, {
"Content-Type": type
});
fs.readFile(filePath, function (err, content) {
if(err){
res.end("Server Error");
}
res.end(content);
});
}
*/
定义一:前面说过在编写代码的时候尽量遵守单一原则,也就是一个函数尽量只做一件事情。例如: 读取数据函数/写入数据函数/生成随机数函数等等。不要一个函数既读取数据又写入数据又生成随机数,这样代码非常容易出错, 也非常难以维护。在模块化开发中也一样, 在一个模块(一个文件中)尽量只完成一个特定的功能,但是有些比较复杂的功能可能需要由多个模块组成,例如: jQuery选择器相关的代码在A模块, CSS相关的代码在B模块, …我们需要把这些模块组合在一起才是完成的jQuery那么这个时候我们就需要一个东西来维护多个模块之间的关系。这个维护多个模块之间关系的东西就是"包"。
定义二:别人写好的、具有特定功能的、我们能直接使用的模块即第三方模块,由于第三方模块通常都是由多个文件组成并且被放置在一个文件夹中,所以又名包
第三方模块有两种存在形式:
简而言之: 一个模块是一个单独的文件, 一个包中可以有一个或多个模块
在NodeJS中为了方便开发人员发布、安装和管理包, NodeJS推出了一个包管理工具NPM(Node Package Manager)。NPM不需要我们单独安装, 只要搭建好NodeJS环境就已经自动安装好了。NPM就相当于电脑上的"QQ管家软件助手",,通过NPM我们可以快速找到我们需要的包,可以快速安装我们需要的包, 可以快速删除我们不想要的包等等。
npmjs.com: 第三方模块的存储和分发仓库。
npm(node package manager): node的第三方模块管理工具。
NPM包安装方式
全局安装 (一般用于安装全局使用的工具, 存储在全局node_modules中)
npm install -g 包名 (默认安装最新版本)
npm uninstall -g 包名
npm update -g 包名 (更新失败可以直接使用install)
本地安装 (一般用于安装当前项目使用的包, 存储在当前项目node_modules中)
npm install 包名
npm uninstall 包名
npm update 包名
初始化本地包
npm init -> 初始化package.json文件
npm init -y -> 初始化package.json文件
npm install 包名 --save
npm install 包名 --save-dev
包描述文件 package.json, 定义了当前项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。
npm install 命令根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境
注意点:package.json文件中, 不能加入任何注释。
1、将项目拷贝给其它人, 或者发布的时候, 我们不会将node_modules也给别人, 因为太大。
2、因为有的包可能只在开发阶段需要, 但是在上线阶段不需要, 所以需要分开指定。
npm i 所有的包都会被安装
npm i --production 只会安装dependencies中的包
npm i --development 只会安装devDependencies中的包
nodemon是一个命令行工具,用以辅助项目开发。
在Node.js中,每次修改文件都要在命令行工具中重新执行该文件,非常繁琐。
1、使用npm install nodemon -g 下载它
2、在命令行工具中用nodemon命令代替node命令执行文件。
nrm(npm registry manager): npm下载地址切换工具。
由于npm默认会去国外下载资源, 所以对于国内开发者来说下载会比较慢,所以就有人写了一个nrm工具, 允许你将资源下载地址从国外切换到国内。
使用步骤:
npm install -g nrm 安装NRM
nrm --version 查看是否安装成功
npm ls 查看允许切换的资源地址
npm use taobao 将下载地址切换到淘宝
PS:淘宝资源地址和国外的地址内容完全同步,。淘宝镜像与官方同步频率目前为 10分钟 一次以保证尽量与官方服务同步。
由于npm默认回去国外下载资源, 所以对于国内开发者来说下载会比较慢。cnpm 就是将下载源从国外切换到国内下载, 只不过是将所有的指令从npm变为cnpm而已。
npm install cnpm -g –registry=https://registry.npm.taobao.org 安装CNPM
cnpm -v 查看是否安装成功
使用方式同npm, 例如: npm install jquery 变成cnpm install jquery 即可
什么是YARN?
Yarn是由Facebook、Google、Exponent 和 Tilde 联合推出了一个新的 JS 包管理工具
Yarn 是为了弥补 npm5.0之前 的一些缺陷而出现的。
注意点:
在npm5.0之前,yarn的优势特别明显.但是现在NPM已经更新到6.9.x甚至7.x了,
随着NPM的升级NPM优化甚至超越Yarn,所以个人还是建议使用NPM
NPM的缺陷:
1、npm install的时候巨慢
npm 是按照队列执行每个 package,也就是说必须要等到当前 package 安装完成之后,才能继续后面的安装
2、同一个项目,npm install的时候无法保持一致性
“5.0.3”表示安装指定的5.0.3版本,
“~5.0.3”表示安装5.0.X中最新的版本,
“^5.0.3”表示安装5.X.X中最新的版本
YARN优点:
YARN的安装
npm install -g yarn
yarn --version
YARN使用
1、初始化包
npm init -y
yarn init -y
2、安装包
npm install xxx --save
yarn add xxx
npm install xxx --save-dev
yarn add xxx --dev
3、移除包
npm uninstall xxx
yarn remove xxx
4、更新包
npm update xxx
yarn upgrade xxx --latest
5、全局安装
npm install -g xxx
npm uninstall -g xxx
npm update -g xxx
yarn global add xxx
yarn global upgrade xxx
yarn global remove xxx
基于node平台开发的前端构建工具,将机械化操作编写成任务,想要执行机械化操作时,执行一个命令行命令任务就能自动执行了。用机器代替手工,提高开发效率。
1、Gulp能做什么
2、Gulp中提供的方法
const gulp = require('gulp');
// 使用gulp.task()方法建立任务
gulp.task('lgg', () => {
// 获取要处理的文件
gulp.src('./src/css/base.css')
// 将处理后的文件输出到dist目录
.pipe(gulp.dest('./dist/css')) // pipe通道
})
3、Gulp插件
1、文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度会很慢很慢。
2、复杂的模块依赖关系需要被记录,确保模版的版本和当前保持一致,否则会导致当前项目运行报错。
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、GitHub地址、当前项目依赖了哪些第三方模块等。使用npm init -y(-y默认生成)命令生产。
{
"dependencies": {
"jquery": "^3.3.0"
}
}
{
"devDependencies": {
"gulp": "^4.0.0"
}
}
require('./lgg.js');
require('./lgg')
1、require方法根据模块路径查找模块,如果是完整路径,直接引入模块。
2、如果模块后缀省略,先找同名JS文件再找同名JS文件夹。
3、如果找到了同名文件夹,找文件夹中的index.js。
4、如果文件夹中没有index.js就会去当前文件夹中的package.json文件中查找main选项中的入口文件。
5、如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到。
require('fs');
1、Node.js会假设它是系统模块。
2、Node.js会去node_modules文件夹中。
3、首先看是否有该名字的js文件。
4、再看是否有该名字的文件夹。
5、如果是文件夹看里面是否有index.js。
6、如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件,找不到报错。