Node学习之手写一个http-server
模仿http-server创建一个静态服务器。
http-server地址 https://www.npmjs.com/package/http-server
功能:Http-server是一个轻量级的基于nodejs的http服务器,可以使任意一个目录成为服务器的目录。
安装:npm i -g http-server
使用:http-server
(在想要使用的目录下直接使用命令)常用在测试Vue React等单页应用打包之后想要预览效果,缺不方便部署到服务器时。
阅读该文章可以获取到的知识:
- node命令行交互使用的一些常用包(commander)
- npm link命令,将npm 模块链接到对应的运行项目中去,方便地对模块进行调试和测试。
首先使用npm init -y创建一个新的项目
项目目录图
项目目录图如上所示。
分别来介绍下每个目录的作用,在使用命令行命令时package.json中的bin属性指明了需要运行的文件:
{
"name": "node-http-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"node-http-server": "./bin/www.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^4.1.0",
"commander": "^6.2.0",
"ejs": "^3.1.5",
"mime": "^2.4.6",
"mz": "^2.7.0",
"url": "^0.11.0"
}
}
然后在bin/www.js
文件中编写交互代码
#!/usr/bin/env node
const { program } = require('commander');
const json = require('../package.json');
const Server = require('../Server');
program.version(json.version)
.option('-p --port <port>', '端口号')
.option('-d --dir <dir>', '目录')
.option('-h --host <host>', '主机')
.parse(process.argv);
program.on('--help', () => {
console.log('');
console.log('Example call:');
console.log(' $ custom-help --help');
});
let config = {
port: program.port,
host: program.host,
dir: program.dir
}
let server = new Server(config);
server.start();
在文件的顶部必须加上 #!/usr/bin/env node
作用是告诉操作系统执行这个脚本的时候,调用/usr/bin下的node解释器。具体的我也不是很了解操作系统这块,可以参考网上资料。https://www.cnblogs.com/qinmengjiao123-123/p/8503163.html
然后使用commander
模块,这是一个第三方模块参考:https://www.kancloud.cn/diaoyundexia/text/149934,commander模块用来制作命令行交互;一般命令行包含:命令,选项,参数。
具体可以看npm官网或者其他资料,在这里就只粗略用了下。
process.argv
属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数,通过program.parse方法会被直接解析,所以参数部分会根据前面的option中配置的命令直接解析。
最后实例一个Server类,传递端口号,启动的目录,还有主机名,调用类上的start方法来启动服务。
先看一下交互:
在命令行输入命令 node-http-server --help
const http = require('http');
const fs = require('mz/fs');
const mime = require('mime');
const chalk = require('chalk');
const path = require('path');
const url = require('url');
const ejs = require('ejs');
let template = fs.readFileSync(path.join(__dirname, 'template.html'), 'utf-8');
const baseConfig = {
port: 3000,
host: '127.0.0.1',
dir: process.cwd()
}
class Server{
constructor({port, host, dir}){
this.port = port || baseConfig.port;
this.host = host || baseConfig.host;
this.dir = dir || baseConfig.dir;
this.template = template;
}
start() {
let server = http.createServer(this.sendRequest.bind(this));
server.listen(this.port, this.host, () => {
console.log(chalk.yellow(`服务启动, 服务路径是${this.dir}`));
console.log(chalk.green(`http://${this.host}:${this.port}`));
})
}
async sendRequest(req, res) {
try{
let {pathname} = url.parse(req.url);
let currentPath = path.join(this.dir, pathname);
let stats = await fs.stat(currentPath);
if(stats.isDirectory()) {
let dirs = await fs.readdir(currentPath);
let datas = dirs.map((item, index) => {
return {
href: path.join(pathname, item),
content: item
}
})
let str = ejs.render(this.template, {data: datas});
res.setHeader('Content-type', 'text/html; charset-utf8');
res.end(str);
} else {
this.responseData(req, res, currentPath);
}
}catch(e){
console.log(e);
this.sendError(req, res);
}
}
responseData(req, res, currentPath) {
res.setHeader('Content-type', mime.getType(currentPath) +'; charset=utf8');
fs.createReadStream(currentPath).pipe(res);
}
sendError(req, res) {
res.statusCode = 404;
res.end('Not Found');
}
}
module.exports = Server;
Server主要的逻辑就是考虑服务启动后服务的目录是一个文件还是文件夹,是文件直接读取展示,是文件夹用ejs模板直接渲染输出到页面,并且添加a链接可以点击进入。
最终在命令行输入命令 node-http-server -p 3000
还可以传其他参数,这里只传了port。
效果:
预览效果1 预览效果2.png