Node基础 2(Node Web开发【上】)

注:笔记虽新,但对应书籍Node版本老旧,相关代码已验证,内容仅供学习参考

Nodejs基础《Node Web开发》1-Node入门

书籍简介:

  • 作为服务器端的JavaScript解释器,Node是一个轻量高效的开发平台,用于构建响应快速、高度可扩展的Web应用;
  • 他使用事件驱动和非阻塞的I/O模型,适合开发数据密集型、对实时响应要求高的分布式应用;

主要内容:

  • 如何使用http服务器和客户端对象、connect和express应用框架、异步执行算法、结合数据库,Node的CommonJS模块系统(可以实现一些重要的面向对象的设计方案)

适合人群:

  • 有JS和Web开发基础知识,打算使用服务端JS开发高性能Web应用的开发人员;

第1章 Node入门

利用JS匿名函数和单线程执行的事件驱动框架,使网络应用有极高扩展性;

1.1 Node能做什么

Node模块:

  • Node是脱离浏览器编写JS应用的应用开发平台平台;与浏览器不同的是Node没有内置DOM,也没有浏览器的内置功能;他使用了JS语言和异步I/O框架;Node没有UI组件库、或GUI工具包;
  • 与大规模使用线程的普通应用服务器不同,使用事件驱动,内存占用量低,吞吐量高,编程模型简单;
  • 具有一个JavaScript虚拟机,适用通用编程,专注应用服务器开发;与传统开发语言、HTTP协议容器技术有本质区别;
  • 实现以非阻塞的I/O事件循环机制和文件与网络I/O库为中心,以V8 JS引擎为基础(支持广泛,常用于构建普通网站);

Node/Node模块提供的功能:

  • 执行JavaScript;
  • 命令行工具、交互式TTY风格编程(REPL);
  • 能监控子进程的进程控制函数;
  • 用Buffer对象处理二进制数据;
  • 使用全面事件驱动回调函数的TCP或UDP套接字;
  • DNS查找;
  • 基于TCP库的HTTP和HTTPS客户大端/服务端;
  • 文件系统的存取;
  • 内置了基于断言的单元测试能力;

Node社区已经开发了许多类似Connect的Web应用框架,配置后可快速提供所有基础内容的HTTP服务器(会话、cookie、静态文件和日志等),而无需自行编写HTTP服务器,虽然用Node编写它并不复杂;

1.2 为什么要使用Node

JavaScript是一门动态编程语言,拥有松散类型且可动态扩展的对象;函数是一级对象,通常作为匿名闭包使用;

Node使用CommonJS系统,模块的局部变量避免了JS全局对象被多个模块混用时可能导致的混乱问题;

架构问题:线程,还是异步事件驱动?

通常应用服务器使用阻塞I/O和线程实现并发,阻塞I/O会导致线程等待,进而影响线程处理请求;由于要解决对共享变量的访问、避免死锁等各种策略和线程间的竞争,存在“开销大,易出错”的问题;

Node有一个无需I/O等待或执行环境切换的单独执行环境;I/O调用会转换为请求处理函数,当某些函数可用时 事件轮询会调度事件 让函数工作;
Node解决并发问题,是通过事件轮询实现异步触发回调函数的并发模型;

举例如下:

// 阻塞I/O 
let result = query('SELECT * from db');

// 异步I/O
query('SELECT * from db', function(result){
    
});

性能和利用率

Node的吞吐能力更优秀:每秒能响应的请求数

// 一个简单的web服务器
var http = require('http')

http.createServer(function (req, res){
    res.writeHead(200, {'Content-Type':'text/plain'});
    res.end('Hello world');
}).listen(8081, '127.0.0.1');

console.log('Server running at http://127.0.0.1:8081/')

注:这里衡量的能力更多的是对小型Web应用,对于大规模的企业应用,往往要使用负载均衡、缓存服务器、大量冗余机器,对于整个系统来说,开发平台已经显得不那么重要了;

1.3 Node、Node.js、还是Nodejs

平台名字:Node.js;书写用的拼写:Node;

1.4 小结

JS在浏览器之外有其他用处。

Nodejs基础《Node Web开发》2-安装并配置Node

当前的Node安装已经简便许多,参看官网下载安装即可:

  • √ 开发环境配置
  • X 生产环境部署

第2章 安装并配置Node

2.x

Node最适合在符合POSIX标准的操作系统上运行,包括UNIX的衍生系统;Node内置的很多函数都是直接调用POSIX系统的接口;

下载安装Node:Download for macOS (x64)

安装条件

C语言编译器:

$ cc --version
Apple clang version 11.0.0 (clang-1100.0.33.8)
Target: x86_64-apple-darwin19.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Python(OpenSSL库):

$ python
Python 3.7.2 (default, Dec 29 2018, 00:00:04)
[Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.

注:在Mac OS X上还可以使用homebrew安装node及其包管理工具npm

$ brew search node
$ brew install node

$ brew search npm
$ ...

终端命令行交互

$ node
Welcome to Node.js v12.16.3.
Type ".help" for more information.
> console.log("hello world")
hello world
undefined

使用Node运行一个简单脚本

var fs = require('fs');
var dir = '.';
/**
 * $ node ls.js .
[
  '/usr/local/bin/node',
  '/Users/huaqiang/Desktop/markdown/Nodejs-books/nodes/ls.js',
  '.'
]
 */
console.log(process.argv);

if (process.argv[2]) {
    dir = process.argv[2];
}

var files = fs.readdirSync(dir);
for (fn in files) {
    console.log(files[fn]);
}

脚本的参数会通过一个全局的数组 process.argv传递:

$ node ls.js /Users/huaqiang/Desktop/markdown/Nodejs-books/
[
  '/usr/local/bin/node',
  '/Users/huaqiang/Desktop/markdown/Nodejs-books/nodes/ls.js',
  '/Users/huaqiang/Desktop/markdown/Nodejs-books/'
]
.DS_Store
nodejs基础1.md
nodejs基础2.md
nodes

用Node启动服务器

#!/usr/bin/env node 
// 在服务器脚本的第一行 增加 #!/usr/bin/env node 这可以让脚本变得可执行

// 一个简单的web服务器
var http = require('http')

http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });
  res.end('Hello world');
}).listen(8081, '127.0.0.1');

console.log('Server running at http://127.0.0.1:8081/')
$ node app.js
Server running at http://127.0.0.1:8081/

为什么服务器的程序没有退出?

Node始终会启动一个事件循环,在app.js中的.listen函数创建了一个实现HTTP协议的事件监听器,这个活动的事件监听器会是app.js一直处于执行状态;

2.5 安装npm ———— Node包管理器

Node本身非常基础:JS解释器、一些异步I/O库等;第三方Node模块生态系统的中心则是npm

  • 非官方标准的Node包管理器,简化了下载和使用Node模块的流程;
  • 当代Node已经带了npm,只不过不是最新版本;

系统启动时自动启动Node服务器

Node包含构建服务器的各种零件,实现一个完整的Web服务器需要综合操作系统本身的后台进程服务,实现记录功能、安全措施、防御措施等;

系统启动时自动启动Node服务器同样如此:方法是将Node作为后台守护进程执行;

举例:使用forever库管理后台守护进程,编写shell脚本处理系统启动和关闭时对forever的操作(start/stop);

V8是一个单线程JS引擎,也就是说没法有效利用多核CPU(一个单线程进程只能使用一个CPU);现在很多项目在努力定制可靠的多线程Node,思路是启动多个Node进程,在进程间共享请求通信量,Cluster就是多线程Node服务器项目的一个示例;

完整代码示例

使用forever库管理后台守护进程,编写shell脚本处理系统启动和关闭时对forever的操作(start/stop)

安装forever:

$ sudo npm install -g forever

编写js脚本:

# !/usr/bin/env node 
 
var http = require('http')

http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });
  res.end('Hello world');
}).listen(8081, '127.0.0.1');

编写shell脚本:/etc/init.d/node

# !/bin/sh -e
set -e
PATH=/usr/local/node/0.4.8/bin:/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/usr/local/app.js
case "$1" in
    start) forever start $DAEMON ;;
    stop) forever stop $DAEMON ;;
    force-reload|restart)
        forever restart $DAEMON ;;
    *) echo "Usage: /etc/init.d/node {start|stop|restart|force-reload}"
        exit 1
        ;;
esac
exit 0

注: DAEMON变量 也可以修改为其他可执行的Node项目入口js文件;

配置脚本到系统中:启动和关闭时会自动调用,也可在终端手动执行

$ sudo /usr/sbin/update-rc.d node defaults

执行脚本:

$ sudo /etc/init.d/node start

Nodejs基础《Node Web开发》3-Node模块

编写Node应用前,需要先学习Node模块和包,她抿额是组成应用的基本单位;

第3章 Node模块

模块的概念、CommonJS模块规范、Node搜索模块的方式、npm包管理系统

注:当前版本Node基础知识参考视频笔记《Node基础(1)》

3.1 什么是模块

模块是构成Node应用的组件:

  • Node中使用的每一个JS文件都是一个模块;如通过require('fs')引入fs模块,从而访问其内部函数;
  • require函数会搜索模块,然后将模块的定义载入Node的执行环境,是内部函数可供外部使用;
exports.xx = xx
// 
const yy = require('yy')
yy.xx...

Node模块存在形式:

  • 通用模块:以文件形式存在;
  • 核心模块:以编译后的二进制Node可执行文件形式存在;

Node中3种定义模块标识符的方式:

  • 相对路径:./or../
  • 绝对路径:/,与文件系统的根目录相关;
  • 顶级目录:以模块名开头;这些模块存储在某个目录中,如node_modules目录;

注:模块名就是一个没有文件后缀的路径名;

Node应用里的本地模块:(项目中临时编写的)

  • 应用内的模块能够获取应用内其他模块的相对路径,并通过相对路径标识符相互访问;

Node应用里的外部依赖:(项目外封装好的)

  • 引用node_modules目录中的模块必须使用顶级目录标识符;Node会在寻找模块是搜索node_modules目录;(即引入模块的位置与node_modules同目录)
  • 若应用内含有多个或多级的node_modules目录,会从当前引入的路径目录开始逐层向父一级目录的node_modules目录寻找引入模块,直到找到为止或到达根目录;(注意是向外找 而不是向里找)

注:在引入模块位置的node_modules目录下存在express模块,express中又有qs模块,qs模块无法被访问;引入模块位置的上一级目录也无法访问node_modules目录;

注:多个node_modules目录方便在特定位置存放特定版本的模块;

复杂的模块——作为目录的模块:

  • 当一个模块足够复杂时,其内部可能会设计多个内部模块,结合模块自身代码,由Node将其视为一个模块;模块入口文件默认是index.js,或者通过package.json文件描述模块数据,指定模块入口文件是其他(当前目录或子目录的)文件;
  • 当require查找模块时,会通过两种方式查找:
    • 没有package.json文件的:Node会查找模块的index.js文件,并加载;
    • 有package.json文件的:Node会查找模块的package.json中描述的模块入口文件,并加载;

3.2 Node包管理器

npm是一个Node包管理和分发工具:

  • 在网上通过简单的命令行发布和管理Node包;如查找特定服务的包、下载、安装并管理;

npm包的格式:

  • 一个npm包是包含了package.json的文件夹,package.json描述了这个文件夹的结构;
  • 包本身使用的模块,可以放在modules目录下;依赖的外部模块则安装在node_modules目录中;

注: 如果需要在公共的npm仓库发布一个包,最好提前检查下包名是否被占用,使用命令npm search xxx;或在官网查询

注:Node包可以以tar-gzip格式打包,尤其在网络传输时;

package.json标签简介:(只是一小部分)

  • main:包的模块文件
  • name:包的名
  • version:包的版本
  • dependencies:声明与其他依赖包的关系,npm可自动安装其他被依赖的包;
  • description:包的描述
  • homepage:主页信息,查看文档
  • author:作者信息
  • directories:记录包的目录结构;(lib目录会被自动加载)
  • scripts:对应包生命周期事件执行的脚本命令,npm help scripts可用于查看脚本命令;

注:查看包的信息,使用npm view xxx;如果需要查看包的package.json中某一标签的信息,在前边的命令后 加上相应的标签名,如npm view vue homepage

npm包的安装:

  • 命令:npm install xxx
  • 两种安装模式:
    • 本地模式:安装到应用代码同级的本地node_modules目录;
    • 全局模式:安装到Node安装目录下,可使用$ which node命令查看;全局模式需要使用-g标志,如npm install -g xxx
    • 了解两种目录可以使用命令npm help folders查看;

也可以修改npm的配置为全局模式,$ npm set global=true,默认$ npm get global是false;

注:已安装的包中依赖的子模块,也可以在应用代码中被引入使用,如require('cur-package/lib/sub-package')

npm查看已安装的Node包:

  • 使用npm list可以列出当前目录已安装的包(取决于当前目录node_modules中的内容);
  • 默认是以树状形式展示,可以配置$ npm set parseable=true,查询结果会展示为/Users/huaqiang/music-system/node_modules/proxy-agent的形式;

Node包的脚本:

  • npm允许Node包脚本在包生命周期的不同时间自动执行;已有的生命周期事件如测试、启动、停止、重新启动;
  • npm包的测试$ npm test <packageName>
  • 启动、停止、重新启动 这些生命周期事件没有固定含义,明显的用途就是启动或者停止一个与Node包相关的后台进程;

更新已安装Node包:

  • $ npm outdated,会展示当前已安装包的版本和线上npm库中最新版本;
  • $ npm update <packageName>,更新已安装的Node包;
  • $ npm uninstall <packageName>,卸载已安装的Node包;

开发npm包:

  • 第一步,到一个指定空文件夹<packageName>,使用$ npm init创建初始版本的package.json(需要初始一些信息);
  • 第二步,创建包源文件,需要自行组织代码,并及时更新到package.json中;

开发中的npm link命令

在开发npm包时,需要调试刚刚编辑的内容,就需要频繁的打包然后更新到引入该包的应用中进行联调,为了避免类似的重建,可以在当前开发的node包目录下,执行npm link命令,它会将正在开发的Node包链接到全局的node_modules目录中(一个符号链接而已);

接下来在调试的node应用中,使用命令$ npm link <packageName>,就相当于安装了开发中的node包;

开发中的npm install命令

区别于npm link命令,在当前开发的node包目录下,执行npm install命令会将当前目录和依赖关系安装到本地的node_modules目录(这可不是符号链接!)

发布npm包:

  • 已经开发调试好的Node包,可以发布到公共的npm仓库,这样别人就可以使用你开发的Node包;
  • 通过npm adduser命令可以注册一个npm仓库账号;
  • 在对应的Node包的根目录下运行命令npm publish命令;
  • npm unpublish命令可以将Node包从npm仓库里移除;

npm的配置:(前文中就有修改过npm配置)

npm config set <key>=<value> [--global]
npm config get <key>
npm config delete <key>
npm config list
npm config ls -l
npm config edit
npm get <key>
npm set <key>=<value> [--global]

注:Node无法识别版本号,只能识别模块,但npm可以识别版本号;如npm view vue version,安装时也可以指定版本进行安装,如npm install express@2.3.1

注:npm有‘标签‘的概念,如npm install sax@stable,标签名非必须,且作者可指定包的标签名;

关于包的版本号

  • Node应用的依赖包,在npm管理时,支持版本号范围声明,如connect:'>= 1.5.1 < 2.0.0';这种比较通常是数值比较;(1.0.0beta1 < 1.0.0beta2 < 1.0.0)
  • 版本号形如X.Y.Z,分别表示主版本、副版本、日常补丁版本;补丁号后紧跟的字符串通常叫做特别版本,如beta版;
  • X为0表示不稳定,向前兼容的修复Z递增,引入向前兼容函数/功能Y递增,版本冲突时X递增;

CommonJS模块

  • Node模块基于CommonJS模块,虽然JS强大,还有很多高级特性,但是缺少一个标准对象库辅助构建应用,CommonJS旨在填补这一缺口,同时提供一个实现JS模块的约定和一些列标准模块;
  • CommonJS模块的导出和引入,参考《Node基础(1)》-模块成员导出详解

注:浏览器中,存在一个全局执行上下文,多个JS脚本共享全局变量;但在CommonJS模块中,每一个模块都有自己的私有全局执行环境,模块内多个函数共享这些变量较为安全,不会影响其他模块中的全局变量;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值