Nodejs结构和创建

本文参考自《Node.js开发指南》和《Node.js实战》两本书。

异步I/0

Node.js最大的特点就是采用了异步I/0与事件驱动的架构设计。

1
2
3
$.post('/resource.json', function(data){
console.log(data);
})

这一小段代码是jQuery中的Ajax请求,我们知道这个结果函数是异步的,等服务器响应结果后才会执行。

对于一些高并发的需求,传统的解决方法是多线程模型,每个业务提供一个线程系统,通过切换和调度线程来实现高并发中的等待开销。但是这样做无疑又增加了线程管理和调度的开销。

Nodejs采用的是单线程模型,对于所有的I/O操作(阻塞操作)都采用类似上面Ajax请求的异步的请求方式,避免了频繁切换线程。

nodejs的单线程模型

Nodejs的异步机制是基于事件的,所有的磁盘I/0、网络通讯、数据库查询都以非阻塞方式请求,返回的结果由事件循环来处理。Nodejs进程在同一时刻只会处理一个事件,完成后立即进入事件循环检查并处理后面的事件。这样做的好处是,CPU和内存在同一时间集中处理一件事,同时尽可能让耗时的I/0操作并行执行。

Node.js架构

Node.js用异步式I/0和事件驱动代替多线程,带来了客观的性能提升。Node.js除了用V8作为JavaScript引擎外,还使用了高效的libev和libeio库支持事件驱动和异步式I/0.

nodejs架构

Node.js的开发者在libev和libeio的基础上抽象出了libuv.

Nodejs安装

Node.js的下载和安装请访问官网:https://nodejs.org/zh-cn/

因为我的系统是deepin的,所以下面是Ubuntn下的安装

1
2
sudo apt-get install nodejs
sudo apt-get install npm

npm是Node包管理器,是一个由Node.js官方提供的第三方包管理工具,就像PHP中的Pear、Python中的PyPI一样。npm是一个完全由JavaScript实现的命令行工具,通过Nodejs执行。

如果我们有需要安装多个版本的Node.js的需求,可以安装多版本管理器。n是一个十分简洁的Node多版本管理器,他的名字就一个字母。

1
npm install -g -n

安装完n后,可以使用n –help查看使用说明。详细可以访问n主页

Node.js Helloword

新建一个hw.js文件

1
console.log('hello word');

然后执行下面命令

1
node hw.js

这就是Node.js的helloword程序。

建立HTTP服务器

新建一个app.js的文件

1
2
3
4
5
6
7
8
9
var http = require('http');

http.createServer(function(req, res){
res.writeHead(200, {'Content-Type' : 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
}).listen(3000);

console.log('HTTP server is listening at prot 3000.');

接下来运行node app.js命令,打开浏览器访问http://127.0.0.1:3000

nodejs服务器访问

用Node.js实现最简单的http服务器就这样诞生了。这个程序中调用了Node.js提供的http模块,对所有HTTP请求答复同样的内容并监听3000端口。

模块和包

模块(Module)和包(Package)是Node.js最重要的支柱。开发一个具有一定规模的程序不可能只用一个文件,通常需要把各个功能拆分、封装、然后组合起来。Node.js提供了require函数来调用其他模块,而且模块都是基于文件的,机制十分简单。

模块

在Node.js中模块和文件是一一对应的,一个Node.js文件就是一个模块,这个文件可能是JavaScript代码、JSON或者编译过的C/C++扩展。

上面我们使用var http = require(‘http’),其中http是Node.js的一个核心模块,其内部是用C++实现的,外部用JavaScript封装。

Node.js提供了exports和require两个对象,其中exports是模块公开的接口,require用于获取模块的接口。我们在同一个目录下创建两个文件

module.js

1
2
3
4
5
6
7
8
var name;
exports.setName = function(thyName){
name = thyName;
}

exports.sayHello = function(){
cosole.log('Hello ' + name);
}

getmodule.js

1
2
3
var myModule = require('./module');
myModule.setName('BYVoid');
myModule.sayHello();

运行node getmodule.js,结果是:

Hello BYVoid

创建包

包是在模块基础上更深一步的抽象,Node.js的包类似于C/C++的函数库或Java中的jar包。它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。

Node.js的包是一个目录,其中包含了一个JSON格式的包说明文件package.json.

Node.js在调用某个包时,会首先检查包中package.json文件的main字段,将其作为包的接口模块,如果package.json或main字段不存在,会尝试寻找index.js或index.node作为包的接口。

示例说明, 创建如下目录和文件:

1
2
3
4
5
6
7
.
│ main.js

└─lib
index.js
lib1.js
lib2.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//lib1.js
exports.showMsg = function(){
console.log("msg --- lib1");
}

//lib2.js
exports.showMsg = function(){
console.log("msg --- lib2");
}

//index.js
var lib2 = require('./lib2');
var lib1 = require('./lib1');

exports.showMsg = function(){
console.log("msg --- index.js");
}

exports.l1 = lib1;
exports.l2 = lib2;

//main.js
var lib = require('./lib');

lib.showMsg();

lib.l1.showMsg();
lib.l2.showMsg();

可以看到上面的 lib 目录下的index.js中引入了 lib1lib2 并使用 exports 暴露给外部,然后使用 require('./lib') 引入 lib 目录下暴露的方法。

如果我们要更改这个入口文件名称或路径,可以使用 package.json 文件来配置,在 lib 目录新增一个 package.json 文件:

1
2
3
4
5
6
7
8
.
│ main.js

└─lib
lib1.js
lib2.js
package.json
rukou.js

package.json 文件内容如下:

1
2
3
4
{
"name": "lib",
"main": "./rukou.js"
}

接下来就可以使用 name 所对应的 main 路径了:

1
2
3
4
5
6
var lib = require('./lib');  //实际路径是  ./lib/rukou

lib.showMsg();

lib.l1.showMsg();
lib.l2.showMsg();

module.exports

事实上上面的 exports 对象指向的是 module.exports, Node为每一个模块提供了一个这样的变量。

1
var exports = module.exports

所以,在你的代码里面千万不要出现将 exports 变量指向一个值,因为这样等于切断了 exports 与 module.exports 的联系。这个联系正是它们不同的地方。

修改上面的 rukou.js 代码如下:

1
2
3
4
5
6
7
8
var lib2 = require('./lib2');
var lib1 = require('./lib1');

module.exports = function(){
lib1.showMsg();
lib2.showMsg();
console.log("msg --- index.js");
}

然后就可以在 main.js 中这样使用了, 注意调用方法的不同:

1
2
3
var lib = require('./lib');

lib();

结论:如果要输出一个键值对象 {},可以利用 exports 这个已存在的空对象 {},并继续在上面添加新的键值;如果要输出一个函数或数组,必须直接对 module.exports 对象赋值。所以我们可以得出结论:直接对 module.exports 赋值,可以应对任何情况。

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Babel Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome Class ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage ES2015 ESLint Element Error Exception Extensions File FileProvider Flow Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Style Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code VUE ValueAnimator ViewPropertyAnimator Vue Vue.js Web Web前端 Workbench api apk bookmark by关键字 cli compileOnly computed css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge methods mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode watch webpack 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指令 指针 插件 插值 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 渲染 源码 源码分析 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 绑定 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 脚手架 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×