将自己在掘金上发的笔记搬了过来:Node.js 与前端开发实战、个人博客
前端工程化
早期的jQuery等库都是直接在页面中引入,后来模块化逐渐成熟,Node.js赋予了开发者在浏览器外运行代码的能力,前端逐渐模块化、
Bundle:webpack、Vite、esbuild、Parcel等
Uglify:UglifyJS
Transplie:babeljs、TypeScript
个人理解:Transplie就是将ES6这样最新的语法转译成低版本的写法,实现浏览器兼容
其他语言加入前段工程化的竞争:esbuild、Parcel 、prisma等
现状:Node.js难以替代
Web服务端应用
Electron跨端桌面应用
特点
异步I/O:
setTimeout(() => { console.log('B'); }) console.log('A');
一个常见场景:读取文件时。当Node.js执行I/O操作时,会在响应返回后恢复操作,而不是阻塞线程并占用额外内存等待。(内存占用更少)
单线程
worker_thread可以起一个独立线程,但每个线程的模型没有太大变化
function fibonacci(num:number):number { if(num === 1 || num === 2) { return 1; } return fibonacci(num-1) + fibonacci(num-2); } fibonacci(42) fibonacci(43)
JS单线程
优点:不用考虑多线程状态同步问题,也就不需要锁。同时还能比较高效地利用系统资源;
缺点:阻塞会产生更多负面影响、异步问题、延时有要求的场景需要考虑。
跨平台(大部分功能、api)
想用linux上的Socket,而不同平台上调用的又不一样,只需:
const net = require('net') const socket = new net.Socket('/tmp/socket.sock')
Node.js跨平台 + JS无需编译环境(+ Web跨平台 + 诊断工具跨平台)
首先编写一个server.js,如下
createServer
说明
req 请求,res响应
server.listen
说明
port 要监听的端口号,成功后的回调函数
const http = require('http'); const server = http.createServer((req, res) => { res.end('hello'); // 响应直接就是hello }); const port = 3000; server.listen(port, () => { console.log(`server listens on:${port}`); // 监听3000端口 })
使用node启动,此时输入localhost:3000就可以看到hello
改为JSON版
const server = http.createServer((req, res) => { // receive body from client const bufs = []; // 取传的数据 req.on('data', data => { bufs.push(data); }); req.on('end', () => { const buf = Buffer.concat(bufs).toString('utf-8'); let msg = 'Hello'; try { reqData = JSON.parse(buf); msg = reqData.msg; } catch (err) { res.end('invalid json'); } // response const responseJson = { msg: `receive:${msg}` } res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(responseJson)); }); });
http.request(url[, options\][, callback])
const http = require('http'); const body = JSON.stringify({ msg: 'hello from my own client' }); // [url] [option] [callback] const req = http.request('http://127.0.0.1:3000', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': body.length, }, }, (res) => { // 响应体 const bufs = []; res.on('data', data => { bufs.push(data); }); res.on('end', () => { const buf = Buffer.concat(bufs); const receive = JSON.parse(buf); console.log('receive json.msg is:', receive); }); }) req.end(body);
可以用 Promise + async和await 重写这两个例子(why?)
当 await 关键字与异步函数一起使用时,它的真正优势就变得明显了 —— 事实上, await 只在异步函数里面才起作用。它可以放在任何异步的,基于 promise 的函数之前。它会暂停代码在该行上,直到 promise 完成,然后返回结果值。在暂停的同时,其他正在等待执行的代码就有机会执行了
async/await
让你的代码看起来是同步的,在某种程度上,也使得它的行为更加地同步。await
关键字会阻塞其后的代码,直到promise完成,就像执行同步操作一样。它确实可以允许其他任务在此期间继续运行,但您自己的代码被阻塞。
回调写太多容易找不到,不宜维护
function wait(t) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, t); }); } wait(1000).then(() => { console.log('get called'); });
并不是所有回调都适合改写成Promise
const server = http.createServer(async (req, res) => { // 注意这里的async // receive body from client 改成了Promise形式 const msg = await new Promise((resolve, reject) => { //执行完再交给msg const bufs = []; req.on('data', data => { bufs.push(data); }); req.on('error', (err) => { reject(err); }) req.on('end', () => { const buf = Buffer.concat(bufs).toString('utf-8'); let msg = 'Hello'; try { reqData = JSON.parse(buf); msg = reqData.msg; } catch (err) { // } resolve(msg); }); }); // response const responseJson = { msg: `receive:${msg}` } res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(responseJson)); });
编写一个简单的静态服务,接受用户发过来的http请求,拿到图片的url约定为静态文件服务器磁盘上对应的路径,再把具体内容返回给用户。这次除了 http 模块,还需要 fs 模块和 path 模块
先编写一个简单的 index.html,放于static目录下
const http = require('http'); const fs = require('fs'); const path = require('path'); const url = require('url'); // __dirname是当前这个文件所在位置, ./为当前文件所在文件夹 folderPath即为static文件夹相对于当前文件路径 const folderPath = path.resolve(__dirname, './static'); const server = http.createServer((req, res) => { // 注意这里的async // expected http://127.0.0.1:3000/index.html const info = url.parse(req.url); // static/index.html const filepath = path.resolve(folderPath, './'+info.path); console.log('filepath', filepath); // stream风格的api,其内部内存使用率更好 const filestream = fs.createReadStream(filepath); filestream.pipe(res); }); const port = 3000; server.listen(port, () => { console.log(`server listens on:${port}`); })
npm init npm i react react-dom
const React = require('react'); const ReactDOMServer = require('react-dom/server'); const http = require('http'); function App(props) { return React.createElement('div', {}, props.children || 'Hello'); } const server = http.createServer((req, res) => { res.end(` <!DOCTYPE html> <html> <head> <title>My Application</title> </head> <body> ${ReactDOMServer.renderToString( React.createElement(App, {}, 'my_content'))} <script> alert('yes'); </script> </body> </html> `); }) const port = 3000; server.listen(port, () => { console.log('listening on: ', port); })
V8 Inspector:开箱即用、特性丰富强大、与前端开发一致、跨平
台
node -- inspect
open http://localhost:9229/json
场景:
写完了,如何部署到生产环境捏?
部署要解决的问题
守护进程:当进程退出时,重新拉起
多进程:cluster便捷地利用多进程
记录进程状态,用于诊断
容器环境
Node. js Core贡献入门
本节课从Node.js介绍起,实现了其编写Http Server的一个实战(并用Promise优化回调,还对SSR有了一定的了解),并在延伸话题里老师也给出了一些建议与拓展阅读,好欸~
本文引用的内容大部分来自欧阳亚东老师的课以及MDN。