安装
https://nodejs.org/en/download/
安装nvm
nodejs多版本切换
window,github中搜索nvm-windows下载
nvm list 查看当前所有的node版本
nvm install v10.13.0 安装指定版本
nvm use --delete-prefix 10.13.0 切换到指定版本
nodejs和js的区别
ECMAScript是语法规范
nodejs = ECMAScript + nodejs API
server开发和前端开发的区别
服务稳定性
server端可能会遭受各种恶意攻击和误操作
单个客户端可以意外挂掉,但是服务端不能
使用PM2做进程守候
考虑内存和CPU(优化,扩展)
客户端独占一个浏览器,内存和CPU都不是问题
server端要承载很多请求,CPU和内存都是稀缺资源
使用stream写日志,使用redis存session
日志记录
前端也会参与写日志,但是日志的发起方,不关心后续
server端要记录日志,存储日志,分析日志,前端不关心
安全
server端随时准备接受各种恶意攻击,前端则少很多
如:越权操作,数据库攻击
预防xss攻击和sql注入
集群和服务拆分
产品发展速度快,流量可能会迅速增加
如何通过扩展机器和服务器拆分来承载大流量
自动重启工具
安装
npm install -g supervisor
启动
supervisor app.js
或者使用nodemom
安装
node install -g nodemom
启动
node app.js
调试
inspect
chrome://inspect/
安装插件NIM
node --inspect-brk a.js
process
参数相关 argv/argv0/execArgv/execPath
const {argv} = require('process') argv.forEach((val, index) => { console.log(`${index}:${val}`) }) // 0:D:develtools odejs ode.exe // 1:F:ProjectNodeJSlearnlearna.js // 2:--s // 3:a // 4:b // 5:c
环境 env
cwd()
nextTick
setImmediate(() => { console.log('setImmediate') }) setTimeout(() => { console.log('setTimeout') }) setTimeout(() => { console.log('setTimeout1') },100) process.nextTick(() => { console.log('nextTick') })
path
normalize 规范path,解析.. 和 .
path.normalize('/foo/bar//baz/asdf/quux/..'); // 返回: '/foo/bar/baz/asdf'
join 给定的path连接在一起
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // 返回: '/foo/bar/baz/asdf'
resolve 把相对路径转换成绝对路径
resolve('./')
basename 扩展名
path.basename('/foo/bar/baz/asdf/quux.html'); // 返回: 'quux.html'
dirname 返回path的目录名
path.dirname('/foo/bar/baz/asdf/quux'); // 返回: '/foo/bar/baz/asdf'
exname 返回path的扩展名
path.extname('index.html'); // 返回: '.html'
path 返回path对象
path.parse('/home/user/dir/file.txt'); // 返回: // { root: '/', // dir: '/home/user/dir', // base: 'file.txt', // ext: '.txt', // name: 'file' }
format 与path相反
// 如果提供了 `dir`、 `root` 和 `base`, // 则返回 `${dir}${path.sep}${base}`。 // `root` 会被忽略。 path.format({ root: '/ignored', dir: '/home/user/dir', base: 'file.txt' }); // 返回: '/home/user/dir/file.txt' // 如果未指定 `dir`,则使用 `root`。 // 如果只提供 `root`,或 'dir` 等于 `root`,则将不包括平台分隔符。 // `ext` 将被忽略。 path.format({ root: '/', base: 'file.txt', ext: 'ignored' }); // 返回: '/file.txt' // 如果未指定 `base`,则使用 `name` + `ext`。 path.format({ root: '/', name: 'file', ext: '.txt' }); // 返回: '/file.txt'
sep
'foo/bar/baz'.split(path.sep); // 返回: ['foo', 'bar', 'baz']
delimiter
console.log(process.env.PATH); // 打印: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin' process.env.PATH.split(path.delimiter); // 返回: ['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']
__dirname/process.cwd()/path.resolve('./)
__dirname 返回当前文件的磁盘路径
process.cwd() 返回的是执行node命令所在文件夹
console.log(__dirname) console.log(process.cwd()) console.log(path.resolve('./'))
Buffer
处理二进制数据流
实例类似整数数组,大小固定
C++代码在V8堆外分配物理内存
// 创建一个长度为 10、且用零填充的 Buffer。 const buf1 = Buffer.alloc(10); // 创建一个长度为 10、且用 0x1 填充的 Buffer。 const buf2 = Buffer.alloc(10, 1); // 创建一个长度为 10、且未初始化的 Buffer。 // 这个方法比调用 Buffer.alloc() 更快, // 但返回的 Buffer 实例可能包含旧数据, // 因此需要使用 fill() 或 write() 重写。 const buf3 = Buffer.allocUnsafe(10); // 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。 const buf4 = Buffer.from([1, 2, 3]); // 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。 const buf5 = Buffer.from('tést'); // 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。 const buf6 = Buffer.from('tést', 'latin1');
Buffer.byteLength
因为占2个自己,中文占3个自己
const str = 'u00bd + u00bc = u00be'; console.log(`${str}: ${str.length} 个字符, ` + `${Buffer.byteLength(str, 'utf8')} 个字节`); // 打印: ½ + ¼ = ¾: 9 个字符, 12 个字节
Buffer.concat
// 用含有三个 `Buffer` 的数组创建一个单一的 `Buffer`。 const buf1 = Buffer.alloc(10); const buf2 = Buffer.alloc(14); const buf3 = Buffer.alloc(18); const totalLength = buf1.length + buf2.length + buf3.length; console.log(totalLength); // 打印: 42 const bufA = Buffer.concat([buf1, buf2, buf3], totalLength); console.log(bufA); // 打印: <Buffer 00 00 00 00 ...> console.log(bufA.length); // 打印: 42
Buffer.isBuffer
Buffer.from
buf.fill
buf.equals
buf.indexOf
http模块
const http = require('http') http.createServer((req, res) => { res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'}) res.end('Hello World!') }).listen(3000)
我们可以看出NodeJS不及是一个应用还是一个HTTP服务器
url模块
url.parse()
url.parse('http://localhost:3000') Url { protocol: 'http:', slashes: true, auth: null, host: 'localhost:3000', port: '3000', hostname: 'localhost', hash: null, search: null, query: null, pathname: '/', path: '/', href: 'http://localhost:3000/' } > url.parse('http://localhost:3000?name=Susan&age=18') Url { protocol: 'http:', slashes: true, auth: null, host: 'localhost:3000', port: '3000', hostname: 'localhost', hash: null, search: '?name=Susan&age=18', query: 'name=Susan&age=18', pathname: '/', path: '/?name=Susan&age=18', href: 'http://localhost:3000/?name=Susan&age=18' } > url.parse('http://localhost:3000?name=Susan&age=18', true) Url { protocol: 'http:', slashes: true, auth: null, host: 'localhost:3000', port: '3000', hostname: 'localhost', hash: null, search: '?name=Susan&age=18', query: [Object: null prototype] { name: 'Susan', age: '18' }, pathname: '/', path: '/?name=Susan&age=18', href: 'http://localhost:3000/?name=Susan&age=18' }
代码
const query = url.parse(req.url, true).query // 设置为true,get传值转换为对象,如果不设置true,那么是字符串
url.resolve()
> url.resolve('http://localhost:3000', 'index') 'http://localhost:3000/index' > url.resolve('http://localhost:3000/home', 'index')
fs模块
stat 获取文件信息,判断是文件或者目录
fs.stat('b', (err, stats) => { if (err) return console.log(stats.isFile()) console.log(stats.isDirectory()) })
mkdir 创建目录
fs.mkdir('c', err => { if (err) return console.log('成功创建目录') })
writeFile 创建文件并且写入
let data = 'Hello World ,Hi 苏珊' fs.writeFile('c.md', data, err => { if (err) return console.log('成功写入文件') })
appendFile 追加文件
fs.appendFile('c.md', 'Welcome', err => { if (err) return console.log('成功追加文件') })
readFile 读取文件
fs.readFile('c.md', 'utf-8', (err, data) => { if (err) return console.log(data.toString()) })
readdir 读取文件目录
fs.readdir('static', (err, files) => { if (err) return console.log(files) })
rename 重命名
fs.rename('c.md', 'a.md', err => { if (err) return console.log('成功重命名') })
rmdir 删除目录
fs.rmdir('c', err => { if (err) return console.log('成功删除目录') })
unlink 删除文件
fs.unlink('b', err => { if (err) return console.log('成功删除文件') })
createReadStream 从文件流中读取数据
let readStream = fs.createReadStream('data.json') let str = '' readStream.on('data', chunk => { str += chunk }) readStream.on('end', () => { console.log(str) }) readStream.on('error', err => { console.log(err) })
createWriteStream 写入文件
let data = 'Hi,苏珊' let writeStream = fs.createWriteStream('a.md') writeStream.write(data, 'utf-8') writeStream.end() writeStream.on('finish', () => { console.log('写入完毕') }) writeStream.on('error', err => { console.log(err) }) console.log('执行完毕')
util的promisify解决回调地域
const util = require('util'); const fs = require('fs'); const stat = util.promisify(fs.stat); stat('.').then((stats) => { // Do something with `stats` }).catch((error) => { // Handle the error. });
const util = require('util'); const fs = require('fs'); const stat = util.promisify(fs.stat); async function callStat() { const stats = await stat('.'); console.log(`This directory is owned by ${stats.uid}`); }
events
const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('触发事件'); }); myEmitter.emit('event');
this指向
const myEmitter = new MyEmitter(); myEmitter.on('event', function(a, b) { console.log(a, b, this, this === myEmitter); // 打印: // a b MyEmitter { // domain: null, // _events: { event: [Function] }, // _eventsCount: 1, // _maxListeners: undefined } true }); myEmitter.emit('event', 'a', 'b');
const myEmitter = new MyEmitter(); myEmitter.on('event', (a, b) => { console.log(a, b, this); // 打印: a b {} }); myEmitter.emit('event', 'a', 'b');
只执行一次
const myEmitter = new MyEmitter(); let m = 0; myEmitter.once('event', () => { console.log(++m); }); myEmitter.emit('event'); // 打印: 1 myEmitter.emit('event'); // 不触发