Vite-Vue3.x-TS-Eslint项目初始化
Lzq811/Vue3.x-vite-ts-eslint-prettier: vue3大屏基础框架 (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()] }) ] })
-
然后在使用的组件中直接调用
hello element btn 自定义主题 文档地址
-
使用 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 } } } # 在组件内使用
3. 使用 Eslint 、 Prettier 做代码校验和自动格式化
-
安装 eslint 以及相关依赖
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'], // 强制数组元素间出现换行 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 以避免产生副作用 'linebreak-style': 'off', // 强制使用一致的换行风格 'import/extensions': 'off', // 确保在导入路径中统一使用文件扩展名 'eol-last': 'off', // 要求或禁止文件末尾存在空行 'no-shadow': 'off', // 禁止变量声明与外层作用域的变量同名 'no-unused-vars': 'warn', // 禁止出现未使用过的变量 'import/no-cycle': 'off', // 禁止一个模块导入一个有依赖路径的模块回到自己身上 'arrow-parens': 'off', // 要求箭头函数的参数使用圆括号 semi: ['error', 'never'], // 要求或禁止使用分号代替 ASI eqeqeq: 'on', // 要求使用 === 和 !== 'no-param-reassign': 'off', // 禁止对 function 的参数进行重新赋值 'import/prefer-default-export': 'off', // 如果模块只输入一个名字,则倾向于默认输出 'no-use-before-define': 'on', // 禁止在变量定义之前使用它们,则倾向于默认输出 'no-continue': 'off', // 禁用 continue 语句 'prefer-destructuring': 'off', // 优先使用数组和对象解构 'no-plusplus': 'off', // 禁用一元操作符 ++ 和 -- 'prefer-const': 'warn', // 要求使用 const 声明那些声明后不再被修改的变量 'global-require': 'on', // 要求 require() 出现在顶层模块作用域中 'no-prototype-builtins': 'off', // 禁止直接调用 Object.prototypes 的内置属性 'consistent-return': 'off', // 要求 return 语句要么总是指定返回的值,要么不指定 'one-var-declaration-per-line': 'off', // 要求或禁止在变量声明周围换行 'one-var': 'off', // 强制函数中的变量要么一起声明要么分开声明 'import/named': 'off', // 确保命名导入与远程文件中的命名导出相对应 'object-curly-newline': 'off', // 强制大括号内换行符的一致性 'default-case': 'off', // 要求 switch 语句中有 default 分支 'no-trailing-spaces': 'on', // 禁用行尾空格 'func-names': 'off', // 要求或禁止使用命名的 function 表达式 radix: 'off', // 强制在 parseInt() 使用基数参数 'no-unused-expressions': 'off', // 禁止出现未使用过的表达式 'no-underscore-dangle': 'off', // 禁止标识符中有悬空下划线 'no-nested-ternary': 'off', // 禁用嵌套的三元表达式 'no-restricted-syntax': 'off', // 禁用特定的语法 'no-await-in-loop': 'off', // 禁止在循环中出现 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', // 不允许空函数 '@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型 'guard-for-in': 'off', // 要求 for-in 循环中有一个 if 语句 'class-methods-use-this': 'off', // 强制类方法使用 this 'vue/html-indent': ['error', 2], // 在中强制一致缩进 'vue/html-self-closing': 'off', // 执行自闭合的风格 'vue/max-attributes-per-line': [ // 强制每行属性的最大数量 'warn', { singleline: { max: 3, allowFirstLine: true }, multiline: { max: 1, allowFirstLine: false } } ], 'vue/singleline-html-element-content-newline': 'off' // 要求单行元素的内容前后有一个换行符 } }
-
根目录添加 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', // 大括号内的首尾需要空格 bracketSpacing: true, // jsx 标签的反尖括号需要换行 jsxBracketSameLine: false, // 箭头函数,只有一个参数的时候,也需要括号 arrowParens: 'always', // 每个文件格式化的范围是文件的全部内容 rangeStart: 0, rangeEnd: Infinity, // 不需要写文件开头的 @prettier requirePragma: false, // 不需要自动在文件开头插入 @prettier insertPragma: false, // 使用默认的折行标准 proseWrap: 'preserve', // 根据显示样式决定 html 要不要折行 htmlWhitespaceSensitivity: 'css', // 换行符使用 lf endOfLine: 'lf' }
-
根目录 添加 .eslintignore 并写入下面代码
/build/ /config/ /dist/ /*.js /*.zip /*.rar
4. 配置环境变量
-
在根目录新建文件夹 env-config,然后在文件夹下新增文件并录入内容
所有的环境变量必须以 VITE_ 开头
# 新增 .env 文件 写入下面内容 VITE_ENV = DEV VITE_APP_BASE_URL = 'http://DEV.com' # 新增 .env.test 文件并写入内容 VITE_ENV = TEST VITE_APP_BASE_URL = 'http://TEST.com' # 新增 .env.prod 文件并写入内容 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 来获取环境变量信息
# 组件里面调用测试 onMounted(() => {console.log(import.meta.env)})
-
执行start命令
yarn start # 执行 dev 环境 # OR yarn start-test # 执行 test 环境 # OR yarn start-prod # 执行 orod 环境
#demo yarn start-test # 调取 import.meta.env 的结果 BASE_URL: "/" DEV: true MODE: "test" PROD: false VITE_APP_BASE_URL: "http://TEST.com" VITE_ENV: "TEST"
-
执行build命令
#demo yarn build-test # 调取 import.meta.env 的结果 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 引入 需要 * 第一个 参数 是 url 必填 * 第二个 参数 是 params对象 默认 {} 非必填 * 第三个 参数 是 GET、POST 请求方式, 默认 POST, 非必填 */ // 后台地址 const BASE_URL: string = import.meta.env.VITE_APP_BASE_URL || '' // 环境变量后台地址 // 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' // 可以在这里做一下请求拦截,设置公共请求头等 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) } // 统一处理 response promise .then((response: any) => { response && response.data ? resolve(response.data) : reject(response) }) .catch((error: any) => { console.log(error) }) }) }
组件内调用
```tsx
import {ReqLogin} from './src/api'
const ajaxDemo = async () => {
const res:any = await ReqLogin({}) // res就是ajax返回的结果
}
```
6. 页面弹性布局
-
新建 src/utils/comfort_page.tsx
/* * 做页面自适应 * 0. 项目跟元素 id = root * 1. width 为适应基准 * 2. 无论 width height 如何变化,都要输出 16 / 9 比例的页面 * 3. 客户的设备分辨率 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 版本的,才对应 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 文件
-
跳转
import {useRouter} from 'vue-router' const router = useRouter() router.push('./first')
8. 路径别名
-
修改 vite.config.js 内容
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'