创建基本项目
确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令:
# npm 6.x
npm create vite@latest vite-vue-js-template --template vue
# npm 7+, extra double-dash is needed:
npm create vite@latest vite-vue-js-template -- --template vue
# yarn
yarn create vite vite-vue-js-template --template vue
# pnpm
pnpm create vite vite-vue-js-template --template vue
这一指令将会安装并执行 create-vite,它是一个基本模板快速启动项目工具。 在项目被创建后,通过以下步骤安装依赖并启动开发服务器:
# 打开项目
cd <your-project-name>
# 安装依赖
npm install
# 启动项目
npm run dev
Vite 基础配置
Vite 配置文件 vite.config.js 位于项目根目录下,项目启动时会自动读取。
本项目针对公共基础路径、自定义路径别名、服务器选项、构建选项等做了如下基础配置:
import { defineConfig } from 'vite';
import { resolve } from 'path';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
base: './',
plugins: [
vue(),
],
resolve: {
alias: {
'@': resolve(__dirname, './src') ,
},
},
server: {
// 是否开启 https
https: false,
// 端口号
port: 3000,
// 监听所有地址
host: '0.0.0.0',
// 服务启动时是否自动打开浏览器
open: true,
// 允许跨域
cors: true,
// 自定义代理规则
proxy: {},
},
build: {
// 设置最终构建的浏览器兼容目标
target: 'es2015',
// 构建后是否生成 source map 文件
sourcemap: false,
// chunk 大小警告的限制(以 kbs 为单位)
chunkSizeWarningLimit: 2000,
// 启用/禁用 gzip 压缩大小报告
reportCompressedSize: false,
},
});
关于 Vite 更多配置项及用法,请查看 Vite 官网 vitejs.dev/config/ 。
规范目录结构
├── dist/
└── src/
├── api/ // 接口请求目录
├── assets/ // 静态资源目录
├── common/ // 通用类库目录
├── components/ // 公共组件目录
├── router/ // 路由配置目录
├── store/ // 状态管理目录
├── style/ // 通用样式目录
├── utils/ // 工具函数目录
├── views/ // 页面组件目录
├── App.vue
├── main.js
├── tests/ // 单元测试目录
├── index.html
├── jsconfig.json // JavaScript 配置文件
├── vite.config.js // Vite 配置文件
└── package.json
集成 Vue Router 路由工具
安装依赖
npm i vue-router@4
创建路由配置文件
- 在
src/router目录下新建index.js文件与modules文件夹
└── src/
├── router/
├── modules/ // 路由模块
├── index.js // 路由配置文件
关于路由表,建议根据功能的不同来拆分到 modules 文件夹中,好处是:
- 方便后期维护
- 减少 Git 合并代码冲突可能性
export default [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue'),
},
{
path: '/about',
name: 'about',
component: () => import('@/views/AboutView.vue'),
},
];
import { createRouter, createWebHistory } from 'vue-router';
import baseRouters from './modules/base';
const routes = [...baseRouters];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
scrollBehavior() {
return {
el: '#app',
top: 0,
behavior: 'smooth',
};
},
});
export default router;
根据路由配置的实际情况,需要在 src 下创建 views 目录,用来存储页面组件。
挂载路由配置
- 在
main.js文件中挂载路由配置
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
集成 Pinia 全局状态管理工具
安装依赖
npm i pinia
创建仓库配置文件
- 在
src/store目录下新建index.js文件与modules文件夹
└── src/
├── store/
├── modules/ // 仓库模块
├── index.js // 仓库配置文件
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 1,
}),
actions: {
accumulate() {
this.count++;
},
},
});
import { createPinia } from 'pinia';
const store = createPinia();
export default store;
export * from './modules/counter';
开发中需要将不同功能所对应的状态,拆分到不同的 modules,好处如同路由模块一样。
挂载Pinia配置
在 main.js 文件中挂载 Vuex 配置
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
import router from './router';
createApp(App).use(router).use(store).mount('#app');
集成 Axios HTTP 工具
安装依赖
npm i axios
请求配置
- 在
utils目录下创建request.js文件,配置好适合自己业务的请求拦截和响应拦截
└── src/
├── api // 接口
├── utils/
├── request.js // axios 请求库二次封装
import axios from 'axios';
// 创建请求实例
const instance = axios.create({
baseURL: '/api',
// 指定请求超时的毫秒数
timeout: 1000,
// 表示跨域请求时是否需要使用凭证
withCredentials: false,
});
// 前置拦截器(发起请求之前的拦截)
instance.interceptors.request.use(
(config) => {
/**
* 在这里一般会携带前台的参数发送给后台,比如下面这段代码:
* const token = getToken()
* if (token) {
* config.headers.token = token
* }
*/
return config;
},
(error) => {
return Promise.reject(error);
},
);
// 后置拦截器(获取到响应时的拦截)
instance.interceptors.response.use(
(response) => {
/**
* 根据你的项目实际情况来对 response 和 error 做处理
* 这里对 response 和 error 不做任何处理,直接返回
*/
return response;
},
(error) => {
const { response } = error;
if (response && response.data) {
return Promise.reject(error);
}
const { message } = error;
console.error(message);
return Promise.reject(error);
},
);
// 导出常用函数
/**
* @param {string} url
* @param {object} data
* @param {object} params
*/
export const post = (url, data = {}, params = {}) => {
return instance({
method: 'post',
url,
data,
params,
});
};
/**
* @param {string} url
* @param {object} params
*/
export const get = (url, params = {}) => {
return instance({
method: 'get',
url,
params,
});
};
/**
* @param {string} url
* @param {object} data
* @param {object} params
*/
export const put = (url, data = {}, params = {}) => {
return instance({
method: 'put',
url,
params,
data,
});
};
/**
* @param {string} url
* @param {object} params
*/
export const _delete = (url, params = {}) => {
return instance({
method: 'delete',
url,
params,
});
};
export default instance;
之后在 api 文件夹中以业务模型对接口进行拆分,举个例子,将所有跟用户相关接口封装在 User 类中,此类称作用户模型。
在 User 类中比如有登录、注册、获取用户信息等方法,如果有业务逻辑变动,只需要修改相关方法即可。
import { post } from '@/utils/request';
export default class User {
/**
* 登录
* @param {String} username 用户名
* @param {String} password 密码
* @returns
*/
static async login(username, password) {
return post('/login', {
username,
password,
});
}
}
把每个业务模型独立成一个 js 文件,声明一个类通过其属性和方法来实现这个模型相关的数据获取,这样可以大大提升代码的可读性与可维护性。
使用案例
- 在需要使用接口的地方,引入对应的业务模型文件,参考如下
<script>
import User from '@/api/user';
export default {
data() {
return {
username: '',
password: '',
};
},
methods: {
async login() {
const res = await User.login(this.username, this.password);
console.log(res);
},
},
};
</script>
集成 CSS 预处理器 Scss
安装依赖
cnpm install sass node-sass sass-loader style-loader -S
在vue.config.js中配置
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: []
}
}
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
runtimeCompiler: true,
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: []
}
}
})
如何使用
在 <style></style> 样式标签中引用 lang="scss" 即可。
<style lang="scss"></style>
全局样式
在 src/style 目录下创建 variables.scss 全局样式文件:
$cinnabar: #ff461f;
$indigo: #065279;
:export {
cinnabar: $cinnabar;
indigo: $indigo;
}
<template>
<div>
<div :style="{color: variables.cinnabar}">雷猴</div>
</div>
</template>
<script setup>
import variables from './styles/variables.module.scss'
console.log(variables)
</script>
样式穿透
在 Vue3 中,改变了以往样式穿透的语法,如果继续使用 ::v-deep、/deep/、>>> 等语法的话,会出现一个警告,下面是新的语法:
/* 深度选择器 */
:deep(selector) {
/* ... */
}
/* 插槽选择器 */
:slotted(selector) {
/* ... */
}
/* 全局选择器 */
:global(selector) {
/* ... */
}
至此,一个基于 Vite3 + Vue3 + Vue Router + Pinia + Axios + Scss 的前端项目开发环境搭建完毕。
接下来增加代码规范约束、提交规范约束、单元测试、自动部署等,让其更完善、更健壮。 随着前端应用逐渐变得大型化和复杂化,在同一个项目中有多个人员参与时,每个人的前端能力程度不等,他们往往会用不同的编码风格和习惯在项目中写代码,长此下去,势必会让项目的健壮性越来越差。解决这些问题,理论上讲,口头约定和代码审查都可以,但是这种方式无法实时反馈,而且沟通成本过高,不够灵活,更关键的是无法把控。不以规矩,不能成方圆,我们不得不在项目使用一些工具来约束代码规范。
本文讲解如何使用 EditorConfig + ESLint + Prettier + Stylelint 组合来实现代码规范化。
这样做带来好处:
- 解决团队之间代码不规范导致的可读性差和可维护性差的问题。
- 解决团队成员不同编辑器导致的编码规范不统一问题。
- 提前发现代码风格问题,给出对应规范提示,及时修复。
- 减少代码审查过程中反反复复的修改过程,节约时间。
- 自动格式化,统一编码风格,从此和脏乱差的代码说再见。
集成 EditorConfig 配置
EditorConfig 主要用于统一不同 IDE 编辑器的编码风格。
在项目根目录下添加 .editorconfig 文件:
# 表示是最顶层的 EditorConfig 配置文件
root = true
# 表示所有文件适用
[*]
# 缩进风格(tab | space)
indent_style = space
# 控制换行类型(lf | cr | crlf)
end_of_line = lf
# 设置文件字符集为 utf-8
charset = utf-8
# 去除行首的任意空白字符
trim_trailing_whitespace = true
# 始终在文件末尾插入一个新行
insert_final_newline = true
# 表示仅 md 文件适用以下规则
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# 表示仅 ts、js、vue、css 文件适用以下规则
[*.{ts,js,vue,css}]
indent_size = 2
很多 IDE 中会默认支持此配置,但是也有些不支持,如:VSCode、Atom、Sublime Text 等。
具体列表可以参考官网,如果在 VSCode 中使用需要安装
EditorConfig for VS Code插件。
集成 Prettier 配置
Prettier 是一款强大的代码格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言,基本上前端能用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。
安装依赖
npm i prettier -D
安装插件
Visual Studio Code 编辑器使用 Prettier 配置需要下载插件 Prettier - Code formatter 。
JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件,可直接使用 Prettier 配置。
创建Prettier 配置文件
Prettier 支持多种格式的配置文件,比如 .json、.yml、.yaml、.js等。
在项目根目录创建 .prettierrc.js 文件,并填入以下内容:
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾需要有逗号
trailingComma: 'all',
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// vue 文件中的 script 和 style 内不用缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf
endOfLine: 'lf',
// 格式化嵌入的内容
embeddedLanguageFormatting: 'auto',
// html, vue, jsx 中每个属性占一行
singleAttributePerLine: false,
};
集成 ESLint 配置
ESLint 是针对 EScript 的一款代码检测工具,它可以检测项目中编写不规范的代码,如果写出不符合规范的代码会被警告。
由此我们就可以借助于 ESLint 强大的功能来统一团队的编码规范。
安装依赖
-
ESLint- ESLint 本体 -
eslint-define-config- 改善 ESLint 规范编写体验 -
eslint-plugin-vue- 适用于 Vue 文件的 ESLint 插件 -
eslint-config-airbnb-base- Airbnb JavaScript 风格指南 -
eslint-plugin-import- 使用eslint-config-airbnb-base时必须安装的前置插件 -
vue-eslint-parser- 使用eslint-plugin-vue时必须安装的 ESLint 解析器
npm i eslint eslint-define-config eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue vue-eslint-parser -D
安装插件
Visual Studio Code 编辑器使用 ESLint 配置需要下载插件 ESLint 。
JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件。
创建 ESLint 配置文件
在项目根目录创建 .eslintrc.js 文件,并填入以下内容:
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
jest: true,
es6: true,
},
plugins: ['vue'],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
allowImportExportEverywhere: true,
ecmaFeatures: {
jsx: true,
},
},
extends: [
'eslint-config-airbnb-base',
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
],
rules: {
// 禁止使用多余的包
'import/no-extraneous-dependencies': 0,
// 确保在导入路径内一致使用文件扩展名
'import/extensions': 0,
// 确保导入指向可以解析的文件/模块
'import/no-unresolved': 0,
// 首选默认导出导入/首选默认导出
'import/prefer-default-export': 0,
// 要求使用 let 或 const 而不是 var
'no-var': 'error',
// 禁止使用 new 以避免产生副作用
'no-new': 1,
// 禁止变量声明与外层作用域的变量同名
'no-shadow': 0,
// 禁用 console
'no-console': 0,
// 禁止标识符中有悬空下划线
'no-underscore-dangle': 0,
// 禁止在可能与比较操作符相混淆的地方使用箭头函数
'no-confusing-arrow': 0,
// 禁用一元操作符 ++ 和 --
'no-plusplus': 0,
// 禁止对 function 的参数进行重新赋值
'no-param-reassign': 0,
// 禁用特定的语法
'no-restricted-syntax': 0,
// 禁止在变量定义之前使用它们
'no-use-before-define': 0,
// 禁止直接调用 Object.prototypes 的内置属性
'no-prototype-builtins': 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
'no-unneeded-ternary': 'error',
// 禁止重复模块导入
'no-duplicate-imports': 'error',
// 禁止在对象中使用不必要的计算属性
'no-useless-computed-key': 'error',
// 禁止不必要的转义字符
'no-useless-escape': 0,
// 禁用 continue 语句
'no-continue': 0,
// 强制使用一致的缩进
indent: ['error', 2, { SwitchCase: 1 }],
// 强制使用骆驼拼写法命名约定
camelcase: 0,
// 强制类方法使用 this
'class-methods-use-this': 0,
// 要求构造函数首字母大写
'new-cap': 0,
// 强制一致地使用 function 声明或表达式
'func-style': 0,
// 强制一行的最大长度
'max-len': 0,
// 要求 return 语句要么总是指定返回的值,要么不指定
'consistent-return': 0,
// 强制switch要有default分支
'default-case': 2,
// 强制剩余和扩展运算符及其表达式之间有空格
'rest-spread-spacing': 'error',
// 要求使用 const 声明那些声明后不再被修改的变量
'prefer-const': 'error',
// 强制箭头函数的箭头前后使用一致的空格
'arrow-spacing': 'error',
// 只强制对象解构,不强制数组解构
'prefer-destructuring': ['error', { object: true, array: false }],
},
});
关于更多配置项信息,请前往 ESLint 官网查看 ESLint-Configuring
创建 ESLint 过滤规则
在项目根目录添加一个 .eslintignore 文件,内容如下:
dist
node_modules
!.prettierrc.js
components.d.ts
auto-imports.d.ts
解决 Prettier 和 ESLint 冲突
本项目中的 ESLint 配置使用了 Airbnb JavaScript 风格指南校验,其规则之一是代码结束后面要加分号,而在 Prettier 配置文件中加了代码结束后面不加分号配置项,从而冲突了。
解决两者冲突问题,需要用到 eslint-plugin-prettier 和 eslint-config-prettier。
eslint-plugin-prettier将 Prettier 的规则设置到 ESLint 的规则中eslint-config-prettier关闭 ESLint 中与 Prettier 中会发生冲突的规则
最后形成优先级:Prettier 配置规则 > ESLint 配置规则
安装依赖
npm i eslint-plugin-prettier eslint-config-prettier -D
修改 ESLint 配置文件
修改 .eslintrc.js 文件,在 extends 中添加 plugin:prettier/recommended 规则(此规则一定要加在最后)。
module.exports = {
extends: [
'airbnb-base',
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended'
],
}