Node.js
一、Node.js简介
1. 回顾 JavaScript
-
历史及发展
1995年 网景公司的布兰登开发;
1997年7月,ECMA组织发布ECMAScript 1.0版;
2007年10月发布3.1版本后不久,ECMAScript 3.1改名为 ECMAScript 5。
2008年,为Chrome浏览器而开发的V8编译器诞生
2011年6月,ECMAscript 5.1版发布,现在使用最为广泛的版本;
2015年6月,ECMAScript 6正式发布,并且更名为“ECMAScript 2015”;
2016年,ECMAScript 7正式发布…
2017年,ECMAScript 8正式发布…
2018年,ECMAScript 9正式发布…
2019年,ECMAScript 10正式发布…
…
-
如何学习JavaScript
JavaScript 的核心语法部分相当精简,也就是语言本身,只包括两个部分:
-
基本的语法构造(比如操作符、控制结构、语句)
-
标准库(就是一系列具有各种功能的对象比如Array、Date、Math等)。
-
想要实现其他复杂的操作和效果,都要依靠 宿主环境 提供API,目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是 浏览器 和 操作系统 ;
- 回顾 JavaScript 语法特性
- 变量、数据类型、流程控制。
- 函数(基本声明参数,作用域,回调函数)、面向对象(原型,构造函数,this的指向,new的过程)。
2. 什么是 Node.js
Node
是一个基于Chrome V8
引擎的JavaScript
运行环境。
Node
不是一种独立的语言、Node
不是 JavaScript
框架,
Node
是一个除了浏览器之外的、可以让JavaScript
运行的环境
Node.js
是一个让 JavaScript 运行在服务端的开发平台(因此使得JavaScript也可以制作web服务器),是使用事件驱动, 异步非阻塞I/O,单线程,跨平台的 JS 运行环境;
3. Node.js 相关资源
4. Node.js 的特点
- 异步
- 事件与回调函数配合
- 保持了JavaScript单线程
- 跨平台
二、Node.js 起步
推荐工具下载:
1. 下载 Node.js
对于已经装过的,重新安装就会升级
安装成功后,打开cmder命令窗口,输入
node --version 或者 node -v (显示node的版本号)
2. REPL环境
node中的REPL环境类似于浏览器中的 Console控制台 ,可以做一些代码测试。
按ctrl + 两次c 退出REPL环境。
但是, 我们写代码肯定不是在控制台中写,而是写在一个单独的.js文件中。
3. node运行 JavaScript 代码
在js文件目录下打开Powershell窗口或者cmder窗口,输入命令:
在vscode中打开终端,输入命令:
4. 理解 Node 中的模块
(1)浏览器(客户端)中的 JavaScript
(2)Node中的 JavaScript
三、node.js 核心模块的使用
1. fs 模块
node核心模块之一,用于操作文件;
- 文件属性读写。
其中常用的有fs.stat
、fs.chmod
、fs.chown
等等。
- 文件内容读写。
其中常用的有fs.readFile
、fs.readdir
、fs.writeFile
、fs.mkdir
等等。
- 底层文件操作。
其中常用的有fs.open
、fs.read
、fs.write
、fs.close
等等。
中文手册 : http://nodejs.cn/api/fs.html
(1)fs.readFile() 读取文件内容
TXT:(05duqu.txt)
hello node
JavaScript:(05fs.读取文件.js)
/* 模块的引入 */
var fs = require('fs');
/* 读取文件中的内容(文本内容) */
fs.readFile('./05duqu.txt','utf8',function(err,data){
/* 存在异常就抛出 */
if(err) throw err;
console.log(err);
console.log(data);
})
console.log(1111);
在vscode终端中输出:(右击项目文件夹,打开终端)
为什么先输出1111???
如果了解node.js环境下的EventLoop就可以明确的清楚为什么1111先输出。
原因就是因为异步的I/O问题。实际上在fs调用完readFile方法时就已经执行完了,但是前端获取资源的速度取决于后端的响应速度(需要时间),然而JavaScript自上而下不等待执行。因此就存在了异步I/O,简单来说就是回调函数。
(2)fs.writeFile() 写文件内容
JavaScript:(06fs.写文件.js)
/* 模块的引入 */
var fs = require('fs');
/* 自动创建文件,写入写入内容 */
fs.writeFile('./06duqu.txt','hello world','utf8',function(err,data){
/* 存在异常就抛出 */
if(err) throw err;
console.log(err);
console.log('保存成功');
});
// 注意写入内容会覆盖原本的内容
在vscode终端中输出:
结果:
追加内容
JavaScript:(07fs.追加内容.js)
var fs = require('fs');
// 追加内容
// 先读出数据,再写入
fs.readFile('06duqu.txt','utf8',function(err,data){
if(!err){
var dat = data+',hello node!';
fs.writeFile('06duqu.txt',dat,'utf8',function(err){
if(!err){
console.log('搞定');
};
});
}else{
throw err;
};
});
在cmder中输入启动命令:
结果:
2. url 模块
nodejs中用户url格式化和反格式化模块。用于url解析、处理等操作的解决方案
(1)url.parse(urlString[, parseQueryString[, slashesDenoteHost]])(常用)
解析 url 的每一个部分。
- urlString:
<strinr>
要解析的 URL 字符串。 - parseQueryString:
<boolean>
如果为true
,则query
属性总会通过querystring
模块的parse()
方法生成一个对象。 如果为false
,则返回的 URL 对象上的query
属性会是一个未解析、未解码的字符串。 默认为false
。 - slashesDenoteHost :
<boolean>
如果为true
,则//
之后至下一个/
之前的字符串会被解析作为host
。 例如,//foo/bar
会被解析为{host: 'foo', pathname: '/bar'}
而不是{pathname: '//foo/bar'}
。 默认为false
。
url.parse() 方法会解析一个 URL 字符串并返回一个 URL 对象。
var url = require("url");
var myurl = "http://www.nodejs.org/some/url/?with=query¶m=that#about";
var parsedUrl = url.parse(myurl);
console.log(parsedUrl);
/*{ protocol: 'http:', //协议
slashes: true, //斜杠
auth: null, //使用人(作者) username password
host: 'www.nodejs.org', //主机
port: null, //端口号
hostname: 'www.nodejs.org', //主机名
hash: '#about', //#号
search: '?with=query¶m=that', //搜索
query: 'with=query¶m=that', //询问
pathname: '/some/url/', //路径名
path: '/some/url/?with=query¶m=that', //绝对路径
href: 'http://www.nodejs.org/some/url/?with=query¶m=that#about' //引用地址
}*/
区别:
var url = require("url");
var myurl = "http://www.nodejs.org/some/url/?with=query¶m=that#about";
var parsedUrl = url.parse(myurl,true);
console.log(parsedUrl);
/*{ protocol: 'http:', //协议
slashes: true, //斜杠
auth: null, //使用人(作者) username password
host: 'www.nodejs.org', //主机
port: null, //端口号
hostname: 'www.nodejs.org', //主机名
hash: '#about', //#号
search: '?with=query¶m=that', //搜索
query: { with: 'query', param: 'that' }, //询问
pathname: '/some/url/', //路径名
path: '/some/url/?with=query¶m=that', //绝对路径
href: 'http://www.nodejs.org/some/url/?with=query¶m=that#about' //引用地址
}*/
/* 获取询问中的属性值 */
var query = url.parse(myurl,true).query.with;
console.log(query); //=> query
(2)url.format(urlObject)
根据一个对象构造 url 。
var url=require('url');
var obj1={ protocol: 'https:',
slashes: true,
auth: null,
host: 'blog.csdn.net',
port: null,
hostname: 'blog.csdn.net',
hash: null,
search: null,
query: null, //此处可以为一串字符,也可以是一个对象,会自动解析成字符串
pathname: '/Errrl',
path: '/Errrl',
href: 'https://blog.csdn.net/Errrl'
};
var url1=url.format(obj1);
console.log(url1); //=> https://blog.csdn.net/Errrl
(3)url.resolve(from, to)
修改路径。
- from:
<string>
解析时相对的基本 URL。 - to:
<string>
要解析的超链接 URL。
url.resolve('/one/two/three', 'four') // '/one/two/four'
url.resolve('http://example.com/', '/one') // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'
3. http 模块
(1)开启服务器
开启服务器,响应数据
/* 引入http模块 */
var http = require('http');
/* 创建服务器 */
var server = http.createServer();
/* 监听本机端口号 */
server.listen(8080,function(){
/* 访问成功后执行 */
console.log('请到浏览器中访问127.0.0.1:8080');
})
/* 事件监听,响应数据 */
server.on('request',function(req,res){
console.log('服务端收到客户端的请求啦!!!');
// 向客户端页面返回字符串
response.write("hello node");
// 结束响应
response.end();
})
命令行输入:
结果:
响应中文
注意:响应中文需要设置响应头。
如果直接
res.write("你好 node 环境");
结果:
设置响应头:
res.setHeader('content-type','text/html;charset=utf-8');
//res.setHeader('content-type','text/plain;charset=utf-8');
res.write("你好 node 环境");
结果:
(2)响应 HTML 页面
设置响应头:
res.setHeader('content-type','text/html;charset=utf-8');
res.write("<h1>你好 node 环境</h1>");
结果:
但是,我们不能一直将html代码写到服务器的方法中,而是需要建一个xx.html的文件,将html文件中的内容返回给客户端;
HTML:(04http响应html格式文件.html)
<body>
<h1>自述:</h1>
<div style="font-size: 20px;font-weight :bold;">Errrl</div>
<div>是帅哥!</div>
<div style="color:red">都不好意思了,哈哈哈</div>
<i>谢谢</i>
</body>
JavaScript:(04http响应html格式文件.js)
/* 引入需要的核心模块 */
var http = require('http');
var fs = require('fs');
/* 创建服务器 */
var server = http.createServer();
/* 监听端口号 */
server.listen(8080,function(){
console.log('请在浏览器中访问127.0.0.1:8080');
})
/* 绑定请求事件 */
server.on('request',funtion(req,res){
fs.readFile('04http响应html格式文件.html',function(err,data){
res.setHeader('content-type','text/html;charset=utf-8');
res.end(data);
})
})
结果:
(3)响应图片
将图片添加到项目的目录中。
HTML:(05http响应html中的图片.html)
<body>
<h1>自述:</h1>
<img src="./3.jpg" alt="">
<div style="font-size: 20px;font-weight :bold;">Errrl</div>
<div>是帅哥!</div>
<div style="color:red">都不好意思了,哈哈哈</div>
<i>谢谢</i>
</body>
JavaScript:(05http响应html中的图片.js)
var http = require('http');
var fs = require('fs');
/* 创建服务器 */
var server = http.createServer();
/* 监听端口号 */
server.listen(8080,function(){
console.log('请在浏览器中访问127.0.0.1:8080');
})
/* 绑定请求事件 */
server.on('request',function(req,res){
/* 先来看看请求地址有几条 */
//console.log(req.url); //=>输出: / /favicon.ico
//url 属性返回请求的URL字符串
var urls = req.url;
if(urls == '/'){
/* 读取html文件 */
fs.readFile('./05http响应html中的图片.html','utf8',function(err,data){
res.setHeader('content-type','text/html;charset=utf-8');
res.end(data);
})
}else if(urls.indexOf('jpg') >= 0){
fs.readFile('./3.jpg',function(err,data){
res.end(data);
})
}
})
结果:
(4)响应其他静态资源
响应一切html需要的静态资源,比如:图片资源,javascript资源,jQuery资源。做到统一响应。
向HTML中添加一个外部的 JavaScript 文件:
<body>
<h1>自述:</h1>
<img src="./3.jpg" alt="">
<div style="font-size: 20px;font-weight :bold;">Errrl</div>
<div>是帅哥!</div>
<div style="color:red">都不好意思了,哈哈哈</div>
<i>谢谢</i>
</body>
<script src="./05.js"></script>
JavaScript:(05.js)
document.getElementsByTagName('i')[0].onclick = function(){
this.style.fontSize = '50px';
}
修改JavaScript:(05http响应html中的图片.js)
var http = require('http');
var fs = require('fs');
/* 创建服务器 */
var server = http.createServer();
/* 监听端口号 */
server.listen(8080,function(){
console.log('请在浏览器中访问127.0.0.1:8080');
})
/* 绑定请求事件 */
server.on('request',function(req,res){
/* 先来看看请求地址有几条 */
//console.log(req.url); //=>输出: / /favicon.ico
//url 属性返回请求的URL字符串
var urls = req.url;
if(urls == '/'){
/* 读取html文件 */
fs.readFile('./05http响应html中的图片.html','utf8',function(err,data){
res.setHeader('content-type','text/html;charset=utf-8');
res.end(data);
})
}else{
fs.readFile('.'+urls,function(err,data){
res.end(data);
})
}
})
结果:
4. 服务器遍历文件及文件夹-案例
模仿Apache服务器,遍历文件及文件,显示时间及大小;
右键另存为,下载页面当作静态页面模板(html)使用,并且删除需要遍历的文件以及文件夹,通过向http.js发送Ajax请求再次获取文件以及文件夹,重新渲染:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<!-- saved from url=(0036)http://localhost/%e6%a1%86%e6%9e%b6/ -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Index of /框架</title>
</head>
<body>
<h1>Index of /框架</h1>
<table>
<tbody>
<tr>
<th valign="top"><img src="./img/blank.gif" alt="[ICO]"></th>
<th><a href="http://localhost/%e6%a1%86%e6%9e%b6/?C=N;O=D">Name</a></th>
<th><a href="http://localhost/%e6%a1%86%e6%9e%b6/?C=M;O=A">Last modified</a></th>
<th><a href="http://localhost/%e6%a1%86%e6%9e%b6/?C=S;O=A">Size</a></th>
<th><a href="http://localhost/%e6%a1%86%e6%9e%b6/?C=D;O=A">Description</a></th>
</tr>
<tr>
<th colspan="5">
<hr>
</th>
</tr>
<tr>
<td valign="top"><img src="./img/back.gif" alt="[PARENTDIR]"></td>
<td><a href="http://localhost/">Parent Directory</a> </td>
<td> </td>
<td align="right"> - </td>
<td> </td>
</tr>
</tbody>
</table>
<address>Apache/2.4.39 (Win64) PHP/7.2.18 Server at localhost Port 80</address>
</body>
<!-- 发送Ajax请求获取数据,并重新渲染 -->
<!-- 此种方法称为客户端渲染 -->
<script>
var xhr = new XMLHttpRequest();
xhr.open('get','./read_list');
xhr.send();/* 发送请求 */
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
var data = JSON.parse(xhr.responseText);
console.log(data);
var htmls = '';
for(let i = 0; i < data.length; i++){
/* 架构渲染 */
htmls += '<tr>'
if(data[i].type == 'text'){
htmls += '<td valign="top"><img src="./img/text.gif" alt="[DIR]"></td>';
}else{
htmls += '<td valign="top"><img src="./img/folder.gif" alt="[DIR]"></td>'
}
htmls += '<td><a href="#">' + data[i].name + '/</a> </td>'
htmls += '<td align="right">' + data[i].mtime + '</td>'
htmls += '<td align="right">' + data[i].size + '</td>'
htmls += '<td> </td>'
htmls += '</tr>'
}
htmls += '<tr>'
htmls += '<th colspan="5">'
htmls += '<hr>'
htmls += '</th>'
htmls += '</tr>'
/* 最后将整一个htmls添加到tbody中 */
document.getElementsByTagName('tbody')[0].innerHTML += htmls;
}
}
</script>
使用node载入静态页面,获取当前路径中的所有文件或文件夹,并获取其所有信息:
JavaScript:(http.js)
/* 引入核心模块 */
var http = require('http');
var fs = require('fs');
/* 创建服务器 */
var server = http.createServer();
server.listen(8080,function(){
console.log('启动成功');
});
server.on('request',function(req,res){
/* 获取请求url */
var urls = req.url;
if(urls == '/'){
//加载html
fs.readFile('./index.html',function(err,data){
res.setHeader('content-type','text/html;charset=utf-8');
res.end(data);
})
}else if(urls == '/read_list'){
/* 此处获取当前路径下的所有文件各自的信息 */
/* 以一个数组的形式存在 */
/**
*[
*{name:'http.js',mtime:'2019-09-04T05:18:11.635Z',size:'866'}
*{name:'index.html',mtime:'2019-09-04T05:18:11.635Z',size:'866'}
*...
*]
*/
/* 读取当前路径下的文件夹名 */
fs.readdir('./','utf8',function(err,data){
//定义一个空数组
var arr = [];
var cont = 0;
/* 循环遍历每一个文件名,获取其中的信息 */
for(let i = 0;i<data.length;i++){
//每一个文件一个对象,用于保存信息
arr[i] = {};
/* for循环中存在异步,解决方法闭包 */
(function(i){
/* 获取每个文件的所有信息 */
fs.stat(data[i],function(err,datas){
/* 分清是文件还是文件夹,自定义一个type属性,响应式用于分别图标 */
if(datas.isFile()){//用于判断是否是文件
arr[i].type = 'text';
}else{
arr[i].type = 'folder'
}
arr[i].name = data[i];
arr[i].mtime = datas.mitime;
arr[i].size = datas.size;
cont++;
if(cont == data.length){
response.end(JSON.stringify(arr));//响应arr
}
})
})(i)
}
})
}else{
fs.readFile('.' + req.url, function (err, data) {
res.end(data);
})
}
})
效果:
5. 规定格式展示文件的mtime
使用第三方模块,规定 mtime 格式。
(1)安装第三方模块
项目路径下打开命令行窗口,输入命令:npm install moment。
开始下载:
下载完成:
(2)引入第三方模块
JavaScript:(http.js)
var moment = require('moment');
(3)使用第三方模块
修改JavaScript:(http.js)
arr[i].mtime = moment(file.mtime).format('YYYY-MM-DD hh:mm:ss');
最终结果: