从零开始搭建一个 Vue 3 项目
你是否曾经面对 Vue 代码,却不知如何开始构建一个完整的项目?是否想要学习如何从零创建一个可维护且可扩展的 Vue 3 项目?本教程将手把手教你构建一个 Vue 3 项目,无论你是刚接触 Vue 的新手,还是希望系统掌握 Vue 3 项目搭建的开发者,都可以从本文中获得实战经验。
为了让你深入理解每一步的搭建过程,本教程将不使用 Vite 的自动初始化功能,而是尽可能手动创建每个配置文件,帮助你真正掌握项目的底层结构。在学习本教程之前,你需要具备一定的 Vue 3 基础知识,并熟悉基本的 Linux 操作。当然,你还需要一个 Linux/MacOS 环境,如果你是 Windows 用户,可以使用 Windows Terminal、Git Bash 或 Node.js Command Prompt。
在正式开始之前,我们先列出一个完整的操作步骤:
- ✅ 初始化 Vue 3 项目
- ✅ 安装依赖包
- ✅ 组织项目的目录结构,创建配置文件
- ✅ 引入 Vue Router 进行路由管理
- ✅ 集成 Axios 进行 API 请求
- ✅ 使用 Pinia 进行状态管理
这些步骤不仅是我们需要完成的任务,也是本次学习的核心目标,帮助你逐步搭建并理解 Vue 3 项目的整个流程。现在,让我们正式开始吧!
安装 Node
如果你已经安装了 Node,可以跳过这一步。
Windows 安装 Node.js
下载安装包:
- 访问 Node.js 官网。
- 选择适合的版本(LTS 或 Current),点击下载。
运行安装程序:
- 双击下载的 .msi 文件。
- 按照提示完成安装,通常选择默认选项即可。
验证安装:
打开命令提示符(cmd),输入以下命令检查版本:
node -v
npm -v
显示版本号即表示安装成功。
Linux 安装 Node.js
使用包管理器(如 Ubuntu):
更新包列表:
sudo apt update
安装 Node.js:
sudo apt install nodejs
安装 npm:
sudo apt install npm
验证安装:
node -v
npm -v
MacOS 安装 Node.js
brew install node
然后检查版本:
node -v
npm -v
使用 Homebrew 安装的 Node.js 可能不太方便管理多个版本,因此更推荐使用 NVM。
使用 NVM(Node Version Manager)
NVM 是一个用于管理多个 Node.js 版本的工具。它允许你在同一台机器上轻松安装、切换和使用不同版本的 Node.js,非常适合开发者在不同项目中使用不同 Node.js 版本的场景。
安装 NVM:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
然后重新加载 shell 配置:
source ~/.bashrc
安装 Node.js:
nvm install node
或安装特定版本:
nvm install 16
验证安装:
node -v
npm -v
初始化 Vue 3 项目
安装完 Node 后,我们就可以初始化项目了。首先,我们需要创建一个空文件夹,作为项目目录,项目就叫 vue3-demo
吧。
cd ~/code/alvin-project #随便找个路径作为项目的上级目录
mkdir vue3-demo
cd vue3-demo
然后使用 npm init
初始化项目:
npm init
一路回车即可。完成后,可以看到项目目录下多了个 package.json
文件,package.json
是 Node.js 项目的核心配置文件,主要作用如下:
- 记录项目信息
{
"name": "vue3-demo",
"version": "1.0.0",
"description": "这是一个示例项目",
"author": "Alvin",
"license": "MIT"
}
name
:项目名称version
:版本号(语义化管理)description
:项目描述author
:作者信息license
:开源协议(如 MIT、Apache-2.0)
- 管理依赖
package.json
记录了项目所需的依赖,方便 npm install
直接安装。
{
"dependencies": {
"express": "^4.18.2",
"vue": "^3.3.0"
},
"devDependencies": {
"eslint": "^8.0.0",
"webpack": "^5.0.0"
}
}
dependencies
:生产环境依赖(如 express、vue)。devDependencies
:开发环境依赖(如 eslint、webpack)。
安装依赖时:
npm install express --save # 安装到 dependencies
npm install eslint --save-dev # 安装到 devDependencies
- 定义脚本命令
可以使用 scripts 来定义自定义命令:
{
"scripts": {
"start": "node server.js",
"dev": "webpack serve",
"build": "webpack --mode production"
}
}
执行时:
npm run start # npm run start 实际上相当于执行 node server.js
npm run dev
npm run build
- 版本管理
package.json
中的依赖版本通常使用语义化版本:
{
"dependencies": {
"lodash": "^4.17.21",
"vue": "~3.3.0"
}
}
^4.17.21
:兼容 4.x.x,但不会升级到 5.x.x~3.3.0
:兼容 3.3.x,但不会升级到 3.4.x4.17.21
:固定版本,不自动升级
安装依赖包
在开始之前我们先确定需要哪些依赖包:
- @vueuse/core:Vue 组合式 API 的实用工具库,简化开发。
- vue:渐进式 JavaScript 框架,构建用户界面。
- vue-router:Vue 的官方路由管理器,处理 SPA 的页面导航。
- pinia:Vue 的状态管理库,替代 Vuex。
- axios:基于 Promise 的 HTTP 客户端,用于发送网络请求。
- nprogress:页面加载进度条,提升用户体验。
- echarts:基于 JavaScript 的图表库,创建交互式数据可视化。
- dayjs:轻量级日期处理库,格式化和操作日期。
- d3:强大的数据可视化库,处理数据和绘制图形。
这些是我们生产环境中需要用到的依赖,所以放在 dependencies
中。
- @types/d3:为 d3 提供 TypeScript 类型声明,增强开发时的类型支持。
- @types/echarts:为 echarts 提供 TypeScript 类型声明。
- @types/node:为 Node.js 提供 TypeScript 类型声明。
- @types/nprogress:为 nprogress 提供 TypeScript 类型声明。
- @vitejs/plugin-vue:Vite 插件,用于支持 Vue 单文件组件(.vue 文件)的编译。
- @unhead/vue:用于动态管理和更新页面的 HEAD 标签内容。
- postcss:CSS 后处理工具,支持各种插件来优化和处理 CSS。
- postcss-html:PostCSS 插件,允许在 HTML 文件中处理嵌入的 CSS。
- postcss-scss:PostCSS 插件,支持 SCSS 语法的 CSS 处理。
- rollup:JavaScript 打包工具,支持模块化和优化。
- rollup-plugin-purgecss:Rollup 插件,用于删除未使用的 CSS。
- rollup-plugin-visualizer:Rollup 插件,用于可视化打包后的文件大小。
- sass:CSS 预处理器,扩展了 CSS 的功能,如变量、嵌套等。
- typescript:强类型的 JavaScript 超集,提供静态类型检查。
- unplugin-auto-import:Vite 插件,自动导入常用模块(如 vue、react 等)函数。
- unplugin-vue-components:Vite 插件,自动导入 Vue 组件,简化开发。
- unplugin-vue-router:Vite 插件,自动按需导入 Vue Router 相关模块。
- vite:现代前端构建工具,快速的开发环境和优化的生产构建。
这些是开发环境依赖,放在 devDependencies
中,确定了需要安装的依赖,我们将这些写入到 package.json
中,完整的文件内容如下:
{
"name": "vue3-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"author": "",
"license": "ISC",
"dependencies": {
"@vueuse/core": "^10.6.1",
"axios": "^1.6.1",
"d3": "^7.8.5",
"dayjs": "^1.11.10",
"echarts": "^5.5.0",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"vue": "3.3.8",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@types/d3": "^7.4.3",
"@types/echarts": "^4.9.21",
"@types/node": "^20.9.0",
"@types/nprogress": "^0.2.3",
"@vitejs/plugin-vue": "^4.5.0",
"@unhead/vue": "^1.8.3",
"postcss": "^8.4.31",
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.9",
"rollup": "^4.4.1",
"rollup-plugin-purgecss": "^5.0.0",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.69.5",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.16.7",
"unplugin-vue-components": "^0.25.2",
"unplugin-vue-router": "^0.7.0",
"vite": "^4.5.0"
}
}
然后我们运行 npm install
,运行后 npm 会自动安装这些依赖包。在安装过程中,国内用户可能因为网络问题而失败,可以尝试使用 cnpm 或者使用镜像。
- 使用 cnpm 安装
npm install -g cnpm
cnpm install
- 使用镜像安装
npm install --registry=https://registry.npm.taobao.org
我这里是使用 cnpm
安装,你也可以使用 pnpm
或者 yarn
,这里不在过多介绍它们的区别和用法。
安装完成后,可以看到项目目录下多出一个 node_modules
目录。这些目录下是我们安装的依赖包,接下来,我们组织项目的目录结构,并创建所需配置文件。
项目目录结构
我在这里直接给出一个常见的 Vue 前端项目结构,你可以根据自己的实际情况修改。
目录结构
.
├── .vscode # VSCode 配置,在多人开发时保持风格一致
├── src # 项目源码
│ ├── api # API 请求
│ ├── stores # 状态管理
│ ├── components # 全局组件
│ ├── pages # 页面组件
│ ├── utils # 公共函数
│ │ ├── request.ts # HTTP 请求
│ │ └── util.ts # 工具函数
│ ├── scss # 全局样式
│ ├── styles.ts # 样式入口文件
│ ├── router.ts # 路由配置
│ ├── shims-vue.d.ts # 组件类型定义
│ ├── app.ts # 应用程序入口
│ └── VueApp.vue # 应用程序入口文件
├── public # 静态资源
├── types # 类型定义
├── dist # 构建输出目录
├── index.html # 网页模板
├── .env.development # 开发环境配置
├── .env.production # 生产环境配置
├── .gitignore # git 忽略文件
├── tsconfig.json # TypeScript 配置
├── stylelint.config.js # Stylelint 配置
├── package.json # 项目配置
├── vite.config.ts # Vite 配置
└── README.md # 项目说明
有了这个目录结构树,我们就可以将对应的文件夹和文件创建出来,这里我们为了提高效率,使用命令行批量创建。
# 创建目录
mkdir -p .vscode src/api src/stores src/components src/pages src/utils src/scss public dist types
# 创建空文件
touch src/utils/request.ts src/utils/util.ts src/styles.ts src/router.ts src/shims-vue.d.ts src/app.ts src/VueApp.vue index.html .env.development .env.production .gitignore tsconfig.json stylelint.config.js vite.config.ts README.md
执行上面的两条命令,可以创建目录和文件,创建成功后,可以使用 ls
命令查看目录结构。 接下来,我们需要修改一些配置文件,这里主要是配置 Vite 和 TypeScript。
修改配置文件
- 配置
vite.config.ts
文件
使用 vi
命令,或编辑器,打开 vite.config.ts
文件,将下面代码替换或添加到文件中。
// 导入必要的依赖
import { defineConfig, loadEnv } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueRouter from 'unplugin-vue-router/vite'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import { VueRouterAutoImports } from 'unplugin-vue-router'
import { unheadVueComposablesImports } from '@unhead/vue'
import { getFileBasedRouteName } from 'unplugin-vue-router'
import purgecss from 'rollup-plugin-purgecss'
// 环境变量配置
const SILENT = Boolean(process.env.SILENT) ?? false // 是否静默模式
const SOURCE_MAP = Boolean(process.env.SOURCE_MAP) ?? false // 是否生成 source map
const CONFIG = loadEnv('development', './') // 加载环境变量
export default defineConfig({
// 项目根目录
root: process.cwd(),
// 部署基础路径
base: '/',
// 静态资源目录
publicDir: 'public',
// 日志级别
logLevel: SILENT ? 'error' : 'info',
// 依赖优化选项
optimizeDeps: {
// 预构建依赖项
include: [
'@vueuse/core',
'axios',
'dayjs',
'd3',
'echarts',
'nprogress',
'vue',
],
},
// 解析配置
resolve: {
// 路径别名配置
alias: [
{
find: '/@src/',
replacement: `/src/`,
},
],
},
// 构建配置
build: {
minify: true, // 是否压缩代码
sourcemap: SOURCE_MAP, // 是否生成 source map
reportCompressedSize: !SILENT, // 是否报告压缩大小
chunkSizeWarningLimit: Infinity, // 块大小警告限制
rollupOptions: {
external: [/\/demo\/.*/], // 外部化处理模块
},
target: 'modules', // 构建目标
},
// 插件配置
plugins: [
// Vue 插件配置
Vue({
include: [/\.vue$/],
}),
// Vue Router 插件配置
VueRouter({
routesFolder: 'src/pages', // 路由文件夹
dts: './types/router.d.ts', // 类型声明文件路径
dataFetching: true, // 启用数据获取
}),
// 自动导入插件配置
AutoImport({
dts: './types/imports.d.ts', // 类型声明文件路径
imports: [
// 自动导入的模块
'vue',
'@vueuse/core',
VueRouterAutoImports,
unheadVueComposablesImports,
],
}),
// 组件自动注册插件配置
Components({
dirs: ['src/components'], // 组件目录
extensions: ['vue'], // 组件文件扩展名
dts: true, // 生成类型声明
include: [/\.vue$/, /\.vue\?vue/], // 包含的文件
}),
// CSS 清除未使用的样式配置
purgecss({
output: false,
content: [`./src/**/*.vue`], // 扫描的文件
variables: false,
safelist: {
// 安全列表,不会被清除的选择器
standard: [
/(autv|lnil|lnir|fas?)/,
/-(leave|enter|appear)(|-(to|from|active))$/,
/^(?!(|.*?:)cursor-move).+-move$/,
/^router-link(|-exact)-active$/,
/data-v-.*/,
],
},
defaultExtractor(content) {
// 自定义提取器
const contentWithoutStyleBlocks = content.replace(
/<style[^]+?<\/style>/gi,
''
)
return (
contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) ||
[]
)
},
}),
],
// 开发服务器配置
server: {
port: 3080, // 服务端口
open: true, // 自动打开浏览器
https: false, // 是否启用 https
host: '0.0.0.0', // 监听地址
proxy: {
// 代理配置
'/webapi': {
target: `${CONFIG.VITE_API_SERVER_URL}/webapi`, // 代理目标地址
changeOrigin: true, // 修改请求头中的 host
secure: false, // 是否验证 SSL 证书
ws: true, // 支持 websocket
headers: {
Referer: CONFIG.VITE_API_SERVER_URL,
},
rewrite: (path) => path.replace(/^\/webapi/, ''), // 重写路径
},
},
},
})
- 配置
tsconfig.json
文件
{
// 编译器选项
"compilerOptions": {
// 目标 ECMAScript 版本
"target": "ESNext",
// 生成代码的模块化标准
"module": "ESNext",
// 允许从没有设置默认导出的模块中默认导入
"allowSyntheticDefaultImports": true,
// 启用所有严格的类型检查选项
"strict": true,
// 模块解析策略
"moduleResolution": "node",
// 启用装饰器
"experimentalDecorators": true,
// 启用元数据装饰器
"emitDecoratorMetadata": true,
// 允许导入 JSON 模块
"resolveJsonModule": true,
// 允许使用 jsx
"jsx": "preserve",
// 跳过库检查
"skipLibCheck": true,
// 基准目录
"baseUrl": ".",
// 路径别名配置
"paths": {
"/@src/*": ["./src/*"]
},
// 类型定义文件
"types": ["vite/client", "node", "vue"],
// 编译时引入的库
"lib": ["ESNext", "DOM", "DOM.Iterable"]
},
// 包含的文件
"include": [
"src/**/*.js",
"src/**/*.ts",
"src/**/*.vue",
"types/**/*.d.ts",
"vite.config.ts"
],
// 排除的文件
"exclude": ["node_modules", "dist"]
}
配置修改完成后,我们来创建和修改 Vue 应用程序的相关文件。
修改 Vue 应用程序的相关文件
- 首先修改
index.html
模板文件
index.html
是 Vue 应用程序的模板文件,其主要作用是:
- 定义 HTML 结构,包括 head 里的 meta 信息、title 和样式等静态资源。
- 提供 Vue 挂载点,即
<div id="app"></div>
,Vue 会将应用挂载到这个 DOM 里面。 - 引入 Vite 处理的 JS 文件,如
<script type="module" src="/src/app.ts"></script>
。
以下是一个基础的 index.html
模板文件的示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3 Demo</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/app.ts"></script>
</body>
</html>
- 修改
router.ts
文件
router.ts
是 Vue Router 的配置文件,主要用于定义应用的路由规则,并创建路由实例,供整个 Vue 应用使用。以下是一个常见的 router.ts
文件的示例:
// 导入所需的 Vue Router 相关函数
import {
createRouter as createClientRouter,
createWebHistory,
} from 'vue-router/auto'
// 创建并配置路由实例
export function createRouter() {
const router = createClientRouter({
// 使用 web history 模式
history: createWebHistory(),
})
return router
}
因为我们使用了基于文件路径的自动路由配置,所以这里需要从 vue-router/auto
导入模块,并使用它提供的函数来创建路由实例。
如果你想手动编写路由规则,可以像下方这样修改 router.ts
文件:
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'
const router = createRouter({
history: createMemoryHistory(),
// 所有的路由需要手动注册
routes: [{ path: '/', component: HomeView }],
})
- 修改
VueApp.vue
文件
VueApp.vue
是 Vue 应用的根组件,它是整个应用的入口,所有页面和组件都会在它的基础上进行渲染。以下是一个 VueApp.vue
的示例:
<script setup lang="ts"></script>
<template>
<div>
<!-- 使用 RouterView 渲染当前路由匹配的组件 -->
<RouterView v-slot="{ Component }">
<!-- 使用 Transition 组件为路由切换添加淡入淡出效果 -->
<Transition name="fade-slow" mode="out-in">
<component :is="Component" />
</Transition>
</RouterView>
</div>
</template>
这里使用了 Transition
组件来为路由切换添加淡入淡出效果。 因此,你需要在 scss 中定义 fade-slow
的动画效果。 让我们来创建样式文件。
首先看下样式文件的目录结构:
.
├── scss
│ ├── layout
│ │ └── _transitions.scss
│ └── main.scss
├── styles.ts
在前面的教程中我们已经创建了 src/scss
目录和 styles.ts
, 它们目前是空的,我们需要在 scss
目录下创建样式文件,并修改 styles.ts
。
根据路径创建文件 src/scss/layout/_transitions.scss
并添加以下内容:
.fade-slow-enter-active,
.fade-slow-leave-active {
transition: opacity 0.5s ease;
}
.fade-slow-enter-from,
.fade-slow-leave-to {
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.fade-slow-enter-active,
.fade-slow-leave-active {
transition: none;
}
}
创建 src/scss/main.scss
文件,并添加以下内容:
@use './layout/transitions';
然后在 src/styles.ts
文件中添加以下内容:
import './scss/main.scss'
接下来,我们需要修改 app.ts
文件来使用 VueApp
, 并引入 styles.ts
中定义的样式文件。
- 修改
app.ts
文件
在 Vue 3 项目中,app.ts
负责创建 Vue 应用实例,并初始化全局插件、状态管理、路由等。实例初始化完在后,使用 mount
方法将应用挂载到 DOM(<div id="app"></div>
) 中,以下是 app.ts
的示例:
import { createApp as createClientApp } from 'vue'
// 导入 head 管理相关依赖,用于管理页面元数据
import { createHead } from '@unhead/vue'
import { createPinia } from 'pinia'
import { createRouter } from './router'
import VueApp from './VueApp.vue'
import './styles'
// 创建应用实例的主函数
export async function createApp() {
// 创建 Vue 应用实例
const app = createClientApp(VueApp)
const router = createRouter()
const head = createHead()
app.use(head)
const pinia = createPinia()
app.use(pinia)
const myapp = {
app,
router,
head,
pinia,
}
app.provide('myapp', myapp)
app.use(myapp.router)
return myapp
}
// 初始化应用
createApp().then(async (myapp) => {
await myapp.router.isReady()
myapp.app.mount('#app')
})
到此,一个基础的 Vue 3 项目已经搭建完成!接下来我们创建两个简单页,来试试能不能跑起来。
创建测试页面
先在 pages
目录下创建 index.vue
文件,并加入以下内容:
<script lang="ts" setup></script>
<template>
<div>
<Menu />
<h1>欢迎来到 Vue3 Demo</h1>
<p>这是一个使用 Vue 3 + TypeScript + Vite 构建的示例项目</p>
</div>
</template>
<style lang="scss"></style>
然后在 pages
目录下创建另一个文件 profile.vue
,并添加以下内容:
<script lang="ts" setup></script>
<template>
<div>
<Menu />
<h1>Hello !My Name is Alvin.</h1>
</div>
</template>
<style lang="scss"></style>
最后在 components
目录下创建一个名为 Menu.vue
的文件,并添加以下内容:
<script lang="ts" setup></script>
<template>
<nav>
<RouterLink to="/">首页</RouterLink>
<RouterLink to="/hello">Hello</RouterLink>
</nav>
</template>
<style lang="scss">
nav {
display: flex;
align-items: center;
gap: 10px;
a {
color: #444;
text-decoration: none;
background-color: #eee;
padding: 4px 20px;
border-radius: 12px;
&.router-link-active {
color: #fff;
background-color: rgba(5, 214, 158, 1);
}
}
}
</style>
OK,我们已经创建了两个页面,和一个单文件组件,在配置 package.json
的时候,细心的你可能会发 scripts
中多出了两条命令:
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
这两个命令是我们可以直使用 npm run
来调用的命令。
npm run dev
启动调试服务npm run build
打包项目
在 vite.config.ts
中,我们已经配置好了开发服务器 server
:
{
server: {
port: 3080, // 服务端口
open: true, // 自动打开浏览器
https: false, // 是否启用 https
host: '0.0.0.0', // 监听地址
}
}
直接运行 npm run dev
启动开发服务器。如果你之前的步骤都正确,浏览会自动打开 http://localhost:3080/
页面,一个完全静态页面的 Vue 项目就运行起来了。
但前端开发往往离不开与后端数据的交互,接下来我们来配置 Axios 用于 API 请求。
配置 axios
在 Vue 项目中,Axios 是最常用的 HTTP 请求库,用于与后端 API 进行通信。相较于原生 fetch,Axios 提供了更强大的功能,比如:
- 自动处理 JSON 数据(fetch 需要手动 response.json())
- 请求和响应拦截器(方便全局处理 token、错误)
- 自动取消请求(避免内存泄漏)
- 请求超时(可设置 timeout,fetch 没有)
- 支持跨浏览器(fetch 在某些浏览器下不兼容)
在 src/utils
目录下创建 request.ts
文件,并添加以下内容:
import axios from 'axios'
export interface Response<T> {
code: number
message: string
data: T
}
// 创建 Axios 实例
const api = axios.create({
baseURL: '/webapi', // 后端 API 地址
timeout: 10000, // 超时时间 10s
headers: { 'Content-Type': 'application/json' },
})
// 请求拦截器(在请求发送前做处理)
api.interceptors.request.use(
(config) => {
// 可以在这里添加 token
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
// 响应拦截器(在返回数据前做处理)
api.interceptors.response.use(
(response) => response,
(error) => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
export default api
这个文件的作用是对 Axios 进行简单封装,最后导出 api
对象,供其他组件使用。
启动 node 服务器
为了方便测试,我们用 nodejs 启动一个简单的 server,实现一个简单的 API 接口,假设要实现一个个人信息接口, 接口文档如下:
GET /webapi/profile
Response:
{
"code": 0,
"message": "success",
"data": {
"id": 1,
"name": "alvin",
"email": "alvinhtml@gmail.com"
}
}
在项目目录中创建一个 server.js
文件,并添加以下内容:
const http = require('http')
// 模拟用户数据
const mockUserProfile = {
id: 1,
name: 'alvin',
email: 'alvinhtml@gmail.com',
}
const server = http.createServer((req, res) => {
// 只处理 /webapi/profile 路径的 GET 请求
if (req.method === 'GET' && req.url === '/webapi/profile') {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(
JSON.stringify({
code: 0,
message: 'success',
data: mockUserProfile,
})
)
} else {
res.writeHead(404)
res.end('Not Found')
}
})
const PORT = 3081
server.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`)
})
在命令行中执行 node server.js
启动 node 服务器,访问 http://localhost:3081/webapi/profile
就可以看到返回的数据了。
然后我们添加一个用于请求用户数据的 api 文件,在 src/api
目录下创建 user.ts
文件,并添加以下内容:
import service from '/@src/utils/request'
export async function apiGetProfile(): Promise<any> {
const res = await service({
url: `/profile`,
method: 'get',
})
return res.data
}
这个文件导出一个 apiGetProfile
函数,用于获取用户数据,稍后我们在 Store 中用到,在此之前,我们先来配置 Pinia。
配置 Pinia
在 Vue 组件中,通常可以用 ref()
和 reactive()
来管理局部状态,但当多个组件需要共享状态时,就需要全局状态管理,这时 Pinia 就派上用场了。
Pinia 是 Vue 3 官方推荐的状态管理库,它是 Vuex 的现代替代品,更简单、类型安全且易于使用。相比 Vuex,Pinia 具有以下优势:
- API 更简单:比 Vuex 更简洁,学习成本低。
- TypeScript 友好:原生支持,类型推断完善。
- 模块化设计:每个 Store 独立,按需使用。
- 轻量高效:体积小,性能更优。
- Devtools 支持:集成 Vue Devtools,方便调试。
- 组合式 API 支持:与 Vue 3 组合式 API 完美结合。
- 灵活易用:支持动态创建 Store,随处可用。
在前面的教程中我们已经在 app.ts
中注册了 Pinia:
const app = createClientApp(VueApp)
const pinia = createPinia()
app.use(pinia)
接下来,我们需要在 stores
目录下创建一个文件,用于管理应用的状态,文件名为 user.ts
,内容如下:
import { defineStore, acceptHMRUpdate } from 'pinia'
import { ref } from 'vue'
import type { Response } from '/@src/utils/request'
import { apiGetProfile } from '../api/user'
export interface Profile {
id: string
name: string
email: string
}
// 定义了一个名为 user 的 Pinia store 并导出
export const useUserStore = defineStore('user', () => {
const profile = ref<Profile>()
async function getProfile(): Promise<Response<Profile>> {
const res = await apiGetProfile()
profile.value = res.data
return res
}
// 返回一个对象,包含了方法 getProfile 和 数据 profile
return {
getProfile,
profile,
} as const
})
// import.meta.hot.accept 是 Vite 提供的一个接口,用于注册 HMR 更新,确保模块能够在代码更新时不重新加载整个页面,只更新模块本身
if (import.meta.hot) {
// acceptHMRUpdate 是 Pinia 提供的一个帮助函数,它用于注册 Pinia store 的热更新
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
}
创建好 stores/user.ts
后,我们需要在 profile.vue
中引入 userStore
并使用。
修改后的 profile.vue
文件如下:
<script lang="ts" setup>
import { useUserStore } from '/@src/stores/user'
import type { Profile } from '/@src/stores/user'
// 获取 `userStore` 实例
const userStore = useUserStore()
// 获取 `profile` 数据,并使用 `computed` 函数将其转换为响应式数据
const profile = computed<Profile | undefined>(() => userStore.profile)
onBeforeMount(() => {
// 在组件加载前调用 `getProfile` 方法
userStore.getProfile()
})
</script>
<template>
<div>
<Menu />
<h1>Hello !My Name is {{ profile?.name }}.</h1>
<pre>{{ profile }}</pre>
</div>
</template>
<style lang="scss"></style>
打开 http://localhost:3080/profile
可以看到数据已经是从后端返回的了。
总结
恭喜你!到这里,我们已经完成了一个完整的 Vue 3 项目的搭建。让我们回顾一下我们完成的主要内容:
- 从零开始搭建了一个 Vue 3 项目的基础架构
- 配置了现代化的开发工具链,包括 TypeScript、Vite、SCSS 等
- 实现了路由管理、状态管理、样式管理等核心功能
- 建立了一个可扩展、易维护的项目结构
这个项目模板不仅适用于小型应用,同样也能支撑起大型应用的开发。通过这个教程,你应该已经掌握了:
- Vue 3 项目的基本架构和配置
- 现代前端工具链的使用方法
- 项目结构的最佳实践
- 各种常用库的集成方式
如果你在开发过程中遇到问题,可以参考:
下一步
如果你想进一步提升项目的完整性,可以考虑:
- 添加单元测试配置(Jest/Vitest)
- 配置 CI/CD 流程
- 添加代码规范检查(ESLint/Prettier)
- 引入组件库(Element Plus/Ant Design Vue)
- 实现国际化 (i18n)
记住,好的项目架构是在实践中不断改进的。希望这个教程能够帮助你更好地理解 Vue 3 项目的搭建过程,为你的 Vue 开发之路打下坚实的基础!
最后,祝小伙伴们编码愉快!
Keywords
Vue
Vue3
项目搭建
Node 安装
package.json 配置
Vite 配置