const fs = require('fs')
fs.readFile(path, [, options], callback)
fs.readFileSync(path, [, options])
-
path: 文件路径
-
option:
-
encoding: 指定文件编码格式等
-
flag: 指定读取模式,默认为
'r'
-
-
callback:
fn(err, data)回调函数,对文件读取的结果进行操作
// 同步
const fileData = fs.readFileSync('./file.txt')
if (fileData) console.log(fileData)
// 异步
fs.readFile('./file.txt', 'utf8', (err, data) => {
if (err) return console.log(err.message)
// 对数据 data 进行处理
})fs.writeFile(path, data[, options], callback)
fs.writeFileSync(path, data[, options])
fs.appendFile(path, data[, options], callback)
fs.appendFileSync(path, data[, options])
-
path: 文件路径
-
data: 写入的内容
-
options:
-
encoding: 指定文件编码格式
-
flag: 指定写入模式,
writeFile默认为'w',appendFile默认为'a'
-
-
callback:
fn(err)文件写入操作完成后的回调函数
// 同步
fs.writeFileSync('./file.txt', 'Hello World!', {flag: 'a'})
// 异步
fs.writeFile('./file.txt', 'Hello World!', 'utf8', err => {
if (err) console.log(err)
})fs.unlink(path, callback)
fs.unlinkSync(path)
-
path: 文件路径
-
callback:
fn(err)删除文件操作完成后的回调函数
// 同步
fs.unlinkSync('./file.txt');
// 异步
fs.unlink('./file.txt', err => {
if (err) console.log(err)
})fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
-
path: 目录路径
-
options:
-
recursive: 是否递归,默认为
false -
callback:
fn(err)回调函数
-
// 同步
fs.mkdirSync('./dir')
// 异步
fs.mkdir('./dir', err => {
if (err) console.log(err)
})fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
- path: 路径
- options:
- encoding: 文件编码格式
- withFIleTypes: 是否显示文件类型,默认为
false
- callback:
fn(err, files)回调函数
// 同步
const fileList = fs.readdirSync('./dir')
// 异步
fs.readdir('./dir', (err, files) => {
if (err) return console.log(err)
// 对文件进行操作
})fs.rmdir(path, callback)
fs.rmdirSync(path)
- path: 路径
- callback:
fn(err)回调函数
// 同步
fs.rmdirSync('./dir')
// 异步
fs.rmdir('./dir', err => {
if (err) console.log(err)
})fs.rm(path[, options], callback)
fs.rmSync(path[, options])
- path: 路径
- options:
- force: 是否忽略异常,默认为
false - recursive: 是否递归删除,默认为
false,递归删除时,操作将在失败时进行重试 - maxRetries: 重试次数,默认为
0 - retryDelay: 重试之间的等待间隔
ms,默认为false
- force: 是否忽略异常,默认为
- callback:
fn(err)回调函数
// 同步
fs.rmSync('./dir/di/d', { recursive: true })
// 异步
fs.rm('./dir/di/d', { recursive: true }, err => {
if (err) console.log(err)
})fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
- oldPath: 原路径
- newPath: 新路径
- callback:
fn(err)回调函数
// 同步
fs.renameSync('./old.txt', './new.txt')
// 异步
fs.rename('./old.txt', './new.txt', err => {
if (err) console.log(err)
})fs.stat(path[, options], callback)
fs.statSync(path[, options])
- path: 路径
- options:
- bigint:
status中的数值是否为bigint,默认为false
- bigint:
- callback:
fn(err, status)回调函数
其中 status 为 fs.Stats对象,属性为对应文件的信息:
- isDirectory(): 查看对象是否为目录
- isFile(): 查看对象是否为文件
- size: 大小
// 同步
const status = fs.statSync('./file.txt')
// 异步
fs.stat('./file.txt', (err, status) => {
if (err) return console.log(err)
// 相关操作
})fs.truncate(path[, len], callback)
fs.truncateSync(path[, len])
- path: 路径
- len: 将文件截取到指定大小
byte,默认为0 - callback:
fn(err)回调函数
// 同步
fs.truncateSync('./file.txt', 5)
// 异步
fs.truncate('./fiel.txt', 5, err => {
if (err) console.log(err)
})fs.watchFile(filename[, options][, listener])
- filename: 路径
- options:
- bigint: 默认为
false - persistent: 正在监听文件时,进程是否继续游戏,默认为
true - interval: 监听的时间间隔,默认为
5007
- bigint: 默认为
- listener:
fn(current, previous)回调函数,参数为修改前后的状态
fs.watch(filename[, options][, listener])
- filename: 路径
- options:
- encoding: 指定传递给监听器的文件名的字符编码
- persistent: 正在监听文件时,进程是否继续游戏,默认为
true - recursive: 是否监听所有子目录,还是仅监听当前目录,默认为
false,仅在 MacOS 和 Windows 系统上受支持
- listener:
fn(eventTYpe, filename)回调函数,event: rename / change,filename:触发事件的文件(夹)名称
const path = require('path')
path.join([...paths])
path.join(__dirname, 'dir', 'file.txt')path.dirname(path)
path.basename(path)
path.extname(path)
`const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
console.log('接收到客户端请求')
})
server.listen(port, () => {
console.log('Server running at http://127.0.0.1:8080')
})响应内容并结束请求: res.end(str)
设置响应头: res.setHeader(attr, val)
server.on('request', (req, res) => {
// 相应处理
const url = req.url
const fpath = url === '/' ? path.join(__dirname, './index') : path.join(__dirname, url)
fs.readFile(fpath, 'utf8', (err, data) => {
if (err) return res.end('<h2>404 Not Found.</h2>')
// 返回结果到网页
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data)
})
})- 内置模块:
fs、path、http... - 自定义模块:自己写的
js文件 - 第三方模块:需要下载
// 内置
const fs = require('fs')
// 自定义
const utils = require('./utils')
// 第三方
const express = require('express')module.exports = {
// ...
}模块加载时优先从缓存读取,缓存中不存在的模块才会从其他目录载入
内置模块 > 第三方模块
- 必须包含
./或者../,否则node会将其视为内置模块或第三方模块 - 如果缺失文件扩展名,按顺序尝试加载
require()中提供的文件:- 按照确切的文件名加载
- 补全
.js进行加载 - 补全
.json进行加载 - 补全
.node进行加载 - 加载失败则报错
从当前模块所在目录开始,尝试在/node_modules/目录下寻找对应的包
如果没找到,就回退到当前模块所在目录的上一级继续进行尝试
重复以上步骤直到系统的根目录
如果目录作为模块标识符,传递给require()进行加载,则会进行如下操作:
- 在目录下查找
package.json,并尝试在其中寻找main作为模块入口 - 若上述操作无法完成,则尝试加载该目录下的
index.js文件作为模块入口 - 若上述操作无法完成,则输出错误信息
和模块为同一个概念,由第三方个人或团队开发
npm install package[@v]
npm i package[@v]
根目录下会生成node_modules和package-lock.json以存放下载的包并记录下它们的信息
在项目根目录中,必须提供package.json的包管理配置文件,记录与项目有关的配置信息,如项目名称、版本号、描述:
npm init -y
由此在共享项目时,方便移除node_modules目录,也就是将node_modules添加到.gitignore忽略文件中
在package.json存在时,可以通过命令行快速将依赖包添加到项目中
npm install
npm i
如果某些包只在开发阶段使用,可以将其添加到devDependencies节点中:
npm i package -D
npm i package --save-dev
通过npm uninstall package可以删除指定的包
- 查看
npm config get registry - 修改
npm config set registry=https://registry.npm.taobao.org/
初始镜像源为https://registry.npmjs.org/
淘宝镜像源为https://registry.npm.taobao.org/
利用nrm可以快速切换镜像源
npm i -g nrm
nrm ls
nrm use taobao利用i5ting_toc可以将.md文件转化为页面
npm i -g i5ting_toc
# -o 表示转化完毕后打开对应的html文件
i5ting_toc -f ./file.md -o- 以单独的目录存在
- 顶级目录下包含
package.json包管理配置文件 package.json中必须包含name、version、main属性,表示包名、版本号、包的入口
{
"name": "package_name",
"version": "i.j.k",
"main": "./package.js"
}package.json的其他属性
{
"description": "包的简要描述",
"keywords": ["检索包的关键词1", "关键词2", "..."],
"license": "ISC"
}uername: weige666*2
email: lekaimie@gmail.com
password: rekaime通过npm login进行登录操作,输入用户名、密码、邮箱完成登录
在登录前需要先把下包服务器地址切换为npm的官方服务器
cd 包的顶级目录
npm publish
包名不能雷同
npm unpublish package --force
npm unpublish只能删除72小时内发布的包npm unpublish删除的包不能于24小时内重新发布- 不要发布没有意义的包
npm i express
const express = require('express')
const app = express()
app.listen(80, () => {
console.log('Server running at http://127.0.0.1:80')
})app.get(url, function (req, res) {
// 数据处理
// 查询字符串 ?key=value
console.log(req.query)
// 动态参数 :id
console.log(req.params)
res.send({
name: "rekaime",
age: 18,
gender: 0
})
})
app.post(url, function (req, res) {
// 数据处理
res.send('请求成功!')
})// dir/css/base.css
// dir/js/base.js
// dir/image/img.jpg
// 可反复调用以托管多个静态资源
app.use(express.static('./dir'))<style src="./css/base.css"></style>
<img src="./image/img.jpg">
<script src="./js/base.js"></script>若需要挂载路径前缀,可以进行如下操作
app.use('/dir', express.static('./dir'))根据method和url进行逐个匹配,先定义的路由将优先被匹配到
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => {})
router.post('/', (req, res) => {})
module.exports = routerconst routerModule = require('./router/user.js')
app.use(routerModule)
// 同样可以添加前缀
app.use('api', routerModule)当请求到达Express服务器后,可以连续调用多个中间件进行请求的预处理
本质上是一个function(req, res, next),被称作**middleware function
区分中间件和路由,可以通过观察函数列表中是否包含next参数
next函数负责将流转关系转交给下一个中间件或路由
多个中间件之间会共享同一份req和res
客户端发起任何请求,到达服务器时都会触发的中间件
// 第一个触发的全局中间件
app.use((req, res, next) => {})
// 第二个触发的全局中间件
app.use((req, res, next) => {})
// ...// 不适用 app.use() 定义
// 包含有局部中间件
app.get('/', mw1, mw2, ..., (req, res) => {})
// 或者写作
app.get('/', [mw1, mw2, ...], (req, res) => {})- 在路由创建前注册中间件
- 客户端发送的请求可以连续调用多个中间件进行处理
- 执行完中间件业务代码后,一定要调用
next() - 为防止代码逻辑混乱,调用
next()后不要再写额外的代码
- 应用级别中间件
- 路由级别中间件
- 错误级别中间件
- Express内置中间件
- 第三方中间件
通过app.use()或app.get()、app.post()绑定到app上的中间件
绑定到express.Router()上的中间件
必须注册于所有路由之后
用于捕获项目中发生的异常错误,防止项目异常崩溃的问题
其function包含四个形参(err, req, res, next)
app.get('/', (req, res) => {
throw new Error('服务器发生错误')
res.send('OK')
})
app.use((err, req, res, next) => {
console.log('发生错误')
console.log(err.message)
res.send('Error')
})express.static快速托管静态资源express.json解析JSON格式的请求体数据express.urlencoded解析URL-encoded格式的请求体数据
// 配置 application/json 格式数据的内置中间件
// 否则 req.body === undefined
app.use(express.json())
// 配置 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({ extended: false }))以body-parser为例
npm install body-parser
// Express 内置的 express.urlencoded 中间件
// 是基于 body-parser 封装的
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))// 中间件函数封装
const qs = require('querystring') // 内置模块
const bodyParser = (req, res, next) => {
let str = ''
req.on('data', chunk => {
str += chunk
})
req.on('end', () => {
// console.log(str)
const body = qs.parse(str)
req.body = body
next()
})
}
module.exports = bodyParserconst express = require('express')
const app = express()
const customBodyParser = require('./customBodyParser')
app.use(customBodyParser)
app.post('/user', (req, res) => {
res.send(req.body)
})利用express.Router()创建路由对象
对路由对象绑定相应的路由
然后将路由对象导出:module.exports = apiRouter
在app.js中导入路由对象并注册:app.use('/api', apiRouter)
router.get('/get', (req, res) => {
const query = req.query
res.send({
status: 0,
msg: 'GET 请求成功',
data: query
})
})// 在 app.js 中配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))router.post('/post', (req, res) => {
const body = req.body
res.send({
status: 0,
msg: 'POST 请求成功',
data: body
})
})协议、域名、端口号任何一项不同就会出现跨域问题
主要解决方案有两种:
- CORS (Cross-Origin Resource Sharing):主流方案
- JSONP:只支持GET请求
浏览器的同源安全策略会阻止网页的跨域获取资源
如果接口服务器配置了 CORS 相关的 HTTP 响应头,就可以解决浏览器端的跨域访问限制
npm i cors const cors = require('cors')
// 在路由前调用,配置中间件
app.use(cors())注意事项:
- CORS 只需在服务器端进行配置,客户端无需任何额外配置
- 兼容性:支持 XMLHttpRequest Level2 的浏览器才可以正常访问开启 CORS 的服务端接口(IE10+、Chrome4+、FireFox3.5+)
格式:Access-Control-Allow-XXX
Access-Control-Allow-Origin: <origin> | *
<origin>为允许访问该资源的外域 URL
默认情况下 CORS 仅支持客户端向服务器发送9类请求头:
Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width、Content-Type(值仅限于text/plain、multipart/form-data、application/x-www-form-urlencoded)
若客户端向服务器发送额外请求头信息,则需要在服务器端设置额外的请求头声明:
res.setHeader('Access-Control-Allow': 'Content-Type, X-Custom-Header')
默认情况下,CORS仅支持客户端发起 GET、POST、HEAD 请求
如果客户端希望发起 PUT、DELETE 请求,则需要在服务器端先行配置
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, HEAD, DELETE')
res.setHeader('Access-Control-Allow-Methods', '*')
满足以下两个条件:
- 请求方式为 GET、POST、HEAD三者之一
- HTTP头部为无自定义头部字段或9种默认允许的请求头
满足以下任一条件:
- 请求方式为 GET、POST、HEAD 之外的类型
- 请求头包含自定义头部字段
- 向服务器发送
application/json格式的数据
在浏览器与服务器正是通信前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求
该次 OPTION 请求成为预检请求,服务器成功响应预检请求后,才会发送真正的请求
浏览器通过<script>标签的src属性请求服务器上的数据
同时服务器返回一个函数的调用
特点:
- 不属于真正的 Ajax 请求,因为没用到 XHR 对象
- 仅支持 GET 请求
如果项目中已经配置了 CORS,为了防止冲突,需要在 CORS 中间件注册前声明 JSONP 接口,否则 JSONP 接口会被处理成开启了 CORS 的接口:
app.get('/api/jsonp', (req, res) => {})
- 获取客户端发送的回调函数名称
- 获取要通过 JSONP 形式发送给客户端的数据
- 根据前两步得到的数据,拼接出一个函数调用的字符串
- 把该字符串响应给客户端的
<script>标签解析执行
app.get('/api/jsonp', (req, res) => {
const funcName = req.query.callback
const data = {name: 'Leo', age: 18}
const scriptStr = `${funcName}(${JSON.stringify(data)})`
res.send(scriptStr)
})// JQuery
$.ajax({
method: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',
success: function (res) {
console.log(res)
}
})