vue3+vite+pinia+cass最新版本编写流程,无报红
Vue Router,pinia插件安装和使用
npm install vue-router@4 pinia
新增src/router/index.ts
// 引入Vue Router的核心方法与类型
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
// 定义所有路由规则,便于权限控制、自动化管理
const routes: RouteRecordRaw[] = [
{
path: '/', // 首页路由
name: 'Home',
component: () => import('@/views/Home.vue'), // 懒加载,提升首屏性能
},
{
path: '/about', // 关于页路由
name: 'About',
component: () => import('@/views/About.vue'),
},
]
// 创建并导出router实例,统一在main.ts注册
export const router = createRouter({
history: createWebHistory(), // 使用HTML5 history模式,支持SEO与回退
routes,
})
新增src/store/index.ts (mkdir -p src/store && touch src/store/index.ts)
// 引入并创建Pinia实例,便于主入口集成
import { createPinia } from 'pinia'
// 导出Pinia实例
export const pinia = createPinia()
新增src/store/useUserStore.ts (mkdir -p src/store && touch src/store/useUserStore.ts)
// src/store/useUserStore.ts
import { defineStore } from 'pinia'
import http from '@/utils/http'
import type { UserInfo } from '@/types/user'
export const useUserStore = defineStore('user', {
state: () => ({
name: '未登录用户' as string,
isLoggedIn: false as boolean,
}),
actions: {
async login(name: string) {
// 演示写死,实际可以用 http.post 登录
this.name = name
this.isLoggedIn = true
},
logout() {
this.name = '未登录用户'
this.isLoggedIn = false
},
async fetchUser() {
// 泛型声明:确保 data 是 UserInfo 类型
const data = await http.get<UserInfo>('/user/info')
this.name = data.name
this.isLoggedIn = data.isLoggedIn
}
}
})
新增 src/views/AppHome.vue (mkdir -p src/views && touch src/views/AppHome.vue)
<template>
<div class="home-box">
<h1>首页</h1>
<p>欢迎:{{ user.name }}</p>
<button v-if="!user.isLoggedIn" @click="user.login('张三')">登录</button>
<button v-else @click="user.logout()">登出</button>
<button @click="refreshUser">刷新用户信息</button>
<router-link to="/about">关于我们</router-link>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/store/useUserStore'
const user = useUserStore()
const refreshUser = () => user.fetchUser()
</script>
<style lang="scss" scoped>
@use 'sass:color';
// 无需再次 import,$primary-color 已注入
.home-box {
padding: 20px;
background: color.adjust($primary-color, $lightness: 40%);
color: $primary-color;
border: 1px solid $primary-color;
border-radius: 8px;
}
</style>
新增tsconfig.app.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
新增src/views/AppAbout.vue (touch src/views/AppAbout.vue)
<template>
<div class="about-box">
<h1>关于我们</h1>
<p>本网站是由 Vue 3 + Vite + TypeScript 构建的现代前端项目。</p>
<router-link class="link" to="/">返回首页</router-link>
</div>
</template>
<script setup lang="ts">
// 此页面目前不需要逻辑,留空 script 即可
</script>
<style lang="scss" scoped>
@use 'sass:color';
// 变量 $primary-color 已自动全局注入,无需重复 @use
.about-box {
padding: 24px;
max-width: 600px;
margin: 0 auto;
border: 1px solid $primary-color;
border-radius: 8px;
color: $primary-color;
background: color.adjust($primary-color, $lightness: 42%);
// 或 color.scale($primary-color, $lightness: 42%);
}
.link {
display: inline-block;
margin-top: 16px;
color: color.adjust($primary-color, $lightness: -10%);
// 或 color.scale($primary-color, $lightness: -10%);
font-weight: bold;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
</style>
新增vite.config.ts
import path from 'path' // ← 这一行很重要!
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'), // 确保 __dirname 和 'src' 用对
},
},
})
修改App.vue
<template>
<router-view />
</template>
sass,axios插件的安装和使用,环境变量配置,跨域
安装
npm install -D sass # 只需安装 sass,Vite 已自动支持 .scss/.sass
npm install axios
新建src/styles/_variables.scss (touch src/styles/_variables.scss)
$primary-color: #42b983;
$font-size-base: 16px;
src/styles/global.scss (touch src/styles/global.scss )
@use './variables' as *;
body {
font-size: $font-size-base;
color: $primary-color;
font-family: 'Roboto', Arial, sans-serif;
margin: 0;
}
main.ts新增
import './styles/global.scss'
vite.config.ts新增
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables.scss" as *;`
}
}
},
新建src/utils/http.ts (touch src/utils/http.ts),处理不当会报错("Property 'isLoggedIn' does not exist on type 'AxiosResponse<UserInfo, any>'.ts(2339)")
import axios from 'axios'
import type { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
})
http.interceptors.request.use(
config => config,
error => Promise.reject(error)
)
http.interceptors.response.use(
(response: AxiosResponse) => {
if (response.data && typeof response.data === 'object' && 'data' in response.data) {
return response.data.data
}
return response.data
},
(error: AxiosError) => Promise.reject(error)
)
// 重点是这一行的泛型
const get = <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
return http.get<unknown, T>(url, config)
}
const post = <T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> => {
return http.post<unknown, T>(url, data, config)
}
export default { get, post }
新建.env .env.production (touch .env && touch .env.production )
VITE_API_BASE_URL=https://dev-api.example.com
VITE_APP_TITLE=My Vite App
VITE_API_BASE_URL=https://prod-api.example.com
VITE_APP_TITLE=My Vite App [PROD]
修改vite.config.ts,新增
server: {
proxy: {
// 只要是 /api 开头的请求,代理到后端
'/api': {
target: 'https://dev-api.example.com', // 后端接口地址
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, ''),
}
}
},
新建src/types/user.ts (mkdir -p src/types && touch src/types/user.ts )
// src/types/user.ts
export interface UserInfo {
name: string
isLoggedIn: boolean
}
总结:
数据流写法,先写http自定义请求,再写pinia连接口拿数据,之后写view层代码,写router层,(注册插件,代码结构,一般是会提前搞好)
浙公网安备 33010602011771号