由于每次在一个新环境电脑中都要去重新搭建项目,到处要去找资料,找官方文档去安装。所以我打算把工作中需要安装的环境都一一记录下来,等我下次在新的环境中能够快速去搭建项目。
一、环境版本:
nvm:1.2.2 (用于node版本切换,主页中有对应的安装教程)
node: 24.12.0
二、快速创建 Vue3+Vite+TS 基础项目
2.1. 在终端中执行
cnpm create vite@latest


2.2 安装router
注意:终端指令要进入到对应项目的目录,不然容易把依赖下载在外部文件中

2.2.1 安装router
cnpm add vue-router@4
2.2.2 配置 Vue Router 4(TS 版,添加路由类型约束)
// src/router/index.ts
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
// 导入页面组件(TS支持@路径别名,官方模板已配置)
import Home from '@/views/Home.vue'
import NotFound from '@/views/NotFound.vue'
// 路由规则:用RouteRecordRaw约束类型,避免路由配置错误
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// 路由懒加载(TS自动识别类型,无需额外约束)
component: () => import('@/views/About.vue')
},
// 404兜底路由(TS版写法和JS一致,类型由RouteRecordRaw约束)
{
path: '/:pathMatch(.*)*',
component: NotFound
}
]
// 创建路由实例
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
2.2.3 创建 TS 版页面组件
Vue3 中组件使用的核心规范(适配 TS):
在src下新建views文件夹,创建 3 个基础页面(Vue3+TS 的<script setup lang="ts">语法糖):
src/views/Home.vue(首页)
// src/views/Home.vue(首页)
<template>
<div class="home-page">
<h1>Vue3+TS基础框架 – 首页</h1>
<p>已集成:Vite + Vue3 + TS + Vue Router + Pinia</p>
</div>
</template>
<script setup lang="ts">
// lang="ts"标记为TS语法,Vue3+TS的核心写法
defineOptions({ name: 'Home' }) // 给组件命名,方便调试和路由识别
</script>
<style scoped lang="scss">
.home-page {
padding: 20px;
h1 {
color: #42b983;
}
}
</style>
src/views/About.vue(关于页,演示 TS 版路由跳转)
<template>
<div class="about-page">
<h1>关于页面</h1>
<button @click="goHome">返回首页</button>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'About' })
import { useRouter } from 'vue-router'
const router = useRouter()
// TS版:给方法添加返回值类型(void表示无返回值,可选但推荐)
const goHome = (): void => {
router.push('/')
}
</script>
<style scoped lang="scss">
.about-page {
padding: 20px;
button {
margin-top: 10px;
padding: 6px 12px;
cursor: pointer;
}
}
</style>
src/views/NotFound.vue(404 页)
<template>
<div class="not-found">
<h1>404 – 页面不存在</h1>
<router-link to="/">返回首页</router-link>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'NotFound' })
</script>
<style scoped lang="scss">
.not-found {
padding: 20px;
h1 {
color: #f56c6c;
}
a {
color: #42b983;
text-decoration: none;
}
}
</style>
2.3 安装 pinia
2.3.1 下载
cnpm install pinia
2.3.2 配置 Pinia(TS 版,强类型状态管理,无需手动写类型声明)
在src下新建store文件夹,创建index.ts(创建并导出 Pinia 实例):
// src/store/index.ts
import { createPinia } from 'pinia'
// 创建Pinia实例,TS自动识别类型
const pinia = createPinia()
export default pinia
创建 TS 版 Pinia 模块(示例计数器):
在store下新建modules文件夹,创建counter.ts(按业务拆分模块,TS 自动推导状态、方法类型):
// src/store/modules/counter.ts
import { defineStore } from 'pinia'
// 定义并导出store,命名规则:useXxxStore(Pinia规范)
export const useCounterStore = defineStore('counter', {
// 状态:TS自动推导state的类型,无需手动接口约束
state: () => ({
count: 0, // 自动推导为number类型
title: 'Pinia TS 计数器' // 自动推导为string类型
}),
// 计算属性:getters的返回值类型会自动推导,也可手动指定
getters: {
// 手动指定返回值类型(可选,推荐复杂逻辑时添加)
doubleCount: (state): number => state.count * 2,
// 基于其他getters推导类型
doubleCountAddOne(): number {
return this.doubleCount + 1
}
},
// 方法:actions支持同步/异步,参数和返回值类型可手动指定
actions: {
// 同步方法:手动指定参数类型(可选)
increment(num: number = 1): void {
this.count += num
},
// 同步方法
decrement(num: number = 1): void {
this.count -= num
},
// 异步方法(示例:模拟接口请求),返回Promise类型
async asyncIncrement(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
this.count++
resolve(this.count)
}, 1000)
})
}
}
})
// 非必要:如果需要在外部解构store且保持响应式,可导出辅助函数(Pinia推荐)
export type CounterStore = ReturnType<typeof useCounterStore>
2.4 安装 Sass
Vite 无缝支持 Scss,无需额外配置
cnpm add sass -D
2.5 Ts语法校验
2.5.1 无需额外装 TS 依赖:官方模板已内置typescript、@vitejs/plugin-vue、vue-tsc(TS 语法检查)
cnpm add eslint eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
2.5.2 配置 ESLint(适配 Vue3+TS,统一代码规范)
npx eslint –init
解释:npx 不是包管理器,而是Node 自带的包执行工具—— 它的作用是临时下载并执行eslint包的–init命令,执行完后会删掉临时文件,不用手动全局装 eslint;而cnpm只是npm 的国内镜像包管理器,只有安装/卸载依赖的功能,没有临时执行包命令的能力,直接敲cnpm eslint –init会报错(cnpm 识别不了eslint这个命令)。
执行之后安装流程:

ESLint 默认不识别 Vite 的@路径别名(指向src),需要修改 ESLint 配置文件.eslintrc.cjs,添加settings和parserOptions配置,避免import @/xxx报红:
// .eslintrc.cjs
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'plugin:vue/vue3-essential',
'airbnb-base',
'plugin:@typescript-eslint/recommended' // TS必备规则
],
parser: 'vue-eslint-parser', // 解析Vue文件
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@typescript-eslint/parser', // 嵌套解析TS文件
project: './tsconfig.json', // 关联TS配置文件,让ESLint识别TS配置
tsconfigRootDir: __dirname // TS配置根目录
},
plugins: [
'vue',
'@typescript-eslint'
],
rules: {
// 自定义规则:关闭不必要的严格规则(新手友好)
'import/no-unresolved': 'off',
'import/extensions': 'off',
'no-unused-vars': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'vue/multi-word-component-names': 'warn' // 关闭组件名必须多单词的严格错误
},
// 关键:让ESLint识别@路径别名
settings: {
'import/resolver': {
alias: {
map: [['@', './src']],
extensions: ['.js', '.ts', '.vue']
}
}
}
};
2.6 全局注册插件(路由 + Pinia)+ 优化入口文件
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
// 导入路由实例(TS版)
import router from './router'
// 导入Pinia实例(TS版)
import pinia from './store'
// 全局样式(后续可写全局样式、引入重置样式)
import './styles/global.scss'
// 创建Vue应用实例,TS自动推导App类型
const app = createApp(App)
// 注册插件(顺序无要求,TS无类型报错)
app.use(pinia)
app.use(router)
// 挂载到DOM节点(index.html中的<div id="app"></div>)
app.mount('#app')
2.7 添加全局样式(Scss 版)
// src/styles/global.scss
// 全局样式重置(简单版,可替换为normalize.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-size: 14px;
line-height: 1.5;
font-family: Avenir, Helvetica, Arial, sans-serif;
}
// 全局Scss变量(后续组件可通过@import引入)
$vue-primary: #42b983; // Vue官方主色
$vue-danger: #f56c6c; // 错误色
$vue-gray: #eee; // 灰色
$font-size-base: 14px; // 基础字体大小
2.8 优化根组件 App.vue(Vue3+TS 版,演示路由 + Pinia 使用)
<!– src/App.vue –>
<template>
<div id="app">
<!– 全局导航栏:路由链接 –>
<nav class="nav-bar">
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
<!– 路由出口:所有路由组件渲染到这里 –>
<router-view />
<!– Pinia TS版使用示例:计数器 –>
<div class="counter-demo">
<h3>{{ counterStore.title }}</h3>
<p>当前计数:{{ counterStore.count }}</p>
<p>双倍计数:{{ counterStore.doubleCount }}</p>
<p>双倍计数+1:{{ counterStore.doubleCountAddOne }}</p>
<button @click="handleIncrement">+1</button>
<button @click="handleDecrement">-1</button>
<button @click="handleAsyncIncrement">异步+1(1s后)</button>
</div>
</div>
</template>
<script setup lang="ts">
// TS版:defineOptions给组件命名(Vue3.3+支持,TS无报错)
defineOptions({ name: 'App' })
// 导入Pinia的计数器store(TS自动推导类型)
import { useCounterStore } from '@/store/modules/counter'
// 实例化store(Pinia自动注入,TS无类型报错)
const counterStore = useCounterStore()
// TS版:给方法添加返回值类型(void表示无返回值,推荐)
// 同步+1
const handleIncrement = (): void => {
counterStore.increment(1)
}
// 同步-1
const handleDecrement = (): void => {
counterStore.decrement(1)
}
// 异步+1(处理Promise)
const handleAsyncIncrement = async (): Promise<void> => {
const newCount = await counterStore.asyncIncrement()
console.log('异步+1后计数:', newCount) // TS自动推导newCount为number类型
}
</script>
<style scoped lang="scss">
#app {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.nav-bar {
margin-bottom: 30px;
a {
margin-right: 20px;
color: $vue-primary;
text-decoration: none;
&.router-link-active {
font-weight: bold;
border-bottom: 2px solid $vue-primary;
}
}
}
.counter-demo {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid $vue-gray;
button {
margin: 0 5px;
padding: 6px 12px;
cursor: pointer;
&:hover {
background: $vue-primary;
color: #fff;
border: none;
border-radius: 4px;
}
}
}
</style>
2.9 配置@别名
Vite 配置(核心,让 Vite 编译识别 @)
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 导入node的path模块,用于解析路径(TS需装@types/node,下文会说)
import { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
// 配置路径别名
alias: {
// @ → 指向src目录(__dirname是当前文件所在目录,即项目根目录)
'@': resolve(__dirname, 'src')
}
}
})
2.10 测试 TS 版完整框架
npm run dev
2.11 生产环境打包 + 预览
2.11.1 执行打包
npm run build
!!!如果报错了
替换tsconfig.json
//tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* 👇 新增这2项:配置@路径别名,让TS识别 */
"baseUrl": ".", // 基础解析路径:项目根目录(必须加,否则paths失效)
"paths": {
"@/*": ["src/*"] // 匹配规则:@/xxx 等价于 src/xxx
},
/* 以下是原有配置,无需修改 */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
替换tsconfig.node.json
//tsconfig.node.json
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node", "vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": false,
"composite": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.js", "vite.config.d.ts"]
}
2.11.2 本地预览生产环境构建的应用
npm run preview
具体来说:
1. 它会启动一个本地静态文件服务器,提供对 dist 目录(生产构建输出目录)的访问
2. 这允许开发者在部署前检查生产构建的应用是否正常工作
3. 它与开发服务器( npm run dev )不同,因为它使用的是实际的生产构建文件,而不是开发模式下的热重载版本

三、安装组件库
3.1 Element Plus 组件库
安装组件库
npm install element-plus –save
步骤 1:在入口文件src/main.ts中全局注册
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
// 导入路由实例(TS版)
import router from './router'
// 导入Pinia实例(TS版)
import pinia from './store'
// 全局样式(后续可写全局样式、引入重置样式)
import './styles/global.scss'
// 1. 导入Element Plus核心库
import ElementPlus from 'element-plus'
// 2. 导入Element Plus全局样式(必须导入,否则无样式)
import 'element-plus/dist/index.css'
// 若需要使用Element Plus的内置图标,额外导入图标库(可选,下文会讲)
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 创建Vue应用实例,TS自动推导App类型
const app = createApp(App)
// 注册插件(顺序无要求,TS无类型报错)
app.use(pinia)
app.use(router)
// 3. 全局注册Element Plus
app.use(ElementPlus)
// // 可选:全局注册Element Plus所有图标(注册后直接用<el-icon><User /></el-icon>)
// for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
// app.component(key, component)
// }
// 挂载到DOM节点(index.html中的<div id="app"></div>)
app.mount('#app')
步骤 2:直接使用 Element Plus 组件(无需额外导入)
<!– src/App.vue –>
<template>
<div id="app">
<!– 全局导航栏:路由链接 –>
<nav class="nav-bar">
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
<!– 路由出口:所有路由组件渲染到这里 –>
<router-view />
<!– Pinia TS版使用示例:计数器 –>
<div class="counter-demo">
<h3>{{ counterStore.title }}</h3>
<p>当前计数:{{ counterStore.count }}</p>
<p>双倍计数:{{ counterStore.doubleCount }}</p>
<p>双倍计数+1:{{ counterStore.doubleCountAddOne }}</p>
<button @click="handleIncrement">+1</button>
<button @click="handleDecrement">-1</button>
<button @click="handleAsyncIncrement">异步+1(1s后)</button>
</div>
<div class="test-elementPlus">
<!– 直接使用Element Plus按钮组件 –>
<el-button type="primary">点击计数:</el-button>
<!– 直接使用Element Plus卡片组件 –>
<el-card shadow="hover" style="width: 300px; margin-top: 20px">
<el-tag type="success">Element Plus全局引入成功</el-tag>
</el-card>
</div>
</div>
</template>
<script setup lang="ts">
// TS版:defineOptions给组件命名(Vue3.3+支持,TS无报错)
defineOptions({ name: "App" });
// 导入Pinia的计数器store(TS自动推导类型)
import { useCounterStore } from "@/store/modules/counter";
// 实例化store(Pinia自动注入,TS无类型报错)
const counterStore = useCounterStore();
// TS版:给方法添加返回值类型(void表示无返回值,推荐)
// 同步+1
const handleIncrement = (): void => {
counterStore.increment(1);
};
// 同步-1
const handleDecrement = (): void => {
counterStore.decrement(1);
};
// 异步+1(处理Promise)
const handleAsyncIncrement = async (): Promise<void> => {
const newCount = await counterStore.asyncIncrement();
console.log("异步+1后计数:", newCount); // TS自动推导newCount为number类型
};
</script>
<style scoped lang="scss">
// 使用Scss嵌套+全局变量(需@import引入)
@import "@/styles/global.scss";
#app {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.nav-bar {
margin-bottom: 30px;
a {
margin-right: 20px;
color: $vue-primary;
text-decoration: none;
&.router-link-active {
font-weight: bold;
border-bottom: 2px solid $vue-primary;
}
}
}
.counter-demo {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid $vue-gray;
button {
margin: 0 5px;
padding: 6px 12px;
cursor: pointer;
&:hover {
background: $vue-primary;
color: #fff;
border: none;
border-radius: 4px;
}
}
}
</style>
步骤 3:运行验证
npm run dev

3.2 Vant 组件库
步骤 1:安装 Vant 4
# npm
npm install vant -S
# cnpm
cnpm install vant -S
# pnpm
pnpm install vant -S
TS 类型声明已内置,无需额外安装。
步骤 2:安装 Vite 按需引入插件(Vant 官方推荐)
# npm
npm install unplugin-vue-components unplugin-auto-import -D
# cnpm
cnpm install unplugin-vue-components unplugin-auto-import -D
# pnpm
pnpm install unplugin-vue-components unplugin-auto-import -D
步骤 3:Vite 配置(自动按需引入 + REM 适配,核心)
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
// 👇 导入Element Plus和Vant的解析器
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { VantResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 自动导入Element Plus(PC)+ Vant(移动端)的API
resolvers: [ElementPlusResolver(), VantResolver()],
}),
Components({
// 自动导入Element Plus(PC)+ Vant(移动端)的组件
resolvers: [ElementPlusResolver(), VantResolver()],
}),
],
resolve: { alias: { '@': resolve(__dirname, 'src') } },
// 👇 手机调试配置:允许IP访问
server: {
host: '0.0.0.0',
port: 5173
}
})
步骤 4:配置 Vant 自动 REM 适配(一键适配所有手机屏幕)
步骤 4.1:安装适配插件
# npm
npm install postcss-pxtorem amfe-flexible -S
# cnpm
cnpm install postcss-pxtorem amfe-flexible -S
# pnpm
pnpm install postcss-pxtorem amfe-flexible -S
步骤 4.2:入口文件引入适配脚本
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
// 导入路由实例(TS版)
import router from './router'
// 导入Pinia实例(TS版)
import pinia from './store'
// 全局样式(后续可写全局样式、引入重置样式)
import './styles/global.scss'
// 👇 新增:Vant移动端REM适配核心脚本
import 'amfe-flexible'
// 1. 导入Element Plus核心库
import ElementPlus from 'element-plus'
// 2. 导入Element Plus全局样式(必须导入,否则无样式)
import 'element-plus/dist/index.css'
// 若需要使用Element Plus的内置图标,额外导入图标库(可选,下文会讲)
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 创建Vue应用实例,TS自动推导App类型
const app = createApp(App)
// 注册插件(顺序无要求,TS无类型报错)
app.use(pinia)
app.use(router)
// 3. 全局注册Element Plus
app.use(ElementPlus)
// // 可选:全局注册Element Plus所有图标(注册后直接用<el-icon><User /></el-icon>)
// for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
// app.component(key, component)
// }
// 挂载到DOM节点(index.html中的<div id="app"></div>)
app.mount('#app')
步骤 4.3:配置 postcss.config.cjs(项目根目录新建)
在项目根目录新建postcss.config.cjs文件,写入 REM 适配配置:
// postcss.config.cjs
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 37.5, // Vant官方推荐(设计稿以375px为准)
propList: ['*'], // 所有CSS属性都转换为REM
selectorBlackList: ['el-'] // 排除Element Plus的组件(避免PC端样式被转换)
}
}
}
配置后,Vant 的组件会自动根据手机屏幕宽度缩放,Element Plus 的 PC 组件不受影响,PC 和移动端样式完美隔离。
步骤 5:直接使用 Vant 组件(和 Element Plus 同时使用,无冲突)
<!– 任意组件中 –>
<template>
<div>
<!– PC端:屏幕宽度≥768px,显示Element Plus –>
<el-button v-if="isPc" type="primary">PC端按钮</el-button>
<!– 移动端:屏幕宽度<768px,显示Vant/Element Plus Mobile –>
<van-button v-else type="primary">移动端按钮</van-button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
// 判断是否为PC端
const isPc = ref(window.innerWidth >= 768)
// 监听窗口大小变化,动态更新
const handleResize = (): void => {
isPc.value = window.innerWidth >= 768
}
// 初始化执行
onMounted(() => {
window.addEventListener('resize', handleResize)
})
// 组件销毁时移除监听
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
</script>



