Vite 零基础搭建前端项目
使用 pnpm
npm i -g pnpm
pnpm config set registry https://registry.npmmirror.com/
项目初始化
pnpm create vite
framework:react、variant:react-ts
// 进入项目目录
cd vite-project
// 安装依赖
pnpm install
// 启动项目
pnpm run dev
目录结构
项目目录结构如下:
.
├── index.html
├── package.json
├── pnpm-lock.yaml
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── favicon.svg
│ ├── index.css
│ ├── logo.svg
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.ts
在项目根目录中有一个index.html
文件,这个文件十分关键,因为 Vite 默认会把项目根目录下的index.html
作为入口文件。
也就是说,当你访问http://localhost:3000
的时候,Vite 的 Dev Server 会自动返回这个 HTML 文件的内容。我们来看看这个 HTML 究竟写了什么:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
这个 HTML 文件的内容非常简洁,在 body
标签中除了 id 为 root 的根节点之外,还包含了一个声明了type="module"
的 script
标签:
<script type="module" src="/src/main.tsx"></script>
由于现代浏览器原生支持了 ES 模块规范,因此原生的 ES 语法也可以直接放到浏览器中执行,只需要在 script 标签中声明 type="module"
即可。比如上面的 script 标签就声明了 type=“module”,同时 src 指向了/src/main.tsx
文件,此时相当于请求了http://localhost:3000/src/main.tsx
这个资源,Vite 的 Dev Server 此时会接受到这个请求,然后读取对应的文件内容,进行一定的中间处理,最后将处理的结果返回给浏览器。
我们可以来看看 main.tsx
的内容:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
你可能会诧异: 浏览器并不识别 tsx 语法,也无法直接 import css 文件,上面这段代码究竟是如何被浏览器正常执行的呢?
这就归功了 Vite Dev Server 所做的“中间处理”了,也就是说,在读取到 main.tsx
文件的内容之后,Vite 会对文件的内容进行编译,大家可以从 Chrome 的网络调试面板看到编译后的结果:
var _jsxFileName = "D:\\workspace\\notes\\vite\\vite-project\\src\\main.tsx";
import __vite__cjsImport0_react from "/node_modules/.vite/deps/react.js?v=d559f595";
const React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react;
import __vite__cjsImport1_reactDom_client from "/node_modules/.vite/deps/react-dom_client.js?v=d559f595";
const ReactDOM = __vite__cjsImport1_reactDom_client.__esModule ? __vite__cjsImport1_reactDom_client.default : __vite__cjsImport1_reactDom_client;
import App from "/src/App.tsx";
import "/src/index.css";
import __vite__cjsImport4_react_jsxDevRuntime from "/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=d559f595";
const _jsxDEV = __vite__cjsImport4_react_jsxDevRuntime["jsxDEV"];
ReactDOM.createRoot(document.getElementById("root")).render(/* @__PURE__ */
_jsxDEV(React.StrictMode, {
children: /* @__PURE__ */
_jsxDEV(App, {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 8,
columnNumber: 5
}, this)
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 3
}, this));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBOztBQUVBQSxTQUFTQyxXQUFXQyxTQUFTQyxlQUFlLE1BQXhCLENBQXBCLEVBQXNEQyxPQUNwRCx3QkFBQyxNQUFNLFlBQVA7QUFBQSxZQUNFLHdCQUFDLEtBQUQ7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQURGO0FBQUE7QUFBQTtBQUFBO0FBQUEsUUFERiIsIm5hbWVzIjpbIlJlYWN0RE9NIiwiY3JlYXRlUm9vdCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJyZW5kZXIiXSwic291cmNlcyI6WyJEOi93b3Jrc3BhY2Uvbm90ZXMvdml0ZS92aXRlLXByb2plY3Qvc3JjL21haW4udHN4Il0sImZpbGUiOiJEOi93b3Jrc3BhY2Uvbm90ZXMvdml0ZS92aXRlLXByb2plY3Qvc3JjL21haW4udHN4Iiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IFJlYWN0RE9NIGZyb20gJ3JlYWN0LWRvbS9jbGllbnQnXG5pbXBvcnQgQXBwIGZyb20gJy4vQXBwJ1xuaW1wb3J0ICcuL2luZGV4LmNzcydcblxuUmVhY3RET00uY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpISkucmVuZGVyKFxuICA8UmVhY3QuU3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvUmVhY3QuU3RyaWN0TW9kZT5cbilcbiJdfQ==
Vite 会将项目的源代码编译成浏览器可以识别的代码,与此同时,一个 import 语句即代表了一个 HTTP 请求,如下面两个 import 语句:
import "/src/index.css";
import App from "/src/App.tsx";
在 Vite 项目中,一个import 语句即代表一个 HTTP 请求
。上述两个语句则分别代表了两个不同的请求,Vite Dev Server 会读取本地文件,返回浏览器可以解析的代码。当浏览器解析到新的 import 语句,又会发出新的请求,以此类推,直到所有的资源都加载完成。
现在,你应该知道了 Vite 所倡导的no-bundle
理念的真正含义: 利用浏览器原生 ES 模块的支持,实现开发阶段的 Dev Server,进行模块的按需加载,而不是先整体打包再进行加载。相比 Webpack 这种必须打包再加载的传统构建模式,Vite 在开发阶段省略了繁琐且耗时的打包过程,这也是它为什么快的一个重要原因。
配置文件
在使用 Vite 的过程,我们需要对 Vite 做一些配置,以满足日常开发的需要。你可以通过两种方式来对 Vite 进行配置,一是通过命令行参数,如vite --port=8888
,二是通过配置文件,一般情况下,大多数的配置都通过配置文件的方式来声明。
Vite 当中支持多种配置文件类型,包括.js
、.ts
、.mjs
三种后缀的文件,实际项目中一般使用 vite.config.ts
作为配置文件,以脚手架项目中的配置为例,具体的配置代码如下:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
})
可以看到配置文件中默认在 plugins
数组中配置了官方的 react 插件,来提供 React 项目编译和热更新的功能。
接下来,我们可以基于这个文件完成更加丰富的配置。例如我们可以修改页面入口文件 index.html
的位置。
页面的入口文件index.html
并不在项目根目录下,而需要放到 src
目录下,如何在访问localhost:3000
的时候让 Vite 自动返回 src 目录下的index.html
呢?我们可以通过root
参数配置项目根目录的位置:
// vite.config.ts
import { defineConfig } from 'vite'
// 引入 path 包注意两点:
// 1. 为避免类型报错,你需要通过 `pnpm i @types/node -D` 安装类型
// 2. tsconfig.node.json 中设置 `allowSyntheticDefaultImports: true`,以允许下面的 default 导入方式
import path from 'path'
import react from '@vitejs/plugin-react'
export default defineConfig({
// 手动指定项目根目录位置
root: path.join(__dirname, 'src')
plugins: [react()]
})
当手动指定root
参数之后,Vite 会自动从这个路径下寻找index.html
文件,也就是说当我直接访问 localhost:3000
的时候,Vite 从src
目录下读取入口文件,这样就成功实现了刚才的需求。
生产环境构建
有人说Vite
因为其不打包的特性而不能上生产环境,其实这种观点是相当有误的。
在开发阶段 Vite 通过 Dev Server 实现了不打包的特性,而在生产环境中,Vite 依然会基于 Rollup 进行打包,并采取一系列的打包优化手段。从脚手架项目的package.json
中就可见一斑:
"scripts": {
// 开发阶段启动 Vite Dev Server
"dev": "vite",
// 生产环境打包
"build": "tsc && vite build",
// 生产环境打包完预览产物
"preview": "vite preview"
}
相信你已经注意到其中的build
命令了,没错,这个命令就是 Vite 专门用来进行生产环境打包的。但可能你会有点疑惑,为什么在vite build
命令执行之前要先执行tsc
呢?
tsc
作为 TypeScript 的官方编译命令,可以用来编译 TypeScript 代码并进行类型检查,而这里的作用主要是用来做类型检查,我们可以从项目的tsconfig.json
中注意到这样一个配置:
{
"compilerOptions": {
// 省略其他配置
// 1. noEmit 表示只做类型检查,而不会输出产物文件
// 2. 这行配置与 tsc --noEmit 命令等效
"noEmit": true,
},
}
虽然 Vite 提供了开箱即用的 TypeScript 以及 JSX 的编译能力,但实际上底层并没有实现 TypeScript 的类型校验系统,因此需要借助 tsc
来完成类型校验(在 Vue 项目中使用 vue-tsc
这个工具来完成),在打包前提早暴露出类型相关的问题,保证代码的健壮性。
你可以试着执行一下这个打包命令:
PS D:\workspace\notes\vite\vite-project> pnpm run build
> vite-project@0.0.0 build D:\workspace\notes\vite\vite-project
> tsc && vite build
vite v2.9.5 building for production...
✓ 34 modules transformed.
dist/assets/logo.ecc203fb.svg 2.61 KiB
dist/index.html 0.44 KiB
dist/assets/index.62f502b0.css 0.75 KiB / gzip: 0.48 KiB
此时 Vite 已经生成了最终的打包产物,我们可以通过 pnpm run preview
命令预览一下打包产物的执行效果。
PS D:\workspace\notes\vite\vite-project> pnpm run preview
> vite-project@0.0.0 preview D:\workspace\notes\vite\vite-project
> vite preview
> Local: http://localhost:4173/
> Network: use `--host` to expose
在浏览器中打开http://localhost:5000
地址,你将看到和开发阶段一样的页面内容,证明我们成功完成第一个 Vite 项目的生产环境构建。
总结
本节中我们一起搭建了基本的前端开发环境,安装常用的编辑器、浏览器、Node.js 环境及包管理器 pnpm,接着我和你使用 Vite 的初始化命令创建一个 React 项目并成功启动,让你真切地体验到 Vite 的快速和轻量。
项目启动之后我也与你分析了项目背后的启动流程,强调了 一个 import 语句代表一个 HTTP 请求
,而正是 Vite 的 Dev Server 来接收这些请求、进行文件转译以及返回浏览器可以运行的代码,从而让项目正常运行。
不仅如此,我还带你一起初步接触了 Vite 的配置文件,并尝试进行生产环境的打包,为下一节的学习作下了铺垫。