Node.js 的利用场景
-
前端工程化
- 打包工具(bundle):webpack、vite、esbuild、parcel
- 代码压缩(uglify):uglifyjs
- 语法转换(transpile):bablejs、typescript
- 其余语言退出竞争:esbuild(golang)、parcel(rust)、prisma
- Node.js 现状:在前端工程化畛域依然难以代替
-
Web 服务端利用
- 学习曲线平缓,开发效率较高
- 运行效率靠近常见的编译语言
- 社区生态丰盛以及工具链成熟(npm、V8 inspector)
- 与前端联合的场景会有劣势(服务端渲染 ssr)
- 现状:竞争强烈,Node.js 有本人独特的劣势
-
Electron 跨端桌面利用
- 商业利用:vscode、slack、discord、zoom
- 大型公司内的效率工具
- 现状:大部分场景在选型时,都值得思考
-
其余利用场景
- BFF(Backends For Frontends)利用:作为中间件自在解决后端接口,使得前后端开发更加拆散
- SSR(Server Side Render)利用:将组件或页面通过服务器生成 HTML 字符串,再发送到浏览器
Node.js 运行时构造
-
Node.js 组成
- V8:JavaScript Runtime,诊断调试工具(inspector)
- libuv:eventLoop(事件循环),syscall(零碎调用)
- Node.js 运行示例(以 node-fetch 为例):
-
Node.js 运行时构造特点
-
异步 I/O
- 当 Node.js 执行 I/O 操作时,会在响应返回后复原操作,而不是阻塞线程并占用额定内存期待
- 当 Node.js 执行 I/O 操作时,会在响应返回后复原操作,而不是阻塞线程并占用额定内存期待
-
单线程
- JavaScript 是单线程的(只有一个主线程),但理论在 Node.js 环境中是:JavaScript 单线程 + libuv 线程池 + V8 工作线程池 + V8 inspector 线程
- 比方读取文件(常见的 I / O 操作)时,将该工作交给 libuv 线程池去操作,JS 主线程便能够进行其余工作
- V8 inspector 独自作为一个线程的作用:比方写了一个死循环,惯例状况无奈再去调试,便能够利用 V8 inspector 线程调试该死循环
- 长处:不必思考多线程状态同步问题,也就不须要锁,同时还能比拟高效地利用系统资源
- 毛病:阻塞会产生更多负面影响,解决办法:多过程或多线程
-
跨平台
- Node.js 跨平台 + JS 无需编译环境 + Web 跨平台 + 诊断工具跨平台
- 长处:开发成本低,整体学习成本低
-
编写 HTTP Server
-
编写一个简略的 server 服务,使得浏览器关上主机的 3000 端口看到 hello!nodejs!
const http = require('http'); const port = 3000; const server = http.createServer((req, res) => {res.end('hello!nodejs!'); }) server.listen(port, () => {console.log('success!', port); });
-
把用户申请中的数据转换为 JSON 格局,并在响应中返回给浏览器
const http = require('http'); const port = 3000; const server = http.createServer((req, res) => {const bufs = []; req.on('data', (buf) => {bufs.push(buf); }); req.on('end', () => {const buf = Buffer.concat(bufs).toString('utf-8'); let msg = 'hello!' try {const ret = JSON.parse(buf); msg = ret.msg; } catch (err) {// 这里申请数据不非法临时不做解决(因为无奈发送非法数据),msg 的值仍是 hello} // 解决响应 const responseJson = {msg: `receive : ${msg}`, } // 响应头设置 Content-Type res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(responseJson)); }); }); server.listen(port, () => {console.log('success!', port); });
-
搭建一个客户端,能给 server 发送 post 申请
const http = require('http'); const port = 3000; const body = JSON.stringify({msg: 'Hello from my client',}) const req = http.request('http://127.0.0.1:3000', { method: 'POST', headers: {'Content-Type': 'application/json',} }, (res) => { // 拿到响应当前,转换为 JSON 格局,输入在控制台 const bufs = []; res.on('data', (buf) => {bufs.push(buf); }) res.on('end', () => {const buf = Buffer.concat(bufs); const res = JSON.parse(buf); console.log('json.msg is :', res); }) }); // 发送申请 req.end(body);
-
用 promise + async await 重写这两个例子
const http = require('http'); const port = 3000; const server = http.createServer(async (req, res) => { // recieve body from client const msg = await new Promise((resolve, reject) => {const bufs = []; req.on('data', (buf) => {bufs.push(buf); }); req.on('error', (err) => {reject(err); }); req.on('end', () => {const buf = Buffer.concat(bufs).toString('utf-8'); let msg = 'hello!nodejs!' try {const ret = JSON.parse(buf); msg = ret.msg; } catch (err) {//} resolve(msg); }); }) // response const responseJson = {msg: `receive : ${msg}`, } res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(responseJson)); }); server.listen(port, () => {console.log('success!', port); });
-
搭建一个能够读取服务器上动态资源的 server
const http = require('http'); const fs = require('fs'); const url = require('url'); const path = require('path'); // 文件门路 const folderPath = path.resolve(__dirname, './static_server'); const server = http.createServer((req, res) => { // expected http://127.0.0.1:3000/index.html?abc=10 const info = url.parse(req.url); // static_server.html const filepath = path.resolve(folderPath, './' + info.path); // stream api const filestream = fs.createReadStream(filepath); filestream.pipe(res); }); const port = 3000; server.listen(port, () => {console.log('listening on :', port); })
- 与高性能、牢靠的动态文件服务器相比,还须要缓存、减速、分布式缓存的能力
-
SSR(server side render)有什么特点
- 相比传统 HTML 模板引擎防止了反复编写代码
- 相比 SPA(single page application)首屏渲染更快,SEO(搜索引擎敌对 Search Engine Optimization)敌对
- 毛病:通常 qps(每秒查问率 Queries-per-second)较低,前端代码编写时须要思考服务端渲染状况
-
编写 React-SSR HTTP Server
const React = require('react'); const ReactDOMServer = require('react-dom/server'); const http = require('http'); const App = (props) => { // 防止编译问题不应用 jsx return React.createElement('div', {}, props.children || 'Hello!') } const port = 3000; const server = http.createServer((req, res) => { res.end(` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>my application</title> </head> <body> ${ReactDOMServer.renderToString(React.createElement(App, {}, 'my_content') )} <script> init...... </script> </body > </html > `); }) server.listen(port, () => {console.log('success!', port); });
-
SSR 难点:
- 须要解决打包代码
- 须要思考前端代码在服务端运行时的逻辑
- 移除对服务端毫无意义的副作用或重置环境
-
HTTP Server -Debug
- 运行 js 文件时,在文件名之前加上 –inspector 指令,在浏览器输出对应的调试地址,查看 json 信息能够跳转到前端调试界面。以 static_file_server 为例,输出 127.0.0.1:9229/json 即可
- 理论开发环境中打断点调试会比拟危险,能够打 logpoint
- Memory 面板:能够打一个 snapshot 查看 JavaScript 主线程中的内存占用信息
- Profile 面板:能够录制一个 profile 查看 CPU 一段时间内的运行状况,能够看到调用了哪些函数。比方利用产生了死循环,能够通过 profile 查看死循环时在运行什么代码
- 也能够通过上述数据察看代码性能,判断函数运行工夫是否合乎预期
-
HTTP Server - 部署
-
部署要解决的问题
- 守护过程:当过程退出时从新拉起
- 多过程:cluster 便携地利用多过程
- 记录过程状态用于诊断
-
容器环境
- 通常有健康检查的伎俩,只须要思考多核 CPU 利用率即可
-