目录
1.Node.js介绍
2.安装Node.js
3.使用Node.js实现第一个服务器
3.1初步感受Node.js
3.2Google Chrome 默认非安全端口列表,尽量避免以下端口。
3.3nodemon自动监控服务端更改
4.模块化——Node.js使用commonjs规范
4.1创建自定义模块(引入文件/文件夹,按需导出)
4.1.1引入文件
4.1.2引入文件夹
4.1.3引入node_modules里的文件夹
4.1.4描述功能性文件package.json
4.1.5 自定义模块的按需导出
4.2.2内置模块;
5.npm包管理器
5.1 npm常用指令;
5.2注册与发布npm项目
5.2.1 注册账号:
5.2.2发布包
5.2.3cnpm的安装及使用
6.fs模块(内置模块)
6.1写入文件writeFile()
6.2删除文件
6.3修改文件名
6.4读取文件
6.5复制文件
6.6自定义复制文件实现原理
6.7创建目录
6.8修改目录名
6.9读取目录中的文件及子目录
6.10删除目录
6.10判断文件或目录是否存在
6.11获取文件或目录的详细信息
6.12删除非空文件夹
7.buffer缓冲区
7.1buffer的创建:
8.stream流
8.1data:监控得到的数据,并将其分成多份
8.2 end监控文件是否读取完成
8.3pipe:管道,将得到的数据通过pipe进行写入(fs.createWriteStream())
9.包管理工具yarn
10.node.js版本管理工具NVM
10.1NVM下载
10.2安装NVM
10.3NVM常用指令
11.通过fs模块加载页面
11.1- 普通方式加载页面
11.2- 通过stream流方式加载页面
12.node+cheerio实现爬虫获取数据
13.实现新闻列表页面
本节知识要点
- - Node.js安装及使用
- - 通过Node.js搭建服务器
- - 模块化及自定义模块
- - 内置模块fs的使用
- - buffer及stream
1.Node.js介绍
- Node.js 诞生于2009年,Node.js采用C++语言编写而成,是 一个Javascript的运行环境。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 ,让JavaScript的运行脱离浏览器端,可以使用JavaScript语言 书写服务器端代码。
2.安装Node.js
? [Node.js官网](https://nodejs.org)下载稳定版本,node偶数版本为稳定版本,奇数版本为非稳定版本。
- - mac 直接安装 或者 brew来安装;windows直接点下一步安装(注意windows系统下需要配置环境变量)
- - 安装完Node.js会自动安装NPM(Node Package Manager):包管理工具;
- - 通过指令 node -v 来查看是否安装完成和查看node版本号;npm -v 来查看npm版本。
3.使用Node.js实现第一个服务器
3.1初步感受Node.js
//引入http模块
let http = require("http");
//创建一个服务器
let serve = http.createServer((req, res) => {console.log("hello");res.end("hello world");
})//设置端口号
serve.listen(3000);
- 使用快捷键ctrl + `打开VScode的终端;
- 使用node server.js(node js文件名)执行js文件;
结果:浏览器端打印hello world; 服务端打印hello
3.2Google Chrome 默认非安全端口列表,尽量避免以下端口。
1, // tcpmux
7, // echo
9, // discard
11, // systat
13, // daytime
15, // netstat
17, // qotd
19, // chargen
20, // ftp data
21, // ftp access
22, // ssh
23, // telnet
25, // smtp
37, // time
42, // name
43, // nicname
53, // domain
77, // priv-rjs
79, // finger
87, // ttylink
95, // supdup
101, // hostriame
102, // iso-tsap
103, // gppitnp
104, // acr-nema
109, // pop2
110, // pop3
111, // sunrpc
113, // auth
115, // sftp
117, // uucp-path
119, // nntp
123, // NTP
135, // loc-srv /epmap
139, // netbios
143, // imap2
179, // BGP
389, // ldap
465, // smtp+ssl
512, // print / exec
513, // login
514, // shell
515, // printer
526, // tempo
530, // courier
531, // chat
532, // netnews
540, // uucp
556, // remotefs
563, // nntp+ssl
587, // stmp?
601, // ??
636, // ldap+ssl
993, // ldap+ssl
995, // pop3+ssl
2049, // nfs
3659, // apple-sasl / PasswordServer
4045, // lockd
6000, // X11
6665, // Alternate IRC [Apple addition]
6666, // Alternate IRC [Apple addition]
6667, // Standard IRC [Apple addition]
6668, // Alternate IRC [Apple addition]
6669, // Alternate IRC [Apple addition]
3.3nodemon自动监控服务端更改
使用node js文件名,方式在node.js下启动时,如果JS文件进行了修改就必须,重启服务;
解决:使用nodemon会自动监听服务端JS文件的修改
在全局下安装nodemon的命令:npm i nodemon -g
nodemon启动:通过nodemon命令 nodemon JS文件名 启动文件后,会自动监听和重启服务器。如 nodemon index.js
4.模块化——Node.js使用commonjs规范
为什么会有模块化:
- 在JavaScript发展初期就是为了实现简单的页面交互逻辑,随着前端代码日益膨胀,JavaScript作为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码;
每个模块之间有独立的空间,从而防止模块间的变量污染
Node.js中的模块化 commonjs规范:
- CommonJS就是为JS的表现来制定规范,因为js没有模块的功能所以CommonJS应运而生,它希望js可以在任何地方运行,不只是浏览器中。
前端规范:AMD sea.js 和 CMD require.js
node.js安装完成后,就自带commonjs规范。
4.1创建自定义模块(引入文件/文件夹,按需导出)
模块化引入:可以引入文件,文件夹和node_modules文件夹下的自定义文件夹。
4.1.1引入文件
注意:当引入文件或自定义文件夹时require("./mydir")中需要加"./",当引入的是node_modules时,不能加"./",如require("node_modules")
- - 引入一个文件 形式模块:require("./JS文件名"),也可以省略.js即require("./home");
moduleA.js:
console.log("这是moduleA.js 文件");
home.js执行文件:输入命令:nodemon home.js
console.log("这是home主页JS文件");
require("./moduleA.js");
结果:执行home.js文件中的内容时也能执行引入文件moduleA.js文件中的内容
4.1.2引入文件夹
- - 引入文件夹形式模块 require("./文件夹名")
如下:home.js为执行JS文件,自身执行及引入moduleA.js的同时引入了文件夹mydir ,则在文件夹mydir中会默认去找index.js文件,发现index.js引入了a.js文件,而a.js文件引入了b.js文件,所以执行home.js时,会执行index.js a.js b.js中的内容。
home.js执行文件:
console.log("这是home主页JS文件");
require("./moduleA.js");//引入moduleA.js文件
require("./mydir");//引入文件夹mydir,就会自动查找文件夹mydir下的index.js文件
? home.js引入mydir文件夹后,会自动查找文件夹下的index.js文件执行:
index.js文件:
console.log("这是index.js文件");
require("./a");
index.js中引入了a.js文件:
console.log("这是a.js文件");
require("./b");
a.js文件中又引入了b.js文件:
console.log("这是b.js文件");
结果:所以最后结果是,会执行mydir下所有js文件,index.js a.js b.js文件
- - 当然也可以配置默认启动文件(默认index.js文件,但也可以修改),在文件夹内新建package.json来指定执行文件
{
"name":"aModule",
"version":"1.0.0",
"main":"test.js"}
4.1.3引入node_modules里的文件夹
node_modules主要针对第三方JS文件的管理。
引入node_modules里的文件夹时,文件夹不需要加"./",如,require("文件夹")
package.json:描述功能性文件。可以在里面设置node_modules中的默认执行文件,版本号,文件夹名等配置
示例:
文件目录层级:
home.js为执行文件:在home.js中引入node_modules中的文件夹mytest
require("mytest");
index.js:
console.log("这是index.js文件");
require("./a");
a.js:
console.log("这是a.js文件");
require("./b");
b.js:
console.log("这是b.js文件");
结果:
4.1.4描述功能性文件package.json
项目上线或者项目转移时,不会转移或上线node_modules文件夹,而是使用时再通过package.json去下载第三方模块。
node_modules的查找规则:向上查找。首先会在当前文件夹下查找node_modules,没有再查找上一级文件夹下有没有node_modules,再没有会找到系统根目录user文件夹下的node_modules。
引入node_modules文件夹下的自定义文件夹时,默认去执行文件夹下面的index.js。
如果想引入的文件夹默认执行文件不是index.js,而执行自定义的文件夹,需要通过package.json文件定义:
package.json文件:
{"name":"mytest","version":"1.0.0","main":"test.js"
}
test.js:
console.log("使用了package.json文件,默认引入text.js");
结果:
4.1.5 自定义模块的按需导出
通过module.exports 导出; \_\__dirname , \_\_filename
exports是module.exports的引用 :可以使用module.exports = {}进行导出,而不能使用exports = {}进行导出,要使用exports导出,只能使用exports.a 的形式。因为exports是module.exports的一个引用。
__dirname: 获得当前执行文件所在目录的完整目录名
__filename: 获得当前执行文件的带有完整绝对路径的文件名
process.cwd():获得当前执行node命令时候的文件夹目录名
- 通过module.exports 导出
Ma.js:有变量a和类Person,通过module.exports = {}将变量a和Person类进行导出
console.log("这是Ma.js文件");
let a = 10;
class Person{constructor(){this.name = "zhangsan";}hobby(){console.log("喜欢篮球");}
}
module.exports = {a,Person
}
Mb.js:使用require("./Ma.js")引入文件,将引入后的结果存起来,再获取里面的变量a和Person类
console.log("这是Mb.js文件");
let Ma = require("./Ma");
console.log(Ma.a);
let p = new Ma.Person();
p.hobby();
- 或者 通过 exports来导出。
exports是module.exports的一个引用,使用时不能使用exports = {}进行导出(不会改变module.exports的值,只会改变exports 的值就没有意义),必须使用exports.a 和exports.Person
所以上例可以改写为以下:
Ma.js:
console.log("这是Ma.js文件");
let a = 10;
class Person{constructor(){this.name = "zhangsan";}hobby(){console.log("喜欢篮球");}
}
module.exports = {a,Person
}exports.a = a;
exports.Person = Person;//解构赋值
// exports.hobby = new Person().hobby;
Mb.js::注意使用解构赋值时 {hobby}结构的值必须和exports.hobby的值相同
console.log("这是Mb.js文件");
let Ma = require("./Ma");
console.log(Ma.a);
let p = new Ma.Person();
p.hobby();//也可以通过结构赋值方式得到值
// 或者 通过解构赋值
// let { hobby } = require("./Ma");
// hobby();
- - 模块加载的优先级 ,先文件再目录;
4.2.2内置模块;
内置模块即不需要下载,nodejs中本身就有的模块,内置模块不需要安装,外置模块需要安装;
nodejs内置模块有:Buffer,C/C++Addons,Child Processes,Cluster,Console,Crypto,Debugger,DNS,Domain,Errors,Events,File System,Globals,HTTP,HTTPS,Modules,Net,OS,Path,Process,P unycode,Query Strings,Readline,REPL,Stream,String De coder,Timers,TLS/SSL,TTY,UDP/Datagram,URL, Utilities,V8,VM,ZLIB;
5.npm包管理器
安装node.js时,npm也会一起安装,npm的版本号和node.js的版本号是关联的。一般如果npm版本不够,只要升级node.js的版本即可。
NPM(Node Package Manager) 官网的地址是 [npm官网](https://www.npmjs.com) ,可以注册和发布项目。
node.js是轻量级加载,用到某个模块时再去安装,没有用到就先不安装。
5.1 npm常用指令;
- - npm init:引导创建一个package.json文件
- - npm help(npm -h) :查看npm帮助信息
- - npm version (npm -v) : 查看npm版本;
- - npm search:查找
- - npm install (npm i) module_name :安装 默认在当前目录,如果没有node_modules 会创建node_modules文件夹,并将下载的第三方模块放在创建好的node_modules里;
- - npm install module_name -S 或者--save 即 npm install module_name --save 写入dependencies运行依赖。新版本中默认会加上-S(--save)
- - npm install module_name -D 或者 --save-dev 即 npm install module_name --save-dev 写入devDependencies开发依赖
- - npm install module_name -g 全局安装(命令行使用),即会按照在npm root -g下。如C:\Users\Administrator\AppData\Roaming\npm\node_modules即npm安装的系统根目录
- - 指定版本安装模块 npm i module_name @1.0 通过 "@"符号指定;
- - npm update(npm -up):更新
- - npm remove 或者 npm uninstall module_name :删除
- - npm root 查看当前包安装(即node-modules)的路径 或者通过 npm root -g 来查看全局安装路径;
- npm config set registry https://registry.npmjs.org/ (官方地址)设置下载源地址,https://registry.npm.taobao.org/(淘宝源地址)
- npm config list 查询源地址
示例:使用npm i cookie创建cookie模块:
如在npm文件夹下执行这一命令后,会自定创建一个node_modules文件夹以及package.json和package-lock.json文件,并创建cookie模块
"cookie": "^0.4.0"的^表示安装时自动查找或使用0.4.0及其以上版本
使用npm init 引导创建package.json文件:
{"name": "test","version": "1.0.0","description": "","main": "main.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC"
}
5.2注册与发布npm项目
5.2.1 注册账号:
[https://www.npmjs.com/](https://www.npmjs.com/) (邮箱验证)
- npm adduser 输入刚刚注册好的用户名和密码 ;
如果注册地址不对需要重新改回来:使用淘宝源地址源下载速度快些。
- npm config set registry https://registry.npmjs.org/ (官方地址)
- https://registry.npm.taobao.org/(淘宝源地址)
查询源地址:npm config list
5.2.2发布包
创建项目:index.js默认入口文件;npm i 创建package.json文件,npm start启动项目
- 发布publish 命令 :npm publish 项目名 。项目名不能重复
- 下载发布上去的项目:npm i 项目名
- 删除发布的项目:npm unpublish 项目名
5.2.3cnpm的安装及使用
cnpm是淘宝出的。使用cnpm会直接在淘宝源地址进行下载。
一般不建议使用cnpm命令 。下载地址会很怪,且会出现未知问题。
可以使用npm命令,使用淘宝源,下载地址就会是国内地址
安装命令:会到国内淘宝源地址进行下载安装
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
6.fs模块(内置模块)
内置模块只需要引入,不需要安装。
fs模块:针对文件和目录的增删改查。
- fs是文件操作模块,所有文件操作都是有同步和异步之分,特点是同步会加上 "Sync" 如:异步读取文件 "readFile",同步读取文件 "readFileSync";
文件操作:
6.1写入文件writeFile()
let fs = require("fs");
fs.writeFile(filename,data,[options],callback)写入内容到文件.
- 第一个参数filename:文件名;
- 第二个参数data:要写入的内容;写入数据的类型必须是字符串或buffer二进制数据 ,对象等。如果
data
是一个 buffer,则encoding
选项会被忽略。如果options
是一个字符串,则可以指定字符编码。 - 第三个参数options:可选,对象形式。encoding 文件编码,mode 写入模式,写入方式(a追加,w覆盖),如{flag:'w'}
- option Object
- encoding String |nulldefault='utf-8'
- mode Number default=438(aka 0666 in Octal)
- flag Stringdefault='w'
在同一个文件上多次使用 fs.writeFile()
且不等待回调是不安全的。 对于这种情况,建议使用 fs.createWriteStream()
。
异步写入:
let fs = require("fs");//异步写入
fs.writeFile("1.txt","323",{flag:'w'},function(err){//如果有错误,就返回错误if(err){return console.log(err);}//写入成功console.log("写入成功");
});
同步写入:没有错误回调
fs.writeFileSync("2.txt","sdfa",{flags:'a'});
6.2删除文件
异步删除:
//异步删除文件
fs.unlink("2.txt",function(err){//删除失败if(err){return console.log(err);}console.log("删除成功");
});
同步删除:
//同步删除文件
fs.unlinkSync("1.txt");
6.3修改文件名
异步修改文件名:
fs.rename(oldFile,newFile,callback)
//异步修改文件
fs.rename("1.txt","11.txt",err=>{if(err){return console.log(err);}console.log("修改文件名成功");
});
同步修改文件名:
//同步修改文件名
fs.renameSync("11.txt","2.txt");
6.4读取文件
异步读取:
fs.readFile(path: string | number | Buffer | URL,callback)
//异步读取文件
fs.readFile("1.txt",(err,data)=>{if(err){return console.log(err);}console.log("读取成功"+data);});
同步读取:
readFileSync(path: string | number | Buffer | URL, options?: {
encoding?: null;
flag?: string;
})
直接读取出来的数据时Buffer数据,需要使用toString()将其转换为字符串
//同步读取文件
let data = fs.readFileSync("1.txt").toString();
console.log(data);
6.5复制文件
异步复制文件:
copyFile(src: PathLike, dest: PathLike, callback)
//异步复制文件
fs.copyFile("1.txt","3.txt",err=>{if(err){return console.log(err);}console.log("复制成功");
});
同步复制文件:
//同步复制文件
fs.copyFileSync("3.txt","4.txt");
6.6自定义复制文件实现原理
原理:先读取到文件,再将读取到的文件进行写入
//复制文件实现原理
function copyFile(src,dir){let data = fs.readFileSync(src).toString();fs.writeFileSync(dir,data);
}copyFile("1.txt","5.txt");
6.7创建目录
异步创建目录:
//创建目录
fs.mkdir("11",err=>{if(err){return console.log(err);}console.log("创建成功");});
同步创建目录:
fs.mkdirSync("22");
6.8修改目录名
异步修改目录名:
//异步修改目录名
fs.rename("11","44",err=>{if(err){return console.log(err);}console.log(修改目录成功);
});
同步修改目录名:
fs.renameSync("44","33");
6.9读取目录中的文件及子目录
会将目录中的子目录和文件都读取出来,放到数组中
//异步读取目录中的文件和子目录
fs.readdir("33",(err,files)=>{if(err){return console.log(err);}console.log(files);//[ '1.txt', '2.txt', '22' ]
})
//同步读取目录中的文件和子目录
let data = fs.readdirSync("33");
console.log(data);//[ '1.txt', '2.txt', '22' ]
6.10删除目录
删除的目录必须是空目录,否则删除不了
//删除空目录
fs.rmdir("./33/22",err=>{if(err){return console.log(err);}console.log("删除成功");
});
6.10判断文件或目录是否存在
//判断文件或目录是否存在
fs.exists("33",exists=>{console.log(exists);
});
//同步判断文件或目录是否存在
let flag = fs.existsSync("33");
console.log(flag);
6.11获取文件或目录的详细信息
//获取文件或目录的详细信息
fs.stat("33",(err,stats)=>{if(err){return console.log(err);}console.log(stats);//通过stas.isFile()判断是否是一个文件console.log(stats.isFile());//通过stats.isDirectory()判断是否是一个目录console.log(stats.isDirectory());
});
结果:
6.12删除非空文件夹
- 先获取路径下所有文件夹和文件;
- 再通过stat.isDirectory()判断时文件夹时继续找下面的文件,是文件就删除;
- 最后循环完成后删除空文件夹
//自定义删除非空文件夹
function removeDir(dir) {let data = fs.readdirSync(dir);data.forEach(item=>{//注意这里的路径循环出来的是原来目录的子级,所以删除时,需要加上原来目录级别才能找到let url = dir + "/" + item;let stats = fs.statSync(url);if(stats.isDirectory()){//如果是文件夹继续向下查找removeDir(url);}else{//如果是文件就删除文件console.log("是文件");fs.unlinkSync(url);}});//循环完后删除空目录fs.rmdirSync(dir);
}
removeDir("33");
7.buffer缓冲区
- - buffer的创建
- - 直接创建
- - 数组创建
- - 字符串创建
- - 乱码的处理
- - buffer转换tostring
7.1buffer的创建:
buffer是以二进制的形式进行保存,但是是以十六进制的方式进行展示
buffer创建方式一:ES6.1之前:new Buffer()
//ES6.1之前buffer缓冲区创建
let buffer = new Buffer("大家好");
console.log(buffer);//<Buffer e5 a4 a7 e5 ae b6 e5 a5 bd>
buffer创建方式二:ES6.1: let buffer = Buffer.alloc(10);
//buffer缓冲区创建方式二:
let buffer = Buffer.alloc(10);//创建一个10字节的buffer缓冲区
console.log(buffer);//<Buffer 00 00 00 00 00 00 00 00 00 00>
buffer创建方式三:Buffer.from()
//buffer缓冲区创建方式三:
let buffer = Buffer.from("大家好");
console.log(buffer);//<Buffer e5 a4 a7 e5 ae b6 e5 a5 bd>
buffer创建方式四:数组方式创建
//buffer缓冲区创建方式四:
let buffer = Buffer.from([0xe5, 0xa4, 0xa7, 0xe5, 0xae, 0xb6, 0xe5, 0xa5, 0xbd]);
console.log(buffer.toString());//大家好
通过数组方式创建,一个中文对应3个16进制位,如果给的位数不对就会乱码:
//通过数组方式创建,一个中文对应3个16进制位,如果给的位数不对就会乱码
let buffer1 = Buffer.from([0xe5, 0xa4, 0xa7, 0xe5]);
let buffer2 = Buffer.from([ 0xae, 0xb6, 0xe5, 0xa5, 0xbd]);
console.log(buffer1.toString());//大?
console.log(buffer2.toString());//??好
乱码解决方法一:使用concat([buffer1,buffer2])连接多个buffer(注意参数必须是数组),并调用toString()再输出
//解决:使用concat连接多个buffer(注意参数必须是数组),再输出
console.log(Buffer.concat([buffer1,buffer2]).toString());
乱码解决方法二(性能更好):引入string_decoder模块,再通过其write()方法
// 乱码解决方法二:
let { StringDecoder } = require("string_decoder");
let decoder = new StringDecoder();
let buf1 = decoder.write(buffer1);
let buf2 = decoder.write(buffer2);
console.log(buf1);//大
console.log(buf2);//家好
8.stream流
- - stream流:流与数据处理方面密不可分
- - 流的原理
- - 流数据的获取 fs.createReadStream()
- - pipe:管道,将得到的数据通过pipe进行写入(fs.createWriteStream())
- - data:监控得到的数据,并将其分成多份
- - end:监控文件是否读取完毕
- - copy的流方法实现
- - 加载视图的流方法实现
数据传输时,如果数据过大,带宽不足,会造成内存溢出,或者叫内存爆仓。
stream流会将数据,分割成多份,然后依次进行传递。
文件上传读取等多会涉及到stream流。
8.1data:监控得到的数据,并将其分成多份
示例:
读取home.js文件中的内容:
home.js:
// console.log("这是home主页JS文件");
// require("./moduleA.js");//引入moduleA.js文件
// require("./mydir");//引入文件夹mydir,就会自动查找文件夹mydir下的index.js文件
// require("mytest");
stream.js读取文件:
方式一:使用fs.readFileSync("home.js");读取时发现是一次性进行读取,一旦文件内容过大,就会造成内存溢出。
//读取文件home.js的数据
const fs = require("fs");
let data = fs.readFileSync("home.js");
console.log(data);//<Buffer 0d 0a 2f 2f 20 63 6f 6e 73 6f 6c 65 2e 6c 6f 67 28 22 e8 bf 99 e6 98 af 68 6f 6d 65 e4 b8 bb e9 a1 b5 4a 53 e6 96 87 e4 bb b6 22 29 3b 0d 0a 2f 2f 20 ... >
方式二:fs.createReadStream("home.js"),通过on方法监听这个数据,并分成多份
//使用可读流进行读取
let re = fs.createReadStream("home.js");
re.on("data",chunk=>{console.log(chunk.toString());
});
创建一个65kb的文件,再通过createReadStream()进行读取,发现会打印两次chunk
//创建一个65kb大小的buffer,再通过createReadStream()读取
let buffer = Buffer.alloc(65*1024);
fs.writeFile("65kb.txt",buffer,err=>{if(err){return console.log(err);}
});
let res = fs.createReadStream("65kb.txt");
let num = 0;
res.on("data",chunk=>{num++;console.log(chunk);//发现会打印两次console.log(num);/*<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >1<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >2*/
});
当创建64kb的文件,通过createReadStream()进行读取,发现只会打印一次:
//再读取64kb文件时,发现只会打印一次
let res = fs.createReadStream("64kb.txt");
let num = 0;
let str = "";
res.on("data",chunk=>{num++;str += chunk;console.log(chunk);//发现会打印两次console.log(num);/*<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >1*/
});
8.2 end监控文件是否读取完成
//再读取64kb文件时,发现只会打印一次
let res = fs.createReadStream("home.js");
let num = 0;
let str = "";
res.on("data",chunk=>{num++;str += chunk;console.log(chunk);//发现会打印两次console.log(num);/*<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >1*/
});//end监控文件是否读取完成,并可以得到读取完成后的数据
res.on("end",()=>{console.log(str);});
结果:会将读取到的所有数据进行打印
8.3pipe:管道,将得到的数据通过pipe进行写入(fs.createWriteStream())
- 通过fs.createWriteStream()创建写入文件;
- 通过res.pipe() 流方式通过管道一点一点写入到文件中
let res = fs.createReadStream("home.js");
let num = 0;
let str = "";
res.on("data",chunk=>{num++;str += chunk;console.log(chunk);//发现会打印两次console.log(num);/*<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >1*/
});//pipe管道,将读到的数据,通过pipe()写入到文件
let pipeTxt = fs.createWriteStream("pipe.txt");
res.pipe(pipeTxt);
9.包管理工具yarn
npm install -g yarn
yarn会相对简单,但是最好使用npm
10.node.js版本管理工具NVM
- 使用NVM(Node Version Manager)控制Node.js版本
10.1NVM下载
- - nvm是mac环境下管理nodejs的工具。在windows环境下推荐使用nvmw或者nvm-windows(建议使用);Nvm-windows 下载地址 https://github.com/coreybutler/nvm-windows
- 也可以在https://github.com/coreybutler/nvm-windows/releases 下载 nvm-setup.zip
10.2安装NVM
mac上安装NVM:
- - 在安装nvm之前需要一个c++编译器,在mac上可以安装Xcode命令工具(已经安装可以忽略) :`xcode-select --install`
- - 使用 curl安装:`curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash`
- - 或者使用wget来安装:`wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash`
- NVM [github的地址](<https://github.com/creationix/nvm>)可以查看最新版本
windows上直接上一步下一步;
10.3NVM常用指令
- nvm --version查看版本`
- nvm install stable //安装最新稳定版nodejs``
- nvm install 8.11.1 //安装指定版本``
- nvm install 8.11 //安装 8.11.x系列最新版本``
- nvm ls-remote //列出远程服务器上所有可用的版本`` nvm use 8.11.1 //切换到8.11.1版本``
- nvm use 8.11 //切换到8.11.x最新版本``
- nvm use node //切换到最新版本``
- nvm alias default node //设置默认版本为最新版本``
- nvm ls //列出所有已经安装的版本``
11.通过fs模块加载页面
通过路由地址/index 和/product路由到不同的页面
11.1- 普通方式加载页面
- - 路由区分
- - 加载页面
index.html:
<body>这是主页........
</body>
product.html:
<body>这是产品页........
</body>
style.css:
body {background: red;
}
index.js:创建服务器,并使用fs模块加载不同页面,并使用nodemon index.js启动服务器,即可热更新,自动监控页面的变化
页面由页面内容和请求头,响应头组成,头信息必不可少。
//使用fs模块加载页面
const fs = require("fs");
const http = require("http");
const url = require("url");
const path = require("path");
//引入静态文件后缀json文件
const mime = require("./mime.json");let server = http.createServer((req, res) => {//如果不设置HTML请求头会乱码(注意参数的写法不能错)// res.setHeader('Content-Type', 'text/html; charset=utf-8');res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });if (req.url === "/index") {// res.write("这是主页");//方法一:通过fs模块读取页面,再进行写入内容let index = fs.readFileSync("index.html");res.write(index);// 监控响应结束,不写的话会一直 请求res.end();} else if (req.url === "/product") {// res.write("这是产品页");//通过fs模块读取页面,再进行写入内容let product = fs.readFileSync("product.html");res.write(product);res.end();} else {//其他静态文件(文件的后缀不统一),使用mime.json文件处理//通过path模块获取到文件后缀名 /.css => .csslet extname = path.extname(req.url);//注意这里必须设置响应头,否则没有响应头页面不会显示(注意拼接形式)res.writeHead(200, { 'Content-Type': mime[extname]});//再读取到静态文件,并响应到页面let static = fs.createReadStream("."+req.url);static.pipe(res);}});
server.listen("8000");
mime.json:各种静态文件(文件的后缀不统一),操作复杂,可以使用mime.json文件处理。
如,通过path模块获取到文件后缀名 /.css => .css , let extname = path.extname(req.url) ;然后必须设置响应头,否则没有响应头页面不会显示(注意拼接形式) res.writeHead(200, { 'Content-Type': mime[extname]});
{ ".323":"text/h323" ,".3gp":"video/3gpp" ,".aab":"application/x-authoware-bin" ,".aam":"application/x-authoware-map" ,".aas":"application/x-authoware-seg" ,".acx":"application/internet-property-stream" ,".ai":"application/postscript" ,".aif":"audio/x-aiff" ,".aifc":"audio/x-aiff" ,".aiff":"audio/x-aiff" ,".als":"audio/X-Alpha5" ,".amc":"application/x-mpeg" ,".ani":"application/octet-stream" ,".apk":"application/vnd.android.package-archive" ,".asc":"text/plain" ,".asd":"application/astound" ,".asf":"video/x-ms-asf" ,".asn":"application/astound" ,".asp":"application/x-asap" ,".asr":"video/x-ms-asf" ,".asx":"video/x-ms-asf" ,".au":"audio/basic" ,".avb":"application/octet-stream" ,".avi":"video/x-msvideo" ,".awb":"audio/amr-wb" ,".axs":"application/olescript" ,".bas":"text/plain" ,".bcpio":"application/x-bcpio" ,".bin ":"application/octet-stream" ,".bld":"application/bld" ,".bld2":"application/bld2" ,".bmp":"image/bmp" ,".bpk":"application/octet-stream" ,".bz2":"application/x-bzip2" ,".c":"text/plain" ,".cal":"image/x-cals" ,".cat":"application/vnd.ms-pkiseccat" ,".ccn":"application/x-cnc" ,".cco":"application/x-cocoa" ,".cdf":"application/x-cdf" ,".cer":"application/x-x509-ca-cert" ,".cgi":"magnus-internal/cgi" ,".chat":"application/x-chat" ,".class":"application/octet-stream" ,".clp":"application/x-msclip" ,".cmx":"image/x-cmx" ,".co":"application/x-cult3d-object" ,".cod":"image/cis-cod" ,".conf":"text/plain" ,".cpio":"application/x-cpio" ,".cpp":"text/plain" ,".cpt":"application/mac-compactpro" ,".crd":"application/x-mscardfile" ,".crl":"application/pkix-crl" ,".crt":"application/x-x509-ca-cert" ,".csh":"application/x-csh" ,".csm":"chemical/x-csml" ,".csml":"chemical/x-csml" ,".css":"text/css" ,".cur":"application/octet-stream" ,".dcm":"x-lml/x-evm" ,".dcr":"application/x-director" ,".dcx":"image/x-dcx" ,".der":"application/x-x509-ca-cert" ,".dhtml":"text/html" ,".dir":"application/x-director" ,".dll":"application/x-msdownload" ,".dmg":"application/octet-stream" ,".dms":"application/octet-stream" ,".doc":"application/msword" ,".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document" ,".dot":"application/msword" ,".dvi":"application/x-dvi" ,".dwf":"drawing/x-dwf" ,".dwg":"application/x-autocad" ,".dxf":"application/x-autocad" ,".dxr":"application/x-director" ,".ebk":"application/x-expandedbook" ,".emb":"chemical/x-embl-dl-nucleotide" ,".embl":"chemical/x-embl-dl-nucleotide" ,".eps":"application/postscript" ,".epub":"application/epub+zip" ,".eri":"image/x-eri" ,".es":"audio/echospeech" ,".esl":"audio/echospeech" ,".etc":"application/x-earthtime" ,".etx":"text/x-setext" ,".evm":"x-lml/x-evm" ,".evy":"application/envoy" ,".exe":"application/octet-stream" ,".fh4":"image/x-freehand" ,".fh5":"image/x-freehand" ,".fhc":"image/x-freehand" ,".fif":"application/fractals" ,".flr":"x-world/x-vrml" ,".flv":"flv-application/octet-stream" ,".fm":"application/x-maker" ,".fpx":"image/x-fpx" ,".fvi":"video/isivideo" ,".gau":"chemical/x-gaussian-input" ,".gca":"application/x-gca-compressed" ,".gdb":"x-lml/x-gdb" ,".gif":"image/gif" ,".gps":"application/x-gps" ,".gtar":"application/x-gtar" ,".gz":"application/x-gzip" ,".h":"text/plain" ,".hdf":"application/x-hdf" ,".hdm":"text/x-hdml" ,".hdml":"text/x-hdml" ,".hlp":"application/winhlp" ,".hqx":"application/mac-binhex40" ,".hta":"application/hta" ,".htc":"text/x-component" ,".htm":"text/html" ,".html":"text/html" ,".hts":"text/html" ,".htt":"text/webviewhtml" ,".ice":"x-conference/x-cooltalk" ,".ico":"image/x-icon" ,".ief":"image/ief" ,".ifm":"image/gif" ,".ifs":"image/ifs" ,".iii":"application/x-iphone" ,".imy":"audio/melody" ,".ins":"application/x-internet-signup" ,".ips":"application/x-ipscript" ,".ipx":"application/x-ipix" ,".isp":"application/x-internet-signup" ,".it":"audio/x-mod" ,".itz":"audio/x-mod" ,".ivr":"i-world/i-vrml" ,".j2k":"image/j2k" ,".jad":"text/vnd.sun.j2me.app-descriptor" ,".jam":"application/x-jam" ,".jar":"application/java-archive" ,".java":"text/plain" ,".jfif":"image/pipeg" ,".jnlp":"application/x-java-jnlp-file" ,".jpe":"image/jpeg" ,".jpeg":"image/jpeg" ,".jpg":"image/jpeg" ,".jpz":"image/jpeg" ,".js":"application/x-javascript" ,".jwc":"application/jwc" ,".kjx":"application/x-kjx" ,".lak":"x-lml/x-lak" ,".latex":"application/x-latex" ,".lcc":"application/fastman" ,".lcl":"application/x-digitalloca" ,".lcr":"application/x-digitalloca" ,".lgh":"application/lgh" ,".lha":"application/octet-stream" ,".lml":"x-lml/x-lml" ,".lmlpack":"x-lml/x-lmlpack" ,".log":"text/plain" ,".lsf":"video/x-la-asf" ,".lsx":"video/x-la-asf" ,".lzh":"application/octet-stream" ,".m13":"application/x-msmediaview" ,".m14":"application/x-msmediaview" ,".m15":"audio/x-mod" ,".m3u":"audio/x-mpegurl" ,".m3url":"audio/x-mpegurl" ,".m4a":"audio/mp4a-latm" ,".m4b":"audio/mp4a-latm" ,".m4p":"audio/mp4a-latm" ,".m4u":"video/vnd.mpegurl" ,".m4v":"video/x-m4v" ,".ma1":"audio/ma1" ,".ma2":"audio/ma2" ,".ma3":"audio/ma3" ,".ma5":"audio/ma5" ,".man":"application/x-troff-man" ,".map":"magnus-internal/imagemap" ,".mbd":"application/mbedlet" ,".mct":"application/x-mascot" ,".mdb":"application/x-msaccess" ,".mdz":"audio/x-mod" ,".me":"application/x-troff-me" ,".mel":"text/x-vmel" ,".mht":"message/rfc822" ,".mhtml":"message/rfc822" ,".mi":"application/x-mif" ,".mid":"audio/mid" ,".midi":"audio/midi" ,".mif":"application/x-mif" ,".mil":"image/x-cals" ,".mio":"audio/x-mio" ,".mmf":"application/x-skt-lbs" ,".mng":"video/x-mng" ,".mny":"application/x-msmoney" ,".moc":"application/x-mocha" ,".mocha":"application/x-mocha" ,".mod":"audio/x-mod" ,".mof":"application/x-yumekara" ,".mol":"chemical/x-mdl-molfile" ,".mop":"chemical/x-mopac-input" ,".mov":"video/quicktime" ,".movie":"video/x-sgi-movie" ,".mp2":"video/mpeg" ,".mp3":"audio/mpeg" ,".mp4":"video/mp4" ,".mpa":"video/mpeg" ,".mpc":"application/vnd.mpohun.certificate" ,".mpe":"video/mpeg" ,".mpeg":"video/mpeg" ,".mpg":"video/mpeg" ,".mpg4":"video/mp4" ,".mpga":"audio/mpeg" ,".mpn":"application/vnd.mophun.application" ,".mpp":"application/vnd.ms-project" ,".mps":"application/x-mapserver" ,".mpv2":"video/mpeg" ,".mrl":"text/x-mrml" ,".mrm":"application/x-mrm" ,".ms":"application/x-troff-ms" ,".msg":"application/vnd.ms-outlook" ,".mts":"application/metastream" ,".mtx":"application/metastream" ,".mtz":"application/metastream" ,".mvb":"application/x-msmediaview" ,".mzv":"application/metastream" ,".nar":"application/zip" ,".nbmp":"image/nbmp" ,".nc":"application/x-netcdf" ,".ndb":"x-lml/x-ndb" ,".ndwn":"application/ndwn" ,".nif":"application/x-nif" ,".nmz":"application/x-scream" ,".nokia-op-logo":"image/vnd.nok-oplogo-color" ,".npx":"application/x-netfpx" ,".nsnd":"audio/nsnd" ,".nva":"application/x-neva1" ,".nws":"message/rfc822" ,".oda":"application/oda" ,".ogg":"audio/ogg" ,".oom":"application/x-AtlasMate-Plugin" ,".p10":"application/pkcs10" ,".p12":"application/x-pkcs12" ,".p7b":"application/x-pkcs7-certificates" ,".p7c":"application/x-pkcs7-mime" ,".p7m":"application/x-pkcs7-mime" ,".p7r":"application/x-pkcs7-certreqresp" ,".p7s":"application/x-pkcs7-signature" ,".pac":"audio/x-pac" ,".pae":"audio/x-epac" ,".pan":"application/x-pan" ,".pbm":"image/x-portable-bitmap" ,".pcx":"image/x-pcx" ,".pda":"image/x-pda" ,".pdb":"chemical/x-pdb" ,".pdf":"application/pdf" ,".pfr":"application/font-tdpfr" ,".pfx":"application/x-pkcs12" ,".pgm":"image/x-portable-graymap" ,".pict":"image/x-pict" ,".pko":"application/ynd.ms-pkipko" ,".pm":"application/x-perl" ,".pma":"application/x-perfmon" ,".pmc":"application/x-perfmon" ,".pmd":"application/x-pmd" ,".pml":"application/x-perfmon" ,".pmr":"application/x-perfmon" ,".pmw":"application/x-perfmon" ,".png":"image/png" ,".pnm":"image/x-portable-anymap" ,".pnz":"image/png" ,".pot,":"application/vnd.ms-powerpoint" ,".ppm":"image/x-portable-pixmap" ,".pps":"application/vnd.ms-powerpoint" ,".ppt":"application/vnd.ms-powerpoint" ,".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation" ,".pqf":"application/x-cprplayer" ,".pqi":"application/cprplayer" ,".prc":"application/x-prc" ,".prf":"application/pics-rules" ,".prop":"text/plain" ,".proxy":"application/x-ns-proxy-autoconfig" ,".ps":"application/postscript" ,".ptlk":"application/listenup" ,".pub":"application/x-mspublisher" ,".pvx":"video/x-pv-pvx" ,".qcp":"audio/vnd.qcelp" ,".qt":"video/quicktime" ,".qti":"image/x-quicktime" ,".qtif":"image/x-quicktime" ,".r3t":"text/vnd.rn-realtext3d" ,".ra":"audio/x-pn-realaudio" ,".ram":"audio/x-pn-realaudio" ,".rar":"application/octet-stream" ,".ras":"image/x-cmu-raster" ,".rc":"text/plain" ,".rdf":"application/rdf+xml" ,".rf":"image/vnd.rn-realflash" ,".rgb":"image/x-rgb" ,".rlf":"application/x-richlink" ,".rm":"audio/x-pn-realaudio" ,".rmf":"audio/x-rmf" ,".rmi":"audio/mid" ,".rmm":"audio/x-pn-realaudio" ,".rmvb":"audio/x-pn-realaudio" ,".rnx":"application/vnd.rn-realplayer" ,".roff":"application/x-troff" ,".rp":"image/vnd.rn-realpix" ,".rpm":"audio/x-pn-realaudio-plugin" ,".rt":"text/vnd.rn-realtext" ,".rte":"x-lml/x-gps" ,".rtf":"application/rtf" ,".rtg":"application/metastream" ,".rtx":"text/richtext" ,".rv":"video/vnd.rn-realvideo" ,".rwc":"application/x-rogerwilco" ,".s3m":"audio/x-mod" ,".s3z":"audio/x-mod" ,".sca":"application/x-supercard" ,".scd":"application/x-msschedule" ,".sct":"text/scriptlet" ,".sdf":"application/e-score" ,".sea":"application/x-stuffit" ,".setpay":"application/set-payment-initiation" ,".setreg":"application/set-registration-initiation" ,".sgm":"text/x-sgml" ,".sgml":"text/x-sgml" ,".sh":"application/x-sh" ,".shar":"application/x-shar" ,".shtml":"magnus-internal/parsed-html" ,".shw":"application/presentations" ,".si6":"image/si6" ,".si7":"image/vnd.stiwap.sis" ,".si9":"image/vnd.lgtwap.sis" ,".sis":"application/vnd.symbian.install" ,".sit":"application/x-stuffit" ,".skd":"application/x-Koan" ,".skm":"application/x-Koan" ,".skp":"application/x-Koan" ,".skt":"application/x-Koan" ,".slc":"application/x-salsa" ,".smd":"audio/x-smd" ,".smi":"application/smil" ,".smil":"application/smil" ,".smp":"application/studiom" ,".smz":"audio/x-smd" ,".snd":"audio/basic" ,".spc":"application/x-pkcs7-certificates" ,".spl":"application/futuresplash" ,".spr":"application/x-sprite" ,".sprite":"application/x-sprite" ,".sdp":"application/sdp" ,".spt":"application/x-spt" ,".src":"application/x-wais-source" ,".sst":"application/vnd.ms-pkicertstore" ,".stk":"application/hyperstudio" ,".stl":"application/vnd.ms-pkistl" ,".stm":"text/html" ,".svg":"image/svg+xml" ,".sv4cpio":"application/x-sv4cpio" ,".sv4crc":"application/x-sv4crc" ,".svf":"image/vnd" ,".svg":"image/svg+xml" ,".svh":"image/svh" ,".svr":"x-world/x-svr" ,".swf":"application/x-shockwave-flash" ,".swfl":"application/x-shockwave-flash" ,".t":"application/x-troff" ,".tad":"application/octet-stream" ,".talk":"text/x-speech" ,".tar":"application/x-tar" ,".taz":"application/x-tar" ,".tbp":"application/x-timbuktu" ,".tbt":"application/x-timbuktu" ,".tcl":"application/x-tcl" ,".tex":"application/x-tex" ,".texi":"application/x-texinfo" ,".texinfo":"application/x-texinfo" ,".tgz":"application/x-compressed" ,".thm":"application/vnd.eri.thm" ,".tif":"image/tiff" ,".tiff":"image/tiff" ,".tki":"application/x-tkined" ,".tkined":"application/x-tkined" ,".toc":"application/toc" ,".toy":"image/toy" ,".tr":"application/x-troff" ,".trk":"x-lml/x-gps" ,".trm":"application/x-msterminal" ,".tsi":"audio/tsplayer" ,".tsp":"application/dsptype" ,".tsv":"text/tab-separated-values" ,".ttf":"application/octet-stream" ,".ttz":"application/t-time" ,".txt":"text/plain" ,".uls":"text/iuls" ,".ult":"audio/x-mod" ,".ustar":"application/x-ustar" ,".uu":"application/x-uuencode" ,".uue":"application/x-uuencode" ,".vcd":"application/x-cdlink" ,".vcf":"text/x-vcard" ,".vdo":"video/vdo" ,".vib":"audio/vib" ,".viv":"video/vivo" ,".vivo":"video/vivo" ,".vmd":"application/vocaltec-media-desc" ,".vmf":"application/vocaltec-media-file" ,".vmi":"application/x-dreamcast-vms-info" ,".vms":"application/x-dreamcast-vms" ,".vox":"audio/voxware" ,".vqe":"audio/x-twinvq-plugin" ,".vqf":"audio/x-twinvq" ,".vql":"audio/x-twinvq" ,".vre":"x-world/x-vream" ,".vrml":"x-world/x-vrml" ,".vrt":"x-world/x-vrt" ,".vrw":"x-world/x-vream" ,".vts":"workbook/formulaone" ,".wav":"audio/x-wav" ,".wax":"audio/x-ms-wax" ,".wbmp":"image/vnd.wap.wbmp" ,".wcm":"application/vnd.ms-works" ,".wdb":"application/vnd.ms-works" ,".web":"application/vnd.xara" ,".wi":"image/wavelet" ,".wis":"application/x-InstallShield" ,".wks":"application/vnd.ms-works" ,".wm":"video/x-ms-wm" ,".wma":"audio/x-ms-wma" ,".wmd":"application/x-ms-wmd" ,".wmf":"application/x-msmetafile" ,".wml":"text/vnd.wap.wml" ,".wmlc":"application/vnd.wap.wmlc" ,".wmls":"text/vnd.wap.wmlscript" ,".wmlsc":"application/vnd.wap.wmlscriptc" ,".wmlscript":"text/vnd.wap.wmlscript" ,".wmv":"audio/x-ms-wmv" ,".wmx":"video/x-ms-wmx" ,".wmz":"application/x-ms-wmz" ,".wpng":"image/x-up-wpng" ,".wps":"application/vnd.ms-works" ,".wpt":"x-lml/x-gps" ,".wri":"application/x-mswrite" ,".wrl":"x-world/x-vrml" ,".wrz":"x-world/x-vrml" ,".ws":"text/vnd.wap.wmlscript" ,".wsc":"application/vnd.wap.wmlscriptc" ,".wv":"video/wavelet" ,".wvx":"video/x-ms-wvx" ,".wxl":"application/x-wxl" ,".x-gzip":"application/x-gzip" ,".xaf":"x-world/x-vrml" ,".xar":"application/vnd.xara" ,".xbm":"image/x-xbitmap" ,".xdm":"application/x-xdma" ,".xdma":"application/x-xdma" ,".xdw":"application/vnd.fujixerox.docuworks" ,".xht":"application/xhtml+xml" ,".xhtm":"application/xhtml+xml" ,".xhtml":"application/xhtml+xml" ,".xla":"application/vnd.ms-excel" ,".xlc":"application/vnd.ms-excel" ,".xll":"application/x-excel" ,".xlm":"application/vnd.ms-excel" ,".xls":"application/vnd.ms-excel" ,".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ,".xlt":"application/vnd.ms-excel" ,".xlw":"application/vnd.ms-excel" ,".xm":"audio/x-mod" ,".xml":"text/plain",".xml":"application/xml",".xmz":"audio/x-mod" ,".xof":"x-world/x-vrml" ,".xpi":"application/x-xpinstall" ,".xpm":"image/x-xpixmap" ,".xsit":"text/xml" ,".xsl":"text/xml" ,".xul":"text/xul" ,".xwd":"image/x-xwindowdump" ,".xyz":"chemical/x-pdb" ,".yz1":"application/x-yz1" ,".z":"application/x-compress" ,".zac":"application/x-zaurus-zac" ,".zip":"application/zip" ,".json":"application/json"
}
11.2- 通过stream流方式加载页面
- - 路由区分
- - 加载页面
- - 设置头部:mime.json
- - 加载第三方资源
通过fs模块流 Stream读取文件:在文件内容很大时一点一点读取效率高,不会内存溢出。
注意点:
- fs.createReadStream()方法读取后,直接通过pipe()方法响应到页面,不需要再写入(fs.createWriteStream())
- 注意HTML头的书写格式:res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
- 静态文件后缀,通过path.extname(req.url)进行获取,且对应json中不同的文件后缀
//使用fs模块加载页面
const fs = require("fs");
const http = require("http");
const url = require("url");
const path = require("path");
//引入静态文件后缀json文件
const mime = require("./mime.json");let server = http.createServer((req, res) => {//如果不设置HTML请求头会乱码(注意参数的写法不能错)// res.setHeader('Content-Type', 'text/html; charset=utf-8');res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });if (req.url === "/index") {//方法二:通过fs模块流 Stream读取文件:在文件内容很大时效率高,不会内存溢出let resIndex = fs.createReadStream("index.html");//pipe管道,将读到的数据响应到页面resIndex.pipe(res);} else if (req.url === "/product") {//注意createReadStream路由只需要读取并响应,不需要写入let product = fs.createReadStream("product.html");product.pipe(res);} else {//其他静态文件(文件的后缀不统一),使用mime.json文件处理//通过path模块获取到文件后缀名 /.css => .csslet extname = path.extname(req.url);//注意这里必须设置响应头,否则没有响应头页面不会显示(注意拼接形式)res.writeHead(200, { 'Content-Type': mime[extname]});//再读取到静态文件,并响应到页面let static = fs.createReadStream("."+req.url);static.pipe(res);}});
server.listen("8000");
12.node+cheerio实现爬虫获取数据
- - http://news.ifeng.com/ 新闻地址
- - http.get()使用
- - cheerio来处理数据:
- - 文件方式储存数据1
cheerio:类似于前端jQuery的node.js的模块,用法基本和jQuery相同,但是没有window对象,即location,history,location等前端对象不存在。
crawlerData.js:
const http = require("http");
//通过get方法获取爬虫爬到的是整个页面,需要使用cheerio模块进行处理(类似JQuery)
const cheerio = require("cheerio");//注意需要使用 npm i cheerio -S 按照cheerio模块到运行环境const fs = require("fs");let newsData = '';
//http的get 方法会并自动调用 req.end()
http.get("http://news.ifeng.com/", (res) => {res.on("data", chunk => {newsData += chunk;});res.on("end", () => {//数据必须在end里面处理,否则获取不到数据//使用cheerio加载整个数据let $ = cheerio.load(newsData);//用于存json数据let data = [];//新闻标题let titles = $(".news-stream-newsStream-news-item-infor h2 a");titles.each((index,ele)=>{data.push({"id":index+1,"title":ele.attribs['title']});});//发布者let publisher = $(".news-stream-newsStream-news-item-infor .clearfix span");publisher.each((index,ele)=>{data[index].publisher = ele.children[0]['data'];});//发布时间let time = $(".news-stream-newsStream-news-item-infor .clearfix time");time.each((index,ele)=>{data[index].time = ele.children[0]['data'];});//将得到的数据写入到json文件中let dataJson = fs.createWriteStream("data.json");//注意需要使用write(),并且将数据转成字符串格式才能进行写出dataJson.write(JSON.stringify(data));res.pipe(dataJson);});
});
获取到的json数据文件:文件已做一定修改
[{"id": 1,"title": "13岁少年成社会的灾难","publisher": "海外网","time": "今天 17:08"},{"id": 2,"title": "(全文实录)","publisher": "中国网","time": "今天 16:59"},{"id": 3,"title": "禁读《哈利·波特》 称咒语召唤邪灵","publisher": "澎湃新闻","time": "今天 16:56"},{"id": 4,"title": "XXXXXXXXXXX","publisher": "海外网","time": "今天 16:54"},{"id": 5,"title": "空缺190天后,迎来王浩","publisher": "上游新闻","time": "今天 16:54"},{"id": 6,"title": "中国游客在日本突然昏迷 2名韩国消防员及时相救","publisher": "海外网","time": "今天 16:53"},{"id": 7,"title": "零售雪上加霜 奢侈品牌普拉达将关闭在港最大门店","publisher": "观察者网","time": "今天 16:46"},{"id": 8,"title": "拖了18年?","publisher": "海外网","time": "今天 16:41"},{"id": 9,"title": "误读","publisher": "环球网","time": "今天 16:37"},{"id": 10,"title": "被男议员骂“不生孩子没尽国家责任” 韩国55岁女学者懵了","publisher": "海外网","time": "今天 16:29"},{"id": 11,"title": "陈刚被提起公诉","publisher": "海外网","time": "今天 16:29"},{"id": 12,"title": "台假装尿急翻墙逃出营区 10天后在网吧被抓","publisher": "海外网","time": "今天 16:22"},{"id": 13,"title": "出糗:成语连说了3遍都没对","publisher": "海外网","time": "今天 16:21"},{"id": 14,"title": "XXXX","publisher": "澎湃新闻网","time": "今天 16:09"},{"id": 15,"title": "XXXX权","publisher": "新京报网","time": "今天 16:00"},{"id": 16,"title": "特征","publisher": "新京报即时新闻","time": "今天 15:53"},{"id": 17,"title": "XXXXXX","publisher": "新京报即时新闻","time": "今天 15:46"},{"id": 18,"title": "XXXXXX","publisher": "中国网","time": "今天 15:41"},{"id": 19,"title": "XXXX","publisher": "新京报即时新闻","time": "今天 15:35"},{"id": 20,"title": "XXXXXX","publisher": "新京报即时新闻","time": "今天 15:18"},{"id": 21,"title": "商务部:上周猪肉批发价格上涨8.9%","publisher": "新京报即时新闻","time": "今天 15:14"},{"id": 22,"title": "出糗:这个成语连说三遍都没对","publisher": "海外网","time": "今天 14:54"},{"id": 23,"title": "两高:高考等4类考试组织作弊属犯罪 最高判7年","publisher": "新京报即时新闻","time": "今天 14:33"}
]
13.实现新闻列表页面
- - 视图逻辑分离
- - 读取页面
- - 读取动态数据
- - 设置头部引入其他资源
- - 详细页显示
index.js:
//将爬虫得到的数据,渲染到页面,需要进行页面路由
const http = require("http");
const fs = require("fs");
const cheerio = require("cheerio");
const url = require("url");//处理地址
const path = require("path");//处理地址后缀名
const mime = require("./mime.json");//处理后缀名文件
const dataJson = require("./data.json");//新闻数据let server = http.createServer((req, res) => {res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });if (req.url === "/" || req.url === "/index") {//方法一:读取到index文件,获取文件标签,设置文件标签内容let index = fs.readFileSync("index.html");let $ = cheerio.load(index);//读取到json文件新闻数据//获取ul 将HTML设置进去let ulHtml = '';dataJson.forEach(item => {ulHtml += `<li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="javascript:;">${item.title}</a></h3><div class="info"><span class="tips"><span>纵火</span><span${item.publisher}</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| ${item.time}</span></div></div></li>`;});$(".news-list").html(ulHtml);res.end($.html());// //方法二:如果要使用createReadStream()流方式读取文件,就必须使用on("data")和on("end")监控页面读取完后再操作DOM// let index = fs.createReadStream("index.html");// let oldIndex = '';// index.on("data", chunk => {// oldIndex += chunk;// });// index.on('end', () => {// console.log(oldIndex);// let $ = cheerio.load(oldIndex);// let ulHtml = '';// dataJson.forEach(item => {// ulHtml += `// <li class="news">// <a href="javascript:;">// <img src="./img/img.png" alt="">// </a>// <div>// <h3>// <a href="javascript:;">${item.title}</a>// </h3>// <div class="info">// <span class="tips"><span>纵火</span><span${item.publisher}</span><span>逮捕</span></span>// <!-- <span class="line"></span> -->// <span class="time">| ${item.time}</span>// </div>// </div>// </li>// `;// });// $(".news-list").html(ulHtml);// res.end($.html());// });} else if (req.url !== "/favicon.ico") {let extname = path.extname(req.url);res.writeHead(200, { 'Content-Type': mime[extname] });let static = fs.createReadStream("." + req.url);static.pipe(res);}});
server.listen(4000);
index.html:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>文章信息展示</title><style>body {margin: 0;}ul {margin: 0;padding: 0;list-style: none;}a {text-decoration: none;color: #404040;}.wrap{width: 600px;margin: 0 auto;}.news-list {width: 600px;}.news {width: 100%;display: flex;justify-content: space-between;padding: 15px 0;border-bottom: 1px solid #999;}.info {display: flex;width: 170px;justify-content: space-between;font-size: 12px;color: #888;}.tips {display: flex;width: 100px;justify-content: space-between;}.news-list li:nth-child(5) {border-bottom: none;}.pagination{display: flex;width: 210px;text-align: center;background-color: rgb(252, 238, 238);border-radius: 25px;overflow: hidden;margin: 0 auto;justify-content: center;}.pagination a{width: 30px;line-height: 30px;color: #404040;}.pagination a:nth-child(1) {transform: rotate(-45deg) ;}.next {transform: rotate(45deg) ;}.pagination a:hover{color: rgb(247, 73, 73);}.news div{width:420px;}.pagination .active {color: rgb(247, 73, 73);}</style>
</head><body><div class="wrap"><ul class="news-list"><li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="javascript:;">18人死伤!韩国一男子纵火后持凶器伤害避险邻居</a></h3><div class="info"><span class="tips"><span>纵火</span><span>韩国</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| 1小时前</span></div></div></li><li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="javascript:;">18人死伤!韩国一男子纵火后持凶器伤害避险邻居</a></h3><div class="info"><span class="tips"><span>纵火</span><span>韩国</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| 1小时前</span></div></div></li><li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="javascript:;">18人死伤!韩国一男子纵火后持凶器伤害避险邻居</a></h3><div class="info"><span class="tips"><span>纵火</span><span>韩国</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| 1小时前</span></div></div></li><li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="javascript:;">18人死伤!韩国一男子纵火后持凶器伤害避险邻居</a></h3><div class="info"><span class="tips"><span>纵火</span><span>韩国</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| 1小时前</span></div></div></li><li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="javascript:;">18人死伤!韩国一男子纵火后持凶器伤害避险邻居</a></h3><div class="info"><span class="tips"><span>纵火</span><span>韩国</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| 1小时前</span></div></div></li></ul><div class="pagination"><a href="javascript:;" class="prev">?</a><a href="javascript:;">1</a><a href="javascript:;">2</a><a href="javascript:;">3</a><a href="javascript:;">4</a><a href="javascript:;">5</a><a href="javascript:;" class="next">?</a></div></div></body></html>
detail.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>.text{width: 640px;margin: 0 auto;}.article-info{color:#999;font-size: 14px;}p{font-size: 16px;line-height: 30px;}</style>
</head>
<body><div class="text"><h1 class="title">新闻标题</h1><div class="article-info"> 类型:纵火 时间:2019-6-18</div><p class="content">新闻内容新闻内容新闻内容新闻内容新闻内容新闻内容</p></div>
</body>
</html>
data.json:同上,爬虫所得的数据
实现详情页展示和分页功能:
更改index.js为indexPager.js:其他文件不变,直接覆盖detail.html中的内容
//将爬虫得到的数据,渲染到页面,需要进行页面路由
const http = require("http");
const fs = require("fs");
const cheerio = require("cheerio");
const url = require("url");//处理地址
const path = require("path");//处理地址后缀名
const mime = require("./mime.json");//处理后缀名文件
const dataJson = require("./data.json");//新闻数据let server = http.createServer((req, res) => {//地址栏加上页码后,会有queryString :pathname:/index ; search: '?pageNum=2';query: 'pageNum=2'let pathname = url.parse(req.url).pathname;if (pathname === "/" || pathname === "/index") {res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });//方法一:读取到index文件,获取文件标签,设置文件标签内容let index = fs.readFileSync("index.html");let $ = cheerio.load(index);//分页实现:总页数=总数据条数/每页多少条//当点击某页时,需要获取到传过来的页码(通过path可以获取到queryString参数)//url.parse(req.url, true).query 当没有参数时[Object: null prototype] {},有参数时:[Object: null prototype] { pageNum: '2' }let pageNum = url.parse(req.url, true).query.pageNum || 1;//第几页(默认第一页)let pageSize = 5;//每页多少条let pageTotal = Math.ceil(dataJson.length/pageSize);//总共多少页//通过数组slice方法进行分页(如果是数据库,需要调数据库)0-4 5-10 11-14 =>(pageNum-1)*pageSize , pageNum*pageSizelet pageData = dataJson.slice((pageNum-1)*pageSize,pageNum*pageSize);//读取到json文件新闻数据//获取ul 将HTML设置进去let ulHtml = '';pageData.forEach(item => {ulHtml += `<li class="news"><a href="javascript:;"><img src="./img/img.png" alt=""></a><div><h3><a href="/detail?id=${item.id}">${item.title}</a></h3><div class="info"><span class="tips"><span>纵火</span><span${item.publisher}</span><span>逮捕</span></span><!-- <span class="line"></span> --><span class="time">| ${item.time}</span></div></div></li>`;});$(".news-list").html(ulHtml);//渲染分页按钮//cheerio没有事件,所以上下页切换不能绑定点击事件,只能通过操作pageNum实现,注意需要将pageNum转为Number类型let p = parseInt(pageNum);let pagerHtml = `<a href="/index?pageNum=${Math.max(1,p-1)}" class="prev">?</a>`;for(let i=1;i<=pageTotal;i++){//点击每页还是跳转到本页面,这是页码变化pagerHtml += `<a href="/index?pageNum=${i}">${i}</a>`;}pagerHtml += `<a href="/index?pageNum=${Math.min(pageTotal,p+1)}" class="next">?</a>`;$(".pagination").html(pagerHtml);//点击事件:设置数组中某个元素的样式,.eq(pageNum)$(".pagination a").each((index,item)=>{if(pageNum == index){$(".pagination a").eq(pageNum).addClass('active')}});res.end($.html());}else if(pathname === "/detail"){//跳转到详情页//使用id重新查找对应数据let id = url.parse(req.url, true).query.id;let detailData = dataJson.find(item=>id==item.id);let detail = fs.readFileSync("detail.html");let $ = cheerio.load(detail);let detailHtml = `<h1 class="title">${detailData.title}</h1><div class="article-info"> 类型:纵火 时间:${detailData.time}</div><p class="content">${detailData.title}</p>`;$(".text").html(detailHtml); //一定要使用res.end()方式时响应结束res.end($.html()); } else if (pathname !== "/favicon.ico") {let extname = path.extname(req.url);res.writeHead(200, { 'Content-Type': mime[extname] });let static = fs.createReadStream("." + req.url);static.pipe(res);}
});
server.listen(3000);
效果: