前言
本文章提供Vue3项目创建,项目基本配置的流程,以便快速开发。
环境准备:
node ^16
pnpm ^8
初始化项目
项目初始化:
pnpm create vite
进入到项目根目录后安装依赖项:
pnpm install
运行程序:
pnpm run dev
运行完毕在 http://127.0.0.1:5173/ 测试访问
项目配置
eslint配置
安装eslint:
pnpm i eslint -D
项目根目录下生成配置文件:eslint.config.js(^9新写法)
npx eslint --init
安装Vue3环境代码校验插件:
pnpm install -D eslint-plugin-import eslint-plugin-vue eslint-plugin-node eslint-plugin-prettier eslint-config-prettier eslint-plugin-node @babel/eslint-parser @typescript-eslint/parser
修改 eslint.config.js 配置文件:
import globals from 'globals'
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
import pluginVue from 'eslint-plugin-vue'
import configPrettier from 'eslint-config-prettier'
import pluginPrettier from 'eslint-plugin-prettier'
import parser from '@typescript-eslint/parser'
export default [
pluginJs.configs.recommended,
...tseslint.configs.recommended,
...pluginVue.configs['flat/essential'],
{
files: ['**/*.ts?(x)'],
ignores: ['**/dist/**', '**/node_modules/**'],
languageOptions: {
parser: parser,
globals: {
...globals.commonjs,
...globals.browser,
...globals.es2021,
...globals.node,
},
},
// 🟡 recommended.plugins: ['prettier']
plugins: {
prettier: pluginPrettier,
},
rules: {
// 🟡 recommended.extends: ['prettier']
...configPrettier.rules,
// 🟡 recommended.rules: { ... }
...pluginPrettier.configs.recommended.rules,
// 🟡 一些自己的自定义 rules
'prettier/prettier': 'warn',
'@typescript-eslint/no-unused-vars': 'off',
'no-unused-vars': 'off',
// eslint(https://eslint.bootcss.com/docs/rules/)
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unexpected-multiline': 'error', // 禁止空余的多行
'no-useless-escape': 'off', // 禁止不必要的转义字符
// typeScript (https://typescript-eslint.io/rules)
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
'@typescript-eslint/prefer-ts-expect-error': 'error', // 禁止使用 @ts-ignore
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
'@typescript-eslint/semi': 'off',
// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变
'vue/attribute-hyphenation': 'off', //
},
},
]
package.json 新增运行脚本:
"scripts": {
"lint": "eslint src",
"fix": "eslint src --fix",
}
配置 prettier
安装依赖包:
pnpm install -D eslint-plugin-prettier prettier eslint-config-prettier
项目根目录下创建 .prettierrc 并添加规则:
{
"singleQuote": true,
"semi": true,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "all",
"tabWidth": 2
}
项目根目录下创建 .prettierignore 忽略文件:
/dist/*
/html/*
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*
通过 pnpm run lint 检测语法:
pnpm run lint
通过 pnpm run fix 修改:
pnpm run fix
配置 stylelint
安装依赖:
pnpm add sass sass-loader stylelint postcss postcss-scss postcss-html stylelint-config-prettier stylelint-config-recess-order stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-vue stylelint-scss stylelint-order stylelint-config-standard-scss -D
项目根目录下创建 .stylelintrc.cjs 配置文件:
module.exports = {
extends: [
'stylelint-config-standard', // 配置stylelint拓展插件
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
'stylelint-config-standard-scss', // 配置stylelint scss插件
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
'stylelint-config-prettier', // 配置stylelint和prettier兼容
],
overrides: [
{
files: ['**/*.(scss|css|vue|html)'],
customSyntax: 'postcss-scss',
},
{
files: ['**/*.(html|vue)'],
customSyntax: 'postcss-html',
},
],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
'**/*.yaml',
],
/**
* null => 关闭该规则
* always => 必须
*/
rules: {
'value-keyword-case': null, // 在 css 中使用 v-bind,不报错
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
'no-empty-source': null, // 关闭禁止空源码
'selector-class-pattern': null, // 关闭强制选择器类名的格式
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
'block-opening-brace-space-before': 'always', //大括号之前必须有一个空格或不能有空白符
'value-no-vendor-prefix': null, // 关闭 属性值前缀 --webkit-box
'property-no-vendor-prefix': null, // 关闭 属性前缀 -webkit-mask
'selector-pseudo-class-no-unknown': [
// 不允许未知的选择器
true,
{
ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略属性,修改element默认样式的时候能使用到
},
],
},
}
项目根目录下创建 .stylelintignore 忽略文件:
/node_modules/*
/dist/*
/html/*
/public/*
在 package.json 中配置运行脚本:
"scripts": {
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
"lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
},
运行 pnpm run format ,把代码格式化:
pnpm run format
配置 husky
安装 husky
pnpm install -D husky
初始化目录:
npx husky-init
在 .husky/pre-commit 文件添加如下命令:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run format
配置 commitlint
安装依赖:
pnpm add @commitlint/config-conventional @commitlint/cli -D
项目根目录下创建 commitlint.config.cjs 并添加配置:
module.exports = {
extends: ['@commitlint/config-conventional'],
// 校验规则
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'chore',
'revert',
'build',
],
],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72],
},
}
在 package.json 中配置scripts命令:
{
"scripts": {
"commitlint": "commitlint --config commitlint.config.cjs -e -V"
},
}
当我们填写 git commit 信息的时候,需要带着下面的 subject:
'feat',//新特性、新功能
'fix',//修改bug
'docs',//文档修改
'style',//代码格式修改, 注意不是 css 修改
'refactor',//代码重构
'perf',//优化相关,比如提升性能、体验
'test',//测试用例修改
'chore',//其他修改, 比如改变构建流程、或者增加依赖库、工具等
'revert',//回滚到上一个版本
'build',//编译相关的修改,例如发布版本、对项目构建或者依赖的改动
配置 husky:
npx husky add .husky/commit-msg
在 commit-msg 文件中配置命令:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm commitlint
强制使用pnpm包管理器工具
项目根目录下创建 scritps/preinstall.js 并添加代码:
if (!/pnpm/.test(process.env.npm_execpath || '')) {
console.warn(
`\u001b[33mThis repository must using pnpm as the package manager ` +
` for scripts to work properly.\u001b[39m\n`,
)
process.exit(1)
}
在 package.json 文件中配置命令:
"scripts": {
"preinstall": "node ./scripts/preinstall.js"
}
项目集成
集成 element-plus
安装依赖:
pnpm install element-plus @element-plus/icons-vue
全局安装 element-plus, 设置支持语言为中文:
// main.ts
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'
//@ts-ignore 忽略文件ts类型的检测
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
app.use(ElementPlus, {
locale: zhCn
})
Element Plus 全局组件类型声明:
// tsconfig.json
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}
注册所有图标:
// src/components/index.ts
import type { App, Component } from 'vue';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
export default {
install(app: App) {
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
},
};
配置 src 别名
安装 node 类型声明:
pnpm i @types/node --save-dev
在 vite.config.ts 文件配置:
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve("./src") // 相对路径别名配置,使用 @ 代替 src
}
}
})
TypeScript 编译配置:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { //路径映射,相对于baseUrl
"@/*": ["src/*"]
}
}
}
配置环境变量
项目根目录下创建三个文件:
.env.development
.env.production
.env.test
开发环境 .env.development :
# 变量以 VITE_ 为前缀
NODE_ENV = 'development'
VITE_APP_TITLE = '开发平台'
VITE_APP_BASE_API = '/dev-api'
生产环境 .env.production:
# 变量以 VITE_ 为前缀
NODE_ENV = 'production'
VITE_APP_TITLE = '生产平台'
VITE_APP_BASE_API = '/prod-api'
测试环境 .env.test:
# 变量以 VITE_ 为前缀
NODE_ENV = 'test'
VITE_APP_TITLE = '测试平台'
VITE_APP_BASE_API = '/test-api'
在 package.json 配置运行命令:
"scripts": {
"build:test": "vue-tsc && vite build --mode test",
"build:pro": "vue-tsc && vite build --mode production",
},
SVG 图标配置
安装SVG依赖插件:
pnpm install vite-plugin-svg-icons -D
在 vite.config.ts 中配置插件:
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default () => {
return {
plugins: [
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
// Specify symbolId format
symbolId: 'icon-[dir]-[name]',
}),
],
}
}
main.ts 入口文件导入:
import 'virtual:svg-icons-register'
SVG封装为全局组件
在 src/components 目录下创建一个 SvgIcon/index.vue 组件:
<template>
<div>
<svg :style="{ width: width, height: height }">
<use :xlink:href="prefix + name" :fill="color"></use>
</svg>
</div>
</template>
<script setup lang="ts">
defineProps({
//xlink:href属性值的前缀
prefix: {
type: String,
default: '#icon-'
},
//svg矢量图的名字
name: String,
//svg图标的颜色
color: {
type: String,
default: ""
},
//svg宽度
width: {
type: String,
default: '16px'
},
//svg高度
height: {
type: String,
default: '16px'
}
})
</script>
<style scoped></style>
在 src/components 目录下创建 index.ts 文件,注册 components 文件夹内部全部全局组件:
import SvgIcon from './SvgIcon/index.vue';
import type { App, Component } from 'vue';
const components: { [name: string]: Component } = { SvgIcon };
export default {
install(app: App) {
Object.keys(components).forEach((key: string) => {
app.component(key, components[key]);
})
}
}
在 main.ts 入口文件引入 src/components/index.ts 文件:
import gloablComponent from './components/index';
app.use(gloablComponent);
集成 sass
使用scss语法,需要加上lang="scss":
<style scoped lang="scss"></style>
在 src/styles 目录下创建一个 reset.scss 文件,清除默认样式:
body,
div,
p,
h1,
h2,
h3,
h4,
h5,
h6,
ul,
li,
dl,
dt,
a,
input,
button,
textarea,
select {
margin: 0;
padding: 0;
outline: none;
}
html,
body {
font-family: Helvetica Neue, Helvetica, Arial, Microsoft Yahei, Hiragino Sans GB, Heiti SC, WenQuanYi Micro Hei, sans-serif;
}
a {
text-decoration: none;
}
ul,
li {
list-style: none;
}
input {
font: normal;
}
input:focus,
a:focus {
outline: none;
}
在 src/styles 目录下创建一个 index.scss 文件:
@import "reset.scss"
在 main.ts 入口文件引入:
import "@/styles/index.scss";
在 src/styles 目录下创建一个 variable.scss 文件:
// 以 $ 为前缀设置全局样式变量
// 例如:$color: red;
在 vite.config.ts 文件配置:
export default defineConfig((config) => ({
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
additionalData: '@import "./src/styles/variable.scss";',
},
},
},
}));
axios二次封装
安装依赖:
pnpm i axios
在 src 目录下创建 utils/request.ts:
import axios from "axios";
import { ElMessage } from "element-plus";
//创建axios实例
const request = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000
})
//请求拦截器
request.interceptors.request.use(config => {
return config;
});
//响应拦截器
request.interceptors.response.use((response) => {
return response.data;
}, (error) => {
//处理网络错误
let msg = '';
let status = error.response.status;
switch (status) {
case 401:
msg = "token过期";
break;
case 403:
msg = '无权访问';
break;
case 404:
msg = "请求地址错误";
break;
case 500:
msg = "服务器出现问题";
break;
default:
msg = "无网络";
}
ElMessage({
type: 'error',
message: msg
})
return Promise.reject(error);
});
export default request;
API接口统一管理
在 src 下创建 api 目录,根据项目业务创建目录并存放 index.ts 和 type.ts 文件:
例如:在 api/user 目录下创建 index.ts 和 type.ts 文件:
在 type.ts 文件导出TS类型:
export interface Result {
code: number;
info: string;
}
export interface PageInfo {
pageNum: number;
pageSize: number;
size: number;
total: number;
pages: number;
}
在 index.ts 文件管理请求:
import { Result } from '../type';
import request from '@/utils/request';
// 请求路径枚举类
enum API {
USER_URL = '',
}
// 请求方法
const reqUser = () => request.get<any, Result>(API.USER_URL);
// 导出方法
export { reqUser };
路由配置
安装路由:
pnpm install vue-router
在 src 下创建 views 目录,根据路由创建目录及 index.vue 组件
在 src 下创建 router/routes.ts 文件:
import { RouteRecordRaw } from 'vue-router';
// 存放路由
export const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import(''),
name: '',
},
{
// 404 路由
path: '/404',
component: () => import(''),
name: '404',
},
{
// 任意路由
path: '/:pathMatch(.*)*',
redirect: '/404',
name: '',
},
];
在 src/router 下创建 index.ts 文件:
import { createRouter, createWebHashHistory } from 'vue-router';
import { routes } from './routes';
const router = createRouter({
history: createWebHashHistory(),
routes: routes,
scrollBehavior() { // 滚动行为
return {
left: 0,
top: 0,
};
},
});
export default router;
在 main.ts 引入:
import router from './router';
const app = createApp(App);
app.use(router);
app.mount('#app');
在 App.vue 添加代码:
<script setup lang="ts"></script>
<template>
<div>
<router-view></router-view>
</div>
</template>
<style scoped lang="scss"></style>
路由鉴权
在 src 目录下创建 permission.ts 文件:
import router from '@/router';
// 全局前置守卫
router.beforeEach((to, from, next) => {
next();
});
// 全局后置守卫
router.beforeEach(() => {});
在 main.ts 文件引入:
import "./permission";
安装 nprogress 进度条:
pnpm install nprogress
安装类型声明:
pnpm i --save-dev @types/nprogress
在 permission.ts 添加:
import router from '@/router';
import nprogress from 'nprogress';
import 'nprogress/nprogress.css';
// 隐藏右上角圆圈
nprogress.configure({
showSpinner: false,
});
// 全局前置守卫
router.beforeEach((to, from, next) => {
nprogress.start();
next();
});
// 全局后置守卫
router.beforeEach(() => {
nprogress.done();
});
配置仓库
安装依赖:
pnpm i pinia
在 src 目录下创建 store/index.ts 文件:
// 主仓库
import {createPinia} from "pinia";
const store = createPinia();
export default store;
在 main.ts 引入:
import store from './store';
app.use(store);
在 store 目录下创建 modules 目录,存放子仓库
在 modules 目录下创建 types/type.ts 存放仓库返回类型:
export interface XXXState {
}
创建子仓库:
// src/modules/xxx.ts
import { defineStore } from 'pinia';
import type { XXXState } from './types/type';
const useXXXStore = defineStore('xxx', {
state: (): XXXState => {
return {};
},
actions: {},
getters: {},
});
export default useXXXStore;
项目配置
在 src 下创建 setting.ts 文件:
export default {
title: ""
}
配置代理
在 vite.config.ts 文件添加配置:
import { defineConfig, loadEnv } from 'vite';
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd());
return {
server: {
port: 9527,
proxy: {
[env.VITE_APP_BASE_API]: {
target: env.VITE_SERVER,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
};
});