当前位置: 首页 > news >正文

koa渲染html文件,Koa 中返回 html 文件引发的思考-一一网络

前言

在使用 Koa 框架搭建网站时,一般返回 index.html 当做入口文件,这需要设置特定的 response header.

由于 Koa 自身极简的设计思想,我相信没看过源码的同学,第一时间会写出如下代码:

const Koa = require("koa");

const fs = require("fs");

const app = new Koa();

app

.use(ctx => {

ctx.set("Content-Type", "text/html");

ctx.body = fs.readFileSync("index.html");

})

.listen(3000);

复制代码

这当然可行,还能加强你对 HTTP Headers 的理解,但真正熟悉 Koa 的人,不屑于用如此原始的方法。

别看 Koa 的源码短小精悍,它依旧为开发者提供了诸多便捷的功能,例如此文着重介绍的 ctx.type.

带你认识 ctx.type

先上代码,只需将上下文(ctx)的 type 设置为 “html” 即可:

app

.use(ctx => {

ctx.type = "html";

ctx.body = fs.readFileSync("index.html");

})

.listen(3000);

复制代码

用 http(没有安装这个命令的小伙伴,访问这个网址:httpie.io/ )在终端进行测试:

6965020771269902366.html

7c59c63e0e0d975f42db906f596522bc.png

其实它的实现非常简单,通过传入不同的 type,自动设置对应的 Content-Type response header.

module.exports = {

// ...

set type(type) {

type = getType(type);

if (type) {

this.set("Content-Type", type);

} else {

this.remove("Content-Type");

}

},

};

复制代码

那么 getType 函数是个啥?原来借助了第三方包的“神秘力量”。

const getType = require("cache-content-type");

复制代码

出现了关键词 “cache”,作为一名专业的前端工程师(切图仔),察觉到这里一定做了某些优化。

为了验证我的猜测,我查看了此文件在 GitHub 上的历史提交记录。

果不其然,在 18 年的 7 月,一个有关性能优化的 PR 被合入 master.

e0a5112572c06f4aefbf54cc6236472f.png

来看看具体改动了哪些内容:

cdf96f9a0cf0bd2a6277bbc04a865497.png

破案了!最先使用的是 mime-types 库。

这个库实现同样很简单,大致就是从一个 JSON 文件(mime-db/db.json)匹配文件后缀名,返回 Content-Type,如 html -> text/html 的映射关系,库还会根据情况来为你加上 charset=utf-8.

// db.json

{

"text/html": {

"source": "iana",

"compressible": true,

"extensions": ["html", "htm", "shtml"]

}

}

复制代码

这里推荐一个非常 nice 的在线运行 Node.js package 的网站:npm.runkit.com/mime-types.

你可以在浏览器上在线体验这些库、还包括查看源码,README.md 等功能。

Koa 的优化之路:LRU

让我们把视角转向现在,Koa 中 mime-types 库已被无情抛弃,取而代之的是 cache-content-type 库.

它的源码更简单,总共就 15 行:

"use strict";

const mimeTypes = require("mime-types");

const LRU = require("ylru");

const typeLRUCache = new LRU(100);

module.exports = type => {

let mimeType = typeLRUCache.get(type);

if (!mimeType) {

mimeType = mimeTypes.contentType(type);

typeLRUCache.set(type, mimeType);

}

return mimeType;

};

复制代码

核心思路是维护一个 Cache:

当 Cache 中没有该 type 对应的 mimeType 时,会去进行一次查找并将结果 mimeType 存储在 Cache 中

当第二次查找 type,发现 Cache 中已存在,就直接返回对应的 mimeType,不再进行查找

因为存储 MIME 信息的 JSON 文件非常大,每次查找都非常耗时,所以尽可能减少查找次数。

细心的小伙伴发现了,这个 typeLRUCache 为什么还要设置最大容量为 100 个?难道存储超过 100 个 type 后,之后查找 type 的结果就不能被缓存了吗?

No,因为代码中 typeLRUCache 跟我说的 Cache 不是同一个东西。

我说的 Cache 是一种单纯以空间换时间的方法,但也不能无脑去拓宽空间,假设容量为 1000000 或 1e10 个,这会急剧增加程序的运行内存,引起电脑卡顿、死机。

归功于 ylru 这个库,我们有一种折中的方案。

因为它实现了 Least recently used (LRU) 算法,这个算法做了什么呢?我举个栗子,便于大家理解。

一个小区里只有两个停车位 P1 和 P2,但有四个车主分别为 A、B、C、D.

第一天特斯拉车主 A 使用了 P1

第二天宝马车主 B 使用了 P2

第三天保时捷车主 C 来停车,发现没有停车位了,咋办呢?

小区物业出于人性化考虑,宝马车主 B 一天前刚用的 P2,而特斯拉车主 A 是两天前用的 P1,估计特斯拉车主 A 已经去世了,于是强制让 A 移出了停车位

保时捷车主 C 顺利将车停入 P1

第四天上午,宝马车主 B 离开了停车位

第四天下午,宝马车主 B 进入了停车位

第五天奔驰车主 D 来停车,发现停车位已满,咋办呢?

小区物业出于人性化考虑,宝马车主 B 一天前刚频繁使用过 P2,而保时捷车主 C 已经一天没动静了,就算你是豪华车也没用,强制让保时捷车主 C 移出了停车位

奔驰车主 D 顺利将车停入 P1

总结:停车位总量不变,但至少能让最近经常使用停车位车主的权益得到保障,停车位始终有你一份,而不是看先来后到,也不是看谁的车贵。

类比到 typeLRUCache 上,只能存储 100 个缓存结果,防止容量无限增大,但能保证最近被频繁查找 type 的结果始终被缓存,提示后续查找效率。

我在这写了一个简易版 LRU 的 demo:

class LRUCache{

constructor(capacity) {

this.cache = new Map();

this.capacity = capacity;

}

get(key) {

if (this.cache.has(key)) {

const temp = this.cache.get(key);

this.cache.delete(key);

this.cache.set(key, temp);

return temp;

}

return -1;

}

put(key, value) {

if (this.cache.has(key)) {

this.cache.delete(key);

} else if (this.cache.size >= this.capacity) {

this.cache.delete(this.cache.keys().next().value);

}

this.cache.set(key, value);

}

}

const cache = new LRUCache(2);

cache.put("A", 1);

cache.put("B", 2);

cache.get("A"); // 返回 1

cache.put("C", 3); // 该操作会使得 'B' 作废

cache.get("B"); // 返回 -1 (未找到)

cache.put("D", 4); // 该操作会使得 'A' 作废

cache.get("A"); // 返回 -1 (未找到)

cache.get("C"); // 返回 3

复制代码

结尾

这篇文章到这就 happy ending 了,从设置 Content-Type 出发,我们一连看了 4 个库的源码,这波血赚不亏。

如果对 Koa 源码感兴趣的同学,请猛击 Koa 源码剖析 & 实现,教你手写一个 “复刻版” Koa.

相关文章:

  • 第一章计算机系统基础知识答案解析,计算机基础知识第一章练习题及答案解析教案资料(20200719174546)(20页)-原创力文档...
  • 计算机中实时与在线的关系,为什么新版QQ不再区分手机在线和电脑在线
  • 计算机教师招聘基本功试题及答案,2015年教师招聘考试新面试答辩题(附答案)第六卷...
  • 计算机系班级未来展望,云程发轫,重装上阵——计算机工程系2020级班级工作阶段性总结汇报与计划展望交流会...
  • 不用计算机证明数学定理,计算机证明数学定理的方式论
  • 计算机游戏设计专业世界大学排名,看过来,世界五大顶尖游戏设计大学在这里...
  • 转专业申请加拿大计算机硕士,GPA低?教你如何成功申请加拿大硕士!
  • 安徽计算机技术学院蚌埠,安徽蚌埠技师学院2021年招生简章
  • 襄阳职业技术学院计算机技能高考,2021湖北技能高考襄阳职业技术学院怎么报名?...
  • 与计算机硬件本身密切相关的是什么语言,计算机硬件单选试题及答案
  • 中南大学和中山大学计算机专业哪个好,中山大学和中南大学哪个好?该如何选择?...
  • 计算机二级foxpro,计算机二级Visual FoxPro练习试题及答案
  • 输入计算机的信息一般分两类,输入计算机的信息一般分为两类,一类称为数据,另一类叫什么...
  • 上海交大电院计算机系,2017上海交大电院计算机自主招生经历
  • 苹果6plus性能测试软件,iPhone 6、iPhone6 Plus性能测试
  • [LeetCode] Wiggle Sort
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • Apache的基本使用
  • Facebook AccountKit 接入的坑点
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • PAT A1050
  • Python_网络编程
  • VUE es6技巧写法(持续更新中~~~)
  • 包装类对象
  • 闭包,sync使用细节
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 官方解决所有 npm 全局安装权限问题
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 原生js练习题---第五课
  • ionic异常记录
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • ​TypeScript都不会用,也敢说会前端?
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • #if和#ifdef区别
  • #vue3 实现前端下载excel文件模板功能
  • #在 README.md 中生成项目目录结构
  • (1)虚拟机的安装与使用,linux系统安装
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (ros//EnvironmentVariables)ros环境变量
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (强烈推荐)移动端音视频从零到上手(上)
  • (区间dp) (经典例题) 石子合并
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .Net 8.0 新的变化
  • .Net Core和.Net Standard直观理解
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • .net和jar包windows服务部署