node.js是JavaScript的一种运行环境,是对Google V8引擎的封装,是服务器端的JavaScript解释器。NPM全名是Node Package Manager,node.js中含有npm,安装node.js时会一起安装npm,npm作用就是对node.js依赖的包进行管理,是node.js的包管理工具。
Step 1: 创建本地文件夹,server,通过vscode打开该文件夹
Step 2: npm init
在server目录下初始化node,构建package.json
npm init -y
// 或者通过 npm init 自定义配置
Step 3: 创建 app.js文件作为服务器入口
const http = require('http');
const port = 3000;
const hostName = '127.0.0.1';
const server = http.createServer((req,res)=>{
console.log('server running');
});
server.listen(port,hostName,function(){
console.log(`server is running at ${hostName}:${port}`);
});
Step 4: 启动服务器
方式一:app.js 文件上右键 Run Code,此时点击vscode右上角🛑可停止服务器
方式二:控制台 TEMINAL运行 node app.js
方式三:npm i -g nodemon 全局安装nodemon,'nodemon app.js'
此时,listen方法监听到服务器已经开始运行,输出 server is running at 127.0.0.1:3000
Step 5: 访问服务器
浏览器输入 127.0.0.1:3000 并回车。此时控制台接收到浏览器发起的请求并响应输出 server running
浏览器此时一直处于等待响应状态。原因在于服务器已经接收到请求,但服务器没有答复。
Step 6: 服务器响应请求
const server = http.createServer((req,res)=>{
console.log('server running');
res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
res.end('this is a response from server'); // 响应信息
});
重启服务器,使得改动生效。
Step 7: 尝试向服务器请求一个网页,服务获取该次请求的参数
7.1 改动服务器端代码,增加req信息的输出
const server = http.createServer((req,res)=>{
console.log('server running');
res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
res.end('this is a response from server'); // 响应信息
console.log(req); // 输出请求信息
});
7.2 浏览器器输入http://127.0.0.1:3000/index.html
此时,输出req信息如下(部分)。url: '/index.html'
rawHeaders: [ // 浏览器发起的请求头文件 request header
'Host',
'127.0.0.1:3000',
'Connection',
'keep-alive',
'Cache-Control',
'max-age=0',
'sec-ch-ua',
'".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
'sec-ch-ua-mobile',
'?0',
'sec-ch-ua-platform',
'"Windows"',
'DNT',
'1',
'Upgrade-Insecure-Requests',
'1',
'User-Agent',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
'Accept',
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Sec-Fetch-Site',
'none',
'Sec-Fetch-Mode',
'navigate',
'Sec-Fetch-User',
'?1',
'Sec-Fetch-Dest',
'document',
'Accept-Encoding',
'gzip, deflate, br',
'Accept-Language',
'en,zh;q=0.9,ja;q=0.8,ar;q=0.7,it-IT;q=0.6,it;q=0.5,zh-CN;q=0.4,ko;q=0.3'
],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '/index.html',
method: 'GET',
statusCode: null,
statusMessage: null,
client: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: ReadableState {
objectMode: false,
7.3 服务器端通过req.url获取请求参数 此处即是 http://127.0.0.1:3000/index.html 中的 /index.html
修改服务器代码
console.log(req.url);
Step 8: 服务器发送HTML文件
8.1 app.js 中引入fs模块
const http = require('http');
const fs = require('fs'); // node自带文件读取模块
// 使用方法
// fs.readFile('文件路径',文件解析方式,回调函数)
8.2 根目录serve下新建src子文件,用于存放静态资源,html文件,css文件,js文件,图片等。src文件夹下新建index.html (! tab)
8.3 修改app.js 读取该文件
const server = http.createServer((req,res)=>{
console.log('server running');
res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
res.end('this is a response from server'); // 响应信息
console.log(req.url);
// 读取文件
fs.readFile('./src/index.html','utf-8',(err,data)=>{
console.log('读取信息',data);
});
});
此时,浏览器发起请求后,控制台会输出html信息
8.4 继续修改,将html信息返回给浏览器
fs.readFile('./src/index.html','utf-8',(err,data)=>{
res.end(data); // 响应信息
});
此时,readFile得到的内容其实是html字符串,通过res.end发送出去,浏览器会直接解析为html字符串。
8.5 设置响应头
fs.readFile('./src/index.html','utf-8',(err,data)=>{
// res.end(data); // 响应信息
// 1. res.writeHead(响应状态码,'配置解析方法,告诉浏览器这个文件你要解析成啥样')
res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'});
// 2. 发送
res.write(data);
// 3. 发送结束,告诉浏览器本次响应结束
res.end();
});
Step 9: 服务器新增 css 和 js 文件
添加简单css样式并引入html。
修改app.js代码,加判断 if(req.url == '/index.html')
if(req.url == '/index.html'){
fs.readFile('./src/index.html','utf-8',(err,data)=>{
// res.end(data); // 响应信息
// 1. res.writeHead(响应状态码,'配置解析方法,告诉浏览器这个文件你要解析成啥样')
res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'});
// 2. 发送
res.write(data);
// 3. 发送结束,告诉浏览器本次响应结束
res.end();
});
}
此时,浏览器发起请求后一直处于等待响应状态,通过服务器的命令行可以发现,浏览器多了一个css的请求,
这是因为浏览器在解析html文件时发现html中引入外部的css文件,浏览器会再次发起请求。这时,服务器只需再次响应浏览器的请求即可。
else if(req.url == '/css/index.css'){
fs.readFile('./src/css/index.css','utf-8',(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/css;Chartset=utf-8'});
res.write(data);
res.end();
});
}
再次启动服务器,浏览器请求后拿到样式文件。
同样的,对于js文件
else if(req.url=='/js/index.js'){
fs.readFile('VueDemo/serve/src/js/index.js','utf-8',(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/javascript;Chartset=utf-8'})
res.write(data);
res.end()
})
}
优化
const myserver = myhttp.createServer((req,res)=>{ //通过myserver去http模块的createServer方法去创建服务,req为请求对象, res为响应对象
res.setHeader('Content-Type','text/plain');
let fileType = req.url.split('.');
if (fileType[1]=='html') {
// fs.readFile('文件路径',文件解析方式,回调函数)
fs.readFile(`./src/${fileType[0]}.html`,'utf-8',(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'})
res.write(data);
res.end()
})
}else if(fileType[1]=='css'){
fs.readFile(`./src/${fileType[0]}.css`,'utf-8',(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/css;Chartset=utf-8'})
res.write(data);
res.end()
})
}else if(fileType[1]=='js'){
fs.readFile(`./src/${fileType[0]}.js`,'utf-8',(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/javascript;Chartset=utf-8'})
res.write(data);
res.end()
})
}else if(fileType[1]=='jpg' || fileType[1]=='png'){//或者更多种类
fs.readFile(`./src/${fileType}`,(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/javascript;Chartset=utf-8'})
res.write(data);
res.end()
})
}
})
Step 10: 抽离服务器入口文件里的响应逻辑
新建controller文件夹 -> indexCtrl.js
将createServer箭头函数中的响应逻辑放到该文件中,indexCtrl.js
const fs = require('fs'); // node自带文件读取模块
module.exports = {
sendHtml(url,res){
fs.readFile('./src/index.html','utf-8',(err,data)=>{
// res.end(data); // 响应信息
// 1. res.writeHead(响应状态码,'配置解析方法,告诉浏览器这个文件你要解析成啥样')
res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'});
// 2. 发送
res.write(data);
// 3. 发送结束,告诉浏览器本次响应结束
res.end();
});
},
sendCss(url,res){
fs.readFile('./src/css/index.css','utf-8',(err,data)=>{
res.writeHead(200,{'Content-Type':'Text/css;Chartset=utf-8'});
res.write(data);
res.end();
});
},
}
通过 module.exports 公开,然后在app.js中引入,
const control = require('./src/controller/indexCtrl');
if(req.url == '/index.html'){
control.sendHtml(req.url,res);
} else if(req.url == '/css/index.css'){
control.sendCss(req.url,res);
}
Step 11: 抽离服务器入口文件的路由逻辑
新建 routers -> indexRouter.js
const control = require('../src/controller/indexCtrl');
module.exports = {
pageRoute(res,req){
if(req.url == '/index.html'){
control.sendHtml(req.url,res);
} else if(req.url == '/css/index.css'){
control.sendCss(req.url,res);
}
}
}
同时,修改 app.js
const routers = require('./routers/indexRouter');
const server = http.createServer((req,res)=>{
console.log('server running');
res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
console.log(req.url);
routers.pageRoute(res,req);
});
此时,将服务器入口文件app.js拆分成三个文件,其中 controller用于响应请求,router用于路由处理。
Step 12: express
参考文献: