一、包管理工具
1.1 npm
常用命令
# 全局安装
npm install -g [package-name]
# 当前项目依赖安装, 加 -D 是开发时依赖
npm install [package-name]
# 安装指定版本包, version 代表版本号
npm install [package-name]@[version]
# 列举当前目录下的安装包
npm list
# 查看包信息
npm info [package-name]
# 检查当前项目的包是否已过时
npm outdated
# 查看本地全局环境的包是否已过时
npm outdated -g --depth=0
# 收藏/取消收藏
npm stat/unstar [package-name]
# 查看收藏列表
npm stars
# 登录 npm,没有的在 npm 注册一个就行了
npm login
# 查看包的文档
npm home [package-name]
# 查看包的代码仓库
npm repo [package-name]
# 快速给包提 issues
npm bugs [package-name]
# 查看当前源地址
npm config get registry
# 切换npm源
npm config set registry=<url>
# 临时使用
npm --registry https://registry.npm.taobao.org install express
包后面符号说明
// ^ 表示 npm i 将会安装 md5 2.*.* 最新版本
{ "md5": "^2.1.0" }
// ~ 表示 npm i 将会安装 md5 2.1.* 最新版本
{ "md5": "~2.1.0" }
// * 表示 npm i 将会安装 md5 最新版本
{ "md5": "*" }
1.2 nrm
npm 的镜像源管理工具,有时候国外资源太慢,使用这个就可以快速的在 npm 源间切换
# 设置淘宝镜像源
npm config set registry http://registry.npm.taobao.org
# 查看镜像源
npm get registry
nrm 常用命令
# 全局安装
npm i -g nrm
# 查看可选的源,带 * 是当前使用的源
nrm ls
# 切换源,假设切换taobao
nrm use taobao
中国NPM镜像cnpm
同步频率为10分钟,保证与官方服务同步
npm install -g cnpm --registry=https://registry.npmmirror.com
1.3 yarn
npm install -g yarn
对比npm:
- 速度快:yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。同时利用并行下载以最大化资源利用率,因此安装速度更快
- 安全:在执行代码之前,yarn 会通过算法校验每个安装包的完整性
# 全局安装
yarn add -g [package]
# 当前项目依赖安装, 加 -D 是开发时依赖
yarn add [package]
# 安装指定版本包, version 代表版本号
yarn add [package]@[version]
# 列举当前目录下的安装包
yarn list
# 查看包信息
yarn info [package]
# 升级包
yarn upgrade [package]@[version]
# 移除依赖包
yarn remove [package]
二、内置模块
2.1 http 模块
2.1.1 创建简单服务器
// 1. 导入http模块
const http = require('http')
// 2. 创建服务器
const server = http.createServer((req, res) => {
// 写入请求头,以 html utf-8 格式解析
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'})
res.write(`
<html>
<div>欢迎来到Node世界!</div>
</html>
`)
res.end()
})
// 3. 监听端口
server.listen(3001, () => {
// 服务器启动后
console.log('server start')
})
2.1.2 CORS
// 设置 access-control-allow-origin 头,允许所有源访问
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
'access-control-allow-origin': '*'
})
2.2 旧版 url 模块用法
网址模块
2.2.1 url.parse
url.parse(urlString[, parseQueryString[, slashesDenoteHost]]) 接受网址字符串,解析并返回网址对象
urlString<string>
要解析的 URL 字符串。parseQueryString <boolean>
如果为 true,则 query 属性将始终设置为 querystring 模块的 parse() 方法返回的对象。 如果为 false,则返回的网址对象上的 query 属性将是未解析、未解码的字符串。 默认值: false。slashesDenoteHost <boolean>
如果为 true,则文字串 // 之后和下一个 / 之前的第一个令牌将被解释为 host。 例如,给定 //foo/bar,结果将是 {host: ‘foo’, pathname: ‘/bar’} 而不是 {pathname: ‘//foo/bar’}。 默认值: false。
const http = require('url')
const urlObj = url.parse('http://localhost:3001/list?a=1', true)
console.log(urlObj)
/* {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?a=1',
query: [Object: null prototype] { a: '1' },
pathname: '/list',
path: '/list?a=1',
href: '/list?a=1'
} */
2.2.2 url.format
url.format(urlObject) 返回从 urlObject 派生的格式化网址字符串
const url = require('url')
url.format({
protocol: 'https',
hostname: 'example.com',
pathname: '/some/path',
query: {
page: 1,
format: 'json'
}
})
// => 'https://example.com/some/path?page=1&format=json'
2.2.3 url.resolve
url.resolve(from, to) 解析相对于基本 URL 的目标 URL
from <string>
如果 to 是相对的 URL,则使用的基本的 URLto <string>
要解析的目标 URL
// 注意加 / 跟不加 / 的区别
const url = require('url')
url.resolve('/one/two/three', 'four') // '/one/two/four'
url.resolve('/one/two/three/', 'four') // '/one/two/three/four'
url.resolve('http://example.com/', '/one') // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'
2.3 新版 URL 类
new URL(input[, base])
input <string>
要解析的绝对或相对的输入网址。 如果 input 是相对的,则需要 base。 如果 input 是绝对的,则忽略 base。 如果 input 不是字符串,则先转换成字符串。base <string>
如果 input 不是绝对的,则为要解析的基本网址。 如果 base 不是字符串,则先转换成字符串。
const myURL = new URL('/foo', 'https://example.org/');
// https://example.org/foo
2.4 querystring 模块
2.4.1 querystring.escape
针对网址查询字符串的特定要求优化的方式对给定的 str 执行网址百分比编码
const querystring = require('querystring')
const str = 'id=北京'
console.log(querystring.escape(str)) // id%3D%E5%8C%97%E4%BA%AC
2.4.2 querystring.unescape
执行网址百分比编码字符的解码
const querystring = require('querystring')
const str = 'id%3D%E5%8C%97%E4%BA%AC'
console.log(querystring.unescape(str)) // id=北京
2.4.3 querystring.parse
将网址查询字符串 (str) 解析为键值对的集合
const querystring = require('querystring')
const str = 'foo=bar&abc=xyz'
console.log(querystring.parse(str)) // { foo: 'bar', abc: 'xyz' }
2.4.4 querystring.stringify
const querystring = require('querystring')
querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' })
// 返回 'foo=bar&baz=qux&baz=quux&corge='
2.5 events 模块
const EventEmitter = require('events')
const myEmitter = new EventEmitter()
myEmitter.on('event', res => {
console.log(res)
})
setTimeout(() => {
myEmitter.off('event')
}, 1000)
myEmitter.emit('event', 'res')
2.6 fs 模块
2.6.1 fs.mkdir
创建文件夹
const fs = require('fs')
fs.mkdir('./img', err => {
// 文件夹新建成功回调
if (err && err.code === 'EEXIST') {
console.log('该目录已创建')
}
})
2.6.2 fs.mkdirSync
fs.mkdir 的同步版本,返回 undefined 或创建的第一个目录路径(如果 recursive 为 true)
const dirPath = fs.mkdirSync('./img', { recursive: true })
2.6.3 fs.rename
const fs = require('fs')
// 把 img 文件夹改为 img2
fs.rename('./img', './img2', err => {
if (err && err.code === 'ENOENT') {
console.log('该目录不存在')
}
})
2.6.4 fs.renameSync
fs.rename 的同步版本,返回 undefined
2.6.5 fs.rmdir
删除文件夹
const fs = require('fs')
// 删除目录
fs.rmdir('./img', err => {
if (err && err.code === 'ENOENT') {
console.log('该目录不存在')
}
})
2.6.6 fs.rmdirSync
fs.rmdir 的同步版本,返回 undefined
2.6.7 fs.readdir
const fs = require('fs')
// 读目录
fs.readdir('./img', (err, files) => {
// files 是当前文件夹下的文件名数组
!err && console.log(files)
})
2.6.8 fs.readdirSync
fs.readdir 的同步版本,返回: <string[]> | <Buffer[]> | <fs.Dirent[]>
const fs = require('fs')
const files = fs.readdirSync('./img')
console.log(files)
2.6.9 fs.writeFile
fs.writeFile(file, data[, options], callback) 写文件,内容会覆盖
file <string> | <Buffer> | <URL> | <integer>
文件名或文件描述符data <string> | <Buffer> | <TypedArray> | <DataView> | <Object>
options <Object> | <string>
encoding <string> | <null>
默认值: ‘utf8’
mode <integer>
默认值: 0o666
flag <string>
请参阅对文件系统 flags 的支持。 默认值: ‘w’。
signal <AbortSignal>
允许中止正在进行的写入文件callback <Function>
err <Error> | <AggregateError>
const fs = require('fs')
// 在 img/a.txt 下写入 Hello World!
fs.writeFile('./img/a.txt', 'Hello World!', err => {
// 写入成功回调
})
2.6.10 fs.appendFile
异步地将数据追加到文件,如果该文件尚不存在,则创建该文件
const fs = require('fs')
fs.appendFile('./img/a.txt', `\n你好,世界!`, err => {})
2.6.11 fs.readFile
异步地读取文件的全部内容
const fs = require('fs')
fs.readFile('./img/a.txt', 'utf-8', (err, data) => {
!err && console.log(data)
})
2.6.12 fs.unlink
删除文件
const fs = require('fs')
fs.unlink('./img/a.txt', err => {})
2.6.13 fs.stat
判断是文件还是文件夹
const fs = require('fs')
const paths = ['./fs-file.js', './img2']
paths.forEach(path => {
fs.stat(path, (err, stats) => {
console.log(stats.isFile()) // 是文件返回 true,不是返回 false
console.log(stats.isDirectory()) // 是文件夹返回 true,不是返回 false
})
})
2.6.14 promises 写法
由于Node环境执行的JS是服务端代码,所以绝大部分反复执行的必须用异步代码,不然同步代码在执行期间会导致服务器停止响应,因为JS只有一个执行线程
const fs = require('fs').promises
fs.readFile('./img/a.txt', 'utf-8').then(data => {
console.log(data)
})
2.6.15 fs.createReadStream
创建一个可读的流
const fs = require('fs')
const rs = fs.createReadStream('a.txt', 'utf-8')
// 读取文件发生错误事件
rs.on('error', err => {
console.log('发生异常:', err)
})
// 已打开要读取的文件事件
rs.on('open', fd => {
console.log('文件已打开:', fd)
})
// 文件已经就位,可用于读取事件
rs.on('ready', () => {
console.log('文件已准备好..')
})
// 文件读取中事件,注意 data 事件可能调用多次,每次传递的 chunk 是流的一部分
rs.on('data', chunk => {
console.log('data:')
console.log(chunk)
})
// 文件读取完成事件
rs.on('end', () => {
console.log('读取已完成..')
})
// 文件已关闭事件
rs.on('close', () => {
console.log('文件已关闭!')
})
2.6.16 fs.createWriteStream
创建一个可读写流
const fs = require('fs')
const ws = fs.createWriteStream('./img/b.txt', 'utf-8')
ws.write('1111')
ws.write('2222')
ws.write('你好!')
ws.end()
创建读写管道
const fs = require('fs')
const rs = fs.createReadStream('a.txt', 'utf-8')
const ws = fs.createWriteStream('b.txt', 'utf-8')
// 相当于 b 复制 a
rs.pipe(ws)
2.6.17 fs.existsSync
fs.existsSync(path) 如果路径存在则返回 true,否则返回 false
const fs = require('fs')
const isPath = fs.existsSync('/static/index.html')
2.7 zlib 模块
const http = require('http')
const fs = require('fs')
const zlib = require('zlib')
const gzip = zlib.createGzip()
http.createServer((req, res) => {
const rs = fs.createReadStream('./index.js')
res.writeHead(200, {
'Content-Type': 'application/x-javascript; charset=utf-8',
'Content-Encoding': 'gzip'
})
rs.pipe(gzip).pipe(res)
}).listen(5000, () => {
console.log('server start')
})
2.8 crypto 模块
加密
const crypto = require('crypto')
const hash = crypto.createHash('md5')
// 加密
hash.update('Hollo World!')
console.log(hash.digest('hex')) // b9713ba17d1a2d5b9f4a8a6ed50c4c77
2.9 path 模块
2.9.1 path.extname
path.extname(path) 返回 path 的扩展名,即 path 的最后一部分中从最后一次出现的 .(句点)字符到字符串的结尾
path.extname('index.html')
// 返回: '.html'
path.extname('index.coffee.md')
// 返回: '.md'
path.extname('index.')
// 返回: '.'
path.extname('index')
// 返回: ''
path.extname('.index')
// 返回: ''
path.extname('.index.md')
// 返回: '.md'
2.9.2 path.parse
path.parse(path) 返回一个对象,其属性表示 path 的重要元素
path.parse('/home/user/dir/file.txt')
// 返回:
// { root: '/',
// dir: '/home/user/dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
2.9.3 path.format
path.format(pathObject) 从对象返回路径字符串。 这与 path.parse() 相反
path.format({
root: '/ignored',
dir: '/home/user/dir',
base: 'file.txt'
})
// 返回: '/home/user/dir/file.txt'
2.9.4 path.join
path.join([…paths]) 使用特定于平台的分隔符作为定界符将所有给定的 path 片段连接在一起,然后规范化生成的路径
path.join('/foo', 'bar', 'baz/asdf')
// 返回: '/foo/bar/baz/asdf'
2.9.5 path.resolve
path.resolve([…paths]) 将路径或路径片段的序列解析为绝对路径
path.resolve('/foo/bar', './baz')
// 返回: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/')
// 返回: '/tmp/file'
三、路由
3.1 路由基础
const http = require('http')
const fs = require('fs')
const server = http.createServer()
server.on('request', (req, res) => {
const url = new URL(req.url, 'http://127.0.0.1')
const pathname = url.pathname
switch (pathname) {
case '/':
case '/home':
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
})
res.write(fs.readFileSync('index.html', 'utf-8'))
break;
case '/about':
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
})
res.write(fs.readFileSync('about.html', 'utf-8'))
default:
res.writeHead(404, {
'Content-Type': 'text/html; charset=utf-8',
})
res.write(fs.readFileSync('404.html'))
}
res.end()
})
server.listen(3000, () => {
console.log('server start')
})