云计算百科
云计算领域专业知识百科平台

Django + Vue电商项目第004讲:前端骨架|Vite + Vue3 + TS从0到1

本讲是M1里程碑的第一块砖。跑完你会看到浏览器里出现「Hello ShopX」,热更新生效,目录结构清楚。这是后续前端学习的地基。
建议先 点赞 + 收藏 + 关注,配环境时随时回查。


一、为什么用Vite而不是Vue CLI

很多 Vue 教程还在教vue-cli init,那是2018年的工具。Vite是2026年的标准选择,原因有四个。

1. 启动速度

Vue CLI用webpack打包,第一次启动要30-60秒,依赖越多越慢。Vite用esbuild预构建依赖,冷启动稳定在1秒以内,无论项目多大。

2. HMR速度

改一行代码,浏览器毫秒级看到变化。Vue CLI的HMR改大文件要3-5秒,Vite是50ms量级——慢的HMR让人失去即改即看的节奏。

3. 官方推荐

Vue团队已正式公告Vue CLI进入维护模式,不再新增大功能。Vite是官方推荐的下一代工具。

4. 生态更广

Vite同时支持Vue3 / React / Svelte / Solid / Vanilla JS。一个项目里要混合技术栈也方便。

💡 唯一注意点:Vite对Node版本有要求。我们装的是Node22,完全满足。


二、创建工程

进入项目目录(沿用第 3 讲的特性分支):

cd ~/workspace/shopx
git switch develop
git pull
git switch -c feature/lecture-004-frontend-scaffold

在项目根目录用 pnpm 创建前端工程:

pnpm create vite frontend –template vue-ts

–template vue-ts是关键,指定Vue 3 + TypeScript模板。frontend是目录名,本专栏用monorepo布局,前端代码都放这里。

命令会问几个交互问题,按回车确认默认即可。几秒后,目录frontend/出现。

进入并安装依赖

cd frontend
pnpm install

pnpm install会按package.json装齐所有依赖。装完后目录里有node_modules/、pnpm-lock.yaml。

启动 dev server

pnpm dev

终端输出:

VITE v7.x.x ready in xxx ms

➜ Local: http://127.0.0.1:5173/
➜ press h + enter to show help

打开浏览器访问 http://127.0.0.1:5173/,看到 Vite + Vue 3的欢迎页。

✅ 你的前端工程跑起来了。这是M1里程碑的第一块砖。


三、目录约定

Vite 默认生成一个最小可运行结构,但本专栏会做扩展。完整目录约定如下:

frontend/
├── public/ # 静态资源(不进打包,原样输出)
├── src/
│ ├── api/ # 接口请求层
│ ├── assets/ # 资源(图片、字体、SVG)
│ ├── components/ # 公共组件
│ │ └── HelloShopX.vue # 第一个组件
│ ├── composables/ # 组合式函数(useXxx)
│ ├── layouts/ # 布局组件
│ ├── router/ # 路由
│ ├── stores/ # Pinia stores
│ ├── styles/ # 全局样式
│ │ ├── reset.css # 浏览器样式重置
│ │ └── variables.css # CSS 变量(颜色、间距)
│ ├── types/ # TS 类型定义
│ │ └── goods.ts # 商品类型
│ ├── utils/ # 工具函数
│ │ └── format.ts # 格式化函数
│ ├── views/ # 页面组件
│ │ └── HomeView.vue
│ ├── App.vue # 根组件
│ ├── main.ts # 入口文件
│ └── env.d.ts # Vite环境变量类型
├── eslint.config.js # ESLint配置(ESLint9+ flat config)
├── .prettierrc.json # Prettier配置
├── index.html # HTML 入口
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
├── vite.config.ts
└── README.md

几个目录的提前说明

  • api/讲Axios统一封装时建——目前空着
  • composables/讲组合式API时建
  • router/ 讲Vue Router时建
  • stores/ 讲Pinia时建
  • layouts/ 讲Element Plus布局时建

现在只建 components/ views/ assets/ 这三个,其他目录等讲到时再建。

命令一次性建好空目录

mkdir -p src/{api,assets,components,composables,layouts,router,stores,styles,types,utils,views}
touch src/{api,composables,layouts,router,stores,types,utils}/.gitkeep

.gitkeep是空目录占位文件——Git不跟踪空目录,但有了这个文件目录就跟着仓库走。


四、<script setup>语法糖

Vite创建的App.vue默认就是<script setup>写法。这是Vue 3推荐的写法,比Options API简洁50%以上。

一个最小示例

<script setup lang="ts">
import { ref, computed } from 'vue'

const count = ref(0)
const double = computed(() => count.value * 2)

function increment() {
count.value++
}
</script>

<template>
<div class="counter">
<p>count is {{ count }}</p>
<p>double is {{ double }}</p>
<button @click="increment">click</button>
</div>
</template>

<style scoped>
.counter {
padding: 20px;
text-align: center;
}
</style>

三件你需要知道的事

第一,lang="ts"启用TypeScript。没有它,<script>里写TS代码会报错。

第二,ref和reactive是响应式API。ref(0)返回一个响应式引用,模板里直接{{ count }}就能取到值(Vue自动解包),脚本里要count.value取值。后面会专门讲响应式原理。

第三,<style scoped>限定样式只作用于当前组件。.counter不会泄漏到其他组件的.counter上。这是模块化CSS的基础。

与Options API对比

老式Vue 2写法:

<script>
export default {
data() {
return { count: 0 }
},
computed: {
double() { return this.count * 2 }
},
methods: {
increment() { this.count++ }
}
}
</script>

新写法(同等功能):

<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
</script>

ref / computed移到顶部、变量直接定义、方法直接写函数——样板代码减少一半。


五、TypeScript配置要点(Vite三文件结构)

最新版的Vite的vue-ts将 TypeScript 配置拆分为三个文件,这是 TypeScript 项目引用(Project References) 功能的体现。

Vite 团队采用这种结构,主要是为了将前端代码(运行在浏览器中)和 Vite 配置文件(运行在 Node.js 中)的 TypeScript 配置分离,实现更清晰、更精确的类型检查。

三个文件的分工

文件作用负责范围
tsconfig.json 根配置,通过references引用另外两个文件 项目管理入口
tsconfig.app.json 你需要重点修改的文件 src/下所有应用代码
tsconfig.node.json 构建工具配置 vite.config.ts等 Node 环境文件

tsconfig.json默认内容

{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.app.json" }
]
}

tsconfig.app.json(核心修改文件)

{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,

"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

几个关键项解释

  • strict: true —— 启用所有严格类型检查。这是 TS 最大的价值所在,不要关
  • noUnusedLocals / noUnusedParameters —— 没用到的变量/参数直接报错,养成干净习惯
  • paths: { "@/*": ["./src/*"] } —— 路径别名,需要同时在 tsconfig.json 根文件和 tsconfig.app.json 中配置(见第六节)
  • moduleResolution: "Bundler" —— Vite 推荐的解析模式,比 Node 更适合打包工具
  • noEmit: true —— 只做类型检查,不输出编译产物,实际打包交给 Vite
  • isolatedModules: true —— 强制每个文件都能独立编译,Vite esbuild 需要

tsconfig.node.json(保持默认即可)

tsconfig.node.json 专门用于 vite.config.ts 的编译规则,模板默认生成的内容已经可用,一般不需要修改:

{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"types": ["node"]
},
"include": ["vite.config.ts"]
}

这个文件告诉TS:「编译vite.config.ts时,把types设为["node"]而不是["vite/client"]」。

env.d.ts声明Vite全局类型

/// <reference types="vite/client" />

interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
}

interface ImportMeta {
readonly env: ImportMetaEnv
}

VITE_ 前缀的环境变量才能被客户端代码访问(import.meta.env.VITE_API_BASE_URL)。


六、路径别名 @

写import Hello from '@/components/Hello.vue'比写import Hello from '../../components/Hello.vue' 优雅太多。配置分两步,两个文件必须同时改,缺一个TS会红。

步骤 1:Vite 配置

编辑vite.config.ts:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'

export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
server: {
port: 5173,
host: '127.0.0.1',
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
},
})

步骤 2:TS 配置

tsconfig.json中添加compilerOptions:

{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}

验证别名生效

<!– src/views/HomeView.vue –>
<script setup lang="ts">
import HelloShopX from '@/components/HelloShopX.vue'
</script>

<template>
<HelloShopX />
</template>

如果两个文件都改对了,HelloShopX 组件能正常引入,TS 不会报错。

几个常见疑问

  • 为什么三处都要配?
    Vite需要vite.config.ts的alias来做实际解析;tsconfig.json根文件让IDE识别路径;
    tsconfig.app.json让vue-tsc类型检查通过。缺任何一处都可能出现“编辑器不报错但构建失败”或反之的情况。
  • 为什么fileURLToPath(new URL('./src', import.meta.url))? 因为Vite是ESM写法,普通字符串路径在ESM下会报错。fileURLToPath 把 URL 转成绝对路径。
  • VSCode不识别? 重启TS服务:Ctrl+Shift+P → “TypeScript: Restart TS Server”。

七、devServer.proxy 代理

后面会让后端跑在 http://127.0.0.1:8000,前端在5173。浏览器有同源策略,前端直接fetch 8000端口会被CORS拦截。

通过Vite代理,前端代码写/api/xxx时,Vite在开发服务器里反向代理到后端,绕过浏览器同源检查。

server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
}

怎么用

前端代码:

const res = await fetch('/api/v1/health/')
const data = await res.json()

浏览器看到的是5173 → 5173,没跨域问题。Vite在内部把请求转发到8000,用户完全无感。

完整CORS原理

浏览器只在生产环境真正需要CORS头。后面会配django-cors-headers,也会讲清整个CORS流程(简单请求、预检请求、Cookie携带、自定义头)。


八、ESLint + Prettier 落地

Linter和Formatter是工程规范的左膀右臂。Linter抓代码错误,Formatter保代码风格。本讲不展开讲它们的规则,只把它们配好。

⚠️ 2026 年重要变化:ESLint9+已全面转向 flat config(eslint.config.js),旧的.eslintrc.cjs写法已不推荐。本讲按新写法配置。

安装

pnpm add -D eslint @eslint/js typescript-eslint eslint-plugin-vue \\
vue-eslint-parser prettier eslint-config-prettier

eslint.config.js

import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import pluginVue from 'eslint-plugin-vue'
import prettier from 'eslint-config-prettier'

export default [
js.configs.recommended,
…tseslint.configs.recommended,
…pluginVue.configs['flat/recommended'],
prettier,
{
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
},
},
]

.prettierrc.json

{
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2,
"arrowParens": "always"
}

package.json加lint脚本

{
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview",
"lint": "eslint . –fix",
"format": "prettier –write ."
}
}

vue-tsc -b说明:-b是build mode,它会按照tsconfig.json的references分别对tsconfig.app.json和tsconfig.node.json做类型检查。

VSCode 工作区配置

新建 .vscode/settings.json(共享配置,可以进git):

{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": ["javascript", "typescript", "vue"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}

装ESLint和Prettier – Code formatter两个VSCode扩展后,保存文件时自动格式化 + 自动修lint错。


九、「Hello ShopX」页面

把App.vue改成我们要的页面:

<script setup lang="ts">
const year = new Date().getFullYear()
</script>

<template>
<div class="hello">
<div class="brand">ShopX</div>
<h1>Hello ShopX</h1>
<p class="subtitle">从零到上线 · 全栈电商项目</p>
<p class="muted">© {{ year }} · 第 4 讲里程碑</p>
</div>
</template>

<style scoped>
.hello {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #0f1b2d 0%, #1a2a44 100%);
color: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;
}
.brand {
font-size: 18px;
letter-spacing: 6px;
color: #ffb020;
margin-bottom: 24px;
}
h1 {
font-size: 64px;
margin: 0 0 16px;
font-weight: 500;
}
.subtitle {
font-size: 18px;
color: #cbd5e1;
margin: 0 0 48px;
}
.muted {
color: #8a93a6;
font-size: 14px;
}
</style>

保存后浏览器无刷新自动更新(HMR验证)。

✅ 这一刻可以提交了。

按第3讲的提交规范,本讲至少两个commit:

cd ~/workspace/shopx

# 第一个 commit:前端工程初始化
git add frontend/package.json frontend/pnpm-lock.yaml frontend/index.html \\
frontend/vite.config.ts frontend/tsconfig.json frontend/tsconfig.node.json
git commit -m "feat(frontend): 第 004 讲 初始化 Vite + Vue3 + TS 工程"

# 第二个 commit:Hello ShopX 页面与配置
git add frontend/src frontend/eslint.config.js frontend/.prettierrc.json frontend/.vscode
git commit -m "feat(frontend): 第 004 讲 Hello ShopX 页面与 ESLint/Prettier 配置"

# 推送特性分支
git push -u origin feature/lecture-004-frontend-scaffold

Hello ShopX 页面


十、本讲作业

  • 跑通pnpm dev
  • 把App.vue改成你自己的「Hello XXX」版本,保存看HMR是否生效
  • 故意写一个TS错误:在 <script setup>里写const x: number = "abc",看pnpm build是否报红
  • 在src/components/HelloShopX.vue建一个组件,导出count数字prop,在App.vue引入并传42
  • 建好路径别名:写import './foo' → 改成import '@/foo'(任意写个文件测试)

  • 十一、常见报错速查表

    报错原因解决
    pnpm报command not found 没装pnpm npm install -g pnpm或corepack enable
    Vite启动后Cannot find module '@vue/xxx' 依赖没装 pnpm install
    @/components/Foo.vue TS飘红「Cannot find module」 tsconfig.paths没配 同步 tsconfig的paths 配置
    HMR不生效 编辑的不是Vite监听的文件 检查vite.config.server.watch配置;保存后看终端日志
    vue-tsc报类型错误但pnpm dev能跑 类型检查只在build阶段 写pnpm build一次强制全量检查
    ESLint报Could not find config file 还在用旧的.eslintrc 改用eslint.config.js flat config
    Prettier和ESLint打架 ESLint默认格式化规则与Prettier冲突 eslint.config.js里加prettier配置,放最后
    端口5173被占 上次pnpm dev没关 lsof -ti:5173 | xargs kill -9(macOS)

    十二、术语自查报告

    • ✅ Vite(不是「Vite.js」、不是 Webpack)
    • ✅ Vue3组合式API(不是「Vue3写法」、「新写法」)
    • ✅ <script setup>语法糖名称准确
    • ✅ TypeScript 5.x(不是泛指TS)
    • ✅ HMR(Hot Module Replacement)写全称
    • ✅ esbuild是Vite预构建工具(不是「Vite用webpack」)
    • ✅ pnpm与npm区别写清(节省磁盘、严格依赖、防幽灵依赖)
    • ✅ <style scoped>模块化CSS原理
    • ✅ lang="ts"启用TypeScript的位置
    • ✅ ESLint 9+ flat config(eslint.config.js),非旧版.eslintrc

    自查通过。无禁用词。


    最后:前端第一块砖,已经砌好

    如果你跟着跑出了Hello ShopX,评论区打卡一句:

    第004讲签到,Hello ShopX已跑通

    • 点赞:让更多正在学前端的看到
    • 收藏:配ESLint、路径别名时随时回查
    • 关注:追更不迷路

    我们第5讲见。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Django + Vue电商项目第004讲:前端骨架|Vite + Vue3 + TS从0到1
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!