以下配置建立在此基础上:Vue-Cli 脚手架搭建 Vue 3 项目
git 默认是不区分大小的,因此当修改了文件名的大小写后 git 不会认为有修改,所以首先得配置 git 大小写敏感。
// 配置 git 大小写敏感
git config core.ignorecase false
vue 3 组件代码和 vue 2 有些差别,使用的语法提示和高亮插件也不一样,所以在 vue 3 项目中:
安装 Vue Language Features (Volar)
语法支持
安装 Eslint
代码风格校验
若项目中使用 TypeScript,则可以安装 TypeScript Vue Plugin (Volar)
vue 3 中更好的 ts 提示
禁用 Vetur
,若不禁用会有冲突(而在 vue 2 项目中要使用 Vetur
,禁用 Volar
)
module.exports = {
root: true,
env: {
node: true,
'vue/setup-compiler-macros': true,
},
extends: [
'plugin:vue/vue3-essential', // vue3 的配置文件
'standard',
'plugin:prettier/recommended',
],
parserOptions: {
parser: '@babel/eslint-parser',
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// 'object-curly-newline': [
// 'warn',
// {
// multiline: true, // 多行时每个key-value换行
// minProperties: 5, // 最少五个属性
// consistent: true, // 多行和单行样式保持一致
// },
// ],
'vue/no-v-html': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'any',
normal: 'any',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
// 组件名称单词的检测(vue 组件需要大驼峰命名,除去 index 之外,App 是默认支持的)
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index']
}
],
// Vue.js风格指南(https://cn.vuejs.org/v2/style-guide/)
// Vue组件排序
'vue/order-in-components': [
'warn',
{
order: [
'el',
'name',
'key',
'parent',
'functional',
['delimiters', 'comments'],
['components', 'directives', 'filters'],
'extends',
'mixins',
['provide', 'inject'],
'ROUTER_GUARDS',
'layout',
'middleware',
'validate',
'scrollToTop',
'transition',
'loading',
'inheritAttrs',
'model',
['props', 'propsData'],
'emits',
'setup',
'fetch',
'asyncData',
'data',
'head',
'computed',
'watch',
'watchQuery',
'LIFECYCLE_HOOKS',
'methods',
['template', 'render'],
'renderError',
],
},
],
// Vue属性排序
'vue/attributes-order': [
'warn',
{
order: [
'DEFINITION',
'LIST_RENDERING',
'CONDITIONALS',
'RENDER_MODIFIERS',
'GLOBAL',
'UNIQUE',
'TWO_WAY_BINDING',
'OTHER_DIRECTIVES',
'OTHER_ATTR',
'EVENTS',
'CONTENT',
],
alphabetical: true, // 字母顺序
},
],
},
}
方式一:在根目录下添加 prettier.config.js
文件
module.exports = {
printWidth: 300, // 单行长度
tabWidth: 2, // 缩进长度
useTabs: false, // 使用空格代替 tab 缩进
semi: false, // 句末使用分号
singleQuote: true, // 使用单引号
quoteProps: 'as-needed', // 仅在必需时为对象的 key 添加引号
jsxSingleQuote: false, // jsx 中是否使用单引号
trailingComma: 'none', // 多行时是否打印尾随逗号
bracketSpacing: true, // 在对象前后添加空格,例如: { foo: bar }
bracketSameLine: false, // 多行Dom元素的>是否放在最后一行的末尾,而不是单独放在下一行
arrowParens: 'always', // 单参数箭头函数参数周围使用圆括号,例如: (x) => x
htmlWhitespaceSensitivity: 'ignore', // 对HTML全局空白不敏感
vueIndentScriptAndStyle: true, // 对 vue 中的 script 及 style 标签缩进
endOfLine: 'lf' // 结束行形式
}
方式二:在 .eslintrc
文件中增加配置如下
module.exports = {
...
rules: {
...
'prettier/prettier': [
'warn',
{
singleQuote: true,
semi: false,
printWidth: 80,
trailingComma: 'none',
endOfLine: 'auto',
...
}
],
...
},
}
创建文件路径: src\utils\request.js
import axios from 'axios'
import store from '@/store'
import router from '@/router'
import { ElMessage } from 'element-plus'
// axios 初始化
const instance = axios.create({
baseURL: '/',
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
// axios 请求拦截器
instance.interceptors.request.use(
(config) => {
// 1. 获取用户信息对象
const token = localStorage.getItem('USER_TOKEN_KEY') || ''
// 2. 判断是否有token
if (token) {
// 3. 设置token
config.headers.Authorization = profile.token
}
return config
},
(err) => {
return Promise.reject(err)
}
)
// axios 响应拦截器
instance.interceptors.response.use(
(res) => {
// 约定只接受 code 为 200,即非 200 为响应不成功
if (res.data?.code !== 200) {
// 若响应不成功,可以选择在此处进行全局信息提示如下:
ElMessage.error(res.data?.msg || '服务繁忙,请稍后重试')
return Promise.reject(res.data)
}
// 响应成功,则返回响应数据
return res.data
},
(err) => {
// 401 状态码,进入该函数(因为一般 401 即表示登录失效,需要进一步操作)
if (err.response && err.response.status === 401) {
// 1. 清空无效用户信息
localStorage.setItem('USER_TOKEN_KEY', '')
localStorage.setItem('USER_INFO_KEY', '')
// 2.1. 跳转到登录页
router.push('/login')
// 2.2. 跳转需要传参(当前路由地址)给登录页面,方便登录后直接返回到当前路由地址
// 举例:若当前路由地址为 '/user?a=10',则 $route.path='/user',$route.fullPath='/user?a=10'
// js模块中:router.currentRoute.value.fullPath 就是当前路由地址,router.currentRoute 是ref响应式数据
// const fullPath = encodeURIComponent(router.currentRoute.value.fullPath)
// encodeURIComponent 转换uri编码,防止解析地址出问题
// router.push('/login?redirectUrl=' + fullPath)
}
ElMessage.error('服务繁忙,请稍后重试')
return Promise.reject(err)
}
)
// 请求工具函数:请求地址,请求方式,提交的数据
const request = (url, method, data) => {
return instance({
url,
method,
// 1. 如果是 get 请求,需要使用 params 来传递(?a=10&c=10)
// 2. 如果不是 get 请求,需要使用 data 来传递
// method参数:get, Get, GET 转换成小写再来判断
[method.toLowerCase() === 'get' ? 'params' : 'data']: data
})
}
export default request
创建文件路径:src\store\index.js
(创建文件夹 src\store\modules
)
// 自动导入所有 vuex 模块,用于解决 vuex 命名冲突
import { createStore } from 'vuex'
const modules = {}
// 获取 modules 下所有以 .js 结尾的文件
const files = require.context('./modules', false, /\.js$/)
// 遍历添加到 modules 中
files.keys().forEach((key) => {
modules[key.replace(/(modules|\/|\.|js)/g, '')] = {
...files(key).default,
namespaced: true
}
})
export default createStore({
modules
})
创建文件路径:common.less
// 默认样式代码(覆盖原生 dom 自带的样式)
* {
box-sizing: border-box;
}
html {
height: 100%;
font-size: 14px;
}
body {
height: 100%;
padding: 0;
margin: 0;
min-width: 1280px;
}
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
form,
fieldset,
input,
textarea,
p,
blockquote,
address,
table,
th,
tr,
td {
padding: 0;
margin: 0;
}
a {
text-decoration: none;
outline: none;
cursor: pointer;
transition: none;
}
i {
font-style: normal;
}
input[type='text'],
input[type='search'],
input[type='password'],
input[type='checkbox'] {
padding: 0;
outline: none;
border: none;
-webkit-appearance: none;
&::placeholder {
color: #ccc;
}
}
img {
max-width: 100%;
max-height: 100%;
vertical-align: middle;
}
ul {
list-style: none;
}
#app {
user-select: none;
}
// 公用样式代码(方便页面复用的样式)
.container {
width: 1240px;
margin: 0 auto;
position: relative;
}
.ellipsis {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.ellipsis-line-two {
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.fl {
float: left;
}
.fr {
float: right;
}
.clearfix:after {
content: '.';
display: block;
visibility: hidden;
height: 0;
line-height: 0;
clear: both;
}