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號