Koa开发:入门、进阶与实战
上QQ阅读APP看书,第一时间看更新

2.3 静态服务器

在早期的Web世界里,前端功能非常简单,就是单纯的页面展示,为了让这些静态页面通过互联网呈现给用户,需要将前端页面部署到一台服务器上,这就是静态服务器的概念。之前,很多人会用Nginx、Apache等部署一个静态服务器,部署前端项目后,就可以在浏览器访问了。其实静态服务器起到了提供一个读取静态文件(包括js、css、png等文件)、静态目录的作用。Koa也可以实现静态服务器的功能,本节带领读者部署一个介绍性的官网到Koa静态服务器上。

本节会介绍两种实现方式:一种是利用Koa中间件实现,这种方式比较简单,可以用现成的包;另一种是原生方式实现,这种方式虽然比较复杂,但是能够还原静态服务器的一些本质,利于理解原理。

现在要展示一个静态站点,最终实现的页面效果如图2-4所示。

0

图2-4 静态站点效果图

2.3.1 koa-static中间件的使用

静态服务器功能可以利用Koa的中间件koa-static实现,读者可以通过官方文档(https://github.com/koajs/static)进行了解。koa-static是一个npm包,在使用之前,需要先安装这个npm包,命令如下。

$ npm install --save koa-static

在分析代码之前,先展示一下项目组成,如图2-5所示。

0

图2-5 目录介绍

图2-5中,static目录下存放的是静态文件,index.js文件是Koa部分的逻辑实现。

index.js文件的代码如下。

const Koa = require('koa')
const path = require('path')
const static = require('koa-static')

const app = new Koa()

// 静态资源目录对于相对入口文件index.js的路径
const staticPath = './static'

app.use(static(
  path.join( __dirname,  staticPath)
))

app.listen(4000, () => {
  console.log('server is running, port is 4000')
})

2.3.2 如何实现一个静态服务器

如果不用Koa中间件,如何原生实现一个静态服务器呢?实现思路可以概括为通过请求的URL来读取静态文件。静态服务器通过请求把内容展示到页面上,只不过不同的静态资源,其mime type不同,能够对应起来即可。接下来看一下实现逻辑,代码如下。

const Koa = require('koa')
const fs = require('fs')
const path = require('path')

// 设置一个mime map, 因为本项目只涉及3种类型, 所以这里只列3种
const MIMES_MAP = {
  'css': 'text/css',
  'html': 'text/html',
  'jpg': 'image/jpeg'
}

const app = new Koa()

// 静态资源目录对于相对入口文件index.js的路径
const staticPath = './static'

// 解析资源类型
function parseMime( url ) {
  let extName = path.extname( url )
  extName = extName ? extName.slice(1) : 'unknown'
  return  MIMES_MAP[extName]
}

app.use( async ( ctx ) => {
  // 静态资源目录在本地的绝对路径
  let fullStaticPath = path.join(__dirname, staticPath)

  // 获取静态资源内容, 有可能是文件内容、目录或404
  let content = fs.readFileSync(path.join(fullStaticPath, 
    ctx.url), 'binary' )

  // 解析请求内容的类型
  let mime = parseMime(ctx.url)

  // 如果有对应的文件类型, 就配置上下文的类型
  if (mime) {
    ctx.type = mime
  }

  // 输出静态资源的内容
  if ( mime && mime.indexOf('image/') >= 0 ) {
    // 如果是图片, 则用Node原生res, 输出二进制数据
    ctx.res.writeHead(200)
    ctx.res.write(content, 'binary')
    ctx.res.end()
  } else {
    // 其他则输出文本
    ctx.body = content
  }
})

app.listen(4000, () => {
  console.log('server is running, port is 4000')
})

通过一个map对静态资源类型和mime type做映射,再依据请求中的URL来读取对应的资源,再将其放回前端进行展示。

提示

在JavaScript中,要善于利用map做代码优化,比如if else、switch case的逻辑,多数情况可以用map来重写,完善后的代码会更加优雅且易于维护。