文章目录
1. node简介
node.js是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, 让JavaScript 运行在服务端的开发平台
Node采用了一个称为“事件循环(event loop)”的架构,使得编写可扩展性高的服务器变得既容易又安全。提高服务器性能的技巧有多种多样。Node选择了一种既能提高性能,又能减低开发复杂度的架构。这是一个非常重要的特性。并发编程通常很复杂且布满地雷。Node绕过了这些,但仍提供很好的性能。
- 事件驱动
事件驱动就是当进来一个新的请求的时,请求将会被压入事件列队中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数 - 非阻塞式I/O模型
Nodejs 采⽤了⾮阻塞型间的形式通知执⾏操作I/O 机制,在做I/O 操作的时候不会造成任何的阻塞,当完成之后,以时时间的形式通知执行操作
例如在执⾏了访问数据库的代码之后,将⽴即转⽽执⾏其后⾯的代码,把数据库返回结果的处理代码放在回调函数中,从⽽提⾼了程序的执⾏效率
-
node的优缺点
优点- 处理⾼并发场景性能更佳
- 适合I/O密集型应⽤,值的是应⽤在运⾏极限时,CPU占⽤率仍然⽐较低,⼤部分时间是在做I/O硬盘内存读写操
缺点
- nodejs是单线程
- 不适合CPU密集型应⽤
- 只⽀持单核CPU,不能充分利⽤CPU
- 可靠性低,⼀旦代码某个环节崩溃,整个系统都崩溃
-
应用场景
应⽤场景分类- 善于I/O,不善于计算。因为Nodejs是⼀个单线程,如果计算(同步)太多,则会阻塞这个线程
- ⼤量并发的I/O,应⽤程序内部并不需要进⾏⾮常复杂的处理
- 与websocket配合,开发⻓连接的实时交互应⽤程序
具体场景可以表现
- 第⼀⼤类:⽤⼾表单收集系统、后台管理系统、实时交互系统、考试系统、联⽹软件、⾼并发量的web应⽤程序
- 第⼆⼤类:基于web、canvas等多⼈联⽹游戏等多⼈联⽹游戏
- 第三⼤类:基于web的多⼈实时聊天客⼾端、聊天室、图⽂直播
- 第四⼤类:单⻚⾯浏览器应⽤程序
- 第五⼤类:操作数据库、为前端和移动端提供基于json 的API
2. 全局对象
1.global
global 最根本的作用是作为全局变量的宿主
当定义一个全局变量时,这个变量同时也会成为全局对象的属性。
注意:
在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。
最好不要使用 var 定义变量以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险。
2.__filename
表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。
console.log('_filename', __filename) // 当前文件的绝对路径
3.__dirname
表示当前执行脚本所在的目录。
console.log('__dirname', __dirname) // 当前文件的目录的绝对路径
4.process
process 是一个全局变量,即 global 对象的属性。
用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。
- cwd()
返回当前进程的工作目录 绝对路径console.log(process.cwd())
- exit()
强制退出当前node进程
可传入退出码,0表示成功退出,默认为0
可以根据退出码进行一些判定。 - argv
argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。console.log(process.argv);
- platform
获取当前的操作系统console.log(process.platform);
- kill(pid)
根据进程ID杀死进程process.kill(pid);
- env
获取环境变量对象console.log(process.env)
- 设置开发环境
// 一版代码可能运行在不同的机器上, // 开发环境(代码需要压缩混淆) // 生产环境(不压缩不混淆) // 测试环境(多一些输出日志) // 环境变量设置node_env if (process.env.NODE_ENV == 'develop') { console.log('开发环境') } else if (process.env.NODE_ENV == 'test') { console.log('测试环境') } else if (process.env.NODE_ENV == 'production') { console.log('生产环境') }
- 判断当前环境是浏览器还是nodejs
try { if (global) { console.log('当前环境是nodejs'); global.xxx = '挂载全局数据' } } catch(e) { if (window) { console.log('当前环境是浏览器'); window.xxx = '挂载全局数据' } }
5.module
模块信息,用于记录当前模块的所有信息
在每个模块中,module 的自由变量是一个指向表示当前模块的对象的引用。
为了方便,module.exports 也可以通过全局模块的 exports 对象访问。
module 实际上不是全局的,而是每个模块本地的。
const a = require('./a.js') // 引入a.js文件
console.log(module)
6.exports
导出对象,默认为{}
exports是一个对于 module.exports 的更简短的引用形式。
exports 变量是在模块的文件级别作用域内有效的,它在模块被执行前被赋予 module.exports 的值。
它有一个快捷方式,以便 module.exports.f = …
可以被更简洁地写成 exports.f = …。
注意,就像任何变量,如果一个新的值被赋值给 exports,它就不再绑定到 module.exports:
- module.exports
module.exports 对象是由模块系统创建的。
有时这是难以接受的;许多人希望他们的模块成为某个类的实例。
为了实现这个,需要将期望导出的对象赋值给 module.exports。
注意,将期望的对象赋值给 exports 会简单地重新绑定本地 exports 变量,这可能不是期望的。
注意,对 module.exports 的赋值必须立即完成。 不能在任何回调中完成。
module.exports 与 exports 的区别:
module.exports 是导出对象的真正引用。
exports 是 module.exports 的快捷方式。不能直接赋值 exports = …,否则会断开引用。
7.require
导入函数,使用该函数可以实现模块的依赖
- require.main
当 Node.js 直接运行一个文件时,require.main 会被设为它的 module。
这意味着可以通过 require.main === module 来判断一个文件是否被直接运行:
对于 foo.js 文件,如果通过 node foo.js 运行则为 true,但如果通过 require(‘./foo’) 运行则为 false。
因为 module 提供了一个 filename 属性(通常等同于 __filename),
所以可以通过检查 require.main.filename 来获取当前应用程序的入口点。
const a = require('./a.js') // 引入a.js文件
console.log(require.main)
2. require.cache
被引入的模块将被缓存在这个对象中。
从此对象中删除键值对将会导致下一次 require 重新加载被删除的模块。
注意不能删除 native addons(原生插件),因为它们的重载将会导致错误。
const a = require('./a.js') // 引入a.js文件
console.log(require.cache)
3. require.resolve(request[, options])
使用内部的 require() 机制查询模块的位置, 此操作只返回解析后的文件名,不会加载该模块。
request:需要解析的模块路径。
options.paths:解析模块的起点路径数组。此参数存在时,将使用这些路径而非默认解析路径。
const a = require('./a.js') // 引入a.js文件
console.log(require.resolve('./a'))
4. require.resolve.paths(request)
返回一个数组,其中包含解析 request 过程中被查询的路径。
request:被查询解析路径的模块的路径。
const a = require('./a.js') // 引入a.js文件
console.log(require.resolve.paths('./a'))
8.UMD模块
UMD模块是一种通用的模块定义规范,旨在使同一个模块能够在不同的环境中使用,包括浏览器和Node.js等。UMD规范通过兼容AMD(异步模块定义)和CommonJS规范,使得模块可以在不同的环境中运行
- UMD模块的基本结构
UMD模块的基本结构通常包含一个自执行函数,该函数接受两个参数:root和factory。root通常指向全局对象(如window),而factory是一个函数,用于定义模块的功能。(function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD 异步模块定义,加载前置 define(['jquery', 'underscore'], factory); } else if (typeof exports === 'object') { // CommonJS module.exports = factory(require('jquery'), require('underscore')); } else { // 浏览器全局变量 window root.returnExports = factory(root.jQuery, root._); } }(this, function($, _) { // 模块的具体实现 function a() {} // 私有方法 function b() {} // 公共方法,将被返回 function c() {} // 公共方法,将被返回 return { b: b, c: c }; // 返回公共方法 }));
- UMD模块的兼容性
UMD规范的主要目的是兼容不同的模块加载器。实现方式:AMD:如果存在define函数且define.amd为真,则使用AMD规范。
CommonJS:如果存在exports对象,则使用CommonJS规范。
浏览器全局变量:如果以上条件都不满足,则将模块作为全局变量导出。
3、核心对象
核心对象通过require的方式引入,名称既是引用路径
1.path
path 模块是 Node.js 的核心模块之一,用于处理和操作文件和目录路径,提供跨平台的路径操作方法。
通过 path 模块,可以拼接、解析、格式化和规范化路径,避免因为操作系统的不同路径格式而导致的错误(如 Windows 使用反斜杠 \,而 Linux 和 macOS 使用正斜杠 /)。
- 方法与属性
方法 | 描述 |
---|---|
path.basename(path[, ext]) | 返回路径中的最后一部分(文件名)。可选参数 ext 用于去除文件扩展名。 |
path.dirname(path) | 返回路径中的目录部分。 |
path.extname(path) | 返回路径中文件的扩展名。 |
path.join([…paths]) | 将多个路径拼接为一个路径,自动处理路径分隔符。 |
path.resolve([…paths]) | 将路径序列解析为绝对路径,从右到左依次处理每个路径片段,直到构建出一个绝对路径为止。 |
path.normalize(path) | 规范化路径,删除冗余的 . 和 …,并根据当前操作系统替换路径分隔符。 |
path.isAbsolute(path) | 检查路径是否为绝对路径。 |
path.relative(from, to) | 返回从 from 路径到 to 路径的相对路径。 |
path.parse(path) | 将路径字符串解析为对象,包含 root、dir、base、ext 和 name 属性。 |
path.format(pathObject) | 将路径对象格式化为路径字符串,与 path.parse 相反。 |
path.sep | 提供当前操作系统的路径分隔符(POSIX 为 ‘/’,Windows 为 ‘’) |
path.delimiter | 提供路径分隔符(环境变量中路径的分隔符),POSIX 为 :,Windows 为 ;。 |
const path = require('path');
// 获取文件名 返回路径的最后一部分
console.log('文件名:', path.basename('/foo/bar/baz.txt')); // 输出: 'baz.txt'
// 获取目录名 返回路径中的目录部分
console.log('目录名:', path.dirname('/foo/bar/baz.txt')); // 输出: '/foo/bar'
// 获取扩展名
console.log('扩展名:', path.extname('/foo/bar/baz.txt')); // 输出: '.txt'
// 拼接路径
console.log('拼接路径:', path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')); // 输出: '/foo/bar/baz/asdf'
// 解析绝对路径
console.log('绝对路径:', path.resolve('/foo/bar', './baz')); // 输出: '/foo/bar/baz'
// 规范化路径
console.log('规范化路径:', path.normalize('/foo/bar//baz/asdf/quux/..')); // 输出: '/foo/bar/baz/asdf'
// 判断是否为绝对路径
console.log('是否为绝对路径:', path.isAbsolute('/foo/bar')); // 输出: true
// 相对路径
console.log('相对路径:', path.relative('/foo/bar/baz', '/foo/bar/qux')); // 输出: '../qux'
// 解析路径为对象
const parsedPath = path.parse('/home/user/dir/file.txt');
console.log('解析路径为对象:', parsedPath);
// 输出: { root: '/', dir: '/home/user/dir', base: 'file.txt', ext: '.txt', name: 'file' }
// 将对象格式化为路径
const formattedPath = path.format(parsedPath);
console.log('格式化路径:', formattedPath); // 输出: '/home/user/dir/file.txt'
2.url
URL 模块包含有助于解析 URL 的函数。URL 模块用于将网址分割成可读的部分。Nodejs 中的 Url 模块和查询字符串用于操作 URL 及其组件。
- URL 类构造函数用于按以下方式创建 url 对象:
const { URL } = require("url"); const myurl = new URL( "https://github.com:80/waylau/harmonyos-tutorial?tab=readme-ov-file#89hj54kl" ); const { href, origin, protocol, username, password, host, hostname, port, pathname, search, searchParams, hash, } = myurl; console.log("Href value :", href); console.log("Origin value :", origin); console.log("Protocol value :", protocol); console.log("Username value :", username); console.log("Password value :", password); console.log("Host value :", host); console.log("Hostname value :", hostname); console.log("Port value :", port); console.log("Pathname value :", pathname); console.log("Search value :", search); console.log("SearchParams value :", searchParams); console.log("Hash value :", hash)
修改url部分
添加参数const myURL = new URL('http://github.com/page?name=anonymous'); myURL.hostname = 'www.baidu.com'; myURL.port = '8080'; myURL.searchParams.set('name', 'admin'); console.log(myURL.href);
const myURL = new URL('http://www.github.com/page'); myURL.searchParams.append('name', 'anonymous'); console.log(myURL.href);
- url.parse()
将url字符串,解析成objectconst url = require("url"); const hrefUrl = "https://github.com:80/waylau/harmonyos-tutorial?tab=readme-ov-file#89hj54kl" const obj = url.parse(hrefUrl) console.log(obj)
- url.format()
将对象转成url字符串。const url = require("url"); const components = { protocol: 'https', host: 'github.com', pathname: 'waylau/harmonyos-tutorial', search: '?tab=readme-ov-file', hash: '#89hj54kl' }; let str = url.format(components) console.log(str)
3.fs
文件系统模块(fs 模块)提供了丰富的 API,用于读取、写入、删除文件以及执行其他文件系统操作。
fs 模块既支持同步方法也支持异步方法,可以根据具体需求选择合适的方式来处理文件操作。
Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
建议使用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。
- 读取文件
使用fs.readFile方法异步读取文件 使用fs.readFileSync方法同步读取文件var fs = require("fs"); // 异步读取 fs.readFile('c.js', function (err, data) { if (err) { return console.error(err); } console.log("异步读取: " + data.toString()); }); // 同步读取 var data = fs.readFileSync('d.js'); console.log("同步读取: " + data.toString()); console.log("程序执行完毕。");
- 写入文件
使用fs.writeFile方法异步写入文件,使用fs.writeFileSync方法同步写入文件会覆盖文件中的所有内容
如果文件不存在,fs.writeFile将自动创建该文件;如果文件存在,将覆盖其中的内容。var fs = require("fs"); fs.writeFile('c.js', 'Hello, World!', (err) => { if (err) { console.error('Error writing file:', err); return; } console.log('File written successfully'); }); fs.writeFileSync('d.js', 'Hello, World!');
- 追加数据
将数据追加到现有文件中
使用fs.appendFile方法异步追加数据,使用fs.appendFileSync方法同步追加数据var fs = require("fs"); fs.appendFile('c.js', '\nAppending C', (err) => { if (err) { console.error('Error appending to file:', err); return; } }); fs.appendFileSync('d.js', '\nAppending D');
- 删除文件
使用fs.unlink方法异步删除文件,使用fs.unlinkSync方法同步删除文件var fs = require("fs"); fs.unlink('c.js', (err) => { if (err) { console.error('Error deleting file:', err); return; } console.log('File deleted successfully'); }); fs.unlinkSync('d.js');
- 创建目录
使用fs.mkdir方法异步删除文件,使用fs.mkdirSync方法同步删除文件fs.mkdir('c', (err) => { if (err) { console.error('Error creating directory:', err); return; } console.log('Directory created successfully'); }); fs.mkdirSync('d');
- 读取目录内容
使用fs.readdir方法异步读取目录内容,使用fs.readdirSync方法同步读取目录内容var fs = require("fs"); fs.readdir('c', (err, files) => { if (err) { console.error('Error reading directory:', err); return; } console.log('Directory contents:', files); }); const files = fs.readdirSync('c'); console.log(files)
- 检查文件或目录是否存在
使用fs.access方法异步读取检查文件或目录是否存在,使用fs.accessSync方法同步检查文件或目录是否存在var fs = require("fs"); fs.access('f', fs.constants.F_OK, (err) => { if (err) { console.log('File does not exist'); } else { console.log('File exists'); } }); fs.accessSync('d', fs.constants.F_OK);
二、循环依赖
当两个或多个模块相互导入时,称为循环依赖。Node.js 能处理简单的循环依赖,但可能导致部分模块导出的对象未完全初始化。
// a.js
const b = require('./b');
console.log('a.js:', b.message);
module.exports = { message: 'Hello from a' };
// b.js
const a = require('./a');
console.log('b.js:', a.message);
module.exports = { message: 'Hello from b' };
// main.js
require('./a');