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'],
  },
})

複製代碼
相關文章
相關標籤/搜索