技术面‍:前端代码是如何与服务器交互的

news2024/11/18 11:36:39

前言: 本篇文章主要是想讲解 .html 文件和 .CSS 文件在实际开发中和后端服务器交互最后上线的基础原理。

面向的人群🆕:是刚入行不久,且目前只会写前端业务代码而不清楚整个工作流的前端新人。我会从 0 开始一步一步带你理解整个流程的底层逻辑是什么,希望你能跟着我一起做完今天的所有步骤。


一. 前期准备

  1. 为了能让更多的人明白这其中的原理,今天我们回归前端最原始的本质,抛开 VueReact 这些前端框架,只用最原始的 .js.css 文件开始今天的讲解。

  2. 你的电脑需要安装 node,因为会用到一些文件读写的操作。

  3. 创建一个文件夹,然后创建出下面两个文件,一个 index.html 文件和 server.js 文件。
    image.png

  4. index.html 文件如下,你也可以自己写喜欢的内容,但是如果懒得写,请 copy 我的代码

        <!DOCTYPE html>
        <html lang="en">
          <head>
        <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <h1>JavaScript is the best language in the world!</h1>
      </body>
    </html>
    
    
  5. 我相信每个前端开发的读者都,或多或少安装过下面这个插件 Live Server
    image.png
    实现的效果是将你编写的 .html文件直接在浏览器打开,可以帮我们极大提升开发体验,实现的效果如下图:
    1.gif

  6. 看起来很神奇对吧?其实 Live Server 实现的功能和我们今天要讲解的知识非常接近,让我们暂时先关闭它,然后准备实现一个自己的简易版 Live Server
    image.png

二. 什么是服务器

  1. 在此之前,我觉得很有必要先解释一下服务器这个概念,因为这个概念之前困扰我很久。

  2. 对于前端来讲,因为不像后端开发者一样需要日常跟服务器打交道,所以可能会觉得服务器是一个很高大上的东西,是一个我们前端开发人员触不可及的东西。包括我刚入行的时候,我也是这样想的,但随着工作阅历的不断增加,发现服务器是一个再普通不过的东西罢了。

  3. 我们从现实生活类比,服务器就好比一家超市。

    • 你去超市的目的是什么?
    • 答:购买日用品 ,那么此时你就是消费者,那么超市就是给你提供服务的地方对吧?在这种场景下,你充当的其实就是 客户端(client) 的角色,而超市就充当着服务器的角色。服务器并不一定只能提供单一的服务,就像超市提供了很多类型的柜台,有日用品,有肉类,有水果,这些不同的柜台充当着给你提供服务的角色,而超市是容纳这些服务运行的地方,这就是服务器服务之间的关系。

    2.jpeg

  4. 你还可以自己再类比所有日常生活中需要你花钱消费的地方。如饭店、宾馆、花店、网吧等等。它们都是为了满足你的需求,来为你提供这些需求的场所,它们都充当着 “服务器” 的角色。

  5. 让我们回到网络上面来,你完全可以把 “服务器” 和上面你类比现实世界所理解到的概念画等号。比如你今天想看视频,我们拿 B站举例, 那么B站 此时就是一个服务器,当你在地址栏输入 bilibili 后,你就相当于走进了 “视频分类” 柜台 ,它上面存放着各种各样的视频,B站 它现在提供了观看视频的一项服务给你。
    image.png

  6. 然后你看视频看累了,想看一看漫画,此时就变成了它提供 “漫画” 服务给你。
    image.png

  7. 还是有点抽象?让我们回到 Live Server。刚刚我们关闭了 Live Server,所以导致我们在浏览器里输入 localhost:5500 这个地址后,页面出现了访问错误。
    image.png

  8. 但是当我们打开 Live Server 的时候,我们发现页面又恢复了正常工作。
    3.gif

  9. 那么上面的步骤,我们就可以这样理解:

    • Live Server 运行的时候,我们的电脑给了我们一个可以使用浏览器访问本地 .html 文件的服务。这个服务的提供者就是 Live Server 这个程序。那么谁是服务器呢?没错,就是帮你运行 Live Server主机-----你的电脑

    • 当你关闭 Live Server 的时候,相当于你的电脑关闭了提供服务的程序,导致你失去了访问 .html 的能力。

  10. 通过上面的例子,不难发现,其实服务本质上是一段代码,而运行这段代码的容器被我们叫做了服务器

  11. 这里有一个十分重要的概念----端口号。也就是 Live Server 启动的 5500 这个数字。这个数字你可以暂时把它理解为,你的电脑(也就是服务器)给它找了一个唯一的服务窗口,这个窗口号是 5500。(想象一个政务大厅,里面不同的业务都开设在不同的窗口位置,即使此时没人访问,它也需要有工作人员坐在那里等待。)
    image.png

  12. 那么上面整个过程可以这样理解:

    • 😣 Live Server: “好烦,周一又要上班了。喂,服务器(你的电脑),5500 这个窗口还没人值班吧?没人的话我就先坐这了。”

    • 🧑‍🏫 你的电脑:“我看一下啊,哦,暂时没人用。那你坐这里吧,即使没人来,你也不能跑出去啊!”

  13. 聪明的你也许会想到,那如果 5500 窗口如果有人先占了怎么办?注意,这里服务器你的电脑)会自动安排你的程序到另外的端口号上提供服务(执行代码)。(如果你的程序中也支持这么做的话,不妨打开两个 vscode 自己尝试一下同时开启 live Server 看看会是什么效果。)

三. 编写 server.js 文件

  1. 既然我们已经知道了,服务其实就是一段代码。那么我们就可以根据需求,去编写出这样的代码。其实我们的需求很简单,就是想让我的浏览器可以正常渲染我的 .html 文件。

  2. 这里我们需要从 node 中引入 http 模块,这个模块封装了一些可以让我们快速编写 http 服务器的方法。
    image.png

  3. 注意:这里我说了编写 “http 服务” 这个概念,结合我们上面对服务器的理解。这句话的完整含义应该是:

    node 提供的 http 模块,让我们可以快速在电脑上,编写一段代码程序。当我们的电脑运行这段程序的时候,我们的电脑可以提供 http 这样一项服务,此时浏览器可以通过使用 http 协议来和这个 http 服务程序进行通信。

  4. 然后我们调用 http 模块提供的 createServer 方法,具体用法在注释中写的很清楚了,不过多赘述。
    image.png

  5. 现在的你已经创建出了一个服务实例,它虽然还没有任何功能,但你已经可以告诉你的电脑,它现在可以被当作一个服务程序启动了。那么此时你还需要告诉电脑你想在那个窗口(端口) 去提供服务,这里我随便写了一个 7777,你可以选择一个任意你喜欢的数字。(注意,有些端口号是操作系统独享的,你不能占用,最好使用 5000-65535 范围内的数字) 然后使用 http_server.listen() 方法去向电脑申请这个端口号
    image.png

  6. 让我们在 http_server 的回调函数中,打印一些数据,来看看我们的服务是正常启动了。
    image.png

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

  8. 可以看到,随着我用浏览器去访问这个在窗口 7777 提供的服务,我们回调函数监听到请求后成功打印了相对应的输出。

  9. 但是此时我们的浏览器好像呆呆的,没有展示任何信息。这是因为你这项服务现在还不够到位,你没有返回给浏览器任何信息。此时我们需要调去 response 身上的 end 函数。response.end()。这个函数第一个参数是你要告诉浏览器的数据,第二个参数也是一个回调函数,会在你返回给浏览器消息后被调用。那么我们就可以这样写:
    (这里别忘了需要 ctrl c,然后重新执行这个文件)
    image.png

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

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

四. 读取 html 文件

  1. 这里涉及到 node 的一些知识,不过不是本篇文章的重点,故不会做过多解释。

  2. 这里有两个重点,第一个就是引入 fs文件系统模块,它提供了一个方法叫做 readFileSync,这个函数是同步读取指定路径下的文件,默认返回值为 buffer 类型。
    image.png

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

五. CSS 文件生效的原理

  1. 让我们在跟文件夹下生成一个 global.css 的文件。
    image.png

  2. 别忘了我们最初是如何引入 cssindex.html 的。
    image.png

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

  4. 让我们打印一下 requesturl 参数信息,这里包含了浏览器请求资源的地址。
    image.png
    它对应了浏览器 request 字段的信息。
    image.png

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

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

  7. 这里我们就需要为 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);
    }  
    
  8. 这里最关键的后缀名如何获取呢?我们需要引入另一个模块 path。我们利用 path.extname 方法,将切割好的 file_path 传递为参数即可获取到正确的文件后缀名。
    image.png

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

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

六. 80 端口的含义

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

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

  3. 所以不要再死记硬背 80443 这两个数字了,它们只不过是你的后端搭档在代码程序里根据业务不同而写下的一个普通数字罢了。

  4. 为什么要这样做?如果每个 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 窗口");
});

八. 总结

  1. 首先我们要对服务器有清晰的认知,任何一个设备都可以当作一个服务器,你的手机,你的笔记本,你的台式机,一个大型的存储计算机,都可以被叫做服务器。

  2. 所谓的服务就是跑在服务器上的一段普通代码程序而已。当代码运行起来后,服务器需要为这个服务分配一个唯一端口号,其它应用可以访问这个端口来接受你提供的服务。

  3. 有些服务并不是要公开为别人使用的,查看你的任务管理器或活动监视器。你的电脑开启了这么多服务,它们占用着不同的端口,而这些服务有的是只为操作系统提供的,并不对普通用户提供任何服务。
    image.png

  4. 我们的前端代码,不管是 .vue.tsx.ts 等等文件,最后都会被打包为原始的 htmlcssjs 文件,因为浏览器只认识这些内容。(不信你去看看有 content-type: vue 这种 mime 类型吗?)然后后端会部署一个 http-server 程序,让它跑在一个专属服务器上执行这段程序,通过我们上面讲解的内容来传递给 80 或者其他任何端口上,等待别人访问。

  5. 你在实际开发中使用的 npm run dev 后,你的前端代码呈现在浏览器上,其底层的原理和上面无异,不过是开发工具帮你将上面步骤封装的功能更加完善和便捷而已。
    image.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1704381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内网安全--隧道技术-CS上线本地

免责声明:本文仅做技术交流与学习...请勿非法搞破坏... ---隧道技术:硬刚网络协议,(你不让我走我偏走!) 解决不出网协议上线的问题&#xff08;利用出网协议进行封装出网&#xff09; 代理协议&#xff1a; SOCKS4/5 代理软件&#xff1a; SocksCap Proxifier ProxyChains(…

Spring Boot 统一数据返回格式

在 Spring Boot 项目中&#xff0c;统一的数据格式返回是一种良好的实践&#xff0c;它提高了代码的可维护性和一致性&#xff0c;并改善了客户端与服务端之间的通信。本文将介绍如何在 Spring Boot 中实现统一的数据格式返回。 1 为什么需要统一数据返回格式 ⽅便前端程序员更…

Mybatis——入门

新建 idea 准备 数据库 create table user(id int unsigned primary key auto_increment comment ID,name varchar(100) comment 姓名,age tinyint unsigned comment 年龄,gender tinyint unsigned comment 性别, 1:男, 2:女,phone varchar(11) comment 手机号 ) comment 用…

文刻创作ai工具官网免费工具

文刻创作ai工具官网免费工具 Docshttps://iimenvrieak.feishu.cn/docx/O0UedptjbonN4UxyEy7cPlZknYc 文刻是一种可以帮助用户进行创作的AI工具。 它使用自然语言处理和机器学习技术&#xff0c;可以生成文章、故事、诗歌等文本内容。 用户可以通过输入一些关键词或指定一定的…

APM2.8用USB在线下载固件

1.把APM飞控用安卓手机的USB线插入电脑。 选择COM口&#xff0c;不要选择auto&#xff0c;如果你没有COM口说明你驱动安装有问题。 波特率115200。点击相应的图标就可以下载固件到飞控板。 请注意&#xff1a;烧录APM必须选择INSTALL FIRMWARE LEAGACY,第一个是用于刷pixhawk的…

每天写两道(一):无重复字符的最长子串、反转链表

3. 无重复字符的最长子串 3. 无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串的长度。 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。(1)滑动窗口 双…

QGraphicsView实现简易地图17『涟漪效果』

前文链接&#xff1a;QGraphicsView实现简易地图16『爆炸效果』 模仿水波荡漾时的涟漪效果&#xff0c;参考了echarts中的散点图 支持设置散点大小、颜色、涟漪线条宽度。 动态演示效果 静态展示图片 核心代码 #pragma once #include "../AbstractGeoItem.h" #incl…

uniCloud云存储uni-cdn七牛云扩展存储-开发uniapp项目节约开发成本

为什么要使用uniCloud的扩展存储&#xff0c;那就是省钱&#xff0c;而且DCloud也一直在推uni-cdn&#xff0c;我在项目中也使用七牛云的扩展存储&#xff0c;确实是省钱&#xff0c;如果你的项目使用到大量的图片后者音视频&#xff0c;这些的算计可以帮你省不少钱。下面就通过…

md是什么?如何打开md类型的文件?假如使用Typora打开,如何免费激活Typora?

md是什么&#xff1f;如何打开md类型的文件 前言一、md是什么简介常见打开md类型文件的方法使用文本编辑器使用专用Markdown编辑器使用在线Markdown编辑器在浏览器中安装插件打开 二、下载安装Typora三、免费激活Typora激活Typora关闭软件每次启动时的已激活弹窗去除软件左下角…

uniapp 安卓 Pc端真机浏览器调试

下载插件:真机模拟浏览器 1. 安装, 每次启用时使用usb 线连接电脑, 并且打开手机或者POS (调试设备)开发者模式, 比如我的是pos 机 则在系统设置中找到版本号,点击多次就会触发开发者模式 2.打开真机模拟软件,打开后会打开一个浏览器,如果想要模拟google的浏览器则 在浏览器地…

【Mybatis】映射文件获取新增记录的id

我们在讲JDBC的时候讲过在插入新数据值的时候需要获得到自动生成的那个主键id的值 ①获取PreparedStatement的对象的时候 PreparedStatement st conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS ); ②在执行SQL语句后 st.executeUpdate();ResultSet rs st.ge…

git的使用之筛选文件上传到远程服务器仓库

.gitignore 让本地文件有选择上传到远程服务器仓库 下面是linux内核的.gitignore # # NOTE! Dont add files that are generated in specific # subdirectories here. Add them in the ".gitignore" file # in that subdirectory instead. # # NOTE! Please use gi…

Antd Vue项目引入TailwindCss之后出现svg icon下移,布局中的问题解决方案

目录 1. 现象&#xff1a; 2. 原因分析&#xff1a; 3. 解决方案&#xff1a; 写法一&#xff1a;扩展Preflight 写法二&#xff1a; 4. 禁用 Preflight 1. 现象&#xff1a; Antd Vue项目引入TailwindCss之后出现svg icon下移&#xff0c;不能对齐显示的情况&#xff0…

怎么把3d模型旋转加复制---模大狮

在进行3D建模和设计过程中&#xff0c;经常需要对3D模型进行旋转和复制操作&#xff0c;这是设计过程中的常见需求。本文将介绍如何对3D模型进行旋转和复制&#xff0c;帮助读者更好地掌握这一重要的操作技巧。 一、旋转3D模型 在大多数3D建模软件中&#xff0c;旋转3D模型通常…

《java数据结构》--顺序表详解

一.顺序表的概念&#x1f649; &#x1f431;顺序表是一段物理地址连续的储存单元&#xff0c;一次储存数据元素的线性结构。一般情况下采用数组储存&#xff0c;和数组的增删查改类似。 但是顺序表和数组还是有区别的比如&#xff0c;数组按照是否可以扩容可以分为&#xff…

VTK 数据处理:几何操作

VTK 数据处理&#xff1a;几何操作 VTK 数据处理&#xff1a;几何操作实例 1&#xff1a;使用 vtkWarpTo 向指定点发生位移实例 2&#xff1a;使用 vtkWarpVector 按照指定向量发生位移实例 3&#xff1a;使用 vtkDeformPointSet 按照框架变形 VTK 数据处理&#xff1a;几何操作…

系统思考—战略沙盘推演咨询服务

今日与JSTO团队一起学习了《战略沙盘推演咨询服务》。通过沙盘体验&#xff0c;我深刻感受到组织与战略就像一张皮的正反两面。在转型过程中&#xff0c;即使战略非常明确&#xff0c;团队成员由于恐惧和顾虑&#xff0c;往往不愿意挑战新的业务&#xff0c;从而难以实现战略目…

PHP框架 Laravel

现在因为公司需求&#xff0c;需要新开一个Laravel框架的项目&#xff0c;毫无疑问&#xff0c;我又被借调过去了&#xff0c;最近老是被借调&#xff0c;有点阴郁&#xff0c;不过反观来看&#xff0c;这也是好事&#xff0c;又可以复习和巩固一下自己的知识点&#xff0c;接下…

Android 生成正式版密钥库 KeyStore

步骤1&#xff1a;打开生成正式版密钥库设置 点击 Build 菜单&#xff0c;选择 Generate Signed App Bundle or APK&#xff1a; 这是打开后的样子&#xff1a; 步骤2&#xff1a;选择 APK Android App Bundle 是用于上架 Google Play 商店的。 正常情况下选择 APK。 选择…

Rust 程序设计语言学习——常见集合:Vector String Map

Rust 中常见的集合包括 Vector&#xff08;列表&#xff09;、String&#xff08;字符串&#xff09;和 Map&#xff08;键值对&#xff09;。 Vec<T>&#xff0c;也被称为 vector。vector 允许我们在一个单独的数据结构中储存多于一个的值&#xff0c;它在内存中彼此相邻…