- Node官网:nodejs.org/zh-cn/
- Express官网:www.expressjs.com.cn/
一、 node安装
- 建议安装长期支持版
- 可以使用nvm安装多个版本的Node
真实项目中的数据存储,都是用数据库
- 配合node的配关系型数据库
- 关系型数据库
- MySQL
- SQLserve
- oracle
1.)什么是node
- node是一套基于V8引擎渲染和解析JS的工具或环境
- 说node是后台语言,是因为:我们在服务器端安装node,这样就可以基于JS编写一些后台处理程序,最后交给node执行
- 所以说JS是“全栈”编程语言:同一个技术栈,既可以完成客户端开发,也可以完成服务端开发
2.) node的新特性
- 基于V8引擎渲染解析JS[相同点]
- 提供I/O操作「文件的读写操作」
- I: input输入
- O: output输出
- JS在客户端浏览器中运行,能否对客户端本地的文件进行读写操作?
- 不能
- input:type='file'【文件上传(用户自己选择上传的)】
- JS在服务器端运行(基于Node运行),能否对服务器端的文件进行操作?
- node赋予了JS操作I/O的能力【fs内置模块】【特点】
- window & global
- 浏览器中运行JS,全局对象是window 【特点】
- setInterval
- setTimeout
- …
- Node中运行JS,全局对象是global【特点】
- process:node中进程管理属性
- process.nextTick()【微任务中优先级最高,永远被最先执行】
- process.env 环境变量
- …
- Buffer【进制编码格式数据】
- setImmediate【异步宏任务】
- …
- process:node中进程管理属性
- 浏览器中运行JS,全局对象是window 【特点】
3.)在node端运行JS
- 编写JS文件,直接基于"node xxx.js"
- this -> 当前模块
- queueMicrotask
- 创建一个异步的微任务
- 客户端和Node端都支持
- 用法:
用法:
queueMicrotask(function(){
// 这个函数必须在同步任务执行完,去EventQueue中找异步微任务的时候执行
})
- REPL命令模式 "输入$ node,即可开启类似于浏览器控制台的REPL模式"
- this -> global
4.) cross-env模块:用来设置环境变量
// package.json
"scripts": {
"start": "cross-env NODE_ENV=development node 1.js",
"build": "cross-env NODE_ENV=production node 1.js"
}
// 1.js
console.log(process.env.NODE_ENV);
- $ npm i cross-env --save-dev
- 这样再执行不同的脚本命令时[$npm run xxx],因为设置了不通过的环境变量,而我们在JS文件中可以获取设置的环境变量值...这样我们可以根据不同的环境变量,处理不同的事情
- 在axios的二次配置,我们完全可以基于环境变量得知是开发环境还是生产环境,或则是测试环境等...然后根据不同的环境,让API请求的公共前缀baseURL设置不同值
- 如果自己配置webpack,那么久需要基于环境变量不同,区分不同的环境,从而写出不同的webpack配置规则;【@vue/cli脚手架内部也就是这样完成的】
5.)node中的事件循环机制
console.log('start');
setTimeout(() => {
console.log(1);
}, 0);
setTimeout(() => {
console.log(2);
setTimeout(() => {
console.log(3);
}, 0);
setImmediate(() => {
console.log(4);
});
}, 100);
Promise.resolve().then(() => {
console.log(5);
});
setImmediate(() => {
console.log(6);
});
process.nextTick(() => {
console.log(7);
process.nextTick(() => {
console.log(8);
});
});
Promise.resolve().then(() => {
console.log(9);
});
console.log('end');
分析过程
setImmediate
- setImmediate Node中新提供的定时器【异步】
- 在同步代码执行中设置的setImmediate,等价于setTimeout时间设置为零
- 但是如果是在某个异步任务执行的时候设置的setImmediate,则它是异步宏任务队列中优先级最高
CommonJS模块规范
- 导出:module.exports
- 导入:require
- node模块
- 内置模块
- 第三方模块
- 自定义模块
导入内置模块和第三方模块是不需要加路径【查找机制:现找本地node——modules文件夹,如果找不到这个模块,再去内置模块中找...】
导入自定义模块一定要加路径[js后缀可以省略]
// 导入内置模块和第三方模块是不需要加路径【查找机制:现找本地node——modules文件夹,如果找不到这个模块,再去内置模块中找...】
let fs = require('fs'),
path = require('path'),
qs=require('qs');
// 导入自定义模块一定要加路径[js后缀可以省略]
let A=require('./1');
node模块
- __dirname:当前文件的绝对路径
- __filename:当前文件绝对路径下的文件名
path模块
path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径
let path = require('path');
console.log(path.resolve(__dirname,'dist'));// \Users\...REACT\0707\dist
console.log(path.resolve('/foo/bar','dist'));// \foo\bar\dist
console.log(path.resolve('/foo/bar','../dist'));// \foo\dist
fs内置模块
FS内置模块:赋予了JS操作I/O的能力
- readFile / readFileSync 读取文件的全部内容
- readdir / readdirSync 读取目录的内容
- mkdir / mkdirSync 创建目录
- rmdir / rmdirSync 删除目录
- unlink / unlinkSync 删除文件
- writeFile / writeFileSync 将数据写入文件
- appendFile / appendFileSync 将数据附加到文件
同步读取文件内容【阻碍式I/O操作:内容没有读取完成,其他的代码都不能进行】
- 默认获取的是Buffer格式的数据【文件流、文件编码】
- 配置项设置成utf-8,则获取的是文本格式数据【文本字符串】
let data= fs.readFileSync('./package.json','utf-8');
console.log(data);
异步读取文件内容:方案一
fs.readFile('./package.json','utf-8',function(err,data){
// 读取成功或者失败都会触发这个回调函数
// + 如果读取失败,err存储的是失败的信息
// + 读取成功,data存储的是读取的内容
if(err){
console.log(`很遗憾,读取信息失败,错误原因:${err.message}`);
return
}
console.log(data);
});
console.log('ok');
异步读取文件内容:方案二:基于promise管理
- 读取文件成功,返回的promise实例就是成功,反之就是失败
let fs = require('fs'),
fsPromise = fs.promises;
fsPromise.readFile('./package.json','utf-8').then(data=>{
console.log('请求成功',data);
}).catch(err=>{
console.log('请求失败',err);
})
二、Express基础知识及应用【node框架】
node中常用的框架
- express[入门级]
- koa2
- egg
后台要处理的事情
- @1创建一个服务【设定端口号,来区分不同服务】
- @2处理客户端资源文件
- @3处理客户端数据请求
- 创建Web服务器:完成对静态资源文件的请求处理
- 创建数据服务器:完成对API接口的请求处理
创建web服务器【静态资源处理】
let express = require('express'),
app = express(),
PORT = 9999;
app.listen(PORT, () => {
// 当WEB服务创建成功,并且成功监听了端口号,触发这个回调函数
console.log(`web service has been created successfully, listening for port number:${PORT}`);
});
// 处理静态资源文件的请求
// + app.use:让当前创建好的服务使用中间件
// + express.static([path]):处理静态资源文件请求的中间件
// + [path]:以后向当前服务器发送请求,我们去哪个目录中找资源
app.use(express.static('./static'));
app.use((req, res) => {
// 当请求的资源不存在,就会走到这里
// + req「request」请求对象,包含了客户端发送过来的请求信息
// + req.body 结合body-parser中间件,可以获取客户端传递的请求主体信息
// + req.headers 获取请求头信息「也可以:req.get('xxx')」
// + req.query 获取URL问号传参信息
// + req.path: 存储请求地址的路径名称
// + req.method: 请求方式
// + ...
// + res「response」响应对象,提供一系列的方法,让服务器端把信息返回给客户端
// + res.status(code) 设置服务器返回的HTTP状态码
// + res.set([key],[value]) 设置响应头信息
// + res.send() 设置响应主体信息「格式:字符串、对象(内部会变为JSON格式字符串返回给客户端)、Buffer...」
// + res.json: 返回给客户端内容,值不过传递的数据可以是json对象(内部会帮我们把其转换为json字符串 => 服务返回给客户端的内容一般都是字符串或者buffer格式)
// + res.type: 返回content-type的类型值
// + ...
res.status(404);
res.send('the requested resource does not exist!');
});
创建数据服务器【接口请求处理】
let express = require('express'),
app = express(),
PORT = 9999;
app.listen(PORT, () => {
// 当web服务创建成功,并且成功监听端口号,触发这个回调函数
console.log(`Web service has been created successfully, listening for port number:${PORT}`);
});
// 中间件:提取请求的公共逻辑,在所有处理之前先干的事
/* app.use((req, res, next) => {
// 只针对post请求
if (/^(POST|PUT|PATCH)$/i.test(req.method)) {
let data = '';
req.on('data', chunk => data += chunk);
req.on('end', () => {
// 把获取的请求主体信息挂到req.body上
let qs = require('qs');
req.body = qs.parse(data);
next();
})
return;
}
// 不是POST请求,直接向下处理
next();
}) */
// 现成的中间件
let bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended: true
}));
// ============================
// API请求
// 1.GET /list?lx=pro/dev 我们把package.json中的开发/生产依赖信息返回
// 2.POST /create 请求主体:name=zhufeng&age=12 返回{code:0,body:{name:zhufeng,age:12}}
let fs = require('fs').promises;
app.get('/list', async (req, res) => {
let {
lx,
} = req.query;
let data = await fs.readFile('./package.json', 'utf-8');
data = JSON.parse(data);
res.send(lx === 'dev' ? data.devDependencies : data.dependencies);
});
/* //没使用中间件之前的代码
let qs = require('qs');
app.post('/create', (req, res) => {
/* // 接收主体信息
let data = '';
req.on('data', chunk => {
//=>正在分批接收客户端传递的请求主体信息 CHART:当前接收的部分
data += chunk;
});
req.on('end', () => {
// data存储的是请求主体信息
data = qs.parse(data);
res.send({
code: 0,
body: data
});
});
}); */
app.post('/create', (req, res) => {
res.send({
code: 0,
body: req.body
})
});
app.use(express.static('./static'));
app.use((req, res) => {
res.status(404);
res.send(`The requested resource does not exist!`)
})
使用中间件
let express = require('express'),
app = express(),
PORT = 9999;
app.listen(PORT, () => {
console.log(`Web service has been created successfully, listening for port number:${PORT}`);
});
// 中间件:提取请求的公共逻辑,在所有处理之前先干的事
/* app.use((req, res, next) => {
// 只针对post请求
if (/^(POST|PUT|PATCH)$/i.test(req.method)) {
let data = '';
req.on('data', chunk => data += chunk);
req.on('end', () => {
// 把获取的请求主体信息挂到req.body上
let qs = require('qs');
req.body = qs.parse(data);
next();
})
return;
}
// 不是POST请求,直接向下处理
next();
}) */
// 现成的中间件
let bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended: true
}));
// API请求
// 1.GET /list?lx=pro/dev 我们把package.json中的开发/生产依赖信息返回
// 2.POST /create 请求主体:name=zhufeng&age=12 返回{code:0,body:{name:zhufeng,age:12}}
let fs = require('fs').promises;
app.get('/list', async (req, res) => {
let {
lx,
} = req.query;
let data = await fs.readFile('./package.json', 'utf-8');
data = JSON.parse(data);
res.send(lx === 'dev' ? data.devDependencies : data.dependencies);
});
app.post('/create', (req, res) => {
res.send({
code: 0,
body: req.body
})
});
app.use(express.static('./static'));
app.use((req, res) => {
res.status(404);
res.send(`The requested resource does not exist!`)
})
使用路由
// server.js
app.use('/user', require('./routes/user'));
// routes/user.js
let express = require('express'),
route = express.Router(); //=>ROUTE就是创建的一个路由,用法和APP一样
route.get('/list', (req, res) => {
...
});
module.exports = route;