本文参考http-server从零开始实现一个自己的http-server命令行工具。准备工作与控制台交互。这里使用commander和chalk:命令行工具commanderconsolestringstylechalk判断http服务中的文件类型。这里使用mime,内容模板使用ejs:文件格式类型mime内容模板ejs在本地开发npm模块时,可以使用npmlink命令设置npm模块链接到对应的运行项目,方便调试和测试模块。新建文件夹http-server,在文件根目录下执行命令npminit-y,生成package.json文件,在package.json中添加bin命令://package.json{"name":"http-server","version":"1.0.0","license":"MIT","bin":{"my-http-server":"./bin/www.js"}}执行npm控制台上的链接全局my-http-server命令指向此目录。当然,名称可以根据自己的需要更改。./bin/www.js路径就是执行这个命令的文件。然后就可以到你要启动的http服务的文件目录下,打开控制台,执行my-http-server启动我们的命令,不过在此之前,你需要创建www.js文件。我们执行的命令其实就是执行nodewww.js。创建:$mkdirbin$touchmkdir/www.js为了使文件在nodejs环境下执行,需要在www.js文件开头输入声明:#!/usr/bin/env在启动节点配置项之前,需要新建一个配置项,在http-server文件夹下新建config.js并输入配置内容:letconfig={port:3000,//端口号host:"127.0.0.1",//启动路径dir:process.cwd()//命令执行目录}module.exports=config;用户可以通过commander轻松与控制台交互,修改bin/www.js文件内容如下:#!/usr/bin/envnodeletconfig=require("../config");constcommander=require("commander");const{version}=require("../package.json");commander.version(version).option("-p--port","sethttp-serverport")//端口号.option("-0--host","sethttp-serverhost")//hostpath.option("-d--dir","sethttp-serverdirectory")//服务启动path.on("--help",()=>{console.log("example");console.log("$my-http-server--port--host");}).parse(process.argv)//获取用户与控制台交互后的命令并合并到配置文件config={...config,...commander};保存文件,在控制台输入my-webpack-server-h可以看到相应的交互信息。createServer在http-server文件夹下新建server.js,创建Server类,封装httpcreateServer方法:server.jsconsthttp=require("http")module.exports=classServer{constructor(config){this.目录=配置。dirthis.port=config.portthis.host=config.host}handleRequest(req,res){}start(){constserver=http.createServer(this.handleRequest.bind(this))server.listen(this.port,this.host,()=>{//成功后的回调函数})}}为了在启动服务后在控制台输出多色字符串信息,我们可以使用chalk依赖包,我们添加如下server.js的代码:constchalk=require("chalk")...server.listen(this.port,this.host,()=>{console.log(chalk.yellow(`Starringuphttp-server,\r\n服务${this.dir}\r\n在\r\n上可用`))console.log(chalk.green(`http://${this.host}:${this.port}`))})...handleRequest处理请求,然后开始封装请求方法。handleRequest函数主要做了以下工作:获取requests的url路径,判断路径是文件还是文件夹fs.stat如果路径中没有对应的文件或文件夹,如果返回404文件,则会判断文件的mime类型,响应文件信息文件夹判断文件夹中是否有index.html文件,有则渲染index.html,否则使用ejs模板渲染文件listconsturl=require("url")constejs=require("ejs")constmime=require("mime")constfs=require("mz/fs")//模板文件consttemplate=fs.readFileSync(path.resolve(__dirname,"./template.ejs"),"utf-8")...constructor(){...}asynchandleRequest(req,res){this.req=reqthis.res=res//通过url解析路径名const{pathname}=url.parse(req.url,true)//获取文件的绝对路径,decodeURIComponent防止中文符号被识别constabsPath=path.join(this.dir,decodeURIComponent(pathname))try{//判断是文件还是文件夹conststatObj=awaitfs.stat(absPath)if(statObj.isDirectory()){letindexPath=absPath+"/index.html";//如果文件夹中有索引.html进入fs.access(indexPath).then(this.sendFile.bind(this,indexPath)).catch(async()=>{//模板文件过滤掉.DS_Store并添加href链接letfiles=awaitfs.readdir(absPath)files=files.filter((f)=>f!==".DS_Store").map((file)=>({file,href:path.join(pathname,file)}))letstr=ejs.render(template,{name:path.basename(pathname)||"文件列表",文件,});res.setHeader("Content-Type","text/html;charset=utf-8")res.end(str)})}else{//文件直接返回.sendFile(absPath)}}catch(错误){this.sendError(error)}}sendError(error){this.res.statusCode=404;//返回错误信息,生产环境需要屏蔽this.res.end(error.toString())}sendFile(filePath){//判断文件mime类型this.res.setHeader("Content-Type",mime.getType(filePath)+";charset=utf-8")//创建文件可读流并返回fs.createReadStream(filePath).pipe(this.res)}start(){}templatefiletemplatefilerender文件夹列表文件//template.ejs<%files.forEach(function(item){%>- "><%=item.file%>
<%})%>
引入最后一个www.js文件导入Server类,初始化并渲染:constServer=require("../server")letserver=newServer(config);server.start()找到你要启动http服务的文件夹,打开控制台,输入my-http-server启动服务,发布npm包并在http-server文件夹Console下打开,输入如下命令:如果是第一次发送包,使用npmadduser;如果不是第一次,使用npmlogin输入账号密码和注册邮箱username:yournamepassword:Email:(这个是public)email@domain.com确保和package.json在同一个目录,然后执行npm发布:$npmpublish