项目初始化
// 使用 Vite 创建项目
npm init @vitejs/app XXX
选择 Vue + TS后,
cd XXX
npm install
npm run dev
一般到这一步,项目就已经初始化成功了。
但是也可能会在 npm run dev 的过程中报错,如下
XXX/node_modules/esbuild/bin/esbuild:2
throw new Error(`esbuild: Failed to install correctly
^
Error: esbuild: Failed to install correctly
Make sure you don't have "ignore-scripts" set to true. You can check this with
"npm config get ignore-scripts". If that returns true you can reset it back to
false using "npm config set ignore-scripts false" and then reinstall esbuild.
If you're using npm v7, make sure your package-lock.json file contains either
"lockfileVersion": 1 or the code "hasInstallScript": true. If it doesn't have
either of those, then it is likely the case that a known bug in npm v7 has
corrupted your package-lock.json file. Regenerating your package-lock.json file
should fix this issue.
从报错信息看,先查看 npm config get ignore-scripts 返回是否是false,否,则执行
npm config set ignore-scripts false
然后可以看的出来,报错的文件是 node_modules/esbuild,那再装一次呗~
node node_modules/esbuild/install.js
默认构建好的目录结构是不包含router和vuex的,手动安装:
npm install vue-router@next vuex@next -S
注:Vue3.0 只支持 Router 和 Vuex 4.0及以上版本
项目目录如下,仅供参考:
目录.jpg
配置别名
默认构建的是没有别名配置的,配置如下
import { defineConfig } from 'vite'
import { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
}
})
然后 TS 就会报错:
dir-error.jpg
需要安装:
npm install @types/node -D
引入 element-plus
如果要使用 element ui 库,需要安装支持 Vue 3.0 的 element-plus 库
eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @type-eslint/parser @typescript-eslint/eslint-plugin
- 配置element-plus.ts
// plugins/element-plus.ts
import { App } from 'vue'
import ElementPlus from 'element-plus'
import * as icons from '@element-plus/icons-vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
export default {
install (app: App) {
// element-plus 图标
let k: keyof typeof icons
for (k in icons) {
app.component(k, icons[k])
}
// element-plus 组件
app.use(ElementPlus, {
locale: zhCn
})
}
}
- main.ts中使用
import { createApp } from 'vue'
import ElementPlus from '@/plugins/element-plus'
const app = createApp(App)
.use(ElementPlus)
封装request
- 配置环境变量
// env.ts
export function matchOrigin() {
const hostname = location.hostname
if (/-dev\./i.test(hostname)) {
return '//dev-xxx.com'
} else if (/-test\./.test(hostname)) {
return '//sit-xxx.com'
} else if (/-uat\./.test(hostname)) {
return '//uat-xxx.com'
} else {
return '//xxx.com'
}
}
export function matchBaseURL() {
return `${calcOrigin()}/api`
}
- 全局loading封装
/**
* 全局loading效果:合并请求 避免重复请求
* 当调用一次showLoading,则次数+1;当次数为0时,即仅第一次发请求时显示loading
* 当调用一次hideLoading,则次数-1; 当次数为0时,即仅最后一次发请求时关闭loading
*/
import { ElLoading } from 'element-plus'
import { LoadingInstance } from 'element-plus/lib//components/loading/src/loading'
// 请求次数:用来记录当前页面总共请求的次数
let loadingRequestCount = 0
// 初始化loading
let loadingInstance: LoadingInstance
// showLoading 请求次数 ++
const showLoading = (target: object) => {
if (loadingRequestCount === 0) {
loadingInstance = ElLoading.service(target)
}
loadingRequestCount++
}
// hideLoading 请求次数 --
const hideLoading = () => {
if (loadingRequestCount <= 0) return
loadingRequestCount--
if (loadingRequestCount === 0) {
loadingInstance.close()
}
}
export {
showLoading,
hideLoading
}
- request封装
/**
* 请求封装 request.ts
*/
import axios, { AxiosInstance, AxiosPromise, AxiosRequestConfig, AxiosResponse } from 'axios'
import { matchBaseURL} from './env'
import { showLoading, hideLoading } from './loading'
const insts: Record<string, AxiosInstance> = {}
// 仅在构建请求实例时才执行 matchBaseURL
function init(baseURL?: string) {
const key = baseURL || '_BASE_URL_'
if (insts[key]) { return insts[key] }
const inst = axios.create({
baseURL: baseURL || matchBaseURL(),
headers: {
...
}
})
// 可在此处做一些请求拦截
decorateService(inst)
insts[key] = inst
return inst
}
function decorateService(service: AxiosInstance) {
service.interceptors.request.use(config => {
showLoading({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)'
})
// 每次请求前,插入token,待封装...
const token = xxx()
if (token) {
config.headers!.Authorization = token
}
return config
})
service.interceptors.response.use(
(response) => {
setTimeout(() => {
hideLoading()
}, 200)
const responseData = response.data
// console.log('response', response)
if (responseData.code === 200) {
// 不能 resolve responseData,因为这里的类型为:AxiosResponse
return Promise.resolve(response)
}
return Promise.reject(responseData)
},
(error) => {
setTimeout(() => {
hideLoading()
}, 200)
console.log('error', error)
return Promise.reject(new Error('error'))
}
)
}
interface IRequest {
<T = any, D = any>(url: string, config?: AxiosRequestConfig): AxiosPromise;
<T = any, D = any>(config: AxiosRequestConfig): AxiosPromise;
}
const request: IRequest = function request<T = any, D = any>(url: string | AxiosRequestConfig, config?: AxiosRequestConfig): AxiosPromise {
if (typeof url !== 'string') {
config = url as AxiosRequestConfig
url = config.url!
} else {
config || (config = {})
config.url = url
}
// 按需实例化和复用
const inst = init(config?.baseURL)
// 发送请求
return inst.request<T, AxiosResponse<T>, D>(config)
}
export function get<T = any, D = any>(url: string, params?: D, config?: AxiosRequestConfig<D>) {
return request<T, D>({ ...(config || {}), params, url })
}
export function post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
return request<T, D>({ ...(config || {}), data, url, method: 'POST' })
}
export { get as $get, post as $post }
export default request