第一章
1. node是基于V8创建的一个轻量级Web服务器
,并提供一套库
2. 设计高性能Web服务器的几个要点:事件驱动、非阻塞IO
3. 为什么是js
js无历史包袱、符合事件驱动、chromeV8的高性能,这三个要素促成了js作为node的实现语言
4. node名字由来
最初web服务器的想法变成了构建网络应用的基础框架
,在它的基础上构建更多的东西,如服务器、客户端、命令行工具。node 为构建大型分布式应用程序提供基础设施;非常容易通过扩展来达成构建大型网络应用的目的。每个node进程都构成这个网络应用中的一个节点。
5. chrome和node的组件构成
js不再只限制于和浏览器中CSS和DOM打交道。
6. node特点:异步IO
Don't call me, I will call you.
通过异步方式,可以自然进行并行操作,每个调用间无须等待。
7. node特点:单线程
无法利用多核cpu、错误会导致整个应用退出、大量计算占用CPU导致无法继续调用异步I/O。Web Workers创造工作线程来计算解决了这个问题,Node采用了相同的思路,child_process
8. node特点:跨平台
平台架构层:libuv
9. 应用场景:I/O密集、CPU密集、分布式应用
上面也提及了,node擅长处理并行I/O;且有两个方式充分利用CPU
- 子进程方式
- 编写C/C++扩展的方式
第二章
1. CommonJS规范
node借鉴CommonJS的Modules规范实现了一套非常易用的模块系统
2. CommonJS模块规范
- 模块引用require
- 模块定义exports.add = xxx,exports对象是唯一的导出的出口。
- 模块标识:符合小驼峰的字符串、或以
.
,..
开头的路径,或绝对路径。
3. node模块
node并非完全按照规范实现,做了一定的取舍。
模块引入:
- 路径分析
- 文件定位
- 编译执行
模块分为两类:
- node提供的核心模块,核心模块编译进了二进制执行文件,启动时就被直接加载进内存,省去了定位和编译两步,是最快的
- 用户编写的文件模块,需要完整的3个步骤,较慢
4. 缓存
node对引入过的模块都会进行缓存,缓存的是编译和执行后的对象。无论是核心还是文件模块,都是优先采用缓存的方式。核心模块的缓存检查先于模块缓存
5. 路径分析
- 核心模块
.
,..
开始的相对路径文件模块/
开始的绝对路径文件模块- 非路径文件模块,如自定义模块
6. 模块路径
模块路径生成规则,可通过module.paths
查看。它是一个路径数组,
- 当前目录下的node_modules目录
- 父目录下的node_modules目录
- 向上直到根目录下的node_modules目录
7. 自定义模块
特殊的文件模块,可能是一个文件或者包的形式,这类模块查找最费时,是所有方式中最慢的一种。
加载中,node会逐个尝试模块路径
中的所有路径,直到找到目标。文件路径越深,耗时越多。
8. 文件定位
文件扩展名按,.js、.json、.node的次序尝试。如果是.node和.json,带上扩展名,会加快一点速度。
如果还未能找到对应的文件,却得到一个目录,node会将目录当做一个包来处理。node查找当前目录下的package.json,解析出包描述对象,从中取出main属性指定的文件名进行定位,如果文件名缺少扩展名,则进入扩展名分析。如果main属性指定文件名错误,或根本一开始没有package.json文件,node会将index作为默认文件名,依次查找index.js、index.json、index.node。
9. 文件编译
定位到具体文件,node会新建一个模块对象,根据路径进行载入并编译。不同文件扩展名,载入的方法也不相同。
- js文件:通过fs模块同步读取文件后编译执行
- node文件:这是用C/C++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件
- json文件:通过fs模块同步读取,用JSON.parse解析返回结果
- 其他:都被当做js文件载入
10. js模块编译
node对获取的js内容进行了头尾包装
(function(exports, require, module, __filename, __dirname) {
文件内容
})
11. js核心模块代码以字符串形式存储在node命名空间中,是不可执行的,在启动node进程时,js代码直接加载进内存。
js核心模块和文件模块区别:
- 获取源码方式:从内存中加载/读取文件
- 缓存执行结果:编译成功缓存到
NativeModule._cache
/Module._cache
。
12. 包的出现,是在模块的基础上进一步组织js代码;包由包结构和包描述文件两个部分组成。
13. 包结构
- package.json 包描述文件
- bin 存放可执行的二进制文件的目录
- lib 存放js代码目录
- doc 存放文档目录
- test 存放单元测试用例目录
14. 包描述文件package.json
必需字段
- name
- description
- version
- keywords
- maintainers
其他
- contributors
- licenses
- dependencies
- devDependencies
- repository
- main: require引入包时,会优先检查这个字段,将其作为模块入口。如果不存在,则查找index.js、index.node、index.json文件
- bin
- script
- ...
15. npm install xxx
npm在当前目录创建node_modules目录,然后在node_modules目录下创建xxx目录,将压缩包接要到该目录中。通过require('xxx')即可引入该包。require在做路径分析时通过模块路径找到xxx所在的位置。
16. 全局模式安装npm install xxx -g
全局模式并不是将一个模块包安装为一个全局包的意思,它并不意味着可以从任何地方通过require来引用到它。
-g是将一个包安装位全局可用的可执行命令,它根据bin字段配置。将实际的脚本链接到node可执行的文件路径下。如
"bin": {
"express": "./bin/express"
}
17. 发布包
- 编写模块
- npm init
- 注册npm仓库账号
- npm publish 文件夹:如
npm publish .
- 使用安装 npm install xxx
管理包权限
- npm owner ls
- npm owner add
- npm owner rm
- npm owner ls
18. npm包的评价标准
- 良好的测试
- 良好的文档 README、API
- 良好的测试覆盖率
- 良好的编码规范
- 其他
19. AMD异步模块定义Asynchronous Module Definition
模块的侧重点:
浏览器端js,瓶颈在于带宽,需要从网络加载代码
服务器端js,瓶颈在于CPU和内存资源,从磁盘中加载,很快。
如果前端模块采用同步的方式引入,用户体验造成很大的问题,AMD规范在前端应用场景中胜出。
define(id?, dependencies?, factory);
// example
define(function() {
var exports = {};
exports.sayHello = function() {
// do something
}
return exports;
});
20. CMD
AMD是在声明模块的时候就指定所有的依赖,而CMD支持动态引入
define(function(require, exports, module) {
// do something
})
在需要依赖模块时,随时调用require引入
21. 类库的兼容性
考虑类库运行在前后端不同的环境,类库开发者需要将类库代码包装在一个闭包中,保证前后端的一致性。
(function(name, definition) {
// 上下文是否是AMD或CMD
var hasDefine = typeof define === 'function';
// 上下文环境是否为Node
var hasExport = typeof module !== 'undefined' && module.exports;
if (hahsDefine) {
define(definition);
} else if (hasExport) {
module.exports = definition();
} else {
// 将模块执行结果挂在window变量中,浏览器this指向window对象
this[name] = definition();
}
})('hello', function() {
var hello = function() {...};
return hello;
});