{"id":18834572,"url":"https://github.com/tphp/koa-web","last_synced_at":"2025-06-20T16:40:57.244Z","repository":{"id":143814500,"uuid":"392287490","full_name":"tphp/koa-web","owner":"tphp","description":"koa web","archived":false,"fork":false,"pushed_at":"2021-11-25T10:24:45.000Z","size":272,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-09T23:04:55.064Z","etag":null,"topics":["koa","web"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tphp.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-03T11:05:58.000Z","updated_at":"2021-11-25T10:49:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"aebc8960-c326-4cfc-99c5-79d28162630a","html_url":"https://github.com/tphp/koa-web","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/tphp/koa-web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tphp%2Fkoa-web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tphp%2Fkoa-web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tphp%2Fkoa-web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tphp%2Fkoa-web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tphp","download_url":"https://codeload.github.com/tphp/koa-web/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tphp%2Fkoa-web/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260980984,"owners_count":23092301,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["koa","web"],"created_at":"2024-11-08T02:13:18.877Z","updated_at":"2025-06-20T16:40:52.232Z","avatar_url":"https://github.com/tphp.png","language":"JavaScript","readme":"koa-web\n\n- 解决WEB开发者不易发现的困惑，WEB开发原来可以这么玩。\n- 可使用koa或者koa-web扩展更多的功能实现。\n- 是全栈开发的桥梁，它将改变繁琐开发的习惯，让代码一目了然。\n\n---\n\n## 应用场景\n\n- 开发WEB页面实时预览效果（无需重启服务）\n- SEO、CSS和JS设置更轻松\n- 简化GET和POST请求处理\n- 也适用于API、web代理、压力测试\n- 代码层次分明，开发大项目优势明显\n- 域名灵活绑定路径，适合开发多项目\n- 更多不确定的用途可能也会在这里 (待发现...)\n\n#### 依赖包\n\n- 应用框架: [koa](https://koa.bootcss.com/)\n- 静态文件处理: [koa-static](https://www.npmjs.com/package/koa-static) + [koa-mount](https://www.npmjs.com/package/koa-mount)\n- 模板引擎: [nunjucks](https://nunjucks.bootcss.com/) (也可设置其他模板引擎)\n- 提交数据处理: [form-data](https://www.npmjs.com/package/form-data) + [formidable](https://www.npmjs.com/package/formidable)\n\n#### 下载DEMO源码\n\n- [koa-web-demo](https://github.com/tphp/koa-web-demo)\n\n#### 安装 koa-web\n\n```\nnpm i koa-web\n```\n---\n\n## 项目配置\n\n```js\nconst Koa = require(\"koa\");\nconst KoaWeb = require('koa-web');\n\nconst app = new Koa();\n\napp.use(\n  KoaWeb(\n    {\n      // 项目根路径\n      path: __dirname,\n\n      // 静态文件路径组，默认指向static\n      // 模式1: static: \"static\"\n      // 模式2: static: [\"static\"]\n      // 模式3: static: {static: \"static\"}\n      // 模式1、2、3效果相同，如果想指定项目外目录须使用模式3\n      static: {\n        static: \"static\"\n      },\n\n      // 静态文件缓存时间，默认0毫秒\n      staticMaxage: 0,\n\n      // 视图模块设置\n      view: {\n        // 视图默认目录 html\n        path: \"html\",\n\n        // 视图文件默认扩展名 html， 如 \"hello.html\"\n        ext: \"html\",\n\n        // 域名(前缀)绑定路径设置\n        domains: {\n          // 如访问: http://www.hello.myweb.com:90\n          // 实际解析到: myweb90/tools 目录\n          // 相当于 view 中的 path 设置为: html/myweb90/tools\n          // 左边: www.*.{abc}.com:{port}不能混合参数\n          // 如 www.*.{abc}{def} 中的 {abc}{def} 将无法解析\n          // 右边: {abc}{port}/tools 可以随意混合参数\n          \"www.*.{abc}.com:{port}\": \"{abc}{port}/tools\",\n\n          // 权重1: 域名前缀数量越多，权重越高\n          // 如访问: http://www.a.com:88 将解析到 xyz\n          // 如访问: http://www.b.com:88 将解析到 abc\n          \"www:88\": \"abc\",\n          \"www.a:88\": \"xyz\",\n\n          // 权重2: 参数越少，权重越高\n          // 如访问: http://www.a.com 将解析到 hello\n          // 如访问: http://www.b.com 将解析到 world\n          \"www.a.com\": \"hello\",\n          \"www.{name}.com\": \"world\", // 等价于: \"www.*.com\": \"world\"\n\n          // 绑定所有域名/IP的90端口， 但权重最低\n          \":90\": \"path\",\n        \n          // :90 权重高于 :{path}\n          // \":{path}\": \"new/path\",\n        },\n\n        // 默认解析路径，不支持变量设置，如: {abc}/tpl\n        // 当domains中配对不成功时生效\n        // 默认为空，即根目录 html\n        domainDefault: \"\"\n      },\n\n      // 热加载，如果不缓存，每次文件改动会重新加载\n      // 生产环境可设置为true提高性能\n      cache: false,\n\n      // 错误页面默认设置，只支持 404 和 500 错误\n      errors: {\n        404: \"errors/404\",\n        500: \"errors/500\"\n      },\n\n      // json配置文件全局配置，会和视图目录中的 \".json\" 文件合并\n      json: {\n        // js: \"hello.js\",\n        // css: ['hello.css', 'world.css'],\n        // layout: \"layout/test\"\n      },\n\n      // 扩展页面默认Content-Type设置，如果不设置则以mime-types扩展名为准\n      // extTypes: {\n      //   // 例如 /hello/world.txt 的设置 Content-Type：\"text/plan\"\n      //   txt: \"text/plan\"\n      // },\n\n      // 扩展页面执行后回调\n      extCalls: {\n        // 访问： http://localhost:3000/任意路径.sh\n        // 同步模式\n        // extData: 调用任意路径下的扩展页面数据\n        sh: (extData, hd, data, files) =\u003e {\n          // 优先级高于 extTypes 配置\n          hd.ctx.type = 'text/plain';\n\n          return extData;\n        },\n\n        // 访问： http://localhost:3000/任意路径\n        // 异步模式\n        // 默认页面 htm、html和无扩展\n        // html: async () =\u003e {\n        //   return \"hello world!\";\n        // }\n      },\n\n      // 如果不习惯默认的nunjucks模板引擎， 可以使用render进行重设\n      // template: 模板页面源码\n      // viewData: 渲染的数据\n      // info: 如果template不能满足要求，info中有更多信息提供使用\n      // render: (template, viewData, info) =\u003e {\n      //   // ****字符串模式****\n      //   let ejs = require('ejs');\n      //   return ejs.render(template, viewData);\n\n      //   // *****文件模式*****\n      //   let error;\n      //   let html;\n      //   ejs.renderFile(info.filename, viewData, (e, h) =\u003e {\n      //     error = e;\n      //     html = h;\n      //   });\n\n      //   if (error) {\n      //     info.handle.ctx.response.status = 500;\n      //     return error['message'];\n      //   }\n      //   return html;\n      // },\n    }\n  )\n);\n\n// app.use(KoaWeb({path: __dirname})); // 快捷默认配置\n\napp.listen(3000, () =\u003e {\n  console.log(\"server is running at http://localhost:3000\");\n});\n```\n---\n\n## 基本用法\n\n#### 创建主页面\n\n- 默认路径对应 view \u003e path 配置\n- 默认路径: /html 默认扩展: .html\n- 新建文件/html/index.html 并填写 Hello World ！\n- 访问方式(效果一样)：\n  - http://localhost:3000\n  - http://localhost:3000/index\n  - http://localhost:3000/index.htm\n  - http://localhost:3000/index.html\n- 当cache=false(默认false)时不需要重启Koa服务，就可以看到实时效果\n\n#### 静态文件\n\n- 创建/static/test.jpg文件\n- 访问路径： http://localhost:3000/static/test.jpg\n\n#### 错误页面\n\n- 404： /html/errors/404.html\n- 500： /html/errors/500.html\n- 页面中设置 {{ code }}: 错误状态码\n- 页面中设置 {{ message }}: 错误消息\n\n#### 创建页面DEMO\n\n- 视图文件：/html/hello/world.html\n- 模板文件：/html/hello/world.js\n- 配置文件：/html/hello/world.json\n- 这3个文件至少存在一个\n\n###### /html/hello/world.html\n\n```html\n\u003cdiv\u003e{{ sayHello }}\u003c/div\u003e\n\u003cdiv\u003e{{ sayWorld }}\u003c/div\u003e\n```\n\n###### 模板引擎说明文档 [nunjucks](https://nunjucks.bootcss.com/)\n\n###### /html/hello/world.js\n\n```js\n/**\n * 入口文，hd、data和files都可以不设置\n * 如 module.exports = async () =\u003e {}; 也是可以的\n * data和files是通过 require(\"formidable\") 实现\n * @param {*} hd 主调用函数\n * @param {*} data form-data、x-www-form-urlencoded和JSON数据会自动转化为该对象\n * @param {*} files 只有在form-data类型中产生\n * @returns \n */\nmodule.exports = async (hd, data, files) =\u003e {\n\n  // hd.ctx.body = '设置了就只会显示这一段';\n\n  // console.log(hd.isPost()); // 判断是否是POST提交\n  \n  // console.log(hd.ctx.query); // url 传递的参数\n  // console.log(hd.ctx.header); // http 头文件\n  // console.log(data); // data == hd.ctx.app.info.data\n  // console.log(files); // files == hd.ctx.app.info.files\n\n  // hd.ctx.app.info.status: 解析状态\n  // hd.ctx.app.info.errMessage: 解析错误提示\n  // hd.ctx.app.info.body: 原始POST提交字符串\n  // hd.ctx.app.info.type: Content-Type标记 form、xform、json\n  // form: multipart/form-data\n  // xform: application/x-www-form-urlencoded\n  // json: application/json\n  // console.log(hd.ctx.app.info); \n\n  // 获取项目中文件路径: /项目路径/hello.js\n  // console.log(hd.path(\"hello.js\"));\n  // 获取视图中文件路径: /项目路径/html/hello.js\n  // console.log(hd.viewPath(\"hello.js\"));\n\n  // 设置模板页面中函数（nunjucks模板）\n  hd.view('sayHello', \"hello hello!\");\n  // 多函数设置\n  // hd.view({sayHello: \"hello hello!\", sayOther: \"hello other\"});\n  // 获取函数\n  // console.log(hd.getView());\n  // console.log(hd.getView('sayHello'));\n\n  // SEO优化（TDK）\n  // hd.title('页面标题');\n  // console.log(hd.getTitle());\n  hd.keywords('关键词');\n  // console.log(hd.getKeywords());\n  // hd.description('描述');\n  // console.log(hd.getDescription());\n\n  // 动态设置页面内CSS和JS\n  hd.style(\".body{color:#F00;}\"); // style只能在head中\n  hd.script(\"var top = 'top';\", true); // js 代码在 head 中\n  hd.script(\"var bottom = 'bottom';\"); // js 代码在body标签尾部\n\n  // 动态加载css和js文件\n  // 加载css不管加不加@都在head标签中或页面顶部\n  // hd.css('/static/c1.css', {class: \"test\"}); // 可设置属性\n  hd.css('/static/c1.css');\n  hd.css(['/static/c2.css', '@/static/c3.css']);\n  // 动态js中加@表示在head标签中或页面顶部\n  // hd.js(['/static/j2.js', '@/static/j3.js'], {class: \"test\"}); // 可设置属性\n  hd.js('/static/j1.js');\n  hd.js(['/static/j2.js', '@/static/j3.js']);\n\n  // 设置页面缓存10秒\n  // hd.age(10);\n  // 如不设置，hd.age()， 则页面缓存默认是 staticMaxage 配置值\n\n  return {\n    sayWorld: \"hello world!\"\n  };\n}\n```\n\n###### /html/hello/world.json\n\n- layout默认为不设置\n- css和js可以是字符串或数组\n\n```json\n{\n  \"layout\": \"hello/layout\",\n  \"css\": \"/static/abc.css\",\n  \"js\": [\n    \"/static/abc.js\",\n    \"@/static/def.js\"\n  ],\n  \"title\": \"this title is json\",\n  \"keywords\": \"this keywords is json\",\n  \"description\": \"this description is json\"\n}\n```\n\n###### /html/hello/layout.html\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"UTF-8\" /\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv\u003e{{ sayLayout }}\u003c/div\u003e\n{{ __layout__ | safe }}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n###### /html/hello/layout.js\n\n```js\nmodule.exports = async (hd) =\u003e {\n  hd.view('sayLayout', \"hello layout!\");\n}\n```\n\n---\n\n## 页面源码效果DEMO\n\n###### 访问页面 http://localhost:3000/hello/world 源码如下\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003ctitle\u003ethis title is json\u003c/title\u003e\n  \u003cmeta name=\"keywords\" content=\"关键词\" /\u003e\n  \u003cmeta name=\"description\" content=\"this description is json\" /\u003e\n  \u003cmeta charset=\"UTF-8\" /\u003e\n  \u003clink rel=\"stylesheet\" href=\"/static/abc.css\" /\u003e\n  \u003clink rel=\"stylesheet\" href=\"/static/c1.css\" /\u003e\n  \u003clink rel=\"stylesheet\" href=\"/static/c2.css\" /\u003e\n  \u003clink rel=\"stylesheet\" href=\"/static/c3.css\" /\u003e\n  \u003cscript src=\"/static/def.js\"\u003e\u003c/script\u003e\n  \u003cscript src=\"/static/j3.js\"\u003e\u003c/script\u003e\n  \u003cstyle\u003e\n    .body{color:#F00;}\n  \u003c/style\u003e\n  \u003cscript\u003e\n    var top = 'top';\n  \u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv\u003ehello layout!\u003c/div\u003e\n\u003cdiv\u003ehello hello!\u003c/div\u003e\n\u003cdiv\u003ehello world!\u003c/div\u003e\n\u003cscript src=\"/static/abc.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/static/j1.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/static/j2.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  var bottom = 'bottom';\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n###### 访问页面 http://localhost:3000/hello/world.json 源码如下\n\n```json\n{\"sayWorld\":\"hello world!\"}\n```\n\n- 扩展名.json不会渲染/html/hello/world.html页面\n\n#### 页面之间的调用 call\n\n###### 创建调用页面\n\n- /html/test/data.html\n\n```html\n\u003cdiv\u003e{{ sayHello }}\u003c/div\u003e\n\u003cdiv\u003e{{ sayWorld }}\u003c/div\u003e\n```\n\n- /html/test/data.js\n\n```js\nmodule.exports = async (hd) =\u003e {\n  return {\n    sayHello: \"hello\",\n    sayWorld: hd.getView('sayWorld')\n  };\n};\n```\n\n###### 内部使用示例\n\n###### /html/test/call.js\n\n```js\nmodule.exports = async (hd) =\u003e {\n  return await hd.call(\n    // 与hd.http模式不同\n    // hd.call 内部访问 (相对路径、绝对路径)\n    // hd.http 外部访问 (url模式)\n    // 可以带参数访问，如:\n    // /test/data.js?say=hello\n    // data 解析为：/test/data\n    // ./data 解析为：/test/data\n    // ../data 解析为：/data\n    // $.abc 解析为：/test/call.abc\n    '/test/data',\n    {\n      sayWorld: \"MyWorld\"\n    }\n  );\n};\n```\n\n###### 外部使用示例（以koa-route为例）\n\n- 如果koa-web不能满足当下要求时，可与其他路由框架进行结合使用\n- 需要另行安装: npm i koa-router\n\n```js\nconst Koa = require(\"koa\");\nconst KoaWeb = require('koa-web');\nconst router = require(\"koa-router\")();\n\nconst app = new Koa();\n\napp.use(KoaWeb({path: __dirname}));\n\nrouter.get(\"/route/web\", async (ctx, next) =\u003e {\n  await next();\n  if (!ctx.body) {\n    let ret = await ctx.app.call(\n      // 因为是外部调用，等同于 /test/data\n      'test/data',\n      {\n        sayWorld: \"MyWorld\"\n      }\n    );\n    ctx.body = ret;\n  }\n});\n\napp.use(router.routes());\napp.use(router.allowedMethods());\napp.listen(3000, () =\u003e {\n  console.log(\"server is running at http://localhost:3000\");\n});\n```\n\n###### 访问以下两个链接\n\n- http://localhost:3000/test/call\n- http://localhost:3000/route/web\n\n###### 其结果相同，预览源码如下\n\n```html\n\u003cdiv\u003ehello\u003c/div\u003e\n\u003cdiv\u003eMyWorld\u003c/div\u003e\n```\n\n---\n\n## HTTP快捷应用\n\n#### 单个请求实例\n\n- 支持 http 和 https\n- /html/test/http.js\n\n```js\n\nconst fs = require(\"fs\");\n\n/**\n * 提交方式说明\n * get: 非POST提交\n * form: multipart/form-data\n * xform: application/x-www-form-urlencoded\n * json: application/json\n * @param {*} hd \n * @returns \n */\nmodule.exports = async (hd) =\u003e {\n  let options = {\n    url: 'http://localhost:3000/test/call?a=123\u0026b=456',\n\n    // 数据代理，支持：get、form、xform、json\n    // get 通过浏览器URL和配置url进行合并，其他通过POST和配置data进行合并\n    // 默认 {get: false, post: false, header:false}\n    // {get: false, all: true} 等价于 {get: false, post: true, header:true, method:true}\n    proxy: {\n      // get: false,\n      // post: false,\n      // header: false,\n      // method: false,\n      all: true\n    },\n\n    // 数据覆盖模式: 默认 asc\n    // asc: POST覆盖配置\n    // desc: 配置覆盖POST\n    proxyCover: 'asc',\n\n    // POST提交数据\n    data: {\n      abc: \"test\",\n      hello: {\n        world: \"Is Ok\"\n      }\n    },\n\n    // 提交方式： get、form、xform、json\n    // 如果不设置，默认以浏览器提交方式一致\n    // type: 'json',\n\n    // 如果未设定，默认以type设置自动产生get或post自动匹配\n    // 不区分大小写: GET POST PUT DELETE HEAD 等\n    // method: 'post',\n\n    // 上传文件\n    files: {\n      // 直接以文件路径提交\n      str: \"/tmp/abc.jpg\",\n      // Buffer\n      buffer: new Buffer(10),\n      bufferFrom: Buffer.from([0, 0]),\n      // 使用 fs.createReadStream 需判断文件是否存在，否则会报错\n      // fs: fs.createReadStream(\"/tmp/abc.jpg\", { highWaterMark: 10 * 1024 * 1024 }),\n      // 数组模式\n      array: {\n        // path可以是str、buffer和fs\n        path: \"/tmp/abc.jpg\",\n        // 可选，默认从path抽取\n        name: \"abc.jpg\",\n        // 可选，默认后缀名.jpg进行判断\n        type: \"image/jpeg\"\n      }\n    },\n\n    // 上传文件最大字节数，默认 10M\n    fileMaxSize: 10 * 1024 * 1024,\n\n    // 头文件设置，以下参数将设置无效\n    // host, referer, content-length, transfer-encoding, content-type\n    header: {\n      'User-Agent': 'Mozilla/5.0'\n    },\n\n    // 超时设置，默认15秒\n    timeout: 15000,\n\n    // 数据返回类型，默认full， HTML是String或Buffer\n    // full: 全部返回，{\"status\": true, \"code\": 200, \"data\": HTML, \"ms\": 62}\n    // text：HTML\n    // json: JSON.parse(\"HTML\")\n    dataType: 'full',\n\n    // 获取二进制，默认为false，如果获取图片之类的须设置为true\n    // 当设置为true时对dataType: \"json\" 无效\n    // buffer: false,\n\n    // 数据结果调试\n    // index: 请求下标，在httpAll下效果显著\n    // data: 页面信息\n    // debug: (index, data) =\u003e {},\n    // debug: async (index, data) =\u003e {},\n  };\n\n  let info = await hd.http(\n    options, // 配置信息\n    false // 可选，是否仅返回生成后的配置信息，默认 false\n  );\n\n  // 通常写法： await hd.http(options);\n\n  return info;\n};\n```\n\n#### 并发请求实例\n\n- /html/test/httpAll.js\n\n```js\n/**\n * 并发处理请求， 采用Promise.all实现\n * @param {*} hd \n * @returns \n */\nmodule.exports = async (hd) =\u003e {\n  // 第二个参数可选，是否仅返回生成后的配置信息，默认 false\n  // hd.httpAll(options, false);\n\n  // Array 列表模式\n  let [al, bl] = await hd.httpAll([{\n      url: 'http://localhost:3000/test/call?flag=a',\n      dataType: 'full'\n    },\n    {\n      url: 'http://localhost:3000/test/call?flag=b'\n    },\n    // ...\n  ]);\n\n  // Object 对象模式\n  let { a, b } = await hd.httpAll({\n    a: {\n      url: 'http://localhost:3000/test/call?flag=a'\n    },\n    b: {\n      url: 'http://localhost:3000/test/call?flag=b'\n    },\n    // ...\n  });\n\n  return {\n    al,\n    bl,\n    a,\n    b\n  }\n};\n```\n\n---\n\n## 页面扩展\n\n#### 以下三种模式效果等效（页面默认设置）\n\n- /html/test/demo.js\n\n```js\n// 模式1： 默认模式\nmodule.exports = async hd =\u003e { return \"hello ext!\"; };\n\n// 模式2：直接返回字符串\nmodule.exports = 'hello ext!';\n\n// 模式3： 对象模式\nmodule.exports.html = 'hello ext!';\nmodule.exports = { html: 'hello ext!' };\nmodule.exports.html = async hd =\u003e { return 'hello ext!'; };\nmodule.exports = { html: async hd =\u003e { return 'hello ext!'; } };\n```\n\n- http://localhost:3000/test/demo\n- http://localhost:3000/test/demo.htm\n- http://localhost:3000/test/demo.html\n- http://localhost:3000/test/demo.json\n- 默认情况下的页面扩展 demo、demo.htm、demo.html、 demo.json 共四种\n- 如果要统一控制需使用全局配置中的 extTypes 或 extCalls\n- 全局中可以设置 html 和 json 扩展\n- 设置 module.exports.htm 和 module.exports.json 是无效的\n\n#### 自定义页面扩展\n\n- /html/test/ext.js\n\n```js\n/**\n * 默认首页\n * 访问： http://localhost:3000/test/ext\n * @param {*} hd \n * @returns \n */\nmodule.exports.html = (hd) =\u003e {\n  hd.css('ext.css');\n  hd.js('ext.js');\n  return \"这是首页\";\n};\n\n/**\n * 图片预览\n * 访问： http://localhost:3000/test/ext.jpg\n * @param {*} hd \n * @returns \n */\nmodule.exports.jpg = (hd) =\u003e {\n  // 浏览器页面缓存时间100秒\n  // 不设置则不缓存\n  // 设置 hd.age() 则页面缓存默认是 staticMaxage 配置值\n  hd.age(100);\n\n  // Content-Type 设置\n  // 设置优先级如下\n  // hd.ctx.type \u003e KoaWeb 中的 extTypes \u003e require(\"mime-types\").types['当前扩展: jpg']\n  // hd.ctx.type = 'image/png';\n  \n  // 文件读取模式\n  // hd.read(): /项目根路径/html/test/ext.jpg\n  // hd.read('txt'): /项目根路径/html/test/ext.txt\n  // hd.read('/home/user/test.txt'): 绝对路径：/home/user/test.txt\n  return hd.read();\n};\n\n/**\n * 获取其他网页图片并展示\n * 访问： http://localhost:3000/test/ext.png\n * @param {*} hd \n * @returns \n */\nmodule.exports.png = async (hd) =\u003e {\n  let info = await hd.http({\n    url: \"https://www.baidu.com/img/flexible/logo/pc/result.png\",\n    buffer: true\n  });\n\n  // 获取图片成功\n  if (info.code === 200) {\n    return Buffer.from(info.data);\n  }\n\n  // 获取图片失败\n  // 修改掉默认的 image/png\n  hd.ctx.type = \"text/plain\";\n  \n  return \"无效图片!\";\n};\n\n/**\n * css文件预览\n * 头文件会自动设置：Content-Type: text/css; charset=utf-8\n * 页面不会缓存\n * 访问： http://localhost:3000/test/ext.css\n */\nmodule.exports.css = `body{ color:#F33; }`;\n\n/**\n * js文件预览\n * 访问： http://localhost:3000/test/ext.js\n * 页面会缓存\n * @param {*} hd \n * @returns \n */\nmodule.exports.js = hd =\u003e {\n  // 这里hd.age就发挥了很大的作用，设置页面缓存将减少请求获取更好的性能\n  hd.age(1000);\n\n  // 读取文件 /html/test/ext.x.js 文件\n  // return hd.read('x.js');\n  \n  // 注意：一般不使用 hd.read() 读取本文件 /html/test/ext.js\n\n  // 渲染 /html/test/ext.x.js 文件，默认使用nunjucks模板引擎\n  // read是读取二进制文件，render是强制转换成字符串\n  // return hd.render({ test: \"Hello Test!\" }, 'x.js');\n\n  return `console.log('打印测试');`;\n};\n```\n\n- http://localhost:3000/test/ext\n- http://localhost:3000/test/ext.jpg\n- http://localhost:3000/test/ext.png\n- http://localhost:3000/test/ext.css\n- http://localhost:3000/test/ext.js\n\n---\n\n## 全局调用\n\n- 全局调用是指与 view 配置中的 domains 无关联调用\n- 全局调用使用 @ 符号为前缀进行设置\n\n```js\n// 域名绑定设置\n// 假设访问: http://www.test.com\nview: {\n  domains: {\n    \"www.test.com\": \"test\"\n  }\n}\n\n// layout普通设置\n// 布局路径指向为: /html/test/abc\njson: {\n  \"layout\": \"abc\"\n}\n\n// layout全局设置\n// 布局路径指向为: /html/abc\njson: {\n  \"layout\": \"@abc\",\n  // \"module\": \"@module\",\n  // \"view\": \"@view\"\n}\n\n// 全局错误页面\nerrors: {\n  404: \"@errors/404\",\n  500: \"@errors/500\"\n}\n\n// 全局函数调用\nhd.call(\"@test\")\n\n// 全局视图路径\nhd.viewPath(\"@test\")\n```\n\n---\n\n## 中间件和数据设置\n\n- 非常适用于开发插件或扩展\n- 默认不绑定任何中间件和数据设置\n- 自定义函数可放入 hd.app 中\n\n```js\nconst Koa = require(\"koa\");\nconst KoaWeb = require('koa-web');\n\nconst app = new Koa();\n\n/**\n * setMid: 中间件设置，可叠加，顺序执行\n * setData: 数据设置，可叠加，顺序执行\n * setLoad: koa中间件设置，仅加载一次\n * KoaWeb((setMid, setData, setLoad) =\u003e {})\n * 或者\n * KoaWeb(async (setMid, setData, setLoad) =\u003e {})\n */\nKoaWeb(async (setMid, setData, setLoad) =\u003e {\n  // 中间件设置\n  setMid(\"mid\", (hd, data, files) =\u003e {\n    hd.app.test = \"koa-web\";\n\n    // 如果未设置return或设置以下将继续执行下一个中间件\n    // 反之程序不再向下执行并显示返回值\n    // return;\n    // return null;\n    // return undefined;\n  });\n\n  setMid(\"mid\", (hd, data, files) =\u003e {\n    // 访问 http://localhost:3000/任意路径.mid\n    // 结果为： {\"show\":\"koa-web\"}\n    // 不会再向下执行\n    return {\n      show: hd.app.test\n    };\n  });\n\n  // 程序执行完成后的数据处理\n  // 与extCalls配置不同的是\n  // extCalls是在js文件方法调用完之后，所以extCalls是优先执行的\n  // 访问 http://localhost:3000/任意路径.src\n  setData(\"src\", (src, hd, data, files) =\u003e {\n    hd.ctx.type = \"application/json; charset=utf-8\";\n\n    // return后不影响下一个数据处理\n    // 并将src数传入到下一个数据处理\n    return src;\n  });\n\n  // 全局设置， 任何页面都会执行\n  // setMid(\"*\", () =\u003e {});\n  // setData(\"*\", () =\u003e {});\n\n  // 默认扩展页面设置： 空、htm、html扩展页面\n  // setMid(\"html\", () =\u003e {});\n  // setData(\"html\", () =\u003e {});\n\n  // 可再次对koa中间件进行设置\n  // koaApp: new Koa()\n  // config: koa-web配置\n  setLoad((koaApp, config) =\u003e {\n    // console.log('此处仅运行一次');\n    koaApp.use(async (ctx, next) =\u003e {\n      await next();\n    });\n  });\n});\n\napp.use(KoaWeb({path: __dirname}));\napp.listen(3000, () =\u003e {\n  console.log(\"server is running at http://localhost:3000\");\n});\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftphp%2Fkoa-web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftphp%2Fkoa-web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftphp%2Fkoa-web/lists"}