vue3+ts+vite 项目配置

项目初始化

// 默认使用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配置

// 在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

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

import { createStore, createLogger } from 'vuex'

const debug: boolean = import.meta.env.MODE === 'development'
const plugins = debug ? [createLogger({ collapsed: true })] : []
export default createStore({
	state: {},
  mutations: {},
  actions: {},
  modules: {},
  strict: debug,
  plugins: plugins,
})

// main.ts
import store from './store'
createApp(App).use(store)
复制代码

引入less

  • yarn add less less-loader
  • 暂无特殊配置

引入UI组件库

  • 我的使用 element-plus,也能够选择使用 ant-design vue
  • vue add element-plus
  • 自动生成相应 plugins/element.js 配置及 scss 样式文件
  • 须要在 vite.config.ts 中添加语言配置项
  • 使用 vite 构建的配置中 element-variables.scss 文件中路径没法解析 - 暂时引入相对路径使用
// element-variables.scss

// $--font-path: '~element-plus/lib/theme-chalk/fonts';
$--font-path: '../node_modules/element-plus/lib/theme-chalk/fonts';

// @import "~element-plus/packages/theme-chalk/src/index.scss";
@import '../node_modules/element-plus/packages/theme-chalk/src/index.scss';
复制代码
// vite.config.ts
optimizeDeps: {
  include: ['element-plus/lib/locale/lang/zh-cn'],
}
复制代码

axios封装

/** * 建立实例, 添加拦截 */

import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import storage from '../utils/storage'
import { get } from 'lodash'

import { useRouter } from 'vue-router'
const router = useRouter()

// 建立 axios 实例
const request = axios.create({
  // API 请求的默认前缀
  baseURL: import.meta.env.VITE_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 = '未受权,请登陆'
      router.push('/login')
      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
复制代码
// 默认配置包含了 boolean, 致使
// 封装 axios 时给 baseURL 赋值报错

interface ImportMetaEnv {
  VITE_APP_BASE_URL?: string
}
复制代码

缓存封装

/** * 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 }
复制代码
/** * 封装对外接口 */
import { localStorageAPI } from './localstorage'
import { SessionstorageAPI } from './sessionstorage'

interface UseStoreType {
  set: Function
  get: Function
  remove: Function
  getExpire?: Function
  setExpire?: Function
}

export default (store?: string): UseStoreType => {
  let UseStore
  switch (store) {
    case 'localstorage':
      UseStore = localStorageAPI
      break

    case 'sessionstorage':
      UseStore = SessionstorageAPI
      break

    default:
      UseStore = 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
# .env.development
VITE_APP_BASE_URL=/api
复制代码
# .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,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
    hmr: {
      overlay: true,
    },
  },

  build: {
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },

    // TODO: 拆分
    rollupOptions: {
      output: {
        manualChunks: {},
      },
    },

    chunkSizeWarningLimit: 800, // FIXME: 鸵鸟 = =...
  },

  plugins: [vue()],

  optimizeDeps: {
    include: ['element-plus/lib/locale/lang/zh-cn'],
  },
})

复制代码
相关文章
相关标签/搜索