vite 开发服务器搭建原理

我们来创建一个简单的 vite 开发服务器,来了解服务器的原理。

启动服务,开启监听

通过下面的命令创建一个项目:vite-dev-server

bash 复制代码
mkdir vite-dev-server
cd vite-dev-server
yarn init -y # -y 的作用是:初始化 yarn 时,全部使用默认配置

添加 koa,koa 是 Express 的下一代基于 Node.js 的 web 框架。

bash 复制代码
yarn add koa

创建 index.js,使用 koa 监听 5173 端口。

javascripty 复制代码
const Koa = require("koa");

const app = new Koa();

app.listen(5173, () => {
    console.log("vite dev server listen on 5173");
});

注意: index.js 是通过 node 执行的,所以导入模块时,必须使用 common js 规范,而不能使用 es modules 的规范。

使用 node 运行 index.js

bash 复制代码
node index.js

此时使用浏览器访问:http://localhost:5173,将会返回 Not Found。

响应 html 文件

对于任何请求,app 将调用 app.use注入的异步函数处理请求:

javascript 复制代码
const Koa = require("koa");

const app = new Koa();

// 当请求来临的时候,会执行 use 注入的回调中
app.use(async (ctx) => {
    console.log(ctx.request);
    console.log(ctx.response);
});

app.listen(5173, () => {
    console.log("vite dev server listen on 5173");
});

此时,我们就可以获取到请求和回复内容:

ruby 复制代码
// request
{          
  method: 'GET',
  url: '/',     
  header: {
    host: 'localhost:5173',
    connection: 'keep-alive',
    'cache-control': 'max-age=0',
    'sec-ch-ua': '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
    accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
    'sec-fetch-site': 'none',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-user': '?1',
    'sec-fetch-dest': 'document',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
    cookie: 'Webstorm-32b69a0d=a5b6a987-8d4c-4c91-bfc5-9d9f4e2f897f'
  }
}
// response
{
  status: 404,
  message: 'Not Found',
  header: [Object: null prototype] {},
  body: undefined
}

node 服务最频繁做的事情就是在处理请求和操作文件。在处理文件和路径时,node 会使用到 fspath模块。

注意: 在 node 环境下,不需要通过yarn add或者npm install安装fspath模块。这两个模块是 node 来提供的。不同的 js 宿主环境,会赋予 js 不同的能力,比如:

  • 在浏览器环境中,浏览器将提供的特殊能力注入到 window 下面的。然后,我们就可以使用这些特殊的能力,例如:通过 document.getElementById(id 名) 来获取指定 ID 的元素。
  • 在 node 的执行环境中,在遇到导入的模块是 node 提供的模块时,就会在 node 模块中查找。node 模块中没有的时候,才会从 node_modules 中查找。

在处理请求获取根路径的信息时,一般返回一个 html 页面作为响应。创建一个 index.html 文件,并设置下面的内容。

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Vite dev server</title>
  </head>
  <body>
    hello vite dev server
  </body>
</html>

在响应的回调函数中,通过 ctx.request.url来获取请求的路径,通过设置 ctx.response.body 来设置响应体,通过"Content-Type"来设置响应的格式,从而影响到接收方以怎样的方式来解析接收到的内容:

ini 复制代码
const Koa = require("koa");
const fs = require("fs");
const path = require("path");

const app = new Koa();

app.use(async (ctx) => {
    console.log("ctx:", ctx.request, ctx.response);

    if (ctx.request.url === "/") {
        const indexContent = await fs.promises.readFile(path.resolve(__dirname, "./index.html")); // 在服务端一般不这么写,而是使用文件流的方式
      	// 设置响应体发给请求资源的对象  
      	ctx.response.body = indexContent; 
        // 设置响应格式
        ctx.response.set("Content-Type", "text/html");
    }
});

app.listen(5173, () => {
    console.log("vite dev server listen on 5173");
});

此时,在浏览器访问:http://localhost:5173,将会接收到我们返回的 html 的内容,并正确的解析。

响应 vue 文件

假如浏览器请求了一个 vue 文件,vite 会对 vue 文件做 AST 语法分析,通过 createElement() 来构建原生的 dom 等一系列操作,最终生成原生的 javascripty 内容,然后返回给浏览器。简单的说,vite 服务器会对 vue 文件中的字符串内容做一个字符串替换,例如会对 <template> 标签进行字符串替换,生成原生的 javascript 内容。

所以在请求 vue 文件时,并不是直接把读取到的内容返回给浏览器,而是将生成的原生 JavaScript 作为返回内容。此时浏览器在遇到 .vue后缀的文件后,依然无法解析。这时,就可以通过设置 "Content-Type"来告诉浏览器,以 JavaScript 文件来解析。

这就是为什么浏览器会处理 .vue 格式的文件了,因为:

  1. 浏览器接收到的是处理之后的原始 JavaScript 内容,并不是将原始的 vue 文件中的内容直接返回给了浏览器;
  2. 服务器通过设置 "Content-Type" 告诉浏览器以 JavaScript 文件来解析文件内容。

例如,在 index.html 中,添加一个引用 ./main.js 文件:

xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>

    <title>Vite dev server</title>
  </head>
  <body>
    hello vite dev server
    <script type="module" src="./main.js"></script>
  </body>
</html>

main.js 内容如下

arduino 复制代码
import "./App.vue"

console.log("main 123");

App.vue 文件的内容如下:

arduino 复制代码
console.log("app vue 123");

index.js 中,添加处理获取 main.js 和 App.vue 的请求:

javascript 复制代码
const Koa = require("koa");
const fs = require("fs");
const path = require("path");

const app = new Koa();

app.use(async (ctx) => {

    if (ctx.request.url === "/") {
        const indexContent = await fs.promises.readFile(path.resolve(__dirname, "./index.html")); // 在服务端一般不这么写,而是使用文件流的方式
        ctx.response.body = indexContent; 
        ctx.response.set("Content-Type", "text/html");
    }

    if (ctx.request.url === "/main.js") {
        const mainContent = await  fs.promises.readFile(path.resolve(__dirname, "./main.js"));
        ctx.response.body = mainContent;
        ctx.response.set("Content-Type", "text/javascript");
    }
    if (ctx.request.url === "/App.vue") {
        const appContent = await fs.promises.readFile(path.resolve(__dirname, "./App.vue"));
        // 省略将 appContent 解析为原生 javascripty 的过程
        ctx.response.body = appContent;
        ctx.response.set("Content-Type", "text/javascript");
    }
});

app.listen(5173, () => {
    console.log("vite dev server listen on 5173");
});

此时,浏览器中就可以正确的运行 main.js 和 App.vue 中的内容了。

参考

相关推荐
吕彬-前端26 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱29 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai38 分钟前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端
齐 飞2 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb