Vite-Vue3.x-TS-Eslint項目初始化
Lzq811/Vue3.x-vite-ts-eslint-prettier: vue3大屏基礎(chǔ)框架 (github.com)
1. init
yarn create vite demo-product --template vue-ts
cd ./demo-product
yarn
yarn dev
# 能正常方法 localhost:3000 頁面花吟,說明 init 成功
2. 使用 element-plus UI 庫
- 安裝 element-plus 官方地址
yarn add element-plus
-
推薦使用 按需引入 文檔地址
# 需要安裝下面兩個依賴 yarn add unplugin-vue-component unplugin-auto-import
-
然后再 vite.config.ts 文件中修改
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Component from 'unplugin-vue-components/vite' import {ElementPlusResolver } from 'unplugin-vue-components/resolvers' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Component({ resolvers: [ElementPlusResolver()] }) ] })
-
然后在使用的組件中直接調(diào)用
<el-button type="primary">hello element btn</el-button>
自定義主題 文檔地址
-
使用 Less
# 安裝依賴 yarn add less less-loader -D # 然后vite.config.js衅澈, 要require('path'), 如果require 和 __dirname報ts錯经备, 使用 @ts-ignore 或者安裝 @types/node 依賴 css: { preprocessorOptions: { less: { modifyVars: { hack: `true; @import (reference) "${path.resolve(__dirname, 'src/assets/base.less')}"` // 全局定義的less文件 }, javascriptEnabled: true } } } # 在組件內(nèi)使用 <style lang="less" scoped></style>
3. 使用 Eslint 部默、 Prettier 做代碼校驗和自動格式化
-
安裝 eslint 以及相關(guān)依賴
yarn add eslint eslint-plugin-vue vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-airbnb-base eslint-plugin-import -D
vscode 編輯器 也要安裝 Eslint 和 Prettier 拓展
-
根目錄添加 .eslintrc.js文件,并添加下面代碼
module.exports = { root: true, globals: { defineEmits: 'readonly', defineProps: 'readonly' }, extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:vue/vue3-recommended', 'airbnb-base' ], parserOptions: { parser: '@typescript-eslint/parser', ecmaVersion: 2020 }, rules: { 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 debugger 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 console 'no-bitwise': 'off', // 禁用按位運算符 'no-tabs': 'off', // 禁用 tab 'array-element-newline': ['error', 'consistent'], // 強制數(shù)組元素間出現(xiàn)換行 indent: [ 'error', 2, { MemberExpression: 0, SwitchCase: 1, ignoredNodes: ['TemplateLiteral'] } ], // 強制使用一致的縮進 quotes: ['error', 'single'], // 強制使用一致的反勾號蘑志、雙引號或單引號 'comma-dangle': ['error', 'always-multiline'], // 要求或禁止末尾逗號 'object-curly-spacing': ['error', 'always'], // 強制在大括號中使用一致的空格 'max-len': ['error', 120], // 強制一行的最大長度 'no-new': 'off', // 禁止使用 new 以避免產(chǎn)生副作用 'linebreak-style': 'off', // 強制使用一致的換行風格 'import/extensions': 'off', // 確保在導入路徑中統(tǒng)一使用文件擴展名 'eol-last': 'off', // 要求或禁止文件末尾存在空行 'no-shadow': 'off', // 禁止變量聲明與外層作用域的變量同名 'no-unused-vars': 'warn', // 禁止出現(xiàn)未使用過的變量 'import/no-cycle': 'off', // 禁止一個模塊導入一個有依賴路徑的模塊回到自己身上 'arrow-parens': 'off', // 要求箭頭函數(shù)的參數(shù)使用圓括號 semi: ['error', 'never'], // 要求或禁止使用分號代替 ASI eqeqeq: 'on', // 要求使用 === 和 !== 'no-param-reassign': 'off', // 禁止對 function 的參數(shù)進行重新賦值 'import/prefer-default-export': 'off', // 如果模塊只輸入一個名字急但,則傾向于默認輸出 'no-use-before-define': 'on', // 禁止在變量定義之前使用它們,則傾向于默認輸出 'no-continue': 'off', // 禁用 continue 語句 'prefer-destructuring': 'off', // 優(yōu)先使用數(shù)組和對象解構(gòu) 'no-plusplus': 'off', // 禁用一元操作符 ++ 和 -- 'prefer-const': 'warn', // 要求使用 const 聲明那些聲明后不再被修改的變量 'global-require': 'on', // 要求 require() 出現(xiàn)在頂層模塊作用域中 'no-prototype-builtins': 'off', // 禁止直接調(diào)用 Object.prototypes 的內(nèi)置屬性 'consistent-return': 'off', // 要求 return 語句要么總是指定返回的值波桩,要么不指定 'one-var-declaration-per-line': 'off', // 要求或禁止在變量聲明周圍換行 'one-var': 'off', // 強制函數(shù)中的變量要么一起聲明要么分開聲明 'import/named': 'off', // 確保命名導入與遠程文件中的命名導出相對應(yīng) 'object-curly-newline': 'off', // 強制大括號內(nèi)換行符的一致性 'default-case': 'off', // 要求 switch 語句中有 default 分支 'no-trailing-spaces': 'on', // 禁用行尾空格 'func-names': 'off', // 要求或禁止使用命名的 function 表達式 radix: 'off', // 強制在 parseInt() 使用基數(shù)參數(shù) 'no-unused-expressions': 'off', // 禁止出現(xiàn)未使用過的表達式 'no-underscore-dangle': 'off', // 禁止標識符中有懸空下劃線 'no-nested-ternary': 'off', // 禁用嵌套的三元表達式 'no-restricted-syntax': 'off', // 禁用特定的語法 'no-await-in-loop': 'off', // 禁止在循環(huán)中出現(xiàn) await 'import/no-extraneous-dependencies': 'off', // 禁止使用外部包 'import/no-unresolved': 'off', // 確保導入指向一個可以解析的文件/模塊 'template-curly-spacing': ['error', 'always'], // 要求或禁止模板字符串中的嵌入表達式周圍空格的使用 '@typescript-eslint/no-var-requires': 'off', // 除import語句外镐躲,禁止使用require語句 '@typescript-eslint/no-empty-function': 'off', // 不允許空函數(shù) '@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 類型 'guard-for-in': 'off', // 要求 for-in 循環(huán)中有一個 if 語句 'class-methods-use-this': 'off', // 強制類方法使用 this 'vue/html-indent': ['error', 2], // 在<template>中強制一致縮進 'vue/html-self-closing': 'off', // 執(zhí)行自閉合的風格 'vue/max-attributes-per-line': [ // 強制每行屬性的最大數(shù)量 'warn', { singleline: { max: 3, allowFirstLine: true }, multiline: { max: 1, allowFirstLine: false } } ], 'vue/singleline-html-element-content-newline': 'off' // 要求單行元素的內(nèi)容前后有一個換行符 } }
-
根目錄添加 prettier.config.js文件,并添加下面代碼
// prettier.config.js or .prettierrc.js module.exports = { // 一行最多 100 字符 printWidth: 100, // 使用 2 個空格縮進 tabWidth: 2, // 不使用縮進符撒穷,而使用空格 useTabs: false, // 行尾需要有分號 semi: false, // 使用單引號 singleQuote: true, // 對象的 key 僅在必要時用引號 quoteProps: 'as-needed', // jsx 不使用單引號裆熙,而使用雙引號 jsxSingleQuote: false, // 末尾不需要逗號 trailingComma: 'none', // 大括號內(nèi)的首尾需要空格 bracketSpacing: true, // jsx 標簽的反尖括號需要換行 jsxBracketSameLine: false, // 箭頭函數(shù),只有一個參數(shù)的時候蛤奥,也需要括號 arrowParens: 'always', // 每個文件格式化的范圍是文件的全部內(nèi)容 rangeStart: 0, rangeEnd: Infinity, // 不需要寫文件開頭的 @prettier requirePragma: false, // 不需要自動在文件開頭插入 @prettier insertPragma: false, // 使用默認的折行標準 proseWrap: 'preserve', // 根據(jù)顯示樣式?jīng)Q定 html 要不要折行 htmlWhitespaceSensitivity: 'css', // 換行符使用 lf endOfLine: 'lf' }
-
根目錄 添加 .eslintignore 并寫入下面代碼
/build/ /config/ /dist/ /*.js /*.zip /*.rar
4. 配置環(huán)境變量
-
在根目錄新建文件夾 env-config,然后在文件夾下新增文件并錄入內(nèi)容
所有的環(huán)境變量必須以 VITE_ 開頭
# 新增 .env 文件 寫入下面內(nèi)容 VITE_ENV = DEV VITE_APP_BASE_URL = 'http://DEV.com' # 新增 .env.test 文件并寫入內(nèi)容 VITE_ENV = TEST VITE_APP_BASE_URL = 'http://TEST.com' # 新增 .env.prod 文件并寫入內(nèi)容 VITE_ENV = PROD VITE_APP_BASE_URL = 'http://PROD.com'
-
在 vite.config.js文件夾中配置 envDir
// @ts-ignore 如果提示錯誤 使用 @ts-ignore 或者 yarn add @types/node 只有node里面才有 require 和 __dirname const path = require('path') function _resolve(dir) { // @ts-ignore return path.resolve(__dirname, dir) } // https://vitejs.dev/config/ export default defineConfig({ envDir: _resolve('env-config'), ... })
-
修改 package.json 的命令(我比較喜歡用 start 代替 dev)
"scripts": { "dev": "vite", "start": "vite --mode env", "start-test": "vite --mode test", "start-prod": "vite --mode prod", "build": "vue-tsc --noEmit && vite build", "build-test": "vue-tsc --noEmit && vite build --mode test", "build-prod": "vue-tsc --noEmit && vite build --mode prod", "preview": "vite preview" },
-
使用 import.meta.env 來獲取環(huán)境變量信息
# 組件里面調(diào)用測試 onMounted(() => {console.log(import.meta.env)})
-
執(zhí)行start命令
yarn start # 執(zhí)行 dev 環(huán)境 # OR yarn start-test # 執(zhí)行 test 環(huán)境 # OR yarn start-prod # 執(zhí)行 orod 環(huán)境
#demo yarn start-test # 調(diào)取 import.meta.env 的結(jié)果 BASE_URL: "/" DEV: true MODE: "test" PROD: false VITE_APP_BASE_URL: "http://TEST.com" VITE_ENV: "TEST"
-
執(zhí)行build命令
#demo yarn build-test # 調(diào)取 import.meta.env 的結(jié)果 BASE_URL: "/" DEV: true MODE: "test" PROD: false VITE_APP_BASE_URL: "http://TEST.com" VITE_ENV: "TEST"
5. 使用 axios 封裝 ajax 請求
-
安裝 axios
yarn add axios
-
配置封裝
- 新建文件src/api/index.tsx
// index.tsx 文件 import ajax from './ajax' /* * ajax 從 ajax.js 引入 需要 * 第一個 參數(shù) 是 url 必填 * 第二個 參數(shù) 是 params對象 默認 {} 非必填 * 第三個 參數(shù) 是 GET、POST 請求方式蚀同, 默認 POST, 非必填 */ // 后臺地址 const BASE_URL: string = import.meta.env.VITE_APP_BASE_URL || '' // 環(huán)境變量后臺地址 // const BASE_URL: string = `http:xxxx/api` // const BASE_URL_OTHER:string = `http:xxx2.api` // 多個后臺地址 interface IParams {} // 登陸接口 export const ReqLogin = (params: IParams) => ajax(`${BASE_URL}login`, params, 'POST') // POST 是默認值拷恨,可以不寫 // export const ReqOther = (params:IParams) => ajax(`${BASE_URL_OTHER}login`, params)
-
新建文件 src/api/ajax.tsx
// ajax.tsx 文件 import axios from 'axios' // 可以在這里做一下請求攔截谢肾,設(shè)置公共請求頭等 export default function ajax(url: string, data: any = {}, type: string = 'POST') { // 判斷 url 地址小泉, 在多個后臺地址時候使用, 可以在這里攔截使用不同的請求頭微姊,傳入不同token等操作 return new Promise((resolve, reject) => { let promise: any // 返回一個 promise 對象 if (type === 'GET') { promise = axios.get(url, { params: data }) // 多個后臺地址時候兢交,傳入不同的token值 promise = axios.get(url, { params: data, headers: { access_totken: sessionStorage.getItem('token_other') || '' } }) } else if (type === 'POST') { promise = axios.post(url, data) // 多個后臺地址時候,傳入不同的token值 promise = axios.get(url, data) } // 統(tǒng)一處理 response promise .then((response: any) => { response && response.data ? resolve(response.data) : reject(response) }) .catch((error: any) => { console.log(error) }) }) }
組件內(nèi)調(diào)用
```tsx
import {ReqLogin} from './src/api'
const ajaxDemo = async () => {
const res:any = await ReqLogin({}) // res就是ajax返回的結(jié)果
}
```
6. 頁面彈性布局
-
新建 src/utils/comfort_page.tsx
/* * 做頁面自適應(yīng) * 0. 項目跟元素 id = root * 1. width 為適應(yīng)基準 * 2. 無論 width height 如何變化,都要輸出 16 / 9 比例的頁面 * 3. 客戶的設(shè)備分辨率 1920 * 1080 */ // @ts-nocheck const W = 1920 const H = 1080 export default () => { const root = document.getElementById('root')?.style window.onresize = () => { root.transform = `scale(${document.body.offsetWidth / W})` root.transformOrigin = `left top 0px` root.width = `${W}px` root.height = `${H}px` } root.transform = `scale(${document.body.offsetWidth / W})` root.transformOrigin = `left top 0px` root.width = `${W}px` root.height = `${H}px` }
-
App.vue 組件引入
import ComfortPage from './utils/comfort_page' onMounted(() => { ComfortPage() })
7. 路由
-
路由要用 4 版本的凳干,才對應(yīng) vue3版本
yarn add vue-router@4
-
新增 src/routes/index.tsx
import { createRouter, createWebHistory } from 'vue-router' const routes: any = [ { path: '/', redirect: 'home' }, { path: '/home', name: 'home', component: () => import('../Home.vue') // 要提前注冊號該組件 }, { path: '/first', name: 'first', component: () => import('../First.vue') // 要提前注冊號該組件 } ] const router = createRouter({ history: createWebHistory(), routes: routes }) router.beforeEach((to, from) => { const { path: toPath } = to const { path: fromPath } = from if (toPath === fromPath) { return false } }) export default router
-
修改 main.ts 文件
import router from './routes' const app = createApp(App) app.use(router) app.mount('#app')
-
修改 App.vue 文件
<template> <router-view></router-view> </template>
-
跳轉(zhuǎn)
import {useRouter} from 'vue-router' const router = useRouter() router.push('./first')
8. 路徑別名
-
修改 vite.config.js 內(nèi)容
export default defineConfig({ envDir: path.resolve(__dirname, 'env-config'), plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Component({ resolvers: [ElementPlusResolver()] }) ], css: { preprocessorOptions: { less: { modifyVars: { hack: `true; @import (reference) "${path.resolve(__dirname, 'src/assets/base.less')}"` // 全局定義的less文件 }, javascriptEnabled: true } } }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@api': _resolve('src/api'), '@components': _resolve('src/components'), '@': _resolve('src') } } })
-
新增 src/ts.extends.json 文件
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": [ "*" ], "@api/*": [ "src/api/*" ] } } }
-
修改 tsconfig.json 文件
{ ... "extends": "./ts.extends.json" # 添加該屬性 }
-
使用
import { ReqLogin } from '@api/index'