共计 9250 个字符,预计需要花费 24 分钟才能阅读完成。
公司我的项目应用 react,然而作为 vue2 的一名 coder,周末花了两天的工夫,整顿了一波 vue3 + tsx + vite + axios 的开发模板,外面涵盖 jest、tailwindcss、pinia、element-plus 等一些日常工具包,以及退出了 eslint、prettier 保障日常开发代码品质工具,基本上可能保障大家可能开箱即用,上面附上模板代码地址,对于代码目录构造能够参考代码仓库的阐明文档,喜爱的敌人能够转评赞给一个,点个珍藏不失落,上面呢我介绍一下根本构建思路;
1、对于我的项目中应用 tsx
要想我的项目中运行 tsx,咱们就得思考到 tsx 语法糖编译的问题,这里就得用到 @vitejs/plugin-vue-jsx
插件,具体用法参考 github 文档,装置后,在 vite 的 plugin 中间接调用即可;
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(), vueJsx()]
})
2、装置 tailwindcss
对于 tailwindcss + vite 计划,它的官网有了很敌对的计划,这块大家循序渐进的装置就够了,没有多少复杂度,参考地址,抉择 tailwindcss 次要是它提供了一些疾速款式,比方 padding、margin、background 等,如果咱们我的项目是后盾管理系统,tailwindcss 会大大降低咱们写 css 款式的工作,大家能够去学习一波在我的项目中用起来,相熟了当前就感觉他是在是太不便了。
这里不做用法的介绍,就举荐一个 vscode 插件Tailwind CSS IntelliSense
,装置后,在我的项目中咱们就能够只能提醒,如下所示:
3、对于 eslint + prettier 代码对立标准
对于代码标准,个别小一点公司不太会做这方面的工程化配置,然而 eslint 等这些代码标准工具,会让咱们团队的代码更标准,格调更对立,团队合作更加不便,我简略说一下配置 eslint 及 prettier 的方法
(1)首先装置 eslint 工具库
pnpm add eslint -D
pnpm eslint –init
(2)装置内部的语法 eslint 标准及 import 校验标准
抉择对应的我的项目内容,这里我的我的项目用到(vue, typescript,browser)这个,当然有这个还不够,咱们须要装置如下两个工具包
pnpm add eslint-plugin-import // 次要对于 es 与 typescript import 门路的一个 eslint 校验
pnpm add eslint-config-airbnb-base // 这个是 airbnb 出的一套 eslint 语法标准的工具库,如果本人公司没有对应的代码标准,这个是很实用的一套
(3)编写 vue3 相干的标准
我的项目中咱们用到的是 eslint-plugin-vue 这个 vue 代码校验标准工具,外面有很多内容及配置项性能,咱们这里举荐大家在配置代码标准,能够参考官网的阐明文档,链接放在这里;
(4)装置和配置 prettier
这个绝对来讲比较简单一些,咱们间接装置pnpm add eslint-plugin-prettier eslint-config-prettier prettier -D
,这里咱们须要留神的是 prettier 与 eslint 抵触问题;
下面是配置时候的根本流程,最终后果我将 eslintrc 文件及 package.json 文件放到这里,有须要的敌人,能够间接 copy 一份去配置,毕竟这个配置很臭很长,深刻学习感觉又没有太大必要(23333~)
{
"name": "vue-tsx-template",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"fix": "eslint --fix --ext .js,.jsx,.tsx,.vue src && prettier"
},
"dependencies": {"vue": "^3.2.25"},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"autoprefixer": "^10.4.7",
"eslint": "^8.15.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.7.1",
"postcss": "^8.4.13",
"prettier": "^2.6.2",
"sass": "^1.51.0",
"tailwindcss": "^3.0.24",
"typescript": "^4.5.4",
"vite": "^2.9.9",
"vue-eslint-parser": "^9.0.1",
"vue-tsc": "^0.34.7"
}
}
上面是 .eslintrc.js
文件
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
// 解决 defineProps 报错
'vue/setup-compiler-macros': true,
},
extends: [
'eslint:recommended',
'airbnb-base',
'prettier',
'plugin:prettier/recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint'],
rules: {
// 避免 prettier 与 eslint 抵触
'prettier/prettier': 'error',
// eslint-plugin-import es module 导入 eslint 规定配置,旨在躲避拼写错误问题
'import/no-unresolved': 0,
'import/extensions': [
'error',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
json: 'always',
},
],
// 应用导出的名称作为默认属性(次要用作导出模块外部有 default,和间接导出两种并存状况下,会呈现 default.proptry 这种问题从在的状况)'import/no-named-as-default-member': 0,
'import/order': ['error', { 'newlines-between': 'always'}],
// 导入确保是否在首位
'import/first': 0,
// 如果文件只有一个导出,是否开启强制默认导出
'import/prefer-default-export': 0,
'import/no-extraneous-dependencies': [
'error',
{devDependencies: [],
optionalDependencies: false,
},
],
/**
* 对于 typescript 语法校验
* 参考文档:https://www.npmjs.com/package/@typescript-eslint/eslint-plugin
*/
'@typescript-eslint/no-extra-semi': 0,
// 是否禁止应用 any 类型
'@typescript-eslint/no-explicit-any': 0,
// 是否对于 null 状况做非空断言
'@typescript-eslint/no-non-null-assertion': 0,
// 是否对返回值类型进行定义校验
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/member-delimiter-style': ['error', { multiline: { delimiter: 'none'} }],
// 联合 eslint 'no-use-before-define': 'off',不然会有报错,须要敞开 eslint 这个校验,次要是减少了对于 type\interface\enum
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': ['error'],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
ignoreRestSiblings: true,
varsIgnorePattern: '^_',
argsIgnorePattern: '^_',
},
],
'@typescript-eslint/explicit-member-accessibility': ['error', { overrides: { constructors: 'no-public'} }],
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/indent': 0,
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'interface',
format: ['PascalCase'],
},
],
// 不容许应用 var
'no-var': 'error',
// 如果没有批改值,有些用 const 定义
'prefer-const': [
'error',
{
destructuring: 'any',
ignoreReadBeforeAssign: false,
},
],
// 对于 vue3 的一些语法糖校验
// 超过 4 个属性换行展现
'vue/max-attributes-per-line': [
'error',
{singleline: 4,},
],
// setup 语法糖校验
'vue/script-setup-uses-vars': 'error',
// 对于箭头函数
'vue/arrow-spacing': 'error',
'vue/html-indent': 'off',
},
}
4、退出单元测试
单元测试,依据本人我的项目体量及重要性而去思考是否要减少,当然单测能够反推一些组件 or 办法的设计是否正当,同样如果是一个稳固的性能在加上单元测试,这就是一个很 nice 的体验;
咱们单元测试是基于 jest 来去做的,具体装置单测的方法如下,跟着我的步骤一步步来;
- 装置 jest 单测相干的依赖组件库
pnpm add @testing-library/vue @testing-library/user-event @testing-library/jest-dom @types/jest jest @vue/test-utils -D
- 装置实现后,发现还须要装置前置依赖
@testing-library/dom @vue/compiler-sfc
咱们持续补充 -
装置 babel 相干工具,用 ts 写的单元测试须要本义,具体装置工具如下
pnpm add @babel/core babel-jest @vue/babel-preset-app -D
,最初咱们配置 babel.config.jsmodule.exports = {presets: ['@vue/app'], }
-
配置 jest.config.js
module.exports = {roots: ['<rootDir>/test'], testMatch: [ // 这里咱们反对 src 目录外面减少一些单层,事实上我并不喜爱这样做 '<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}', '<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}', // 这里我习惯将单层文件对立放在 test 独自目录下,不在我的项目中应用,升高单测文件与业务组件模块混合在一起 '<rootDir>/test/**/*.{spec,test}.{js,jsx,ts,tsx}', ], testEnvironment: 'jsdom', transform: { // 此处咱们单测没有实用 vue-jest 形式,我的项目中咱们江永 tsx 形式来开发,所以咱们如果须要退出其它的内容 // '^.+\\.(vue)$': '<rootDir>/node_modules/vue-jest', '^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': '<rootDir>/node_modules/babel-jest', }, transformIgnorePatterns: [ '<rootDir>/node_modules/', '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$', '^.+\\.module\\.(css|sass|scss|less)$', ], moduleFileExtensions: ['ts', 'tsx', 'vue', 'js', 'jsx', 'json', 'node'], resetMocks: true, }
具体写单元测试的办法,能够参考我的项目模板中的组件单元测试写法,这里不做过多的阐明;
5、封装 axios 申请库
这里呢其实思路有很多种,如果有本人的习惯的封装形式,就依照本人的思路,上面附上我的封装代码,简短的说一下我的封装思路:
- 1、根底的申请拦挡、相应拦挡封装,这个是对于一些申请参数格式化解决等,或者返回值状况解决
- 2、申请异样、谬误、接口调用胜利返回后果谬误这些谬误的集中处理,代码中申请就不再做 trycatch 这些操作
- 3、申请函数对立封装(代码中的
get、post、axiosHttp
) -
4、泛型形式定义申请返回参数,定义好类型,让咱们能够在不同中央应用有良好的提醒
import type {AxiosRequestConfig, AxiosResponse} from 'axios' import axios from 'axios' import {ElNotification} from 'element-plus' import errorHandle from './errorHandle' // 定义数据返回构造体(此处我简略定义一个比拟常见的后端数据返回构造体,理论应用咱们须要依照本人所在的我的项目开发) interface ResponseData<T = null> { code: string | number data: T success: boolean message?: string [key: string]: any } const axiosInstance = axios.create() // 设定响应超时工夫 axiosInstance.defaults.timeout = 30000 // 能够后续依据本人 http 申请头非凡邀请设定申请头 axiosInstance.interceptors.request.use((req: AxiosRequestConfig<any>) => { // 非凡解决,后续如果我的项目中有全局通传参数,能够在这儿做一些解决 return req }, error => Promise.reject(error), ) // 响应拦挡 axiosInstance.interceptors.response.use((res: AxiosResponse<any, any>) => { // 数组解决 return res }, error => Promise.reject(error), ) // 通用的申请办法体 const axiosHttp = async <T extends Record<string, any> | null>( config: AxiosRequestConfig, desc: string, ): Promise<T> => { try {const { data} = await axiosInstance.request<ResponseData<T>>(config) if (data.success) {return data.data} // 如果申请失败对立做提醒(此处我没有装置组件库,我简略写个 mock 例子) ElNotification({ title: desc, message: `${data.message || '申请失败,请查看'}`, }) } catch (e: any) { // 对立的错误处理 if (e.response && e.response.status) {errorHandle(e.response.status, desc) } else { ElNotification({ title: desc, message: '接口异样,请查看', }) } } return null as T } // get 申请办法封装 export const get = async <T = Record<string, any> | null>(url: string, params: Record<string, any>, desc: string) => { const config: AxiosRequestConfig = { method: 'get', url, params, } const data = await axiosHttp<T>(config, desc) return data } // Post 申请办法 export const post = async <T = Record<string, any> | null>(url: string, data: Record<string, any>, desc: string) => { const config: AxiosRequestConfig = { method: 'post', url, data, } const info = await axiosHttp<T>(config, desc) return info }
申请谬误(状态码谬误相干提醒)
import {ElNotification} from 'element-plus' function notificat(message: string, title: string) { ElNotification({ title, message, }) } /** * @description 获取接口定义 * @param status {number} 谬误状态码 * @param desc {string} 接口形容信息 */ export default function errorHandle(status: number, desc: string) {switch (status) { case 401: notificat('用户登录失败', desc) break case 404: notificat('申请不存在', desc) break case 500: notificat('服务器谬误,请查看服务器', desc) break default: notificat(` 其余谬误 ${status}`, desc) break } }
6、对于 vue-router 及 pinia
这两个绝对来讲简略一些,会应用 vuex 状态治理,上手 pinia 也是很轻松的事儿,只是更简单化了、更不便了,能够参考模板我的项目外面的用法 example,这里附上
router
及pinia
配置办法,路由守卫,大家能够依据我的项目的要求再增加import type {RouteRecordRaw} from 'vue-router' import {createRouter, createWebHistory} from 'vue-router' // 配置路由 const routes: Array<RouteRecordRaw> = [ { path: '/', redirect: '/home', }, { name: 'home', path: '/home', component: () => import('page/Home'), }, ] const router = createRouter({ routes, history: createWebHistory(),}) export default router
针对与 pinia,参考如下:
import {createPinia} from 'pinia' export default createPinia()
在入口文件将 router 和 store 注入进去
import {createApp} from 'vue' import App from './App' import store from './store/index' import './style/index.css' import './style/index.scss' import 'element-plus/dist/index.css' import router from './router' // 注入全局的 store const app = createApp(App).use(store).use(router) app.mount('#app')
说这些比拟干燥,倡议大家去 github 参考我的项目阐明文档,下载我的项目,本人过一遍,喜爱的敌人珍藏点赞一下,如果喜爱我构建好的我的项目给个 star 不失落,谢谢各位看官的反对。