文章目录
一、node是什么?
1 目的
只有了解服务端才可更好配合服务端开发人员,前端+后端+运维部署
2 是什么
前端:html、js、css
后端:JAVA、PHP、Python、Rudy、Net、Nodejs
- node不是一门语言
- 不是库、不是框架
- 是一个js运行环境,可以解析和执行js代码
- 以前只有浏览器可以解析执行js代码
- 现在的js可以脱离浏览器执行,归功于nodejs
浏览器里的js
- EcmaScript 基本语法
- BOM
- DOM
nodejs中的js
- 没有BOM和DOM
- EcmaScript
- 在node这个js执行环境中为js提供一些服务器级别操作的api,例如文件读写、网络服务的构建、网络通信、http服务器等处理
所以主要学的就是api
3 特性
- 事件驱动
- 非阻塞IO模型
- 后面会学习
4 npm
npm是基于nodejs开发的包管理工具,世界上最大的开源库生态系统,绝大多数js相关的第三方包都存放在npm上,为了让开发人员更方便去下载和使用。
5 V8引擎
- node构建于V8引擎之上。
- 浏览器内分为渲染引擎和js执行引擎。
- 代码只是具有特定格式的字符串,引擎可以认识他,帮助我们去解析和执行。
- chrome的v8是公认的最快引擎
二、介绍node
1 nodejs能做什么
- web服务器后台
- 开发命令行工具【git(基于c)就是一个命令行工具,还有hexo(基于node)、npm(基于node)】
- 对于前端开发工程师来说,接触node最多的是他的命令行工具
自己写的很少,主要是使用别人发的
webpack
gulb
npm
2 书本
- 深入浅出nodejs(偏理论和底层,帮助理解原理)
- nodejs权威指南(api讲解,没有实战,做api参考)
- js标准参考教程alpha(阮一峰)
- node入门(易于入门※)
- 官网api文档
- 中文文档(版本旧,可参考)
- cnode社区(分享交流)
- cnode新手入门
3 学到什么
- b/s编程模型:任何服务端bs编程模型都是一样的,和语言无关;node只是作为一个工具
- 模块化编程:Requirejs,Seajs
- node中常用api
- 异步编程
- express开发框架
- es6
三、基本编写
1 读写文件api
- 在node中,采用es编码
- 没有dom和bom,console不出来document和window
- 和浏览器中的js不一样
- 可以用来读取文件,浏览器中的js没有文件操作能力
// fs是file-system的简写,就是文件系统的意思
// 在node中进行文件操作,就必须引入fs这个核心模块
// 在fs这个核心模块中,提供了文件操作相关的api
let fs = require('fs');
// 第一个参数是路径,第二个参数是回调函数
// 成功 data数据,error null
// 失败 data undefined,error数据
fs.readFile('hello.txt', function (err, data) {
// 默认文件存储的都是二进制数据,看到的是二进制转为16进制
// 需要使用tostring转成我们认识的字符
console.log(data.toString());
})
输出为helle nodejs,成功。注意回调函数第一个参数是error,第二个是data,不要弄错顺序。还要注意路径。
增加一个错误处理
fs.readFile('hello.txt', function (err, data) {
// 默认文件存储的都是二进制数据,看到的是二进制转为16进制
// 需要使用tostring转成我们认识的字符
if (err) {
console.log('读取文件失败');
return;
}
console.log(data.toString());
})
若要写入文件,代码如下:
let fs = require('fs');
// 成功 error是null,
// 失败,error是错误对象
fs.writeFile('hello.txt', 'write something', function (err) {
console.log('error', err);
})
文件成功被改写, 错误处理如下
if(err) {
console.log('写入失败');
} else {
console.log('写入成功');
}
2 简单的http
基础
可以使用node非常轻松构建一个node服务器
在node中,专门提供一个核心模块:http,该模块职责就是帮助创建编写服务器。
let http = require('http');
// 创建web服务器,server实例
let httpServer = http.createServer();
// 注册request请求事件,当客户端请求过来,自动触发request请求事件,然后执行回调函数
httpServer.on('request', function () {
console.log('收到请求');
})
// 启动服务器
httpServer.listen(3000, function () {
console.log('服务器启动成功,可以通过localhost:3000访问');
});
使用terminal执行该文件,随后在浏览器输入localhost:3000, 得到结果如图
因为代码中没有设置响应,所以浏览器一直处于转圈圈状态
CTRL+C就退出了
处理响应
let http = require('http');
// 创建web服务器,server实例
let httpServer = http.createServer();
// 注册request请求事件,当客户端请求过来,自动触发request请求事件,然后执行回调函数
// request处理函数需要接收两个参数
// Request请求对象,可以用来获取请求信息
// Response响应对象 可以用来给客户端发送响应信息
httpServer.on('request', function (request, response) {
console.log('收到请求,路径为', request.url);
// write可以用来给客户端发送响应数据
// wirte可以使用多次,但是最后一定要end来结束响应,否则客户端会一直等待
response.write('hello');
response.write('nodejs');
response.end(); // 很重要!!!!
})
// 启动服务器
httpServer.listen(3000, function () {
console.log('服务器启动成功,可以通过localhost:3000访问');
});
此时访问localhost:3000得到
终端界面为
不同路径响应不同结果
例如/login, 响应 login,之前已经获取到url了,使用if-else即可。
let http = require('http');
let server = http.createServer();
server.on('request',function (req, res) {
let url = req.url;
console.log(url);
// 使用write比较麻烦,推荐使用end直接发送响应数据
// 根据不不同url响应
// req.url获取到的是端口号之后的那一部分路径,都是以/开头
if (url === '/') {
res.end('index');
} else if (url === '/login') {
res.end('login');
} else {
res.end('404 not found');
}
})
server.listen(3000, function () {
console.log('服务器已启动与localhost:3000');
});
// 浏览器默认以80请求`
【注意】:响应内容只能是二进制数据或字符串!不能res.end(12),会报错。所以如果想返回对象的话需要转为json字符串。
server.on('request',function (req, res) {
let url = req.url;
console.log(url);
// 使用write比较麻烦,推荐使用end直接发送响应数据
// 根据不不同url响应
// req.url获取到的是端口号之后的那一部分路径,都是以/开头
if (url === '/') {
res.end('index');
} else if (url === '/login') {
res.end('login');
} else if (url === '/product') {
let product = [
{
name: 'pencil',
price: 1
}, {
name: 'flower',
price: 20
}
]
res.end(JSON.stringify(product));
} else {
res.end('404 not found');
}
}) `
四、总结一
nodejs是什么
- js运行时
- 既不是语言,也不是框架,而是一个平台
nodejs中的js - 没有BOM、DOM
- ES基本的js语言部分
- 在node中为js提供了一些服务器级别的api,如文件操作能力、http服务能力
五、node中的js
- EcmaScript
-
- 没有DOM和BOM
- 核心模块
- 第三方库
- 用户自定义模块
1 核心模块
Node为js提供了很多服务器级别的api,这些api绝大多数都被包装到了一个具名的核心模块中了,例如文件操作的fs核心模块,http服务构建的http,path路径操作模块,os操作系统信息获取模块。
如果说到这个模块是核心模块,就要知道如果想使用,比如引入:
let fs = require('fs');
在node官网可以查看核心模块。
let os = require('os');
// 获取机器信息
console.log(os.cpus());
console.log(os.totalmem());
let path = require('path');
// 处理路径
console.log(path.extname('c:/code/self/node/study/hello.txt'));
2 模块化编程
基本用法
node想执行多文件需用模块化编程
举例a文件和b文件
require是用来加载模块的:核心模块;自定义模块(js文件)
a文件:
console.log('a start');
// b 被解析并执行
// 相对路径的./不可省略,会当成核心模块
// 可以省略后缀名
require('./b');
console.log('a end');
b文件
console.log('b start');
可见require的意思就是加载并执行这部分代码, 按顺序
模块作用域
【注意】node中没有全局变量这个概念
模块作用域简单来说就是文件作用域,超出这个文件的都没用,外部访问不到内部,内部也访问不到外部,比如b和a中都有foo变量,互不影响,不会因为a require了b,变量名就冲突了。
既然是模块作用域,那如何让模块和模块之间进行通信,默认是封闭的,你可以加载我但是得不到我任何成员。有时候加载模块的目的不是简简单单执行一段代码,而是使用里面的一些成员,所以需要加载与导出。
requie方法有两个作用:
- 加载并执行某段代码
- 拿到被加载文件模块导出的接口对象,在每个文件模块都提供了一个对象:exports(默认是一个空对象,需要做的就是将所有需要被外部访问的成员挂载到对象中,对象是动态的)
示例代码如下:
a:
let b = require('./b.js');
console.log(b);
b:
exports.foo = 'hello';
·
六、Web服务开发
3 ip和端口号
所有联网的程序都需要进行网络通信。
计算机中只有一个物理网卡,而且同一局域网中,网卡的地址是唯一的。
网卡是通过唯一ip地址来进行定位的。
把我之前写的http程序运行起来,同一局域网的主机访问“172.20.10.7:3000“即可访问。
【注意】所有需要联网的应用程序都会占用一个端口号,我们浏览器去访问服务器也是有一个端口号的,浏览器自己开了一个闲的端口号。
- 0-65536之间
- 开发过程中不用默认的
- 使用3000,5000没用好记的
- 网站上线部署默认是80
- 可以开启多个服务,但开启端口号不能重复
4 响应内容类型
如果不设置头,响应内容为中文时会乱码,设置如下:
let http = require('http');
let server = http.createServer();
server.on('request', function (req, res) {
// 在服务端默认发送的数据是utf8编码内容
// 但是浏览器不知道是utf8, 按当前操作系统默认方式解析
// 中文操作系统是gbk
// 告诉浏览器我给你发送的内容是什么编码的
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('你好');
})
server.listen(3000, function () {
console.log('服务启动');
})
如果响应内容是< p >hello< /p >,不加setHeader,浏览器收到后会将其作为html渲染。但是此时中文还是乱码,采用上述方式加setHeader解决乱码会发现浏览器不以html解析了,因为text/plain是普通文本,如果发送的是html格式,需要用text/html;charset=utf-8。
所以建议都要加上正确的contentType。
3 发送页面
之前的http服务器比较简单,接下来尝试返回html页面。在项目目录下新建resouce文件夹,在其中放一些文件,比如index.html, main.js, main.css, puppy.jpg, hello.txt。编写服务端文件如下:
let http = require('http');
const fs = require('fs');
let server = http.createServer();
server.on('request', function (req, res) {
let url = req.url;
// 想要返回html文件,但只能返回字符串,所以实际上是从文件中读取字符串返回
if (url === '/') {
// 结合fs
fs.readFile('./resource/index.html', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('文件读取失败');
} else {
// data默认二进制数据
res.setHeader('Content-type', 'text/html; charset=utf-8');
res.end(data);
}
})
}
})
server.listen(3000, function () {
console.log('server is running');
})
如果想要改变index.html,无需重启,因为再发请求就服务端程序会再获取一次文件内容,访问到的就是新的index.html,服务端程序无需重启。
如果想返回图片或css文件等,访问oschina,里面有标明不同资源对应的content-type,如下为增加图片。(图片无需指定编码)
let http = require('http');
const fs = require('fs');
let server = http.createServer();
server.on('request', function (req, res) {
let url = req.url;
// 想要返回html文件,但只能返回字符串,所以实际上是从文件中读取字符串返回
if (url === '/') {
fs.readFile('./resource/index.html', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('文件读取失败');
} else {
res.setHeader('Content-type', 'text/html; charset=utf-8');
// data默认二进制数据,但在这里不用toString,因为已经指定utf-8了,除非想在后台打印
res.end(data);
}
})
} else if (url === '/puppy') {
fs.readFile('./resource/puppy.jpg', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('文件读取失败');
} else {
// 图片无需制定编码
res.setHeader('Content-type', 'image/jpeg');
res.end(data);
}
})
}
})
server.listen(3000, function () {
console.log('server is running');
})
七、总结
1 模块系统
- 在node中没有全局作用域的概念
- 在node中可以通过require来加载和执行多个js脚本文件
- require加载只是执行其中的代码,文件和文件之间由于模块作用域,所以不会有污染的问题
-
- 模块是完全封闭的
-
- 外部无法访问内部
-
- 内部也无法访问外部
- 模块作用域固然带来一些好处,可以加载多个js文件,可以完全不避免变量命名该冲突
- 但是某些情况下模块与模块之间是需要通信的
- 在每个模块中都提供了一个
exports
对象 - 该对象默认是一个空对象
- 你要做的就是把需要被外部访问使用的成员手动挂载到
exports
接口对象中 - 然后谁要
require
这个模块,谁就可以得到模块内部的exports接口对象
2 核心模块
- 核心模块是由node提供的一个个具名模块,他们都有自己特殊的名称标识
- 所有核心模块使用的时候都必须用require手动加载
3 http
- require
- 端口号
-
- ip地址定位计算机,端口号定位具体应用程序
- Content-Type
-
- 服务器最好把每次响应数据是什么内容都告诉客户端,而且要正确告诉
-
- 不同资源对应的饿Content-Type是不一样的
-
- 对于文本类型的数据,最好都加上编码,目的是为了防止中文解析乱码的问题
- 通过网络发送文件
-
- 发送的并不是文件,本质上来说是发送文件的内容
-
- 当浏览器收到服务器响应内容之后,就会根据你的Content-Type进行对应的解析处理