我用Node.js的Koa2框架搭建了一个静态站点。当然,这个站点只是部署在自己的电脑上,主要是为了一些测试:比如写一个小页面,尝试新技术。以前在电脑上建过很多类似的静态站点,因为用了一年php,所以之前都是用php+nginx建站,这两年一直在做前端,和我懒得碰php了。前段时间在看一个公开课的时候(忘记是哪个机构哪个老师了,不好意思),这个公开课的主要内容是教大家如何实现一个简单的koa框架。我听了听然后跟着葫芦画画自己写了一个简单的“koa框架”。后来想用这个简单的框架搭建一个静态站点。折腾了一下,基本是可行的,但是我对koa的源码很好奇,就去看了一下,然后觉得自己写的确实不行。所以还是打算用koa搭建静态站点。Koa使用起来非常方便。其实Node.js已经为web开发提供了很多不错的API接口。Koa只是简单的封装了一些API,让我们的开发更加方便。按照我的理解,koa的核心(或者说更好的地方)就是它提供的中间件机制:constapp=newKoa()app.use(asyncctx=>{//middlewarecontent1})app.use(asyncctx=>{//中间件内容2})app.listen(port,()=>{console.log('start')})这个方法用起来也比较方便。它的核心实现是(借鉴public类)将所有传入的中间件合并为一个,然后递归调用,这里就不展开了。静态站点需求其实很简单,所以用koa搭建静态站点比较简单。静态站点主要展示静态文本内容(html)和图片,加上一些简单的样式美化(css)和交互(js),即我只需要一个可以提供html、图片(jpg、jpg、jpg等)、js和css。下面通过一个页面请求来简单分析一下站点的实现:1、用户通过浏览器访问站点中的a.html2,web服务器收到请求后解析请求的文件名和文件类型。3、根据上面得到的文件名在服务器上找到对应的文件4,如果找到,则设置响应头:状态码(200)、响应内容类型和响应体;如果没有找到,设置错误的状态码(比如404)和对应的响应体。当然,还有很多细节需要考虑:比如服务器的页面存放目录,目录是否可读等等。代码目录src|-app-koa.js//核心文件,处理请求和返回响应|-file-util.js//读取文件的方法集合|-mime.js//mime类型映射|-status-code.js//状态码映射|-views///存放html文件|-static///存放js、css、图片等静态资源代码先贴下我写的代码:主文件app-koa。jsconsturl=require('url')constpath=require('path')constKoa=require('koa')constCONTENT_TYPE=require('./mime')constSTATUS_CODE=require('./status-code')const{accessFilePromise,statFilePromise,readFilePromise}=require('./file-util')constROOT=__dirnameconstapp=newKoa()app.use(asyncctx=>{constreqUrl=ctx.request.urlletpathname=url.parse(reqUrl).pathnameletfilePath=''letfileformat=''//单独访问web根路径if(pathname==='/'){pathname='/index.html'}constext=pathname.indexOf('.')!==-1?pathname.match(/(\.[^.]+)$/)[0]:'.html'if(ext==='.html'){filePath=path.join(ROOT,`/views${pathname}`)fileformat='utf-8'}else{filePath=path.join(ROOT,pathname)fileformat='binary'}try{constisAccessed=awaitaccessFilePromise(filePath)letfileData=nullif(!isAccessed){constcode=STATUS_CODE.ENOENT//文件或目录不可访问,直接返回404ctx.res.writeHead(code,{'Content-Type':CONTENT_TYPE['.html']})fileData=awaitreadFilePromise(path.join(ROOT,`/views/error/${code}.html`),'utf-8')ctx.body=fileData}else{constisFile=awaitstatFilePromise(filePath)if(isFile===true){fileData=awaitreadFilePromise(filePath,fileformat)}else{//试读获取该目录下的index.htmlfileData=awaitreadFilePromise(`${filePath}/index.html`,'utf-8')}ctx.res.setHeader('Content-Type',CONTENT_TYPE[ext])if(ext!=='.html'){ctx.res.setHeader('Content-Length',Buffer.byteLength(fileData))}ctx.res.writeHead(STATUS_CODE.SUCCESS)ctx.body=fileDataif(ext!=='.html'){ctx.res.write(ctx.body,'binary')}}}catch(err){ctx.res.writeHead(STATUS_CODE[err.code],{'Content-Type':CONTENT_TYPE[ext]})}})app.listen(3000,()=>{console.log('你的应用程序运行在http://localhost:3000')})file-util.js--文件处理工具constfs=require('fs')constreadImagePromise=(filePath)=>{conststream=fs.createReadStream(filePath)conststreamData=[]//存储文件流letdata=''returnnewPromise((resolve,reject)=>{stream.on('data',(chunk)=>{streamData.push(chunk)})stream.on('end',()=>{data=Buffer.concat(streamData)resolve(data)})stream.on('error',(err)=>{console.assert(isAssert,'ReadStreamonerror事件:',err,err&&err.message)reject(err)})}).catch(err=>{console.assert(isAssert,'ReadStreampromise链catch读取文件错误:',err,err&&err.message)throwerr})}constaccessFilePromise=(filePath)=>{returnnewPromise((resolve,reject)=>{fs.access(filePath,fs.R_OK,(err)=>{if(err){console.assert(false,'ifThe访问文件不可访问:',err,err&&err.message)resolve(false)//reject(err)}resolve(true)})}).catch(err=>{console.assert(true,'promise链捕获访问文件错误:',err,err&&err.message)throwerr})}conststatFilePromise=(filePath)=>{returnnewPromise((resolve,reject)=>{fs.stat(filePath,(err,stats)=>{if(err){console.assert(true,'statfileerrorinif:',err,err&&err.message)reject(err)}resolve(stats.isFile())})}).catch(err=>{console.assert(true,'promisechaincatchstatfileerror:',err,err&&err.message)throwerr})}constreadFilePromise=(filePath,format)=>{if(format==='binary'){returnreadImagePromise(filePath)}returnnewPromise((resolve,reject)=>{fs.readFile(filePath,format,(err,data)=>{if(err){console.assert(true,'如果读取文件时出错:',err,err&&err.message)reject(err)}resolve(data)})}).catch(err=>{console.assert(true,'promisechaincatchreadfileerror:',err,err&&err.message)throwerr})}module.exports={readImagePromise,accessFilePromise,statFilePromise,readFilePromise}mime.js--mime类型映射module.exports={'.css':'text/css;charset=utf8','.js':'application/javascript;charset=utf8','.html':'text/html;charset=utf8','.png':'image/png','.jpg':'image/jpeg','.jpeg':'image/jpeg','.ico':'image/x-icon'}status-code.js--状态代码映射module.exports={SUCCESS:200,EACCES:403,ENOENT:404,EISDIR:403}以下,我觉得自己写的还是挺罗嗦的,没办法,目前的水平只能写到这个水平,但是用它来跑一个简单的静态站点还是可以的,没有,我已经跑过了将近半月初,我傻傻的用命令行运行,然后想到应该设置一个daemon进程,然后百度了一个node进程管理工具:pm2,加油,这样省事.嗯,先写到这里吧。文章最初发表于我搭建的博客:我用Node.js的Koa框架搭建了一个静态站点,稍作改动。
