前言
哈喽大家好!我是 嘟老板。Vite 我也用了有一段时间了,但是说实话并没有很深入的研究过它,为了加深个人对于 Vite 基本原理的理解,特地敲一个本地服务,模拟下 Vite 加载和处理资源的过程。
介绍
Vite 是一种新型的构建工具,能够显著提升前端开发体验。内部预置了一系列默认配置,可以让我们很轻松的用它来构建项目。
快速搭建
最快速的方式还是使用脚手架 create-vite 提供的命令行,比如创建 vue 项目,可使用以下命令:
pnpm create vite vite-vue-app --template vue
然后按照提示操作即可。
若没有全局安装过
create-vite,会提示先安装。
介绍下手动使用 Vite 搭建项目的过程。
初始化 npm 项目
终端输入以下命令,回车执行:
mkdir vite-test
cd vite-test
npm init -y
执行成功后将在当前目录下生成一个 package.json 文件。
安装 vite
终端输入以下命令,安装 vite:
pnpm add -D vite
创建 index.html
在项目根目录下创建 index.html 文件,作为项目的入口文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite Test</title>
</head>
<body>
<div>hello vite</div>
</body>
</html>
添加 npm script
在 package.json 的 scripts 配置中添加以下脚本:
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
启动项目
终端输入以下命令,启动项目:
pnpm dev
执行成功后,Vite 会在本地启动一个开发服务器,默认端口是 5173。
浏览器访问 http://localhost:5173/:
页面正常展示。
vite.config.js
完成上面的步骤,已经可以使用 Vite 启动服务并正常访问了,但是,我们还没有进行任何配置,若要项目支持支持更多自定义的特性,就需要配置文件 - vite.config.js 了。
在项目根目录下创建 vite.config.js 文件。
touch vite.config.js
现在我想把本地服务启动的端口改为 9000,在 vite.config.js 中添加以下代码:
export default {
server: {
port: 9000
}
}
保存后重新启动服务,
OK,已启动 9000 端口。
Vite 提供了许多可定义的配置,如共享配置(root、base、plugins....),服务器配置(server.*),构建配置(build.*) 等等...,感兴趣的同学可以自行查阅,可玩度相当高。
bundleless
bundleless 是啥玩意,不太了解的小伙伴,肯定感觉莫名其妙吧。莫急,我们往下看。
传统的构建工具,比如 webpack,会从项目入口文件开始,解析模块依赖,构建依赖图,然后打包成一个或多个文件,这个过程叫做 bundle。
相对 webpack 而言,Vite 则提倡少打包甚至不打包,将代码直接交给浏览器处理,这种方式则被称为 bundleless。
那为什么 Vite 可以实现 bundleless,而 webpack 却没有呢?
一句话总结,时势造英雄。可以说 Vite 是时代发展的产物。正是由于浏览器对 esm 的支持,才使得 Vite 能够实现 bundleless。
一个完整的前端项目,需要处理的文件类型很多,比如:
- js/jsx/ts/tsx
- css
- 图片 webp、png、svg...
- 字体文件
- 等等...
除了 js,其他类型的资源,浏览器并不能处理。
因此 Vite 也不是完全不打包,对于浏览器无法直接处理的资源,Vite 会对其进行构建处理。
这些工作基本都是由插件完成,在官网可以看到很多可用的插件,如 @vitejs/plugin-vue,用来支持 Vue3 单文件组件;@vitejs/plugin-vue-jsx 用来支持 Vue3 JSX;@vitejs/plugin-react 用来支持 React,等等等等...
开发环境和构建环境
目前 Vite 对于开发环境和构建环境的处理方式不同。主要区别在于开发环境使用 esbuild 对代码进行构建、打包,而构建环境使用 rollup 进行打包。
为什么这样区别处理呢?
- esbuild 构建速度够快,可以显著提升开发体验。
- esbuild 需要自行处理
dts文件。 - esbuild 不支持
es5以下版本转换。 - 等等...
目前 Vue 团队使用 Rust 开发了一个整合工具 - rolldown,后续将代替
esbuild和rollup,统一开发环境和构建环境的处理方式。
动手模拟 Vite 加载资源的过程
上面有提到,一个完整的前端项目,会包含多种多样的资源,比如 js、jsx、ts、tsx、css、图片以及字体(woff2)等等...很多很多,而这其中,浏览器仅能直接处理 js,那如何才能让其他文件被正常加载、执行呢?这就需要对其他类型的资源转换成浏览器可以处理的类型,比如 ts 转为 js,css 添加到 html 中...然而这一切在使用 Vite 时,对我们来说都是无感的,它会自动帮我们完成这些工作。
但是你对这中间的处理过程就不好奇吗,Vite 到底用了什么魔法,能帮我们做这么多事?莫急,在本节中,我们将搭建一个本地服务,模拟 Vite 对于资源文件的加载和处理。
目标
- 搭建一个本地服务,实现资源加载
- 使用 esbuild 处理 TypeScript 资源。
初始化
终端输入以下命令,创建基础项目结构:
mkdir min-Vite
cd min-Vite
npm init -y
mkdir src
cd src
touch index.html
touch index.js
执行成功后,目录结构如下:
在 index.html 和 index.js 中分别写入如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Min Vite</title>
</head>
<body>
<script src="./index.js" ></script>
</body>
</html>
console.log('index.js')
index.js 中直接打印日志,然后在 index.html 中引入 index.js。
创建 server
现在开始编写本地服务,我们使用 express 来搭建。
终端输入以下命令,回车安装 express:
pnpm add express -S
安装完成后,在根目录下创建 server.js,写入以下代码:
const express = require('express')
const app = express()
app.get('/', (req, res) => {
res.send('Hello Vite!')
})
const port = 3000
app.listen(port, () => {
console.log("🚀~~ 服务已启动,访问:", `http://localhost:${port}`)
})
基础的服务就写好了,接下来在 package.json 添加一条启动命令:
{
"scripts": {
"start": "nodemon ./server.js"
},
}
nodemon 可以监听文件变更,当我们修改了代码后,服务会自动重启。 执行
pnpm add nodemon -D安装。
终端输入 pnpm start 启动服务:
OK,启动完成。
浏览器打开 http://localhost:3000/:
加载 index.html
细心的小伙伴可能发现,server 启动的服务比较独立,并没有加载 src/index.html,我们来处理一下。
get 请求调整如下:
const fs = require('fs')
const path = require('path')
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, './src/index.html'))
})
表示当前请求路径为 / 时,响应 src/index.html。
刷新浏览器,看下效果:
咦!报错了,没有找到 index.js,这是因为 index.html 中引入了 index.js,<script src="./index.js" ></script>,但是 server 服务还没有处理 js 文件的加载。
我们继续完善...
加载 index.js
思考一下,应该如何加载 js 文件呢?
浏览器完全可以处理 js 文件,所以我们直接将 js 文件的内容返回即可。
说干就干,在 server.js 中新增一个加载 js 文件的路由。
app.get('/*.js', (req, res) => {
const file = fs.readFileSync(path.resolve(__dirname, `./src${req.path}`), 'utf-8')
res.type('js')
res.end(file)
})
通过 fs 模块的 readFileSync 同步读取 js 文件,并响应文件内容。
刷新浏览器,看下效果:
OK,index.js 正常加载。
看下控制台是否打印消息:
OK,正常打印,符合预期。
处理 TypeScript 资源
目前已经可以正常加载 js 资源了,但是我不想用 js 开发,我想用 ts,于是我把 index.js 改为 index.ts,并写入如下代码:
function add(a: number, b: number): number {
return a + b;
}
console.log("🚀 ~ 'index.ts':", add(1, 2));
然后在 index.html 中引入 index.ts。
<script src="./index.ts" ></script>
这样浏览器可以正常加载吗,我们刷新下看看:
哦!server 服务里还没有处理 ts 类型的请求,我们参考 js 类型请求加一个:
app.get('/*.ts', (req, res) => {
const file = fs.readFileSync(path.resolve(__dirname, `./src${req.path}`), 'utf-8')
res.type('js')
res.end(file)
})
现在可以请求 ts 资源了,应该没问题了吧,继续刷新浏览器验证:
不出意外的话,出意外了!!为什么呢?
因为浏览器无法识别 ts 语法,代码中定义的类型,浏览器无法执行,只能报错了,那怎么办呢?这就需要 esbuild 帮忙了,帮我们把 ts 代码编译成 js。
esbuild 入场
终端输入以下命令,回车安装:
pnpm add esbuild -S
ts 请求调整如下:
app.get('/*.ts', async (req, res) => {
const file = fs.readFileSync(path.resolve(__dirname, `./src${req.path}`), 'utf-8')
let transformedResult = await esbuild.transform(file, {
loader: 'ts',
format: 'esm',
target: 'es6'
})
res.type('js')
res.end(transformedResult.code)
})
读取文件后,借助 esbuild 的 transform api 将源代码转换为 esm 格式,并响应转换后的代码。
现在能否正常加载了呢?浏览器验证下:
OK,控制台正常打印,符合预期。
提示
实际上 Vite 源码中,基本上以插件的形式处理资源,比如使用 esbuildPlugin 处理需要 esbuild 编译的资源,如 ts、jsx、tsx 等;使用 cssPlugin 处理类 css 资源,如 css、scss、less 等;
结语
本文重点介绍了 Vite 的基本使用及资源加载方式,通过编写一个本地服务,模拟 Vite 加载和处理资源的过程,旨在加深对于 Vite 基本原理的理解,希望对您有所帮助!
如您对文章内容有任何疑问或想深入讨论,欢迎评论区留下您的问题和见解。
技术简而不凡,创新生生不息。我是 嘟老板,咱们下期再会。
往期干货
- 🔥Vue3响应式系统玩明白了吗?一文带你从0入门响应式
- 🚀前端懂算法,一个顶俩!超细解读常用的排序算法,Passion!!! (持续更新版~~~)
- 🚀面试离不开的首屏性能优化是什么,到底该怎么做
- 🔜想开发 vscode 插件却不知从何入手?超速入门,助力你弹射起步
- 💯What?维护新老项目频繁切换node版本太恼火?开发一个vscode插件自动切换版本,从此告别烦恼
- (⊙ˍ⊙)哦? ElementPlus 官网导航栏有点意思,来看看咋实现的
- 🧨🧨🧨你想要的 RBAC 权限管理实现全流程来啦!~~ 代码含量过多,请谨慎阅读 ~~
- 💡💡💡Vue3 用了这么久还没体验过 JSX/TSX?来封装个业务弹窗玩玩
- 👏👏👏厉害了 Vue Vine !Vue 组件还能这样写!!!
- 一文带你了解多数企业系统都在用的 RBAC 权限管理策略