成功打印了相应的输出,但是我们浏览器显示的却是

让我们在终端用 node 运行这个文件,你可以在控制台看到你的这段代码已经被你的电脑成功启动了。

可以看到,随着我用浏览器去访问这个在窗口 7777 提供的服务,我们回调函数监听到请求后成功打印了相对应的输出。但是此时我们的浏览器好像呆呆的,没有展示任何信息。这是因为你这项服务现在还不够到位,你没有返回给浏览器任何信息。此时我们需要调去 response 身上的 end 函数。response.end()。这个函数第一个参数是你要告诉浏览器的数据,第二个参数也是一个回调函数,会在你返回给浏览器消息后被调用。那么我们就可以这样写:

(这里别忘了需要 ctrl c,然后重新执行这个文件)

你会看到虽然我们的服务成功打印了相应的输出,但是我们浏览器显示的却是乱码。

这是因为你没告诉浏览器应该用什么格式去渲染这段数据,你可能会有疑问,浏览器这么笨吗?默认为 utf-8 不就行了?如果你能联想到这里,不得不给你点个赞,但是假如这段数据是图片、视频呢,那不就乱套了吗?这里不卖关子,解决方法很简单,就是我们的服务在返回数据之前,告诉浏览器该如何展示我们的内容,怎么告诉?调用 response.writeHeader()设置相对应的 Content-type 即可。

现在的显示效果就符合我们的预期啦!

四. 读取 html 文件这里涉及到 node 的一些知识,不过不是本篇文章的重点,故不会做过多解释。这里有两个重点,第一个就是引入 fs文件系统模块,它提供了一个方法叫做 readFileSync,这个函数是同步读取指定路径下的文件,默认返回值为 buffer 类型。

当拿到这个 data 后,我们就可以返回给浏览器这个数据。此时你的浏览器应该已经正确渲染出这些内容了。

五. CSS 文件生效的原理让我们在跟文件夹下生成一个 global.css 的文件。

别忘了我们最初是如何引入 css 在 index.html 的。

这里有一个关键的知识点需要了解,我们打开 localhost:7777,其实是会向我们的服务发起三个请求的。其中,发送 index.html 是我们的主动行为,favicon.ico 这个请求是浏览器的默认行为,global.css 是由于我们的 index.html 携带了 标签,从而引起浏览器附带请求导致的。

让我们打印一下 request 的 url 参数信息,这里包含了浏览器请求资源的地址。

它对应了浏览器 request 字段的信息。

刷新一下浏览器,你会看到控制台有以下三个输出,和我们上面的推测是符合的。注意,这里的根路径 / 路径之后会被我们替换为 index.html。

聪明的你可能已经发现了,我们浏览器其实已经请求了 global.css 但是样式好像没有正确的生效。那是因为 .css 文件没有设置正确的 mime 格式。被浏览器当成普通的文件格式处理了。

这里我们就需要为 index.html 和 .css 分别设置不同的 content-type 来让 css 文件生效。此时你的 http_server 的代码应该如下。

  const http_server = http.createServer((request, response) => {
  let file_path = ""; //1. 这里存放文件的真实路径
  let data = ""; //2. 这里准备存放文件的 buffer 数据
  let ext = ""; //3. 这里准存放文件的后缀名称
  if (request.url === "/") { //4. 如果请求路径是跟路径,那么替换为 index.html
    file_path = "index.html";
  } else {
     file_path = request.url.replace("/", ""); //5. 否则的话,去掉路径前面的斜杠 ''
  }
  data = fs.readFileSync(file_path);
}  

这里最关键的后缀名如何获取呢?我们需要引入另一个模块 path。我们利用 path.extname 方法,将切割好的 file_path 传递为参数即可获取到正确的文件后缀名。

之后为每次请求设置正确的类型即可。具体文件类型 mime 和 content-type 的映射关系请参照:MDN提供的 MIME 对照表。

此时我们可以看到,样式已经正确生效。

六. 80 端口的含义想必大家都知道 http 服务是跑在 80 端口这一前端常识的吧?其实它没什么特别的,它只不过是把端口申请在服务器的80窗口上而已,然后我们配合浏览器的默认行为---当没有指定明确端口号时,帮你自动填写为 80 端口。

我们来试验一下。

注意,此时我没有像之前一样输入 7777,但是浏览器却依然正确找到了我http-服务的位置,和我们对浏览器默认行为的猜想一致。

所以不要再死记硬背 80 和 443 这两个数字了,它们只不过是你的后端搭档在代码程序里根据业务不同而写下的一个普通数字罢了。为什么要这样做?如果每个 http-server 开发者,大家都用不同的端口号。那么你就需要不仅仅需要把它们的域名记下来,还要记住相对应的端口号。就像上面一样,你不觉得每次手动输入 7777 很麻烦吗?那么干脆大家和浏览器商量好,就用 80 这个端口,浏览器默认帮你填写就好了。七. 源码

这里故意屏蔽了 favicon.ico 的请求,和文章整体内容关系不大。

const http = require("node:http"); //从 node 中引入 http 模块
const fs = require("node:fs"); //引入 fs 模块
const path = require("node:path");
// 这个函数接收一个回调函数
// 1.第一个函数接收的是前端传递过来的 request 参数
// 2.第二个函数是要返回给浏览器的信息
const http_server = http.createServer((request, response) => {
  let file_path = ""; //1. 这里存放文件的真实路径
  let data = ""; //2. 这里准备存放文件的 buffer 数据
  let ext = ""; //3. 这里准存放文件的后缀名称
  if (request.url === "/") {
    //4. 如果请求路径是跟路径,那么替换为 index.html
    file_path = "index.html";
  } else {
    file_path = request.url.replace("/", ""); //5. 否则的话,去掉路径前面的斜杠 ''
  }
  if (file_path !== "favicon.ico") {
    response.writeHeader = `Content-type:text/${ext}`;
    data = fs.readFileSync(file_path);
    ext = path.extname(file_path);
  }
  response.end(data);
});
// 告诉你的电脑,你想用 7777 这个端口
http_server.listen("7777", () => {
  console.log("我提供的服务在 7777 窗口");
});

八. 总结首先我们要对服务器有清晰的认知,任何一个设备都可以当作一个服务器,你的手机,你的笔记本,你的台式机,一个大型的存储计算机,都可以被叫做服务器。所谓的服务就是跑在服务器上的一段普通代码程序而已。当代码运行起来后,服务器需要为这个服务分配一个唯一端口号,其它应用可以访问这个端口来接受你提供的服务。有些服务并不是要公开为别人使用的,查看你的任务管理器或活动监视器。你的电脑开启了这么多服务,它们占用着不同的端口,而这些服务有的是只为操作系统提供的,并不对普通用户提供任何服务。

我们的前端代码,不管是 .vue 、.tsx、.ts 等等文件,最后都会被打包为原始的 html 和 css 和 js 文件,因为浏览器只认识这些内容。(不信你去看看有 content-type: vue 这种 mime 类型吗?)然后后端会部署一个 http-server 程序,让它跑在一个专属服务器上执行这段程序,通过我们上面讲解的内容来传递给 80 或者其他任何端口上,等待别人访问。你在实际开发中使用的 npm run dev 后,你的前端代码呈现在浏览器上,其底层的原理和上面无异,不过是开发工具帮你将上面步骤封装的功能更加完善和便捷而已。

本文中对于 server.js 对创建服务器的流程做了最大化的精简,来确保读者能够适应服务这个概念。在实际开发中,公司真正的后端服务开发绝不是这么简单。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享