TIP
vue3+vite+eslint+typescript+prettier+lintstage+husky+commitlint搭建脚手架
创建项目
bash
#创建项目
yarn create vite vue-pc --template vue-ts
cd vue-pc
yarn
yarn dev
代码风格规范
bash
# 安装eslint
yarn add -D eslint
# 支持vue单文件组件
yarn add eslint-plugin-vue -D
# 使用prettier进行代码格式化
yarn add eslint-plugin-prettier -D
# 兼容ts
yarn add @typescript-eslint/eslint-plugin -D
# eslint配置提示
yarn add eslint-define-config -D
yarn add @typescript-eslint/parser -D
# 安装prettier
yarn add prettier --D
# 出现冲突以prettier为主
yarn add eslint-config-prettier -D
根目录下新建.eslintrc.js
js
// @ts-check
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es2021: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2022,
sourceType: 'module',
// jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
plugins: ['vue', '@typescript-eslint', 'prettier'],
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
// 'vue/script-setup-uses-vars': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
},
});
根目录下新建.eslintignore
js
node_modules
dist
public
根目录下新建prettier.config.js
js
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
vueIndentScriptAndStyle: true,
singleQuote: true,
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'es5',
jsxBracketSameLine: false,
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
rangeStart: 0,
};
根目录下新建.prettierignore
js
node_modules
dist
public
配置package.json
js
{
"script": {
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
}
}
husky + lint-staged
- husky git钩子工具
- lint-staged 本地代码暂存工具
- commitlint commit信息校验
- commitizen 辅助commit
bash
# 安装
yarn add lit-staged husky -D
js
// package.json
{
"script": {
"prepare": "husky install"
}
}
bash
# 没有初始化git要先初始化
git init
# 初始化husky,将 git hooks 钩子交由,husky执行
yarn prepare
bash
# pre-commit 执行 npx lint-staged 指令
npx husky add .husky/pre-commit "npx lint-staged"
json
// 根目录创建 .lintstagedrc.json 文件控制检查和操作方式
{
"*.{js,jsx,ts,tsx,vue}": ["prettier --write .", "eslint --fix"],
"*.md": ["prettier --write"]
}
bash
# @commitlint/config-conventional 提交规范
yarn add commitlint @commitlint/config-conventional -D
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit \"$1\"'
bash
# 辅助提示
yarn add commitizen cz-conventional-changelog -D
js
// package.json
{
"script": {
"git": "git add -A && git-cz && git pull && git push"
}
}
bash
# 初始化
npx commitizen init cz-conventional-changelog --save-dev --save-exact
js
// 根目录下commitlint.config.js
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 108],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release',
],
],
},
};
stylelint样式格式化
bash
# 安装
yarn add stylelint stylelint-config-standard stylelint-config-standard-scss stylelint-config-prettier stylelint-config-prettier-scss stylelint-order postcss-html -D
js
// .stylelintrc.js
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
customSyntax: 'postcss-html',
plugins: ['stylelint-order'],
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global'],
},
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin', 'for'],
},
],
'no-duplicate-selectors': null,
'no-empty-source': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
'declaration-block-trailing-semicolon': 'always',
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested'],
},
],
'property-no-unknown': [
true,
{
ignoreProperties: ['lines'],
},
],
'media-feature-name-no-unknown': [
true,
{
ignoreMediaFeatureNames: 'min-device-pixel-ratio',
},
],
'unit-no-unknown': [
true,
{
ignoreUnits: ['rpx'],
},
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep'],
},
],
// 指定声明块内属性的字母顺序
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'hyphens',
'src',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-attachment',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'border',
'border-collapse',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-color',
'border-image',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-spacing',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'border-radius-topright',
'border-radius-bottomright',
'border-radius-bottomleft',
'border-radius-topleft',
'content',
'quotes',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'zoom',
'transform',
'box-align',
'box-flex',
'box-orient',
'box-pack',
'box-shadow',
'box-sizing',
'table-layout',
'animation',
'animation-delay',
'animation-duration',
'animation-iteration-count',
'animation-name',
'animation-play-state',
'animation-timing-function',
'animation-fill-mode',
'transition',
'transition-delay',
'transition-duration',
'transition-property',
'transition-timing-function',
'background-clip',
'backface-visibility',
'resize',
'appearance',
'user-select',
'interpolation-mode',
'direction',
'marks',
'page',
'set-link-source',
'unicode-bidi',
'speak',
],
},
};
json
// package.json
"script": {
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
}
js
// .lintstagedrc.json
{
"*.{js,jsx,ts,tsx}": ["prettier --write .", "eslint --fix"],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
"package.json": ["prettier --write"],
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"]
}
别名配置
js
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
},
});
json
// tsconfit.json
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
预处理器
bash
# 安装
yarn add -D sass
js
// vite.config.js
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "@/assets/style/mian.scss";',
},
},
},
});
使用vue-router
bash
# 安装
yarn add vue-router
ts
// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Login',
component: () => import('@/pages/login/Login.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
js
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from '@/router';
const app = createApp(App);
app.use(router);
app.mount('#app');
请求
bash
# 安装 nprogress进度加载条
yarn add axios nprogress
yarn add @types/nprogress -D
ts
// src/utils/http/index.ts
import axios, { AxiosRequestConfig } from 'axios';
import NProgress from 'nprogress';
const http = axios.create({});
// 添加请求拦截器
http.interceptors.request.use(
function (config: AxiosRequestConfig): AxiosRequestConfig<any> {
// 在发送请求之前做些什么
NProgress.start();
return config;
},
function (error) {
// 对请求错误做些什么
NProgress.done();
return Promise.reject(error);
}
);
// 添加响应拦截器
http.interceptors.response.use(
function (response) {
// 对响应数据做点什么
NProgress.done();
return response;
},
function (error) {
// 对响应错误做点什么
NProgress.done();
return Promise.reject(error);
}
);
export default http;
状态管理pinia
bash
# 安装
yarn add pinia
js
// main.ts
import { createPinia } from 'pinia'
app.use(createPinia())
js
// src/store/main.ts
import { defineStore } from 'pinia';
export const useMainStore = defineStore('storeId', {
state: () => ({
test: 'sss'
})
});
js
// 组件中使用
import { useMainStore } from "@/store/mian"
const mainStore = useMainStore();
console.log(mainStore.test);
环境变量
bash
# .env 必须以'VITE_'开头
VITE_TITLE=xx管理系统
js
// 使用
import.meta.env.VITE_TITLE
ElementPlus
bash
# 安装
yarn add element-plus
由于是管理系统,就全量引入,不按需引入了
js
// main.js
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
import router from '@/router';
import { createPinia } from 'pinia';
const app = createApp(App);
app.use(ElementPlus);
app.use(router);
app.use(createPinia());
app.mount('#app');
ts
// tsconfig.json
// volar配置
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}
请求代理proxy
js
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
}
}
})