阅读 218

Node服务器端JavaScript(node服务器端渲染)

核心的JavaScript语法定义了最小限度的API,可以操作数值、文本数组等,但不包含输入输出功能。输出入是内嵌JavaScript的宿主环境的责任 。

宿主环境:浏览器、node

  • 前端的JavaScript是由ECMAScript、DOM、BOM组合而成,Node.js是由ECMAScript、OS、File、Net、DB组成

  • Node.js是使 JavaScript 运行在浏览器之外,即服务端的开发平台, 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,也是一个

  • 可以在node.green/上获取到Node各个版本对ES语法的支持

Node,JavaScript与底层操作系统绑定的结合,与限制JavaScript只能使用浏览器提供的API不同,Node给予JavaScript可以访问整个操作系统的权限,允许JavaScript读写文件、通过网络发送和接收数据。应用:

  • 替代命令行脚本、交互式终端程序

  • 运行受信程序的编程语言,没有浏览器运行不受信代码带来的安全限制

  • 编写高效、高并发web服务器的流行环境:高并发、实时聊天、实时消息推送、基于社交网络的大规模 Web 应用

  • 客户端逻辑强大的SPA

  • Restful API,可以处理数万条链接

  • 实时websocket应用

  • 前端工具链,e.g:单元测试工具、客户端 JavaScript 编译器

  • 桌面开发、带有图形用户界面的本地应用程序

  • TCP/UDP 套接字应用程序

  • Node.js 还可以部署到非网络应用的环境下,比如一个命令行工具。Node.js 还可以调用C/C++ 的代码,这样可以充分利用已有的诸多函数库,也可以将对性能要求非常高的部分用C/C++ 来实现。

Node.js的结构图 在这里插入图片描述 libuv是提供异步功能的C库,在运行时负责一个事件循环,一个线程池、文件系统I/O、DNS相关的IO和网络IO。

  • node.js启动后,会开启一个JS主线程和libuv提供的线程池和Event Loop。当发现有IO操作就交给线程池并注册回调函数。

  • node.js的全局对象有global、process、console、module、exports

  • process :node进程相关的信息,比如运行node程序时的命令行参数。或者设置进程相关信息,比如设置环境变量。

REPL 模式

  • REPL【Read-eval-print loop输入—求值—输出循环】

  • 和python一样,Node.js 也有这样的功能,运行无参数的 node 将会启动一个 JavaScript 的交互式 shell

  • 打开命令提示符,然后输入 node,进入 REPL 模式以后,会出现一个“>”提示符提示你输入命令,输入后按回车,Node.js 将会解析并执行命令。

  • 执行了一个函数,REPL 会在下一行显示这个函数的返回值。

  • 连续按两次 Ctrl + C 退出Node.js 的 REPL 模式。

  • 如果你输入了一个错误的指令,REPL 则会立即显示错误并输出调用栈。

process.argv命令行参数

  • 第一、第二个的元素是node可执行文件(环境)和被执行JavaScript文件路径

  • 提供给node可执行文件且由它解释的命令行参数会被node可执行文件使用,不会出现在process.argv中

node也会从unix风格的环境变量中获取输入,node把这些环境变量保存在process.env对象中使用

SHELL: '/bin/bash', USER: 'joy', HOME: '/Users/joy' 复制代码

单线程、非阻塞的事件编程模式

  • 传统的架构是多线程模型,为每个业务逻辑提供一个系统线程,通过系统线程切换来弥补同步式【即时回复】 I/O 调用时的时间开销

  • Node.js 使用的是单线程模型

  • V8引擎提供的异步执行回调接口可以处理大量的高并发,IO密集型处理是强项

  • 对于所有 I/O 都采用异步请求,避免了频繁的上下文切换

  • node创建于JavaScript支持promise之前,其异步主要依赖回调函数实现

  • **事件式编程:在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式 I/O 请求完成后会被推送到事件队列,等待程序进程进行处理。

  • 同步的阻塞式IO在高并发环境将是一个很大的性能问题,所以同步一般只在基础框架启动时使用,用来加载配置文件、初始化程序等。

  • 其他同步的应用需求:和操作系统的shell命令交互,调用可执行文件等

  • Node与进程相关的模块process、child_process、cluster

  • 大多数API都有同步/异步两个版本

注意:Node.js在底层访问I/O其实还是多线程,可以翻看fs模块源码,里面用libuv来处理I/O

异步错误不能被同步线程捕获,有可能会变成致命错误,终止程序。注册全局处理程序

process.on("unhandledRejection", (reason, promise) => {     // }); 复制代码

多线程

在支持HTML5的浏览器里,我们可以使用web worker来处理一些耗时运算。 对应Node.js也提供了cluster、child_process模块的web worker来解决。

模块

  • node在JavaScript支持模块系统之前就诞生,所以它拥有自己的模块系统——module.exports 和 require

  • 模块是 Node.js 应用程序的基本组成部分,文件即模块。文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。

  • Node 13增加了对ES6模块import、export的支持,同时支持自己的模块系统。

  • mjs扩展名文件-ES6模块

  • cjs扩展名文件-CommonJS模块格式

  • 没有明确扩展名的,会向上查找package.json文件,检查JSON对象的顶级type属性的值commonjs、module

  • 运行node并不需要有package.json,如果没有找到默认CommonJS模块

//module.js  var name;  exports.setName = function(thyName) {   name = thyName;  };  exports.sayHello = function() {   console.log('Hello ' + name);  };  //getmodule.js  var myModule1 = require('./module');  myModule1.setName('1');   var myModule = require('./module');  myModule.setName('BYVoid');  myModule1.sayHello();  复制代码

运行node getmodule.js,结果是: Hello BYVoid

require 不会重复加载模块,也就是说无论调用多少次获得的模块都是最后一次。

封装一个对象到模块

//hello.js  function Hello() {   var name;     this.setName = function(thyName) {   name = thyName;   };     this.sayHello = function() {   console.log('Hello ' + name);   };  };  module.exports = Hello;//如果是exports.Hello = Hello; //gethello.js  var Hello = require('./hello'); //那么是require('./hello').Hello hello = new Hello();  hello.setName('BYVoid');  hello.sayHello(); 复制代码

http模块

http 是 Node.js 的一个核心模块,其内部是用 C++ 实现的,外部用 JavaScript 封装。 node内建了 HTTP 服务器支持,这和 PHP、Perl 不一样,因为在使用 PHP 的时候,必须先搭建一个 Apache 之类的HTTP 服务器,然后通过 HTTP 服务器的模块加载或 CGI 调用,才能将 PHP 脚本的执行结果呈现给用户。 而使用 Node.js 时,无需额外搭建一个 HTTP 服务器。直接可以用来调试代码,并且它本身就可以部署到产品环境,其性能足以满足要求。

function serve(rootDirectory, port) {     let server = new http.Server();     server.listen(port);     console.log('listening on port', port);          server.on('request', (request,response) => {         let temp = url.parse(request.url).pathname;         //查找文件,返回状态码及文件     }) } 复制代码

包package

NPM

安装node同时会得到npm包管理工具

通常一个包的配置如下

  1. GZIP压缩文件

  2. 解析GZIP的URL

  3. 为注册表添加< name > @< version >的URL信息

  4. package.json配置文件(在包目录中运行npm init 会根据交互式问答产生一个符合标准的package.json)

  • package.json文件中使用script定义脚本命令(脚本文件一般位于node_modules/.bin子目录里)

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

{     //...     "script":{         "build":"node index.js"     }     //...     "main" : "./lib/interface.js"  } //npm run build等同于node index.js 复制代码

  • 二进制文件应该在 bin 目录下;

  • JavaScript 代码应该在 lib 目录下;

  • 文档应该在 doc 目录下;

  • 单元测试应该在 test 目录下

工具模块

  • JavaScript 的单元测试可使用一个 Node.js 的 模 块,Karma 执行

  • JavaScript 没有编译器,不能在早期验证代码的合法性。有效的单元测试常常扮演一个伪编译器的角色,立刻反馈代码的质量,并且一有缺陷,就能检测到。

  • JavaScript 中有很多自动化文档生成工具。JSDoc有着和Javadoc 类似的标记和输出,Dox是一个生成文档的Node.js 模块。

Docco将代码和注释组织成一种类似文章的格式。虽然Docco 不直接验证和执行代码规范,使用它却能鼓励大家使用良好的代码结构和注释

package.json

完全符合规范的 package.json 文件应该含有以下字段。

  • name:包的名称,必须是唯一的,由小写英文字母、数字和下划线组成,不能包含空格。

  • description:包的简要说明。

  • version:符合语义化版本识别规范的版本字符串。

  • keywords:关键字数组,通常用于搜索。

  • maintainers:维护者数组,每个元素要包含 name、email(可选)、web(可选)

字段

  • contributors:贡献者数组,格式与maintainers相同。包的作者应该是贡献者

数组的第一个元素。

  • bugs:提交bug的地址,可以是网址或者电子邮件地址。

  • licenses:许可证数组,每个元素要包含 type (许可证的名称)和 url (链接到

许可证文本的地址)字段。

  • repositories:仓库托管地址数组,每个元素要包含 type (仓库的类型,如 git )、

url (仓库的地址)和 path (相对于仓库的路径,可选)字段。

  • dependencies:包的依赖,一个关联数组,由包名称和版本号组成。

{   "name": "mypackage",   "description": "Sample package for CommonJS. This package demonstrates the required  elements of a CommonJS package.",   "version": "0.7.0",   "keywords": [   "package",   "example"   ],   "maintainers": [   {   "name": "Bill Smith",   "email": "bills@example.com",   }   ],   "contributors": [   {   "name": "BYVoid",   "web": "http://www.byvoid.com/"   }   ],   "bugs": {   "mail": "dev@example.com",   "web": "http://www.example.com/bugs"   },   "licenses": [   {   "type": "GPLv2",   "url": "http://www.example.org/licenses/gpl.html"   }   ],   "repositories": [   {   "type": "git",   "url": "http://github.com/BYVoid/mypackage.git"   }   ],   "dependencies": {   "webkit": "1.2",   "ssl": {   "gnutls": ["1.0", "2.0"],   "openssl": "0.9.8"   }   }  }  复制代码

使用npm安装包

在使用 npm 安装包的时候,有两种模式

  1. 本地模式:把目标包作为工程的一部分

使用 npm 安装包的命令格式为:

npm [install/i] [package_name]  复制代码

  • 安装成功放置在当前目录的 node_modules 子目录下,require 在加载模块时会尝试搜寻 node_modules 子目录

  • npm 本地模式仅仅是把包安装到 node_modules 子目录下,其中的 bin 目录下可执行文件没有包含在 PATH 环境变量中,不能直接在命令行中调用。

  1. 全局模式:全局使用,多个工程,支持在命令行下使用包

npm [install/i] -g [package_name]  复制代码

  • 使用全局模式安装时,npm 会将包安装到系统目录,譬如/usr/local/lib/node_modules/,同时 package.json 文件中 bin 字段包含的文件会被链接到 /usr/local/bin/

  • /usr/local/bin/ 是在PATH 环境变量中默认定义的,因此就可以直接在命令行中运行 xxx.js命令了。

  • 注:使用全局模式安装的包并不能直接在 JavaScript 文件中用 require 获得,因为 require 不会搜索 /usr/local/lib/node_modules/

相比npm、yarn有了很多改进:yarn new package/

  • 安装速度快(cache和依赖解析做得好)

  • 模块安装保证幂等性

  • 锁定各依赖模块的版本(npm 使用的semver默认是指定了一个range)

  • 兼容性好(兼容旧有npm的工作流)

npm link创建符号链接

npm 提供了一个命令 npm link,在本地包和全局包之间创建符号链接,解决-使用全局模式安装的包不能直接通过 require 使用、局部包不支持命令行

npm link 命令不支持 Windows。

npm install -g express npm link express  // 显示 ./node_modules/express -> /usr/local/lib/node_modules/express  复制代码

在 node_modules 子目录中发现一个指向安装到全局的包的符号链接。通过这种方法,就可以把全局包当本地包来使用了。

将本地的包链接到全局:

在包目录( package.json 所在目录)中运行 npm link 命令。 复制代码

开发包时,利用这种方法可以非常方便地在不同的工程间进行测试。

发布包

包目录下运行

npm init :生成一个符合 npm 规范的 package.json 文件 npm adduser :获得一个账号用于今后维护自己的包 npm whoami :测验是否已经取得了账号 npm publish:发布 复制代码

访问 search.npmjs.org/ 就可以找到刚刚发布的包了。可以在世界的任意一台计算机上使用 npm install byvoidmodule 命令来安装它 更新

在 package.json 文件中修改 version 字段,然后npm publish npm unpublish 命令来取消发布。 复制代码

Runtime和vm

Runtime :解释型语言的数据类型的确定由编译推迟至运行时,所以需要一个运行时系统来处理编译后的代码。

JavaScript引擎负责解析和JIT编译,例如编译成机器码。Runtime提供内建的库(例如常用数据类型、Window对象、DOM API),可以在运行时使用。

  • vm:通常认为是硬件和二进制文件之间的中间层

  • c++编译好的二进制文件交给OS直接调用

  • Java编译好的二进制文件交给Java虚拟机运行,对开发者屏蔽了不同操作系统的差异

  • Node的一个核心模块vm提供了一系列API用于在V8虚拟机环境中编译和运行代码。

node.js中的stream流

基于EventEmitter的数据管理模式,由各种不同的抽象接口组成,主要包括可写、可读、可读写、可转换等类型

  • 流是非阻塞数据处理模式,可以提升效率、节省内存、有助于处理管道且可扩展等。

  • 流的应用:文件读写、网络请求、数据转换、音频、视频等方面有很广泛的应用

  • 监听error事件,可以捕获流的错误事件

node支持四种流

  1. 可读流

  2. 可写流

  3. 双工流

  4. 转换流

流常与管道共用

有时候,我们需要把可读流中获取的数据写入可写流。例如,写一个HTTP服务器提供对静态文件目录的访问。需要将文件流写入网络套接字。与其自己写代码来处理读和写,不如把这两个接口连接为一个“管道”,将可写流简单传递给可读流的pipe()方法,即可让node帮我们实现复杂操作

const fs = require('fs'); function pipeFileToSocket(file, socket){     fs.createReadStream(file).pipe(socket); } 复制代码

使用

//app.js  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 port 3000.");  复制代码

运行 node app.js命令,打开浏览器访问 http://127.0.0.1:3000 这个程序调用了 Node.js 提供的http 模块,对所有 HTTP 请求答复同样的内容并监听 3000 端口。在终端中运行这个脚本时,我们会发现它并不像 Hello World 一样结束后立即退出,而是一直等待,直到按下 Ctrl + C 才会结束。这是因为 listen 函数中创建了事件监听器,使得 Node.js 进程不会退出事件循环。

因为 Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入,开发 Node.js 实现的 HTTP 应用时会发现,无论修改了代码的哪一部份,都必须终止Node.js 再重新运行才会奏效。

supervisor 可以监视你对代码的改动,并自动重启 Node.js。【相当于持续重启,有选择的使用】 首先使用 npm 安装 supervisor: $ npm install -g supervisor  接下来,使用 supervisor 命令启动 app.js: $ supervisor app.js 复制代码

调试

node debug debug.js:打开了一个 Node.js 的调试终端 node-inspector

控制流很大程度上要靠事件和回调函数来组织,一个逻辑要拆分为若干个单元。

Mocha

一个基于Node.js和浏览器的集合各种特性的JavaScript测试框架


作者:贾明恣
链接:https://juejin.cn/post/7031816581273681927


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐