使用 Koa 搭建前端服务

环境准备

  1. node 环境

Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持,我本地的node环境为 v14.4.0

使用 Koa 搭建前端服务

快速开始

使用koa启动本地静态资源主要需要解决两个问题,一个是使用history路由时,刷新页面使之能正常进入页面,另一个就是请求能正常代理到目标服务,这里如果为了方便可以直接使用 koa 脚手架,也可以自己手动搭建。下面分别介绍一下这两种方法的使用。

从零搭建

① 初始化 package.json

$ npm init

输入该命令后,一路回车,会生成一个package.json文件

② 安装 koa
这里可以在当前目录安装,也可进行以全局安装

// 在当前目录安装
$ npm install koa -S

// 或全局安装
$ npm install koa-generator -g

③ 新建app.js

const Koa = require('koa')   
const app = new Koa()   

app.use(async ctx => {
  ctx.body = 'Hello World';
});
app.listen(9999, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:9999 或 localhost:9999');
}) 
//打开终端
$ node app.js

终端如下,记住之后每次添加依赖后都需要重启服务:

使用 Koa 搭建前端服务

此时在浏览器中访问该地址后,可看到打印出的 “Hello World” 文本,这时一个简单的服务就搭建起来了~

此时服务是启动了,但是浏览器怎么能打印某个文件的内容呢? 首先新建 dist 文件夹,然后建几个文件index.html、todo.html、404.html、img.png


//app.js 
const Koa = require('koa')   
const app = new Koa()   

//要涉及读文件的功能,所以引入node 的 fs 文件系统
const fs = require('fs')

/**
 * 用Promise封装异步读取文件方法
 * @param  {string} page html文件名称
 * @return {promise}      
 */
function render( page ) {
  return new Promise(( resolve, reject ) => {
    let distUrl = `./dist/${page}`
    fs.readFile(distUrl, "binary", ( err, data ) => {
      if ( err ) {
        reject( err )
      } else {
        resolve( data )
      }
    })
  })
}

/**
 * 根据URL获取HTML内容
 * @param  {string} url koa2上下文的url,ctx.url
 * @return {string}     获取HTML文件内容
 */
async function route( url ) {
  let dist = '404.html'
  switch ( url ) {
    case '/': 
    case '/index':
      dist = 'index.html'
      break
    case '/todo':
      dist = 'todo.html'
      break
    case '/404':
      dist = '404.html'
      break
    default:
      break
  }
  let html = await render( dist )
  return html
}

app.use(async ctx => {
  let url = ctx.request.url
  let html = await route( url )
  ctx.body = html
});
app.listen(9999, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:9999 或 localhost:9999');
}) 


// dist/index.html 文件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>index</title>
</head>
<body>
   <h1>koa2 demo index page</h1>
    <p>this is a index page</p>
    <ul>
      <li><a href="/">/</a></li>
      <li><a href="/index">/index</a></li>
      <li><a href="/todo">/todo</a></li>
      <li><a href="/404">/404</a></li>
    </ul>
    <img src="https://i.loli.net/2021/01/04/UMwA2d1QtKu4cWl.png" alt="" width="100" height="100">
    <img src="./img.png" alt="" width="100" height="100">
</body>
</html>

此时访问 http://127.0.0.1:9999http://127.0.0.1:9999/index,http://127.0.0.1:9999/todo 都可以进入对应页面
效果:

使用 Koa 搭建前端服务

现在可以访问文件内容了,但是本地静态资源却不能访问,这时 koa-static 就可以解决这个问题

$ npm install koa-static -S
//改装 app.js ,将所有文件作为静态资源访问且指向入口 ./dist/index.html上 
const Koa = require('koa')    
const app = new Koa()

app.use(require("koa-static")(__dirname + "/dist"));

app.listen(9999, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:9999 或 localhost:9999');
}) 

重启后文件访问到啦,此时所有文件都作为静态资源访问,所以进入子页面时需要带全文件名称,否则是访问不到的呢~

使用 Koa 搭建前端服务

有没有发现,如果依靠 ctx.request.url 手动处理路由,将会写很多处理代码,这时候就需要对应的路由的中间件对路由进行控制,这里介绍一个比较好用的路由中间件 koa-router (由于目标项目中使用的是单页面应用,所以这里暂不多介绍)

$ npm install koa-router@7 -S

此时可以上手我们的项目了,把已写好的项目打包完成并替换 dist 文件
可以看到已经访问到项目主页了~

使用 Koa 搭建前端服务

此时发现一个问题,当再次刷新的时候,竟然404!这是因为vue-router使用history模式返回index.html,仅能通过入口文件去加载其子路由。

而koa2的一个中间件,专治SPA应用程序的history模式路由问题,那就是 koa2-connect-history-api-fallback

$ npm install koa2-connect-history-api-fallback -S
//app.js
const Koa = require('koa')    
const { historyApiFallback } = require('koa2-connect-history-api-fallback');

const app = new Koa()

app.use(historyApiFallback());
app.use(require("koa-static")(__dirname + "/dist"));

app.listen(9999, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:9999 或 localhost:9999');
}) 

继续测试,发现不管什么路由都可以正常访问到啦~

这时候又遇到问题了,接口请求全部 404,那是因为请求走的都是http://127.0.0.1:9999,那404是必然的啦,那如何把请求代理转发到其他服务器呢? http-proxy-middleware 可以解决这个问题。

$ npm install http-proxy-middleware -S
$ npm i koa2-connect -S
 //app.js
const Koa = require('koa')   
const Router = require('koa-router')  
const { historyApiFallback } = require('koa2-connect-history-api-fallback');
const { createProxyMiddleware } = require('http-proxy-middleware')
const c2k = require('koa2-connect')

const app = new Koa()

app.use(historyApiFallback());
app.use(require("koa-static")(__dirname + "/dist"));

var router = new Router()
var target = 'https://222.222.222.222:8080' // 要代理的目标地址,vue项目写 vue.config.js 里的 target 地址
router.get(
  '*',
  c2k(
    createProxyMiddleware({
      target,
      changeOrigin: true,
      ws: true
    })
  )
)
app.use(router.routes())

app.listen(9999, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:9999 或 localhost:9999');
}) 

这时发现请求已经代理成功了,GET 请求的 HTTP 码返回了200,但是 POST、PUT 等其它请求方式依旧 404,那是因为咱仅给GET方法添加了target 地址,再将各种方式都补齐:

const Koa = require('koa') 
const Router = require('koa-router') 
const { createProxyMiddleware } = require('http-proxy-middleware')
const { historyApiFallback } = require('koa2-connect-history-api-fallback');
const c2k = require('koa2-connect')

const app = new Koa() 

app.use(historyApiFallback());
app.use(require("koa-static")(__dirname + "/dist"));

var router = new Router()
var target = 'https://abc.com' // 修改成你要代理的目标地址,vue项目写 vue.config.js 里的 target 地址

//项目一共使用5种请求方式: get post put patch delete  /* 'head','connect','options','trace','patch' */
let method = ['get', 'post', 'put', 'patch','delete']
method.forEach(item => {
  router[item](
    '*',
    c2k(
      createProxyMiddleware({
        target,
        changeOrigin: true,
        ws: true
      })
    )
  )
})

app.use(router.routes()) 
app.listen(9999) 

使用 Koa 搭建前端服务

使用 Koa 搭建前端服务大功告成!如果要用 Koa 脚手架搭建的话更加简单,执行以下命令、再安装上述依赖即可。

$ koa koaServer

总结一下,项目依赖一共包括以下几个:

"dependencies": {
    "http-proxy-middleware": "^1.0.6",
    "koa": "^2.13.0",
    "koa-router": "^7.4.0",
    "koa-static": "^5.0.0",
    "koa2-connect": "^1.0.2",
    "koa2-connect-history-api-fallback": "^0.1.3"
  }

tips:

  1. 如果习惯使用 npm run serve 或者 yarn serve 启动项目的话,可以为 package.json 文件的 scripts 属性添加一条:"serve":"node app.js" 即可
    "scripts": {
        "serve":"node app.js",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    
  2. koa2与koa1的最大区别是koa2实现异步是通过async/awaite,koa1实现异步是通过generator/yield,而express实现异步是通过回调函数的方式。
上一篇:node(三)


下一篇:使用koa+mongodb构建的仿知乎接口(一)