1.vite搭建
yarn create vite
可能会提示node版本不支持,需要根据提示升级或降级node版本
使用nvm下载对应版本
nvm download 18.x.x
nvm use 18.x.x
// 需要安装yarn
npm install -g yarn
// 重新执行
yarn create vite
过程中会提供选择,分别选择vue、typescript,然后根据提示定位到项目,yarn安装默认依赖
2.安装router
yarn add vue-router
新建一个基础vue文件
<template>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" v-if="$route.meta.keepAlive"/>
</keep-alive>
<component :is="Component" v-if="!$route.meta.keepAlive"/>
</router-view>
</template>
新建home文件夹,index页面
<template>
<div>home</div>
</template>
<script lang="ts" setup>
</script>
<style scoped>
</style>
新建router文件夹,index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import ViewBase from '../views/viewBase.vue'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'index',
redirect: '/home/index',
component: ViewBase,
children: [
{
path: '/home',
redirect: '/home/index',
component: ViewBase,
children: [
{
path: 'index',
name: 'home',
component: () => import('../views/home/index.vue'),
meta: {title: '首页', keepAlive: false}
},
]
},
]
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
main.ts文件中使用router
import { createApp } from 'vue'
import router from './router' // 路由
import App from './App.vue'
createApp(App).use(router).mount('#app')
给路由跳转添加css效果nprogress
yarn add nprogress
// router/index.ts
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
router.beforeEach((to, from, next) => {
NProgress.start()
next()
})
router.afterEach(() => {
NProgress.done()
})
3.安装pinia
yarn add pinia pinia-plugin-persistedstate
main.ts
import { createApp } from 'vue'
import './style.css'
import router from './router' // 路由
import { createPinia } from 'pinia' // 状态管理
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' // 持久化
import App from './App.vue'
createApp(App)
.use(createPinia().use(piniaPluginPersistedstate))
.use(router)
.mount('#app')
新建store文件夹,user.ts
import { defineStore } from 'pinia'
export const userStore = defineStore('user', {
state: () => ({
userInfo: {
userId: 0,
userName: ''
}
}),
persist: true // 持久化
})
在home页面获取userId
<template>
<div>home</div>
<div>{{userInfo.userId}}</div>
</template>
<script lang="ts" setup>
import { userStore } from "../../store/user"
import { storeToRefs } from 'pinia'
const user = userStore()
const { userInfo } = storeToRefs(user) // 保持响应
</script>
<style scoped>
</style>
4.安装element-plus
unplugin-auto-import和 unplugin-vue-components 自动引入组件和按需引入插件、@element-plus/icons-vue @iconify-json/ep unplugin-icons图标需要额外安装才能使用。
yarn add element-plus @element-plus/icons-vue @iconify-json/ep
yarn add unplugin-auto-import unplugin-vue-components unplugin-icons -D
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
// ElementPlus
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// 图标
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [
// 自动导入element-plus组件
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({
prefix: 'Icon',
})
],
include: [
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/,
/\.vue\?vue/, // .vue
/\.md$/ // .md
],
// 自动导入vue,vue-router相关函数,如ref,reactive
imports: ['vue', 'vue-router', '@vueuse/core'],
// 可以选择auto-import.d.ts生成的位置,使用ts建议设置为'src/auto-import.d.ts'
dts: 'src/auto-import.d.ts',
}),
// 自动下载图标
Icons({
autoInstall: true,
compiler: 'vue3'
}),
Components({
resolvers: [
ElementPlusResolver(),
IconsResolver({
// icon前缀默认i,使用方式:<i-ep-xxx/>
// prefix: 'i',
// 指定图标集ep
enabledCollections: ['ep']
})
],
dirs: ['src/components']
})
],
})
使用方式:
<i-ep-edit/>
5.安装axios
yarn add axios
新建http文件夹,index.ts
// 根据接口逻辑不同调整
import axios from 'axios'
const $http = axios.create({
timeout: 200000,
})
$http.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8'
$http.interceptors.request.use(config => {
config.url = '/api' + config.url
return config
})
$http.interceptors.response.use(
function (response) {
const code = response.data?.code
if (code === 0) {
return response
} else {
return Promise.reject(response.data)
}
},
function (error) {
// Do something with response error
return Promise.reject(error)
}
)
新建一个接口文件
// list.ts
import $http from '../index'
export function fetchList(): any {
return $http({
url: '/list',
method: 'GET'
})
}
页面中使用
<script lang="ts" setup>
import { fetchList } from '../../http/api/list'
onMounted(() => {
fetchList().then(res => {
const data = res.data?.data || []
console.log('data', data)
}).catch(e => {
ElMessage.error('error:' + e)
})
})
</script>
当后端接口没有写好时,前端使用mock数据调试,安装mock插件
yarn add vite-plugin-mock mockjs -D
配置
// vite.config.ts
import { viteMockServe } from 'vite-plugin-mock'
//plugins中加入
viteMockServe()
根目录新建文件夹mock,list.ts
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/api/list',
method: 'get',
response: () => {
return {
"code": 0,
"message": "",
"data": [
{
id: 1,
name: 'Alice'
}
]
}
}
}
] as MockMethod[]
6.安装less
yarn add less less-loader -D
7.其他配置
(1)src文件夹简写为@
// vite.config.ts
import path from 'path'
export default defineConfig({
plugins: [...],
resolve: {
// 简写
alias: {
'@': path.resolve(__dirname, 'src')
}
}
})
// tsconfig.json
{
compilerOptions: {
"baseUrl": "./",
"paths":{
"@/*": ["src/*"]
}
}
}
(2)vue、lodash-es/nprogress等插件类型声明
// vite-env.d.ts
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'lodash-es'
declare module 'nprogress'
(3)vue.config.ts配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
// ElementPlus
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// 图标
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
// mock
import { viteMockServe } from 'vite-plugin-mock'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [
// 自动导入element-plus组件
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({
prefix: 'Icon',
})
],
include: [
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/,
/\.vue\?vue/, // .vue
/\.md$/ // .md
],
// 自动导入vue,vue-router相关函数,如ref,reactive
imports: ['vue', 'vue-router', '@vueuse/core'],
// 可以选择auto-import.d.ts生成的位置,使用ts建议设置为'src/auto-import.d.ts'
dts: 'src/auto-import.d.ts',
}),
// 自动下载图标
Icons({
autoInstall: true,
compiler: 'vue3'
}),
Components({
resolvers: [
ElementPlusResolver(),
IconsResolver({
// icon前缀默认i,使用方式:<i-ep-xxx/>
// prefix: 'i',
// 指定图标集ep
enabledCollections: ['ep']
})
],
dirs: ['src/components']
}),
viteMockServe()
],
resolve: {
// 简写
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
host: '127.0.0.1',
port: 9000,
open: true,
https: false,
proxy: {
'/api': {
target: 'https://xx.xx.xx.xx',
secure: false,
changeOrigin: true,
timeout: 600 * 1000
}
}
},
// 生产环境打包配置
build: {
minify: 'terser',
terserOptions: {
//去除 console debugger
compress: {
drop_console: true,
drop_debugger: true
}
},
assetsDir: '',
outDir: path.resolve(__dirname, '../resources/static'),
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
assetFileNames: '[ext]/[name]-[hash].[ext]',
manualChunks(id) { //静态资源分拆打包
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString()
}
}
},
}
},
optimizeDeps: {
include: ['lodash-es']
}
})
(4)基本样式,新建style文件夹,index.less,reset.less,custom.less
// index.less
@import './reset.less' // 覆盖浏览器默认样式
@import './custom.less' // 自定义公共样式
//main.ts
import '@/assets/style/index.less'
// reset.less
body,
#app {
margin: 0;
font-size: 12px;
}
html,
body,
#app {
height: 100%;
min-width: 1100px;
margin: 0;
padding: 0
}
.container{
font-size: 12px;
}
body {
overflow-y: hidden;
}
#app {
font-family: Microsoft YaHei, 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #606266;
}
ul {
list-style-type: none;
}
ul,
ol {
list-style: none
}
:link,
:visited,
ins {
text-decoration: none
}
:focus {
outline: 0
}
@media (min-device-width: 375px) and (max-device-width: 667px) and (-webkit-min-device-pixel-ratio: 2) {
html {
font-size: 117.1875px
}
}
@media (min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) {
html {
font-size: 129.375px
}
}