项目初始化
// 默认使用yarn
yarn create @vitejs/app my-vue-ts-app --template vue-ts
// 模板包括 vanilla, vue, vue-ts, react, react-ts ...
ts配置项
tsconfig.json
- 目前暂时采用项目构建后的默认配置
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"lib": ["esnext", "dom"],
"types": ["vite/client"],
"plugins": [{ "name": "@vuedx/typescript-plugin-vue" }]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules", "dist"]
}
shim.d.ts配置
- 解决找不到
.vue
模块的报错问题
- 参考 juejin.cn/post/688923…
// 在src目录下添加 shim.d.ts, 名称可以自定义 xxx.d.ts
declare module '*.vue' {
import { Component } from 'vue'
const mod: Component
export default mod
}
eslint集成配置
.eslintrc.js
yarn add -D eslint eslint-plugin-vue babel-eslint
- 根据个人习惯配置就好~
- === 总感觉对
ts
的支持不是很好,待完善 ===
module.exports = {
root: true,
env: {
node: true,
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'plugin:prettier/recommended',
],
parserOptions: {
parser: 'babel-eslint',
},
plugins: ['prettier'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-unused-vars': process.env.NODE_ENV === 'production' ? 'error' : 'warn',
},
}
prettier集成配置
.prettierrc
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
- 根据个人习惯配置就好~
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"arrowParens": "always",
"trailingComma": "es5",
"printWidth": 90,
"useTabs": false
}
集成vue-router
yarn add vue-router@4
- 参考 github.com/vuejs/vue-r…
import {RouteRecordRaw, createRouter, createWebHashHistory} from 'vue-router'
const routes:RouteRecordRaw[] = [
{ path: '/', name: 'Home', component: () => import('../views/home/home.vue') }
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router
// main.ts
import router from './router'
createApp(App).use(router)
集成vuex
yarn add vuex@next
- 参考 github.com/vuejs/vuex
import { createStore } from 'vuex'
export default createStore({
state: {},
mutations: {},
actions: {},
modules: {},
})
// main.ts
import store from './store'
createApp(App).use(store)
引入less
yarn add less less-loader
- 暂无特殊配置
引入UI组件库
- 个人使用
element-plus
,也可以选择使用 ant-design vue
yarn add element-plus
- 可配置自定义主题颜色,参考 element-plus.gitee.io/#/zh-CN/com…
- 个人使用在线主题生成工具实现,将其中
index.css
文件引入至 main.ts
中
// main.ts
import ElementPlus from 'element-plus'
import './style/element-theme.css'
createApp(App).use(ElementPlus)
axios封装
yarn add axios @types/node @types/lodash
- 参考 juejin.cn/post/690146…
- === 可继续完善 ===
/**
* 创建实例, 添加拦截
*/
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import storage from '../utils/storage'
import { get } from 'lodash'
// 创建 axios 实例
const request = axios.create({
// API 请求的默认前缀
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 10000, // 请求超时时间
})
// 异常拦截处理器
const errorHandler = (error: any) => {
const status = get(error, 'response.status')
switch (status) {
case 400:
error.message = '请求错误'
break
case 401:
error.message = '未授权,请登录'
window.location.href = '/login' // FIXME: router.push
storage().remove('ACCESS_TOKEN')
break
case 403:
error.message = '拒绝访问'
break
case 404:
error.message = `请求地址出错: ${error.response.config.url}`
break
case 408:
error.message = '请求超时'
break
case 500:
error.message = '服务器内部错误'
break
case 501:
error.message = '服务未实现'
break
case 502:
error.message = '网关错误'
break
case 503:
error.message = '服务不可用'
break
case 504:
error.message = '网关超时'
break
case 505:
error.message = 'HTTP版本不受支持'
break
default:
break
}
return Promise.reject(error)
}
// request interceptor
request.interceptors.request.use((config: AxiosRequestConfig) => {
// 如果 token 存在
// 让每个请求携带自定义 token
config.headers.Authorization = storage().get('ACCESS_TOKEN')
return config
}, errorHandler)
// response interceptor
request.interceptors.response.use((response: AxiosResponse) => {
// 若返回的请求头中包含 authorization, 则存入到缓存中
if (response.headers.authorization) {
storage().set('ACCESS_TOKEN', response.headers.authorization)
}
const dataAxios = response.data
// 获取返回的状态码
const { code } = dataAxios
// 根据 code 进行判断
if (code === undefined) {
// 如果没有 code 代表这不是项目后端开发的接口
return dataAxios
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (code) {
// 正确返回
case 0:
return dataAxios.data
default:
// 不是正确的 code
return dataAxios.message
}
}
}, errorHandler)
export default request
缓存封装
localstorage
sessionstorage
- 参考 github.com/sunweijieMJ…
/**
* localstorage 封装
*/
class localStorageAPI {
set(key: string, value: string): void {
try {
localStorage.setItem(key, value)
} catch (e) {
if (e.name === 'QuotaExceededError') {
throw new Error('Out of Memory Limit Localstorage')
} else {
throw new Error(e.name)
}
}
}
get(key: string): string {
return localStorage.getItem(key) || ''
}
remove(key: string): void {
localStorage.removeItem(key)
}
// 有时效的 localStorage
setExpire(key: string, value: string, expire: number): void {
const curTime = new Date().getTime()
return this.set(key, JSON.stringify({ val: value, time: curTime + expire }))
}
getExpire(key: string): string {
const val: string = this.get(key)
const dataObj = JSON.parse(val)
if (new Date().getTime() - dataObj.time < 0) {
return dataObj.val
} else {
return ''
}
}
}
export { localStorageAPI }
/**
* sessionstorage 封装
*/
class SessionstorageAPI {
set(key: string, value: string): void {
return sessionStorage.setItem(key, value)
}
get(key: string): string {
return sessionStorage.getItem(key) || ''
}
remove(key: string): void {
return sessionStorage.removeItem(key)
}
}
export { SessionstorageAPI }
/**
* 封装对外接口
*/
interface UseStoreType {
set: Function
get: Function
remove: Function
getExpire?: Function
setExpire?: Function
}
export default (store?: string): UseStoreType => {
let UseStore
switch (store) {
case 'localstorage':
UseStore = require('./localstorage').localStorageAPI
break
case 'sessionstorage':
UseStore = require('./sessionstorage').SessionstorageAPI
break
default:
UseStore = require('./localstorage').localStorageAPI
break
}
return new UseStore()
}
全局样式
/style/reset.less
- 主要是滚动条样式
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
font-size: 14px;
}
div {
box-sizing: border-box;
}
/**
* 全局滚动条样式
*/
// 滚动条背景,宽高
::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: #f5f5f5;
}
// 滚动条轨道
::-webkit-scrollbar-track {
border: none;
background-color: #fff;
}
// 滚动条滑块
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(144, 147, 153, 0.3);
cursor: pointer;
}
环境变量
.env.development
.env.production
# import.meta.env.NODE_ENV
NODE_ENV=development
VITE_APP_BASE_URL=/api
# import.meta.env.NODE_ENV
NODE_ENV=production
VITE_APP_BASE_URL=/
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
const pathResolve = (pathStr: string): string => {
return resolve(__dirname, '.', pathStr)
}
export default defineConfig({
alias: {
'@': pathResolve('./src'),
},
server: {
open: false,
https: false,
proxy: {
'/api': {
target: 'http://',
changeOrigin: true,
ws: false,
secure: false,
ignorePath: true,
},
},
},
plugins: [vue()],
})
发表评论
还没有评论,快来抢沙发吧!