Node.js基础
😎简介
官网解释
Node.js® is a JavaScript runtime built on. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem,npm, is the largest ecosystem of open source libraries in the world.
NodeJS 是基于Chrome V8引擎的 JavaScript 运行环境。NodeJS使用事件驱动的、非阻塞的I/O模型,这使它变得轻量级和高效。NodeJS的包管理生态是 NPM,是现在世界上最大的开源程序包库。
创始
创始人Ryan Dahl,是一位专注于实现高性能Web服务器优化的专家,一直想要解决服务器高并发问题,在Chrome V8 引擎(高性能js引擎)发布之后,基于V8引擎开发出了一个可以让js运行在服务器端的环境。
V8运行在客户端,既然在客户端是可以发请求接收返回信息,那么把它放到服务器端应该也可以,所以他改了一些V8引擎的内核,收发请求之类的,然后将V8搬到了服务器端。
nodejs不是一种独立的语言,是使用js来进行编程,运行在V8引擎上。(底层代码是C++)
NodeJS之前,JavaScript运行环境是浏览器,NodeJS之后JavaScript又多了一个运行环境,就是NodeJS。
为什么要学node.js
- 性能好、部署容易,能够轻松处理高并发问题。
- 它让我们可以用js写后端程序,顶层路由设计等。
- Node.js 是前端工程化的重要支柱之一。(目前前端开发环境离不开nodejs,是我们使用很多工具和提高开发效率的基础)
👀Node.js和Javascript的关系
javascript的组成
- ECMAScript(语言基础,如:语法、数据类型结构以及一些内置对象)
- DOM(一些操作页面元素的方法)
- BOM(一些操作浏览器的方法)
node.js的组成
- ECMAScript(语言基础,如:语法、数据类型结构以及一些内置对象)
- OS(操作系统)
- file(文件系统)
- net(网络系统)
- database(数据库)
总结
- node.js是平台,javascript是编程语言;
- node.js应用于后端,javascript应用于前端
- javascript是客户端编程语言,需要浏览器的javascript解释器进行解释执行;
- node.js是一个基于Chrome JavaScript运行时建立的平台,它是对Google V8引擎进行了封装的运行环境;
- 简单的说node.js就是把浏览器的解释器封装起来作为服务器运行平台,用ECMAScript语法进行编程,在node.js上运行。因此,可以将node.js看成是运行在服务端的 javascript。
🧐特点
即是如何提高服务器的性能的
单线程
java/php/.net服务器语言中,为每个客户端创建一个新的线程,要让Web应用程序支持更多的用户,就需要增加服务器的数量,这样硬件成本就变高了。
nodejs只使用一个线程,用户连接就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让程序宏观上是并行的。
减少了内存开销,省去了操作系统创建/销毁线程的时间开销。
非阻塞I/O
传统单线程处理机制中,一旦有I/O操作,比如访问数据库,整个线程都会停下来,等待数据库的响应,才会执行后面的代码。也就是说I/O阻塞了代码的执行,极大地降低了代码的运行效率。
非阻塞IO,在遇到I/O操作时,比如访问了数据库的代码后,会立即转而执行后面的代码,把对数据库返回结果的处理代码放在回调函数中。当某个I/O执行完毕,会以事件的方式通知这个线程,然后线程执行这个事件的回调函数。这个线程一直在进行计算操作,其CPU核心利用率一直是100%。
事件驱动
不管是新用户的请求还是老用户的I/O完成,都将以事件的方式加入事件循环,等待调度(注意不是排队,会优先执行老用户的回调函数)。
底层代码中近半数都用于事件队列,回调函数队列的构建。
其实三个特点是一回事。
nodejs适合做什么
善于I/O,不善于计算,即善于任务调度,不善于大量CPU计算。也非常适合与websocket配合,开发长连接的实时交互应用程序。
- 用户表单收集(百度表单填写就是用的node)
- 考试系统
- 聊天室
同时它只适用于小型开发,无法挑战PHP等老牌后台语言
感觉其更重要的意义在于是一个强大的工具。
🐵NPM
npm node package manager,通常称为node包管理器。npm不需要单独安装。在安装Node的时候,会连带一起安装。
node包就是封装了特定功能的模块,有人将一些经常使用的、成熟的功能封装成node包并将其放在了npm上。通过npm我们可以免费使用其他人做好的模块,只需下载后引入即可。当然你也可以自己造轮子为社区做贡献hhh。
cnpm
package.json
package.json 用于定义包的属性,模块的描述文件。
npm init用来初始化生成一个新的package.json文件。
//最简单的,scripts可以自行定义命令
{
"name": "nodejs",
"version": "1.0.0",
"description": "one demo",
"main": "demo.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node demo.js"
},
"author": "zlj",
"license": "ISC"
}
- main 入口文件
- version 版本号 ('^'表示固定版本号)
- dependencies 项目运行所依赖的模块
- devDependencies 项目开发所需要的模块
- scripts 用于指定脚本命令(npm run + 命令)
- dependencies是生产环境,devDependencies是开发环境。在开发项目时,npm install 会自动下载devDependencies和dependencies下面的模块。当你只是使用某个包时不会下载devDependencies下面的模块,而当你修改某个包/项目时所有依赖都会下载。
- ``npm run
为每条命令提供了pre-和post-两个钩子(hook)。以npm run lint为例,执行这条命令之前,npm会先查看有没有定义prelint和postlint两个钩子,如果有的话,就会先执行npm run prelint,然后执行npm run lint,最后执行npm run postlint`。
//复杂一点的项目
{
"name": "mydemo",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "eslint --ext .js,.vue src test/unit/specs"
},
"dependencies": {
"core-js": "^3.6.4",
"vue": "^2.6.11",
"vue-router": "^3.1.5"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.2.0",
"@vue/cli-plugin-router": "^4.2.0",
"@vue/cli-service": "^4.2.0",
"node-sass": "^4.12.0",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11"
}
}
package-lock.json 只是为了确保依赖包的正常使用,比如锁定依赖树,包的版本等等。
npm install
npm install <pkg>安装node包/模块。(也可以去GitHub下载后放到node_modules文件夹下,再自己改package.json,不建议)
--save / -S 模块名将被添加到dependencies
--save-dev / -D 模块名将被添加到devDependencies
-g 将模块安装到全局环境中
install命令总是安装模块的最新版本,如果要安装模块的指定版本,可以在模块名后面加上@和版本号。
node_modules
require('某个模块'); //默认是从node_modules文件夹下引入
通过npm install可以安装依赖node包/模块,当上传GitHub或给他人看项目时忽略即可。
🐾实操
安装
nvm:可以切换node版本的工具,可选择性安装。
检查安装是否成功(注意环境变量)
- node -v
- npm -v
几个小点
- 是跨平台的,Linux和windows编程基本一样。
- node命令进入命令交互模式,Node 自带了交互式解释器,表示一个电脑的环境。
- 本质就是一个JavaScript运行环境。
- 如果想修改程序,要重启服务。(nodemon:可自动重启服务的工具)
简单使用栗子
const http = require('http')
const server = http.createServer((req,res)=>{
res.writeHead(200,{'Content-type':'text/html;charset=UTF-8'});
res.end("嘿嘿嘿,这是我的第一个Node页面")
});
server.listen(8080,'127.0.0.1');
console.log('Server is running at http://127.0.0.1:8080/');
⭐基本模块
fs
fs模块就是文件系统模块,负责读写文件。nodeJs内置的fs模块提供了异步和同步的方法
异步
//异步读取文件
fs.readFile("./demo.txt", function (err, data) {
if (err) {
console.log(err);
} else {
console.log("data:" + data.toString());
}
});
//异步写入文件
const data = {
'name' : 'hy',
'age' : '16',
'id' : '2022'
}
const dataText = JSON.stringify(data)
const option = {
encoding : 'utf8' ,
flag : 'w'
}
fs.writeFile('demo.txt' ,dataText, option , function(err){
if(err){
console.log('写文件失败')
}else{
console.log('写文件成功')
}
} )
同步
//同步读文件
file = fs.readFileSync(filePath , 'utf-8')
//同步写入文件
fs.writeFileSync(filename, data[, options])
查看文件信息
fs.stat("./demo.txt", function (err, stats) {
if (err) {
console.log("failed");
return 0;
}
console.log(stats);
});
| 方法 | 描述 |
|---|---|
stats.isFile() | 如果是文件返回 true,否则返回 false。 |
stats.isDirectory() | 如果是目录返回 true,否则返回 false |
//列出文件
fs.readdir(path , callback)
//删除文件
fs.unlink(path , callback)
//截断文件
fs.truncate(path , len , callback) 返回true和flase
//创建目录
fs.mkdir(path , [mode] , callback)
//删除目录
fs.rmkdir(path , callback)
url
用来处理和解析url的模块
//parse 用来解析url,返回url属性对象
const url = require("url")
const myURLA = url.parse('https://www.baidu.com/p/a/t/h?query=string#hash');
console.log(myURLA);
/**
Url {
protocol: 'https:', 协议
slashes: true,
auth: null, 用户密码
host: 'www.baidu.com', 主机名
port: null, 端口号
hostname: 'www.baidu.com', 主机名(不带端口号)
hash: '#hash', 哈希值
search: '?query=string', 查询字符串
query: 'query=string', 请求参数
pathname: '/p/a/t/h', 路径名
path: '/p/a/t/h?query=string', 带查询的路径名
href: 'https://www.baidu.com/p/a/t/h?query=string#hash' }
*/
resolve
//以一种 Web 浏览器解析超链接的方式把一个目标 URL 解析成相对于一个基础 URL
url.resolve('http://example.com/', '/one'); // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'
format
let urlobj = url.format({
protocol: 'https',
hostname: 'example.com',
pathname: '/some/path',
query: {
page: 1,
format: 'json'
}
}); //https://example.com/some/path?page=1&format=json
path
path.join
用于连接路径。该方法的主要用途在于,会正确使用当前系统的路径分隔符,Unix系统是"/",Windows系统是" \"。
console.log(path.join("/path" , "/user")); //\path\user
path.resolve
//path.resolve([from ...], to)
//将 to 参数解析为绝对路径,给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径
console.log(path.resolve("" , "../")) //C:\Users\Administrator\Desktop
console.log(path.resolve("" , "./")) //C:\Users\Administrator\Desktop\node-learn
http
//server.js
//引入模块
const http = require('http')
//创建http服务器
const server = http.createServer((req , res)=>{
const {url , method } = req
console.log("url:%s,method:%s" , url , method)
//监听客户连接事件
req.on("connection" , (socket)=>{
console.log("客户端连接" , socket)
})
//接受传送数据
req.on("data", data=>{
console.log("recive data" , data)
})
req.on("end" ,()=>{
//返回数据
res.end('hello world')
})
})
//监听8000端口,等待连接
server.listen(8000 , ()=>{
console.log("server start")
})
//client.js
const http = require("http")
const postData = "test"
const options = {
hostname: 'localhost',
port: 8000,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
console.log(`状态码: ${res.statusCode}`);
console.log(`响应头: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`响应主体: ${chunk}`);
});
res.on('end', () => {
console.log('响应中已无数据');
});
});
req.on('error', (e) => {
console.error(`请求遇到问题: ${e.message}`);
});
// 将数据写入请求主体。
req.write(postData);
req.end();